Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
81743b7
feat(smithers): seed durable workflow layer with autonomous advisor a…
pgebheim Jul 25, 2026
3cb4325
docs: add 'Pairs with Smithers (optional)' README section
pgebheim Jul 26, 2026
89ed67b
fix(rig-epic): child fan-out deadlock on dangling merge-<prev> depend…
pgebheim Jul 28, 2026
ce27e2f
feat(smithers): composable TaskFlow/EpicFlow fragments + inline compo…
pgebheim Jul 28, 2026
c1da275
fix(smithers): clear the 10 typecheck findings — CI now green
pgebheim Jul 28, 2026
ad3625a
refactor(smithers): rename rig-crank workflow to rig-loop (#35)
pgebheim Jul 28, 2026
b356f58
draft(rig-sync): spec ⇄ code reconciler (terraform loop for code)
pgebheim Jul 31, 2026
976bcfa
demo(rig-sync): notes-api sandbox — spec ⇄ code drift on a tiny HTTP API
pgebheim Jul 31, 2026
5645500
feat(rig-sync): SPEC + drift engine + report (M0 T1,T3,T4)
pgebheim Jul 31, 2026
976c7b3
feat(rig-sync): extractor validator + contract doc + plan-verb wiring…
pgebheim Jul 31, 2026
770f808
feat(rig-sync): drift -> reconciling drift-spec + report sink (M1 T6,T7)
pgebheim Jul 31, 2026
98ab52f
feat(rig-sync): durable Smithers reconcile workflow + wiring (M2 T9,T…
pgebheim Jul 31, 2026
6ac4ff6
fix(rig-sync): gate lanes on the approval decision, not as Approval c…
pgebheim Jul 31, 2026
dd442cf
fix(rig-sync): slugify unit id for worktree branch/path + node ids
pgebheim Jul 31, 2026
1891d50
chore: add rig-plan + rig-sync to DEFAULT_SKILLS (installable downstr…
pgebheim Jul 31, 2026
054059d
fix(rig-sync): default the reconcile workflow to Claude, drop agents.…
pgebheim Aug 1, 2026
714c857
Merge pull request #45 from agent-rig/worktree-rig-sync-skill
pgebheim Aug 1, 2026
fb9bda8
fix(workflows): guard ctx.input arrays for 0.32 UI discovery (#59)
pgebheim Aug 1, 2026
7624700
feat(gateway): robust Smithers Gateway manager + Tailscale exposure (…
pgebheim Aug 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions .rig/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"$schema": "../rig.schema.json",
"project": {
"name": "rig",
"repo": "agent-rig/rig"
},
"runtime": {
"packageManager": "bun"
},
"test": {
"command": "bun test",
"requiresDatabase": false
},
"sourceScope": ["skills", "scripts", "smithers", "docs"],
"vcs": {
"defaultBranch": "main",
"baseRef": "origin/main",
"branchConvention": "{user}/{ticket}-{slug}",
"protectedBranchMergeQueue": false
},
"tracker": {
"provider": "github",
"team": "agent-rig",
"githubIntegration": false,
"shapeLabels": { "epic": "epic", "sprint": "sprint" },
"board": {
"owner": "agent-rig",
"projectNumber": 1,
"statusField": "Status",
"statusOptions": { "todo": "Todo", "inProgress": "In Progress", "done": "Done" },
"closingKeyword": "Closes"
}
},
"review": {
"patternsFile": ".claude/REVIEWER.md",
"bot": "none",
"maxRounds": 5
},
"agents": {
"architect": "rig-architect",
"coder": "rig-coder",
"reviewer": "rig-reviewer",
"qa": "rig-qa",
"debugger": "rig-debugger"
}
}
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,34 @@ project. Skills read it at runtime — they never hardcode your specifics. See
the machine-readable schema, and `rig.config.example.json` for a filled-in
reference (the origin project's own values).

## Pairs with Smithers (optional)

[Smithers](https://smithers.sh) is a **separate, complementary** tool — a
crash-resistant AI-workflow orchestrator (multi-step runs persisted to SQLite,
resumable after a crash, human-approval gates, harness-agnostic agent configs).
The two stack cleanly:

- **Rig** = the *conventions*: skills, agents, CI, a config profile. Lightweight,
copy-in, runtime-agnostic.
- **Smithers** = the *durable engine*: a CLI + `.smithers/` runtime that *runs*
long, multi-step agent workflows with checkpoint/resume.

Rig deliberately does **not** bundle or auto-install Smithers (it wants its own
JS-runtime deps — that would break Rig's lightweight, runtime-agnostic promise).
Adoption is a manual, opt-in step for projects that want the durable workflow
layer:

```bash
bunx smithers-orchestrator init --yes # NB: the package is smithers-orchestrator, not smithers
```

Rig ships starter workflows under [`smithers/`](smithers/) (durable `rig-task`
and `rig-epic` runs, plus example agent configs) that you can copy into your
`.smithers/` once it's scaffolded. Set Smithers' `repoCommands.test` to match
your Rig `test.command` so the two share one source of truth. Smithers' own
agent skill and Rig's skills coexist at different layers (task procedures vs.
driving the orchestration CLI).

## Design principles

- **Config over forking.** A parameterizable skill reads `.rig/config.json`
Expand Down
12 changes: 12 additions & 0 deletions demos/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# rig demos

Small, self-contained sandboxes for trying a rig skill without wiring it into a
real project. Each demo ships its own `.rig/config.json` and copies in just the
skill(s) + agent(s) it needs under `.claude/`, so you can `cd` in and run.

| Demo | Skill | What it shows |
|------|-------|---------------|
| [`notes-api`](./notes-api) | `rig-sync` | Spec ⇄ code drift on a tiny HTTP API — `plan` finds it, `apply` (report sink) proposes the fix. |

These are illustrative, not production scaffolding. In a real repo you'd install
rig with `rig-onboard` instead of copying skills in by hand.
60 changes: 60 additions & 0 deletions demos/notes-api/.claude/agents/rig-architect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
---
name: rig-architect
description: Tech lead agent for planning, architecture decisions, and ticket creation. Use when breaking down a feature, designing a solution, evaluating tradeoffs, or creating implementation tickets. Invoke before coding begins on any non-trivial change.
model: opus
tools: Read, Bash, Grep, Glob, WebFetch, WebSearch, TodoWrite, LSP
---

You are the tech lead. Your job is to think before code is written.

## Your responsibilities

- Read requirements (specs, product docs, user intent) and translate them into a concrete implementation plan.
- Identify what already exists vs. what needs to be built.
- Design solutions that fit the existing architecture — don't invent new patterns where an established one already fits.
- Create well-scoped tickets with clear acceptance criteria and implementation steps.
- Flag risks, dependencies, and open questions before work starts.
- Decide which tickets can be parallelized and which must be sequential.

## Non-negotiables you enforce

- **Respect the project's established runtime and toolchain.** Don't
propose swapping the language runtime, package manager, build system,
database, or auth layer for something else. Design within them.
- New work lands in the project's established source layout (see
`sourceScope` in `.rig/config.json`) — don't scatter a parallel tree.
- **Extraction over duplication.** Before proposing a new abstraction,
module, or service, find the existing functionality it overlaps and
design to *extend or extract* it — never a parallel implementation of
something the codebase already does. Naming a competing abstraction is
a design smell.
- **Surface the decision.** When a design establishes or changes how a
core-domain concept works (how money/spend is tracked, how tenancy is
scoped, how auth flows), say so explicitly in the ticket and flag it
for architecture review; don't let a foundational decision hide inside
feature tickets.

## How you work

1. Read the relevant files before forming opinions. Use the project's
own docs (agent/README/spec files) for context.
2. Explore the affected code areas before designing changes — including a
search for existing functionality the change could reuse instead of
reimplement. For code navigation (find-references, go-to-definition),
prefer LSP tools over grep when available.
3. Create tickets through the project's configured tracker
(`tracker.provider` in `.rig/config.json` — Linear, GitHub
Issues, etc.). If the provider is `none`, deliver the plan as
structured Markdown instead.
4. Write ticket bodies with: goal, acceptance criteria, files to touch,
and ordered implementation steps.
5. For multi-ticket features, list the dependency order explicitly, and
record hard dependencies in the tracker's native blocked-by relation.

## Output style

- Lead with the decision or plan, not the reasoning.
- Use tables for tradeoff comparisons.
- Use numbered lists for ordered steps.
- Flag open questions with **Decision needed:**.
- Be direct about what you'd cut from scope.
190 changes: 190 additions & 0 deletions demos/notes-api/.claude/skills/rig-sync/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
---
name: rig-sync
description: "Keep a repo in sync with its spec — the terraform loop for code. Treats the spec as desired state and the code as actual state, computes the drift between them (both directions), and reconciles it. `plan` (default): read-only drift report — what the spec demands that the code lacks, and what the code has that the spec doesn't. `apply`: reconcile the actionable drift through a pluggable sink — an ephemeral Smithers workflow (default), a tracked milestone of tickets, or report-only — and it never edits product code directly. Triggers on: 'rig-sync', 'sync the repo to the spec', 'spec sync', 'spec drift', 'drift between spec and code', 'reconcile spec and code', 'is the code still in sync with the spec', 'what changed vs the spec', 'terraform for code', 'plan the spec drift'."
argument-hint: "[plan | apply] [spec-glob] [--section <name>] [--truth spec|code] [--sink workflow|backlog|report] [--yes] — default 'plan' (read-only drift report)"
---

# Spec ⇄ code reconciler

Treat the **spec as desired state** and the **code as actual state**, then run the
terraform loop over them:

- **`plan`** (default) — compute the **drift** between spec and code, in both
directions, and write a report. **Read-only** — it changes neither the spec nor
the code (the same posture as `/rig-review find`).
- **`apply`** — reconcile the actionable drift. rig-sync's `apply` is **not
"write code"** — code generated from a spec is non-deterministic and must be
reviewed. It routes the drift to a **pluggable sink** (below), each of which
keeps the review/gate; the only things `apply` writes itself are spec-side
artifacts (the projection, doc drift), which are safe.

Unlike `/rig-plan` — greenfield, spec → *initial* backlog with nothing to diff
against — rig-sync is **continuous**: it diffs the spec against the code you
already have and proposes only what reconciles the difference.

## Reconciliation sinks

Where `apply` sends the drift is a choice about **lifespan × audience**, not a
fixed pipeline. A ticket does two jobs — *dispatch* work to a worker, and
*govern/record* it for humans. For AI work the dispatch half is overhead (the
worker is autonomous), so tickets are not the default.

- **`workflow`** (default) — generate a **Smithers workflow** from the drift and
run it ephemerally: it does the reconciliation now, with its own approval gate,
durability, retries, and a live run record — governance without a permanent
ticket. Best for the continuous case, where board churn would be noise.
- **`backlog`** — create **one milestone** ("reconcile `<spec>` → drift vN") and
hand the drift to `/rig-plan`, so units land as tickets grouped under that
milestone (not loose issues). Best when the drift needs human prioritization,
scheduling, or cross-team visibility over time.
- **`report`** — write the drift + proposed units to files only; a human decides.

The **drift report is written regardless of sink** — it is the durable record of
intent that lets ephemeral execution be resumed or explained later.

## Configuration

Reads `.rig/config.json` (defaults in parentheses):

- `sync.specGlob` — the desired-state source: one file or a glob of spec/catalog
docs (default: first of `SPEC.md`, `specs/prd.md`, `docs/prd.md`).
- `sync.projection` — optional path to a **generated, machine-readable
projection** of the spec (e.g. `.rig/spec.lock.json` or `spec/`). The diffable
middle layer — the analogue of a terraform state file. When set, rig-sync
regenerates it from the spec and diffs *it* against code; unset, the agent
reasons prose-spec vs code directly (coarser).
- `sync.extractor` — a **project-supplied adapter** that enumerates the actual
surface of the code as structured JSON (see *The extractor adapter*).
Resolver: `.rig/rig-sync-extractor` if executable, else the value of this key,
else none. With no extractor, actual-state discovery is best-effort agent
reasoning over `sourceScope` — **say so** in the report.
- `sync.preserve` — globs of hand-maintained files/regions inside the projection
that are **never regenerated** — preserved verbatim; rig-sync only cross-checks
them and flags contradictions.
- `sync.truth` — default direction of truth when spec and code disagree:
`spec` (spec wins → code drift becomes work) · `code` (code wins → spec drift
becomes a doc update) · `ask` (default — report both, human decides).
- `sync.apply.sink` — default reconciliation sink: `workflow` (default) ·
`backlog` · `report`. `--sink` overrides per run.
- `sync.driftReport` — where the report is written (default `.rig/DRIFT.md`).
- Reused: `sourceScope` (areas the extractor/agent scans), `agents.architect`
(extraction + drift reasoning), `vcs.baseRef` (projection baseline for
re-runs), and — for the `backlog` sink only — `tracker.*` + `tracker.board`.

`apply` delegates: the `backlog` sink to `/rig-plan` (which fans out to
`/rig-epic` / `/rig-sprint` / `/rig-issue`); the `workflow` sink to Smithers. It
never invokes `/rig-task` to write code itself.

## The extractor adapter

Drift only generalizes if rig-sync doesn't hardcode what "a surface" is. The
project owns that, exactly like the `rig-tracker` adapter owns "a board." The
extractor is any executable that prints JSON to stdout:

```json
{
"surface": [
{ "kind": "topic", "id": "orders.filled", "role": "producer",
"owner": "services/matching", "ref": "services/matching/publish.ts:42" }
],
"invariants": [
{ "assert": "every topic has exactly one producer" }
]
}
```

Each element is keyed by `(kind, id)`. rig-sync diffs the code's `surface`
against the spec's expected surface (from the projection) on that key, staying
domain-agnostic: `kind` can be `topic`, `rpc`, `endpoint`, `table`, `flag`,
`cli-command` — whatever the extractor emits. `invariants` are project-declared
assertions rig-sync checks against the merged set.

## Arguments

`$ARGUMENTS` begins with an optional verb, then args:

- **`plan [spec-glob]`** (default) — drift report only.
- **`apply [spec-glob]`** — reconcile after approval.
- `[spec-glob]` — override `sync.specGlob`.
- `--section <name>` — restrict to one spec section/milestone (match a heading).
- `--truth spec|code` — override `sync.truth`.
- `--sink workflow|backlog|report` — override `sync.apply.sink`.
- `--yes` — skip the `apply` approval gate. Default is to STOP for review.

## Procedure

1. **Resolve** config, the spec source(s), and the extractor. If no spec is
found, ask. Read the whole spec (or just the `--section`).

2. **Build the desired-state projection — fresh context, `agents.architect`.**
When `sync.projection` is set, extract the spec's expected surface into the
projection format. **Do not invent**: record ambiguity as anomalies and
spec-internal contradictions in the report, never as invented surface.
**Preserve `sync.preserve` regions verbatim.** No projection configured → skip
and carry the spec forward as prose.

3. **Extract the actual state.** Run the extractor over `sourceScope`; capture
its `surface` + `invariants`. No extractor → `agents.architect` enumerates the
surface heuristically from `sourceScope`, marked **best-effort** in the report.

4. **Diff desired vs actual — both directions.** Classify every element:
- **missing** — in the spec, absent from the code → reconcilable *work*.
- **undocumented** — in the code, absent from the spec → a *spec* update, or
out-of-scope code to flag.
- **diverged** — present on both sides but attributes disagree → a decision,
resolved by `truth`.
Then check the extractor's `invariants` against the merged set; a violation is
a finding in its own right.

5. **Report — then STOP** (where `plan` ends). Write `sync.driftReport` and print
a summary: counts per class, invariant violations, and which side `truth`
favors for each diverged item. This is the terraform *plan*.

6. **Apply — on approval only, and never by editing product code.** Split the
drift: **missing** + spec-winning **diverged** are *work*; **undocumented** +
code-winning **diverged** are *spec/doc* fixes. Then, by sink:
- **`workflow`** → synthesize a scoped drift-spec and generate a **Smithers
workflow** that reconciles it — one lane per unit, each built through
`/rig-task` *inside the workflow* so the RED→GREEN→review gates still hold,
with a workflow-level approval before anything merges. Ephemeral: no
tickets. (Smithers unavailable → fall back to `backlog`, and say so.)
- **`backlog`** → create one milestone `reconcile <spec> → drift vN` and hand
the drift-spec to `/rig-plan`; the units land as tickets under that
milestone on the board.
- **`report`** → write the drift-spec + proposed units to `.rig/plan.md`.
For the *spec/doc* side (any sink): write the projection + a **proposed**
catalog/doc change (docs are safe) and flag it for human confirmation — never
silently rewrite the human spec.
**Refresh + gate.** Regenerate the projection (preserving `sync.preserve`).
For every unit whose contract drifted, reset its board/run gate no higher than
a *contract-re-verify* state so a stale acknowledgment can't ride along.

7. **Report + hand off.** Print what was produced — the workflow run (or the
milestone + ticket IDs + board link, or the plan file), plus which spec-side
files changed — and the next step. rig-sync's job ends at **reconciliation in
motion + an updated projection**, not at modified product code.

## Notes

- **Plan/apply, not auto-code.** `plan` is a read-only drift report; `apply`
routes drift to a sink that keeps a gate — never keystrokes into your source.
That boundary is what makes the gate meaningful, same as `/rig-plan` never
starting work and `/rig-review find` never editing.
- **Tickets aren't the default.** For AI work the dispatch half of a ticket is
overhead. The ephemeral `workflow` sink gives governance (approval + live run +
history) without permanent board churn; reach for `backlog` only when the drift
needs human scheduling or lasting cross-team visibility.
- **The record survives the run.** The drift report is written for every sink, so
ephemeral execution is still resumable and explainable after the fact.
- **Direction of truth is a human call.** rig-sync reports both directions and
defaults to `ask`; it auto-picks only under `sync.truth` / `--truth`.
- **The adapter is the seam.** Without `sync.extractor` this degrades to
best-effort agent reasoning — fine for a read, not authoritative. A crisp
extractor (a pub/sub registry, an OpenAPI surface, a schema catalog) makes
drift precise and portable.
- **Re-runnable / idempotent.** Match drift to existing work by surface
`(kind, id)` so a re-run proposes only *new* drift and won't duplicate a
workflow lane or a ticket already in flight.
- **Degrades.** No projection → prose vs code. No extractor → heuristic surface.
No Smithers → `workflow` falls back to `backlog`. `tracker: none` → `backlog`
falls back to `report`. Useful at every rung.
9 changes: 9 additions & 0 deletions demos/notes-api/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# generated by rig-sync runs
.rig/DRIFT.md
.rig/plan.md
.rig/spec.lock.json
node_modules/

# smithers scaffold (generate on demand with `smithers init`; never committed)
.smithers/
smithers.db*
19 changes: 19 additions & 0 deletions demos/notes-api/.rig/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"project": {
"name": "notes-api"
},
"sourceScope": ["src"],
"agents": {
"architect": "rig-architect"
},
"tracker": {
"provider": "none"
},
"sync": {
"specGlob": "SPEC.md",
"extractor": ".rig/rig-sync-extractor",
"truth": "ask",
"apply": { "sink": "report" },
"driftReport": ".rig/DRIFT.md"
}
}
Loading
Loading