feat(smithers): optional durable-orchestration integration (umbrella) - #28
Draft
pgebheim wants to merge 19 commits into
Draft
feat(smithers): optional durable-orchestration integration (umbrella)#28pgebheim wants to merge 19 commits into
pgebheim wants to merge 19 commits into
Conversation
pgebheim
force-pushed
the
feature/smithers
branch
from
July 26, 2026 06:29
e5f92f7 to
24d4b02
Compare
pgebheim
added a commit
that referenced
this pull request
Jul 28, 2026
…ness) (#34) main had no CI at all — the 26 scope-reviewer tests plus check-review-p1.ts, pr-review-labels.ts and the shell scripts were entirely ungated. This lands the same dev-only harness introduced on feature/smithers (#33), scoped to what main has today: - test job: bun test scripts/ (26/26) — now gates every push/PR to main. - typecheck job: skips gracefully here (no smithers/ layer yet) via the branch-universal guard; it activates automatically once the smithers/ layer merges to main via the umbrella (#28). Files are identical to feature/smithers' harness, so the umbrella merge reconciles them with no conflict. Dev-only — never vendored (install.sh copies only skills/, agents/, scripts/, smithers/ content). Claude-Session: https://claude.ai/code/session_01GHxxuWxnyaNz3cZkcxmnRM Co-authored-by: Paul Gebheim <86010+pgebheim@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rch gate Seed rig's second execution surface: Smithers workflow equivalents of the rig-epic/rig-task skills, so autonomous runs can enforce what skill prose only instructs. First pass of the workflow-layer upstreaming (#12). rig-epic gains an `advisor` input flag (#13): when set, the front-loaded `spec-direction` gate renders as a Fable (`providers.claude`) Task instead of a HumanTask — same node id + {proceed,direction} schema — so a kicked-off epic runs unattended to child PRs, halting with a blocked report on proceed=false instead of parking on a human. Trunk merge stays human. Includes workflows/{rig-epic,rig-task}.tsx, ui/{rig-epic,rig-task}.tsx, a reference agents.example.ts (machine-specific agents.ts is regenerated via `smithers init`, not vendored), and a README documenting the dual-surface parity contract and the remaining #12 work (install.sh vendoring adapter). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Es83TAgTGyhCG9z8rczB4z
Salvaged from the closed PR #9 (magic --with-smithers installer). Keeps the manual, opt-in Smithers story and points at the vendored smithers/ workflow layer (#26); drops the auto-scaffold that broke Rig's lightweight promise. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GHxxuWxnyaNz3cZkcxmnRM
…sOn (#27) Once a child merged, its lane — including its merge-<prev> node — is skipped via `continue`, but the next child still declared dependsOn:[merge-<prev>], pointing at an unmounted node → DEPENDENCY_DEADLOCK at every child boundary. The prior `break` guard only deferred the deadlock to the next re-render. Gate the dependency on `prevUnmerged`: depend on the predecessor's merge gate only while it is still in-flight; a merged predecessor means this child is already free to start, so no dependency is emitted. Fixes #27 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GHxxuWxnyaNz3cZkcxmnRM
…sition + rig-crank (#32) * feat(smithers): composable TaskFlow/EpicFlow fragments + inline composition + rig-crank Refactor the Smithers workflow layer from monolithic workflows + childRun Subflow fan-out into composable React fragments the parent renders INLINE: - flows/task-flow.tsx (TaskFlow) + flows/epic-flow.tsx (EpicFlow) — the graphs as fragments (no <Workflow>), taking a `tables` bag + `idPrefix` so they compose inline: one run, native deps, full time-travel, no childRun. Helpers taskSchemas/taskBag, epicSchemas/epicBag (optionally namespaced). - rig-task.tsx / rig-epic.tsx are now thin <Workflow> wrappers over the fragments. - EpicFlow runs each child as an INLINE TaskFlow (was a childRun Subflow). This supersedes the Subflow fan-out — and with it the dangling-`merge-<prev>` deadlock that #27/#31 patched, which the inline lanes sidestep structurally (render-gating, no cross-run dependsOn). - The advisor spec gate is now a composed <GatherAndSynthesize> (Fable synthesizer) on the advisor path; same node ids + schema as the human path. - rig-crank.tsx — NEW autonomous build loop: advisor-picks the next ready ticket, routes epic-vs-task to EpicFlow/TaskFlow inline, verifies via an evidence-based risk-probe gate, lands, loops (continueAsNewEvery) until the backlog is dry. - rig-delegation-spike.tsx — a spike evaluating Smithers' DelegationChain suite. - README: document the fragments, the tables-bag/idPrefix seams, rig-crank. Validated graph-clean across all workflows in the trial project. Follow-up: the prompt examples still carry the trial's domain flavor (pre-existing in the pack); a genericization pass can land separately. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ENct54EpFjBkVMecSGh4z * feat(install): --smithers vendors the Smithers workflow pack Opt-in flag copies smithers/{workflows,ui} + agents.example.ts + the pack README into <target>/.smithers/ (no-clobber, agent-agnostic). Never vendors agents.ts (machine-specific — regenerate via `smithers agents add`); the surrounding package comes from `smithers init`. Ticks off the install.sh + agents.ts items in the smithers/README TODO. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ENct54EpFjBkVMecSGh4z --------- Co-authored-by: Paul Gebheim <86010+pgebheim@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Resolves every error the new typecheck job surfaced. All fixes preserve runtime behavior; none weaken the check. - rig-crank.tsx: remove duplicate `result: resultSchema` key (TS1117 — a copy-paste leftover; `result` is already registered with `input` above). Narrow `pick` before use (Boolean(pick) && … doesn't narrow → guard inside Boolean(…)). - rig-epic.tsx: read the dynamic-registry keys childRun/epicResult through a `Record<string, any>` alias. These are registered at runtime via epicSchemas()/taskSchemas() (typed Record<string, any>), so they're spread- only and not statically visible on `outputs` — same treatment epicBag()/ taskBag() already give their `outputs` param. NOT a runtime bug. - epic-flow.tsx: annotate the 5 render-prop params `(d)` -> `(d: any)`, matching task-flow.tsx's existing convention (smithers' <Task> doesn't infer `d` from `deps`). Verified locally: tsc --noEmit exits 0; scripts/ suite 26/26. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GHxxuWxnyaNz3cZkcxmnRM
'crank' was obscure; 'loop' names what it is — the autonomous backlog-draining build loop (advisor-picks the next ready ticket, builds via rig-task, verifies with backpressure, lands, repeats until the backlog is dry). Chosen over rig-run (collides with smithers' up/run verbs) and rig-watch (implies passive monitoring; this actively builds/lands). - smithers/workflows/rig-crank.tsx -> rig-loop.tsx (git rename) - <Workflow name>, Loop node id, header/display-name/description updated - all cross-references in README + sibling workflows/flows updated - metaphor uses of 'the crank' reworded so nothing orphaned remains install.sh needs no change (vendors via the rig-*.tsx glob). Verified: tsc --noEmit exits 0; scripts/ suite 26/26. Claude-Session: https://claude.ai/code/session_01GHxxuWxnyaNz3cZkcxmnRM Co-authored-by: Paul Gebheim <86010+pgebheim@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
pgebheim
force-pushed
the
feature/smithers
branch
from
July 29, 2026 18:33
8d26e4d to
ad3625a
Compare
A design-draft skill: treat the spec as desired state and code as actual state, compute bidirectional drift (plan, read-only), then reconcile via a pluggable sink — an ephemeral Smithers workflow (default), a tracked milestone of tickets, or report-only. Never edits product code directly; work lands through gated rig-task/rig-plan or a Smithers workflow lane. Modeled on rig-plan; extractor is a project-supplied adapter (rig-tracker pattern). Not wired into DEFAULT_SKILLS — proposal for discussion. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GHxxuWxnyaNz3cZkcxmnRM
demos/ is a home for try-it sandboxes. notes-api ships a spec, Express-style code with three planted drifts (missing DELETE, PUT<->PATCH divergence, undocumented GET /health), a real grep-based extractor adapter, and the rig-sync skill + rig-architect agent copied in so /rig-sync plan runs in place. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GHxxuWxnyaNz3cZkcxmnRM
- SPEC.md for rig-sync (plan/apply, durable Smithers workflow sink, extractor adapter) — the backlog on agent-rig board #1. - T1: sync.* config block in schema + example. - T3: scripts/rig-sync.ts computeDrift — directional bidirectional diff by (kind,id): missing / undocumented / diverged / aligned + carried invariants. 16 bun tests; reproduces the notes-api demo drift from the real extractor. - T4: renderReport + 'report' verb -> terraform-plan-style .rig/DRIFT.md. - Dogfood: .rig/config.json points rig at agent-rig board #1. Refs #46 #48 #49 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GHxxuWxnyaNz3cZkcxmnRM
… (M0 T2,T5) - T2: validateSurfaceDoc + 'validate-extractor' verb (rejects malformed adapter output with exit 1); docs/rig-sync.md contract + reference extractor; validated against the real notes-api extractor. 23 bun tests. - T5: SKILL plan verb wired to scripts/rig-sync.ts (extractor resolver -> validate -> desired surface -> diff/report -> STOP). Corrected the apply/ workflow wording to the durable, parameterized Smithers workflow model (run once, drift as input; never make-workflow; engine-agnostic). M0 complete (T1-T5). Refs #47 #50 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GHxxuWxnyaNz3cZkcxmnRM
- T6: driftToUnits — routes each drift item by truth (missing=work always; diverged/undocumented -> work|doc|decision by spec|code|ask). - T7: renderDriftSpec + 'spec' verb -> the report sink's .rig/plan.md; also the input /rig-plan decomposes for the backlog sink (T8, SKILL-wired). 28 bun tests. Refs #51 #52 #53 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GHxxuWxnyaNz3cZkcxmnRM
…10,T11) - T9: smithers/workflows/rig-sync.tsx — parameterized reconcile workflow (driftReport units as INPUT, not regenerated): verify-drift -> plan gate -> per-unit Worktree + coder/reviewer Ralph lanes -> merge gate -> MergeQueue -> final-verify. Renders under `smithers graph` (verified with real notes-api drift input). Authored once, run many; engine-agnostic (agents from the target project's smithers/agents.ts). - T10: 'workflow-input' verb -> deterministic drift -> the workflow's --input. - T11: engine-agnostic docs (docs/rig-sync.md). M2 complete. Refs #54 #55 #56 #57 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GHxxuWxnyaNz3cZkcxmnRM
…hildren Running the reconcile workflow end-to-end surfaced a real bug: nesting the per-unit lanes + merge-gate + final-verify as CHILDREN of <Approval> did not schedule them — the run finished right after plan-gate approval (only verify-drift + plan-gate executed). Switched to rig EpicFlow's convention: Approval is a decision node, and subsequent steps render conditionally on ctx.outputMaybe(gate)?.approved. Verified: after approval the coder/reviewer lanes now dispatch (impl-* + review-* nodes go live). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GHxxuWxnyaNz3cZkcxmnRM
Second run-only bug: unit ids like 'DELETE /notes/{id}' were used verbatim as
the git branch (rig-sync/DELETE /notes/{id}) and worktree path — invalid ref
(spaces, slashes, braces), so every lane failed at worktree creation (12
retries, ~18min, zero coding). Added slug() and applied to branch/path + node
ids. Verified: the worktree now creates and the coder agent runs.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GHxxuWxnyaNz3cZkcxmnRM
…eam) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GHxxuWxnyaNz3cZkcxmnRM
…ts dependency Downstream testing surfaced two real problems: (1) smithers init generates Codex/Fable-first agent pools that most Claude users don't have, and rig-sync inherited them by importing ../agents; (2) editing agents.ts to fix that fights Claude Code's bg-isolation. Both vanish if the workflow doesn't touch agents.ts: the two seats (coder, reviewer) now default to ClaudeCodeAgent inline, so it runs out of the box with no agents.ts to configure. Still multi-modal — swap the seats for your own agents.ts pools. Renders under smithers graph with no agents.ts present. Docs/SKILL wording corrected (Claude default, overridable). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GHxxuWxnyaNz3cZkcxmnRM
feat(rig-sync): spec ⇄ code reconciler — M0–M2 (plan + drift engine + durable Smithers sink)
* fix(workflows): guard ctx.input arrays for 0.32 UI discovery Smithers 0.32's gateway renders each workflow's <UI> at startup to discover its views, calling the component with an empty `ctx.input` (schema defaults are NOT applied during discovery). rig-sync and rig-loop both call an array method on an input field at module top — `ctx.input.units.filter(...)` and `ctx.input.built.join(...)` — so discovery throws `TypeError: undefined is not an object`, the UI fails to register, and the run's custom UI never renders in the monitor. Guard both with `?? []`, which restores exactly the schema's own `.default([])` during discovery and is a no-op at run time. Verified against a 0.32 gateway: the two "workflow UI discovery render failed" warnings are gone and both UIs register. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TMUG7KT32tVitdDTjjyrgE * fix(rig-sync): type the `key` prop on the Unit lane component `<Unit key={u.id} …>` is rendered in `work.map(...)`, but Unit is a plain function component whose props type didn't declare `key`. Under the smithers JSX types, `key` isn't auto-injected for function components (built-ins like <Task>/<Sequence> carry it), so it was rejected as an excess prop — TS2322 at the call site. Declaring `key?: string` on Unit's props keeps the list-identity key and makes `typecheck smithers/` pass. Pre-existing failure on feature/smithers, surfaced independently of the UI-discovery guard. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TMUG7KT32tVitdDTjjyrgE --------- Co-authored-by: Paul Gebheim <86010+pgebheim@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
) * feat(gateway): robust Smithers Gateway manager + Tailscale exposure Add scripts/smithers-gateway.sh — an idempotent manager for the Smithers workspace Gateway that publishes it to a tailnet for token-free browser access — plus a `gateway` config block in rig.schema.json and the example. Why: the Gateway's token auth is Authorization-header only, so a token-gated UI is unreachable from a browser (no query-param / cookie / Basic fallback). The manager runs the Gateway on loopback (no token needed) and publishes it over HTTPS via `tailscale serve`, making Tailscale device identity the auth boundary while the Gateway stays unauthenticated on 127.0.0.1. It exports SMITHERS_GATEWAY_TRUST_ANY_HOST=1 so the tailnet DNS name in the Host header isn't rejected as a DNS-rebinding attempt. Commands: up (default) / down / restart / status / url / discover. - Auto-discovers Tailscale (ip, MagicDNS name, HTTPS certs) and picks the mode (auto -> tailscale-serve when available, else loopback). - Liveness is an HTTP console probe, not `smithers gateway status` (which can report running:false for a serving, manually-started Gateway). - Idempotent; graceful fallback to loopback when Tailscale is absent; explicit `--insecure` opt-in for direct tailnet-IP binding. Config: gateway.{port,mode,servePort,trustAnyHost}. install.sh already vendors scripts/* into <target>/.claude/scripts/, so no installer change is needed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TMUG7KT32tVitdDTjjyrgE * feat(rig-sync): bring up a reachable UI after launching the workflow sink Wire scripts/smithers-gateway.sh into rig-sync apply's `workflow` sink: after `smithers up ... --input`, run `smithers-gateway.sh up` so the durable run gets a browser-reachable console (loopback Gateway + Tailscale HTTPS when present) and report the console URL alongside the run id. Document the `gateway.*` config in the skill's Configuration section. rig-sync is the only skill that launches a durable Smithers run (rig-epic/rig-task are agent-orchestrated and don't `smithers up`), so this is the one apply flow that needs the wiring. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TMUG7KT32tVitdDTjjyrgE --------- Co-authored-by: Paul Gebheim <86010+pgebheim@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Long-running integration branch for optional Smithers durable-orchestration support in Rig. Children merge into
feature/smithers; this PR is the review gate intomain. Kept as a draft until the verification-hardening work is complete.Tracking umbrella: #11
Landed so far
smithers/(durablerig-task/rig-epicruns, UIs, example agent configs, autonomous advisor arch gate).Design decision
Rig does not bundle or auto-install Smithers. The
--with-smithersmagic installer (old #9) is dropped — Rig stays lightweight and runtime-agnostic; users who want the durable layer runbunx smithers-orchestrator initthemselves and copy in thesmithers/workflows. Only the README doc from #9 was kept.Remaining work (into this branch)
Squash-to-
mainonce the tracked set is green.