Are you the author? Sign in to claim
A software factory for Claude Code, driven in natural language: a conductor runs a ten-station line (spec, decompose, de
A software factory for Claude Code — an assembly line where every station has a deterministic gate, not just an inspector.
gated-downshift is a Claude Code plugin that turns "ask a model to change some code" into a
disciplined software factory. A feature flows down a ten-station assembly line — intake/spec →
decompose → design-record → plan → review → execute → verify → CI → merge — and five of those
stations carry a deterministic gate: a go/no-go calibre that makes a defect impossible to pass, not
an inspector who might catch it. One fail-safe hook enforces the whole chain — no executor runs until
every upstream station of its feature has passed. Because the reasoning is airtight before a line is
typed, the typing itself downshifts to a cheaper, faster model — so the rigor also trims your token
bill — and a per-station SPC board measures which calibre leaks defects, so the line improves on
evidence, not vibes.
Quick start · The assembly line · How it works · Usage · Cost control · Contributing
# 1. Add the marketplace and install
/plugin marketplace add AldemarHdez/gated-downshift
/plugin install gated-downshift@gated-downshift
# 2. Install the one dependency
pip install jsonschema
Now just say what you want in plain language — there are no commands to memorize:
add rate-limit headers to the orders endpoint
A non-trivial request like that is picked up by the conductor, which drives the whole loop — author → gate → cross-model review → downshift to the cheapest safe executor → verify — narrating each step and telling you what's next. No code is written until the plan passes the gate, and it pauses for a plain-language "go" only at the points that spend or are irreversible. You never type a command; you just describe the change, a feature ("build the checkout-tax feature"), a spike ("prototype a…"), a follow-up ("now make it blue"), or a question ("how are we doing?").
[!NOTE] Review the model tiers in
routing.jsonand the relative prices inpricing.jsonto match your setup.
Getting a model to write code is easy; getting it to write the right code, reliably, is not. Hand a
task straight to a model and it fills reasoning gaps on the fly and drifts. gated-downshift refuses to
let that happen: it forces the reasoning to be written down and checked before any code is
produced, and makes that check impossible to skip. Handing execution to a cheaper model is safe only
because the plan is airtight first — the efficiency is a byproduct of the discipline, not the goal.
[!IMPORTANT] The invariant, stated precisely: no executor runs until its plan (and its feature's whole upstream chain) passes the gate. Any failure or absence leaves the work on the expensive planning model — the pipeline never falls forward into unchecked cheap execution. It is fail-safe by construction. The orchestrating loop itself can still edit files directly — that path loses the savings, not the safety — and an opt-in edit gate (ADR-0004, in shadow) instruments exactly those direct edits so the residual gap is measured, and can later be enforced.
A feature flows down ten stations. A real factory isn't defined by its inspectors — it's defined by its fixtures and gauges: mechanical constraints that make a defect impossible to pass. Five stations here carry exactly that (a deterministic gate); the rest are cross-model judgment or a human gate. Just describe the feature in plain language; the conductor drives the whole line.
| # | Station | Artifact | Deterministic gate | Judgment |
|---|---|---|---|---|
| 0 | Intake / spec | docs/features · .planning/{f}/spec.json | spec_gate — actors, observable criteria, declared out-of-scope | grilling |
| 1 | Decompose | tasks/ · .planning/{f}/decompose.json | decompose_gate — DAG acyclic, dependency-ordered, every spec criterion covered | reviewer |
| 2 | Design record | docs/adr/ · docs/glossary.md | design_record_gate — ADR linked & present, domain terms in glossary | reviewer (skill software-architect-adr) |
| 3 | Plan | .planning/{id}/plan.json | plan_gate — Toulmin plan complete (the original gate) | — |
| 4 | Review | review-result.json | sha-bound APPROVE required | cross-model reviewer |
| 5 | Execute | the diff | — | cheap executor (the downshift) |
| 6 | Verify | gate-result.json | break_paths present + diverse (shadow) | cross-model verifier runs them |
| 7 | CI | — | ci_gate — gh run green/red | On-Call diagnoses red |
| 8 | Merge | — | — | human |
The load-bearing trick: one enforcement point, extended. The single PreToolUse hook denies an
executor spawn unless the feature's entire upstream chain passed — spec_gate ∧ decompose_gate ∧ design_record_gate ∧ plan_gate ∧ review. There is no hook-per-station (a Claude Code restriction, and
it would be fragile); there is one gate that checks the whole chain, so the fail-safe invariant holds
across the line. ci_gate is the post-execution exception — it blocks the done stamp, not a spawn.
A feature-less single change skips the upstream stations entirely — that's the single-change fast path (the conductor still drives it).
[!NOTE] The full-SDLC line is the design recorded in ADR-0001. Stations 0–7 and the SPC board ship today;
break_pathsruns in shadow (measured, not yet blocking); the factory-flow worktree + On-Call loop are the orchestration layer over the proven gates.
Zoom in on the per-task inner loop (stations 3–6 of the line above — plan → review → execute → verify, driven by the production pipeline). This is the gate/hook mechanism up close:
The per-task inner loop (stations 3–6): the Gate is the deterministic hook; incomplete/REVISE/FAIL loop back to the author, APPROVE downshifts to the cheap executor, PASS reaches Done.
Three checks stand between reasoning and a committed change:
Above trivial complexity there is a fourth, load-bearing rule: the gate refuses any plan whose acceptance criteria lack a failing-first test (RED→GREEN). So the executor always implements against a test that was red on today's code, and the verifier re-runs the cycle to confirm it went green for the right reason — not because the test was vacuous.
A skill can reason but may not fire; a hook always fires and cannot be reasoned around. Authoring is therefore a skill, enforcement is a hook — conflating the two is the anti-pattern this plugin avoids. That is exactly why test-first here is a gate rule, not advice: you cannot skip it.
PreToolUse hook denies an executor spawn unless the feature's entire upstream chain passed (spec ∧ decompose ∧ design-record ∧ plan ∧ review), and the plan itself must name a real task of the feature's DAG (task_id membership — a stranger plan can't ride a passing decompose). Dependency ORDER between tasks runs as a shadow rule (DEP_INCOMPLETE: recorded, not blocking) until its ROI is measured. One enforcement point, not a fragile hook-per-station — fail-safe across the whole line.max_tasks budget. Coverage is checked against the spec_criteria embedded in the decompose plan and cross-checked against spec.json itself (SPEC_CRITERIA_DRIFT), so a criterion dropped in transcription can't silently vanish from the guarantee. The gate validates structure; the reviewer judges the split. A shadow rule (TASK_TOO_LARGE) also flags any single task that owns more acceptance criteria than the tier's per-task budget — an atomicity/timeout signal that a task is too big to finish in one executor turn — surfaced by --check as advisory until it's measured and promoted.board.py --status renders a WIP board and per-station leak-rate (passed here, failed downstream) — the confusion matrix generalized across the line. Cross-task measurement, so "which calibre is too loose" is a number.gh run, capability-detected); red re-enters at the execute station.lint_clean: true in a repo with no linter is theater. The opt-in toolchain-setup skill detects (or asks) the language and offers the deterministic tools — linter, type checker, test runner, pre-commit — that turn a trusted gate check into a verified one. Tools are chosen dynamically per language (no catalogue to drift); only the stable role→check map is pinned, and the gate never reads it.PreToolUse hook that denies the cheap executor unless a validated plan passes.PreToolUse hook over Edit/Write that instruments direct edits by the orchestrating loop: armed via .planning/ENFORCE_EDITS, it logs (never blocks, v1) every edit that has no active plan or falls outside the plan's file_manifest. The retro surfaces the findings; promotion to blocking is a corpus-locked flip (ADR-0004). Inert by default.medium complexity up, the gate blocks any plan whose acceptance criteria lack a failing-first test (fails on today's code, passes after the change). Test-first is a deterministic gate rule here, not a convention a skill can forget to fire — and the cross-model verifier re-runs the cycle to catch tests that are green theater.medium complexity up, a plan must name the pattern(s)/best-practice it applies (SRP, DIP, DRY, hexagonal, guard clauses…) or the gate blocks. The reviewer validates the named patterns against a catalog and flags anti-patterns (god function, duplicated logic, hidden side effects…). Design intent becomes explicit and auditable, not implicit.warrant), so the cheap model never fills that inference in weakly.APPROVE bound to the plan's exact hash.LESSONS.md, and fed back to the author on the next session — automatically, via hooks..planning/targets.json. retro.py --mine-surprise turns those mis-calibrations into candidate corpus cases (bad/ from false positives, good/ from false negatives). The gate is never auto-mutated: candidates land in .planning/surprise/ for a reviewed PR, and the corpus (CI) locks any rule change. Tell the plugin in plain language that a block was wrong ("the gate was too strict here") and the gate-feedback skill records the false-negative signal for you — it never unblocks the plan, only feeds future calibration.warrant/file_manifest more cheaply and precisely; when absent it falls back to grep/git. Resolved by capability, not tool name (contracts/code-intelligence.md), so any provider works and none ships bundled — the plugin stays standalone. Which provider grounded each plan is logged and correlated against gate-block / REVISE / FAIL rates in the retro, so "does memory help?" becomes a measured number. The deterministic gate stays 100% offline.tests/plans/{good,bad}/ corpus is the definition of "ready-for-code": every good plan must pass, every bad plan must block.explore lane — an opt-in prototype tier below trivial that skips TDD, design patterns, the failure-path, the contract, and both cross-model review and verification, so spikes stay fast. The gate marks it prototype: true (not certified for production) and the retro tracks prototype slices separately. It is never reachable by the size floor (which only raises a tier), so it can't be used to dodge rigor — graduate a spike by re-authoring it at trivial+ before it ships.downshift-run skill (author → gate → cheap executor), only at the explore tier; production still goes through the production pipeline. Small follow-ups ("now make it blue") are handled by the plan-amend skill, which edits the current plan in place — cheap for a prototype, and a fresh sha-bound review for production.ui_surface routes to verifier-visual, which runs the app and asserts on the rendered output (screenshot / DOM / http) instead of only reading the diff — because for UI, green unit tests are not the feature working. It prefers a browser/preview MCP if one is present and falls back to running the app + curl; capability-detected, nothing bundled (contracts/runtime-verification.md).You talk; the plugin drives. There are no commands to learn. Say what you want in plain language
and the conductor (skills/downshift-conductor) routes it
to the right stage, narrates each step, and hands off to the next one — pausing for a plain-language
"go" only at the three real gates (approve the plan/decompose, spend on execution, merge), so nothing
spends silently. What you say and where it goes:
| Say this (plain language) | The conductor drives |
|---|---|
| "add rate-limit headers to the orders endpoint" | one production slice: author → gate → review → execute → verify |
| "build the checkout-tax feature" | the whole line: spec → decompose → design-record → per-task pipeline → CI → board |
| "do these three related changes together" | a batch: one review/verify amortized over the slices |
| "prototype a…" / "spike this" | the fast explore lane (cheap executor, no review/verify — not production) |
| "now make it blue" / "also handle the empty case" | amend the current plan in place |
| "that block was wrong" | record the false-negative calibration signal (still doesn't unblock) |
| "the test is failing" / a verify FAIL | evidence-first debugging → back to verify |
| "set up linting / types / pre-commit" | detect the language and offer the tools that make the checks real |
| "how are we doing? / what did that cost?" | the learning digest: calibration (FP/FN), ROI, per-station leak-rate |
Each stage ends by telling you what happened and what's next, so you never have to know the machinery. The deterministic gate still enforces on every execution regardless of how the flow was entered.
[!TIP] Each executor/verifier/reviewer declares its own
model:and Claude Code honours it — amodel: haikuagent runs on haiku even though the orchestrator runs on the expensive model. Do not pass amodeloverride or launch them as a fork, or everything drags back onto the expensive model and the savings vanish.
The plan is a Toulmin argument. The gate checks presence deterministically; soundness is judged by the cross-model reviewer and verifier — never by the hook.
| Toulmin element | Plan field |
|---|---|
| Claim | goal |
| Grounds | file_manifest + acceptance_criteria |
| Backing | contract |
| Warrant | warrant — why the changes achieve the goal |
| Qualifier | warrant_strength |
| Rebuttal | risk_register |
A weak warrant forces the executor up one tier. A strong warrant must cite a verifiable fact in
warrant_evidence or the gate blocks (WARRANT_STRONG_UNSUPPORTED) — the gate checks the citation is
present, the reviewer judges whether it is real.
The hook matches the Task tool (how subagents spawn). When the spawn targets an executor*
subagent, the hook binds it to a specific plan and denies unless that plan passes. Binding is
deterministic, in precedence order: plan_id in the spawn input → the pinned GD-PLAN: {plan_id}
marker line in the spawn prompt (the Task tool drops non-standard input keys, but the prompt always
survives — this is what lets every spawn in a batch bind to its own plan instead of all falling back
to the shared pointer) → plan_path → the .planning/CURRENT pointer written by the author skill. A
plan_id mismatch is rejected (closing the replay hole where a stale passing plan could authorize an
unrelated spawn), and each evaluation stamps a plan_sha256 into gate-result.json so any post-gate
mutation is auditable. Non-executor spawns pass through untouched.
The cross-model verifier is the most expensive step, so it does not run blindly:
verify_required is true only on real risk (warrant_strength: weak, an API/contract/MCP surface, or complexity >= complex).verifier-sonnet; medium+ use verifier-opus, always a different model than the executor.--waiver and every acceptance criterion pins a verify_command + expected, the verifier is exempted on API/complex slices (never on weak/architectural).plan_gate.py --check runs the gate deterministically so the expensive author never simulates the gate in-model.The plugin learns from its own prediction errors (its "surprise"), on both sides, and turns them into evidence — never into an automatic rule change.
gate-feedback skill runs --log-override. Without the second signal a gate can only ratchet
tighter forever — the override is what lets it loosen with evidence.retro.py --build reports the matrix — FP-rate, FN-rate, a per-period trend, and pass/fail
against a target in .planning/targets.json (e.g. {"false_positive_rate_max": 0.1}), so
"it improved" is a measured number, not a feeling. Runs automatically on every subagent stop.retro.py --mine-surprise converts each mis-calibration into a candidate corpus case in
.planning/surprise/ — bad/ from false positives, good/ from false negatives — each annotated
with what the current gate does and a triage note (a FAIL is not proof the plan was bad; it may be
an executor slip). Runs automatically too; the retro digest surfaces the results.tests/plans/ and adjusts the rule; the corpus in CI locks it. Evolution rides the
same regression discipline as every other rule — advisory learning (LESSONS.md) is free to change,
enforcement changes only through the corpus + human review.Recording an override does not unblock the current plan — the gate still blocks. To proceed, fix the plan; the override is a calibration signal for the future.
In-flight shadow rule — Occam / parsimony. OVERBUILT_UNJUSTIFIED flags a plan that creates more
new production modules than the tier's budget (routing.json max_new_files; tests/config/docs are
never counted) without a simplicity_justification. It ships in shadow (recorded in
shadow_failures, not blocking) so the retro can measure whether it predicts real verification FAILs
before it is promoted to a blocking rule via a corpus PR. The reviewer judges whether a justification
is real (YAGNI / premature-abstraction), the gate only counts + checks presence.
_deny in plan_gate.py return _allow while still writing gate-result.json. Observe what would have blocked. No behaviour change.tests/plans/{good,bad}/ until every good plan passes and every bad plan blocks with the expected rule._deny. The downshift is now live and safe.Per the official docs, the version in
plugin.json is the cache key: Claude Code re-fetches an installed plugin only when that version
changes, so pushing changes without a bump makes /plugin update say "already at the latest
version" — and the usual workaround is a tedious uninstall + reinstall. (Git tags are not consulted;
release.sh creates them purely for human release management.) Two ways to avoid the dance:
main, then update normally:
scripts/release.sh # patch bump (or: minor | major | X.Y.Z): bumps plugin.json + commits + pushes
# then, in Claude Code — no uninstall:
/plugin marketplace update gated-downshift
/plugin update gated-downshift@gated-downshift
claude --plugin-dir . # run from the repo root
python3 -m pytest tests/ # the corpus is the executable definition of "ready-for-code"
[!WARNING] Plugin subagents cannot carry their own
hooksfield (a Claude Code security restriction). That is fine here: the gate is a plugin-level hook inhooks/hooks.json, not attached to any subagent. If you vendor this into a single project instead, you may place the same hook in.claude/settings.json— same matcher, same script.
| Path | Role |
|---|---|
skills/downshift-conductor/ | Natural-language driver: routes plain-language intent to the right stage, narrates, and hands off — so you never type a command; pauses only at the three real gates |
skills/plan-author/ | Authors the ready-for-code plan (the reasoning half) |
skills/systematic-debugging/ | Evidence-first debugging method for test failures / verifier FAILs |
skills/gate-feedback/ | Natural-language bridge: records a developer's "that block was wrong" as the false-negative calibration signal (--log-override) |
skills/toolchain-setup/ · contracts/tool-roles.json · scripts/toolroles.py | Offers (opt-in) the deterministic tooling that makes checks REAL — detects/asks the language, picks the tool per role dynamically (no per-language catalogue); a stable role→gate-check map is pinned, the gate never reads it |
hooks/hooks.json → scripts/plan_gate.py | The deterministic plan gate + single-chain hook enforcement + the learning-loop hooks |
scripts/spec_gate.py · scripts/decompose_gate.py | Intake/spec and decompose station calibres (DAG + coverage), each with a free --check CLI |
scripts/ci_gate.py | CI station calibre — gh run green/red, capability-detected (--check, --record-ci) |
scripts/board.py | The SPC production board: WIP + per-station leak-rate (--status) |
agents/plan-reviewer-*.md | Cross-model, pre-spend soundness reviewers |
agents/executor-*.md | The cheap executors, one per tier |
agents/verifier-*.md | Cross-model, post-spend verifiers (also execute declared break_paths) |
scripts/retro.py | Distils the ledger into LESSONS.md (recurring blocks + gate calibration + Cost/ROI); --mine-surprise emits candidate corpus cases |
skills/downshift-pipeline/ · skills/factory-pipeline/ · skills/downshift-batch/ · skills/downshift-retro/ | The flow skills the conductor drives (single slice · full feature · batch · learning digest) |
skills/downshift-run/ · skills/plan-amend/ | The vibe lanes: explore-tier prototype loop, and in-place plan amendments |
contracts/plan-gate.schema.json · decompose-plan.schema.json · spec-plan.schema.json | The station contracts |
contracts/design-patterns.md | Best-practice / anti-pattern catalog the reviewer checks against |
contracts/code-intelligence.md | Capability catalog + provider map so the author prefers a code-memory MCP when present (grep/git fallback) |
docs/adr/ | Architecture Decision Records — starting with ADR-0001, the factory design |
routing.json · pricing.json | Complexity→tier routing (executor/verifier/reviewer + max_tasks) and relative model prices |
Contributions are welcome. The gate's behaviour is pinned by the corpus in tests/plans/, so:
python3 -m pytest tests/ — every good plan must pass and every bad plan must block.MIT © Aldemar Hernández
Rule enforcement plugin — save rules with natural language, enforce with 17 lifecycle hooks
Blocks dangerous git and shell commands from being executed by AI coding agents
One command to install 6 essential safety hooks in 10 seconds — zero dependencies
Give Claude Code memory that evolves with your codebase via hooks and LLM-compiled knowledge