feat(specify): bound the auto-sign panel with an editorial self-correction loop

The spec-skeptic auto-sign panel was one-shot: under /boss with
spec_auto_sign on, any panel BLOCK fell straight back to the human
sign-off pause. Because the panel is adversarial-by-design and rarely
returns a unanimous SOUND, /boss escalated to the user even for defects
the orchestrator could mechanically repair.

Partition the five lenses by what a BLOCK means and run Step 6 as a
bounded loop:

- Editorial (criterion, ambiguity, plan-readiness) — the spec is
  under-articulated; the orchestrator self-corrects in place, re-runs
  Step 4 + Step 5, and re-dispatches all five lenses.
- Design (scope-fork, grounding) — the ground is not settled; escalate,
  never self-correct.

Re-loop limit: <= 2 rounds, the 3rd unresolved editorial BLOCK
escalates (hard-coded N, matching implement-orchestrator's idiom).
INFRA_ERROR and exhausted budget escalate too. Auto-sign still requires
a fresh unanimous SOUND on a green objective gate.

Re-dispatching all five lenses every round is the backstop: an
editorial repair that launders an unsourced design decision is caught
by the re-run scope-fork / grounding jurors and escalates. A criterion
repair is bounded to supplying the worked evidence the criterion already
demands — not re-judging acceptance (architect drift item).

specify/SKILL.md owns the loop; spec-skeptic.md keeps the juror
lens-agnostic; boss/SKILL.md, README.md, docs/profile-schema.md and
docs/pipeline.md are synced to the new semantics.

closes #6
This commit is contained in:
2026-06-11 11:46:08 +02:00
parent cbd460e242
commit a87916e578
6 changed files with 111 additions and 42 deletions
+51 -12
View File
@@ -328,17 +328,50 @@ gate then depends on the profile slot `pipeline.boss.spec_auto_sign`:
`plan-readiness`. Carrier per dispatch: `spec_path` (absolute),
`iteration_scope`, and the single `lens`. Each juror tries to
*refute* the spec along its lens and returns `SOUND` / `BLOCK` /
`INFRA_ERROR`.
`INFRA_ERROR`. Auto-sign requires a **unanimous `SOUND`** — that
never changes. What the partition below adds is a bounded path to
*reach* that unanimity for defects the orchestrator can repair
without judging the spec.
| Panel outcome | Orchestrator action |
|---------------|---------------------|
The five lenses split by what a `BLOCK` actually means:
| Lens class | Lenses | A `BLOCK` means |
|------------|--------|-----------------|
| **Editorial** | `criterion`, `ambiguity`, `plan-readiness` | The spec is under-*articulated*: missing worked evidence, a requirement readable two ways, scope too broad for one plan. Repairable by editing the prose without settling any new design or fact. A `criterion` repair may only *supply or sharpen the worked evidence the criterion already demands* (paste the concrete before → after / user-facing code the spec implied) — it may **not** soften what the criterion requires or re-judge acceptance, and if the missing evidence cannot be shown without picking an un-settled design, that is a `scope-fork` in disguise: leave it, and the re-dispatched `scope-fork` juror escalates it. |
| **Design** | `scope-fork`, `grounding` | The *ground is not settled*: the sources do not resolve a load-bearing fork, or a load-bearing assumption is not ratified by a green test. The orchestrator may **never** settle these in the user's place. |
Resolve the panel against that split, in a bounded loop
(`round` starts at `0`):
| Panel outcome this round | Orchestrator action |
|--------------------------|---------------------|
| All five `SOUND` | **Auto-sign.** Commit the spec yourself (subject `spec: <cycle> <topic> (boss-signed)`), send the informational auto-sign notify (see `boss` § Notifications — names the capability in user terms and states the plan is proceeding and a reply vetoes), then proceed to Step 7. No pause. |
| Any `BLOCK` or `INFRA_ERROR` | **No auto-sign.** Fall back to the human sign-off pause; the notify carries which lens(es) blocked and their findings, so the user sees why it routed to them. |
| Any `INFRA_ERROR` | **Escalate.** Fall back to the human sign-off pause; the notify carries the failing dispatch. |
| Any **design**-lens (`scope-fork` / `grounding`) `BLOCK` | **Escalate.** No self-correction — this is exactly the judgement the gate refuses to make for the user. Fall back to the human sign-off pause, carrying *all* lens findings this round. |
| Only **editorial**-lens `BLOCK`(s), and `round == 2` | **Escalate.** The self-correction budget is spent. Fall back to the human sign-off pause, carrying the findings *and* the per-round correction history. |
| Only **editorial**-lens `BLOCK`(s), and `round < 2` | **Self-correct.** Edit the spec in place to address each editorial finding (each names its `§section` / offending text / defect). Then **re-run Step 4 self-review and re-dispatch Step 5 `grounding-check`** — the edit invalidated the prior `PASS` (see "Re-dispatch on any post-PASS edit"); if `grounding-check` does not return `PASS`, escalate. Increment `round` and re-dispatch all five lenses from the top of this table. |
The fall-back is not a failure — it is the panel correctly routing a
spec to the human who was going to review it anyway. A unanimous
`SOUND` is the *only* thing that lets the orchestrator sign;
"the spec looks good to me" never is.
**Re-loop limit: ≤ 2 self-correction rounds. The 3rd unresolved
editorial `BLOCK` escalates** — same shape as the per-task re-loop
limit in `implement-orchestrator`.
Three properties keep this loop honest, not a way to grind the panel
into submission:
- **The panel stays the sole arbiter.** The orchestrator never judges
the spec; it only edits in response to a juror's finding, and a
fresh panel re-adjudicates. "The spec looks good to me" still never
signs.
- **Re-dispatch is all five lenses, every round.** This is the
backstop. If an editorial repair silently *launders* an unsourced
design decision into the spec (resolving an ambiguity by picking an
un-settled fork), the re-run `scope-fork` / `grounding` jurors now
`BLOCK` on it — and a design `BLOCK` escalates. The only thing the
loop can converge to autonomously is a spec whose every lens is
`SOUND` *and* whose only repairs were editorial.
- **Escalation is the panel doing its job**, not a failure — it routes
the spec to the human who was going to review it anyway, the moment
a real design question surfaces or the budget runs out.
**Auto-sign commits on `main` are forward-only like every other
commit.** A later user veto is a forward correction (revert / revised
@@ -399,7 +432,7 @@ discipline `tdd` applies when behaviour is not test-specifiable.
| "Just polishing a wording after PASS, no need to re-dispatch" | The grounding-check report attests to specific bytes. A polish edit changes the bytes; the attestation no longer covers them. Re-dispatch is cheap. |
| "It's in a model doc / the design ledger, so it's canonical" | A `models` / RFC / proposal doc holds aspirational code — a target, not verified fact. Code lifted from a `status: aspirational` source must clear the Step-4 parse gate like any other block. |
| "In `/boss` with auto-sign on, this spec looks solid — I'll sign and move on" | Your sense that it looks solid is the exact signal the auto-sign gate refuses to trust. Signing requires all objective gates green AND a unanimous `spec-skeptic` panel. If you find yourself wanting to sign on confidence, that is the cue to run the gate, not to skip it. |
| "One lens blocked on something minor — I'll sign anyway since the rest passed" | The panel is unanimous-or-nothing by design. One `BLOCK` routes the spec to the human who was going to review it regardless. You do not get to overrule a juror; that is the whole point of replacing your eye with five independent ones. |
| "One lens blocked on something minor — I'll sign anyway since the rest passed" | The panel is unanimous-or-nothing by design. You never *sign over* a `BLOCK`. An editorial `BLOCK` you may *repair* (within the ≤ 2-round budget, with a full re-panel after); a design `BLOCK` routes to the human. Neither is overruling a juror that is the whole point of replacing your eye with five independent ones. |
## Red Flags — STOP
@@ -414,7 +447,10 @@ discipline `tdd` applies when behaviour is not test-specifiable.
- Editing the spec file after a Step 5 PASS without re-dispatching
- (auto-sign) About to sign a spec in the user's place on confidence —
without all objective gates green AND a unanimous `spec-skeptic`
panel; or overruling a single juror's `BLOCK`
panel; signing over any `BLOCK`; self-correcting a *design*-lens
(`scope-fork` / `grounding`) `BLOCK` instead of escalating; or
re-dispatching only the blocked lens rather than all five, or past
the 2-round self-correction budget
- (auto-sign) Running the panel before the objective gates are green,
or running it at all when `spec_auto_sign` is not enabled
- Jumping straight from spec to `implement` (must go via `planner`)
@@ -438,8 +474,11 @@ discipline `tdd` applies when behaviour is not test-specifiable.
dispatched in Step 6 five times in parallel (one per lens) ONLY under
a `/boss` session with `pipeline.boss.spec_auto_sign` enabled. Each
juror tries to refute the spec along its lens; a unanimous `SOUND` is
what lets the orchestrator sign in the user's place. Any `BLOCK`
falls back to the human sign-off pause.
what lets the orchestrator sign in the user's place. An editorial-lens
`BLOCK` (`criterion` / `ambiguity` / `plan-readiness`) is self-
corrected in a bounded loop (≤ 2 rounds, full re-panel each round); a
design-lens `BLOCK` (`scope-fork` / `grounding`), an `INFRA_ERROR`, or
an exhausted budget falls back to the human sign-off pause.
- **Ad-hoc dispatch.** The orchestrator MAY also ad-hoc dispatch
`../planner/agents/plan-recon.md` during Step 1 when the work enters
code territory not recently read; opt-in, not part of the standard
+14 -10
View File
@@ -23,16 +23,17 @@ eye. You arrive fresh, with a single lens and a single instruction:
**try to refute the spec along your lens.** You are not asked "is
this spec roughly fine?" — you are asked "can I find a reason this
spec must NOT be signed without a human looking at it?" If you can,
you `BLOCK`, and the auto-sign collapses back to a human sign-off
pause. Your loyalty is to the refutation, not to the orchestrator's
momentum.
you `BLOCK`, and auto-sign is denied this round — at worst routing the
spec to the human sign-off pause it would have hit anyway. Your loyalty
is to the refutation, not to the orchestrator's momentum.
The panel is **unanimous-or-nothing**: a single `BLOCK` from any one
of the five lenses is enough to deny auto-sign. So you do not need to
catch everything — only your own lens. Lean toward `BLOCK` when in
doubt; the cost of a false `BLOCK` is one human sign-off the user was
going to do anyway, while the cost of a false `SOUND` is an unreviewed
spec ratified into the pipeline.
doubt; the cost of a false `BLOCK` is, at worst, one human sign-off the
user was going to do anyway (and often just a cheap repair-and-re-panel),
while the cost of a false `SOUND` is an unreviewed spec ratified into
the pipeline.
## The lens you are assigned
@@ -133,13 +134,16 @@ it is the rubber-stamp the human sign-off was protecting against.
| Status | Meaning |
|--------|---------|
| `SOUND` | After an honest refutation attempt along this lens, no defect found. This juror permits auto-sign. |
| `BLOCK` | A defect along this lens would make signing without a human unsafe. Auto-sign is denied; specify falls back to a human sign-off pause and shows this report. |
| `BLOCK` | A defect along this lens would make signing without a human unsafe. Auto-sign is denied this round. What `specify` does next depends on your lens class — an editorial defect it may repair and re-panel, a design defect it routes to the human — but that is the orchestrator's call (defined in `../SKILL.md` Step 6), not yours. |
| `INFRA_ERROR` | The dispatch cannot complete (spec missing, no/invalid `lens`, workspace does not build for a `grounding` test-list, etc.). Auto-sign is denied; orchestrator falls back to human sign-off. |
There is no override path here and no "needs context": you are one of
five jurors and the panel is unanimous-or-nothing, so an
honest `BLOCK` simply routes the spec to the human who was going to
review it anyway.
five jurors and the panel is unanimous-or-nothing. Report the defect
you found along your lens, full stop — do not soften a `BLOCK` because
you guess the orchestrator will only repair it, and do not escalate
your own verdict to "this needs a human." How a `BLOCK` is handled is
decided in `../SKILL.md` Step 6 by lens class; your job is the honest
verdict, not its routing.
## Output format