feat(boss): bolder autonomous /boss — grounding-PASS auto-sign, rollback sandbox, reference issue

Retire the obligatory five-lens spec-skeptic auto-sign panel (worst case
18 agent runs per spec under /boss). The autonomous signature is now the
Step-5 grounding-check PASS alone — an independent fresh-context agent's
verdict against currently-green tests; a no-override BLOCK/INFRA_ERROR
routes to the human sign-off pause.

The judgement the panel applied moves upstream into specify Step 1.5,
whose stance inverts: a load-bearing fork the orchestrator can DERIVE an
answer for (sources, code, consistency, risk) is decided boldly and
recorded on the run's reference issue; only a pure-preference fork
bounces. spec-skeptic survives as an optional ad-hoc bias-breaker the
orchestrator pulls when unsure whether its leaning is judgement or bias —
not a gate, not a veto.

Every /boss run carries a mandatory reference issue (created if the cycle
had none): the fork-decision log, the orchestrator's cross-run memory,
and the only surviving trace of a hard-dropped attempt.

Relax main-sacrosanct below the session anchor: within an autonomous run
the orchestrator may git reset --hard its OWN UNPUSHED commits above the
anchor on a dead end — never below the anchor, never a pushed commit
(forward-only/revert). Discarded attempts are hard-dropped, no parking.

Verified across the 9 edited files by a 4-lens adversarial review
(retired-panel residue, cross-file contracts, rollback-boundary safety,
stance coherence): unanimous SOUND. Baseline tagged pre-autosign-rework.

closes #8
This commit is contained in:
2026-06-19 00:06:44 +02:00
parent 012e70b058
commit eb74f99bd4
9 changed files with 470 additions and 388 deletions
+190 -194
View File
@@ -17,7 +17,7 @@ planner consumes: with review, but **without an interview**.
It is the **sole home of the spec-production gates**: the
feature-acceptance criterion, the write-spec step, the
`grounding-check` hard-gate, and the user-review gate. Whichever way design work enters — fresh
`grounding-check` hard-gate, and the sign-off gate. Whichever way design work enters — fresh
through `brainstorm`'s discovery, or directly from sources that
already resolve the design — it passes through `specify` to become a
spec. `specify` carries the universal invariant **no plan without an
@@ -61,28 +61,34 @@ Triggers:
**Skipping a gate is never permitted.** The interview is `brainstorm`'s,
and its absence here is the whole point — but the *production gates*
(criterion, grounding-check, user-review) are
(criterion, grounding-check, sign-off) are
non-negotiable regardless of how the work entered. "The issue looks
exhaustive, skip the grounding-check" is the reactive-deference failure
this skill must not reintroduce.
this skill must not reintroduce. (Under `/boss` the sign-off gate is the
`grounding-check` `PASS` itself — see Step 6.)
## The Iron Law
```
THE SOURCES MUST RESOLVE EVERY LOAD-BEARING DESIGN DECISION BEFORE THE SPEC IS WRITTEN.
IF WRITING THE SPEC FORCES A CHOICE BETWEEN PLAUSIBLE DESIGNS THE SOURCES DO NOT RESOLVE,
STOP AND BOUNCE TO brainstorm. DO NOT SILENTLY PICK ONE.
THE PRODUCTION GATES — ACCEPTANCE CRITERION, GROUNDING-CHECK, USER-REVIEW —
EVERY LOAD-BEARING DESIGN DECISION MUST BE RESOLVED BEFORE THE SPEC IS WRITTEN — BUT
"RESOLVED" INCLUDES A FORK THE ORCHESTRATOR CAN DERIVE AN ANSWER FOR (FROM THE SOURCES,
THE CODE, EXISTING-DESIGN CONSISTENCY, OR RISK). A DERIVABLE FORK -> DECIDE IT BOLDLY AND
RECORD THE DECISION IN THE REFERENCE ISSUE. ONLY A FORK THAT HANGS ON PURE USER PREFERENCE
WITH NO DERIVABLE BETTER -> STOP AND BOUNCE / PAUSE. DO NOT BOUNCE A FORK YOU CAN DECIDE.
THE PRODUCTION GATES — ACCEPTANCE CRITERION, GROUNDING-CHECK, SIGN-OFF —
ARE NON-NEGOTIABLE REGARDLESS OF ENTRY PATH.
NO PLAN, SCAFFOLDING, OR DOWNSTREAM SKILL UNTIL THE SPEC IS APPROVED — BY THE USER, OR
(UNDER /boss) BY THE AUTO-SIGN GATE: ALL OBJECTIVE GATES GREEN
AND A UNANIMOUS ADVERSARIAL spec-skeptic PANEL. MODEL SELF-CONFIDENCE ALONE IS NEVER APPROVAL.
(UNDER /boss) BY A grounding-check PASS. MODEL SELF-CONFIDENCE ALONE IS NEVER APPROVAL;
THE grounding-check IS AN INDEPENDENT FRESH-CONTEXT AGENT, NOT THE PRODUCER'S CONFIDENCE.
```
Every clause is non-negotiable. The precondition gate is what makes
the no-interview path legitimate rather than merely convenient: it is
the honest fork-check that `tdd`'s "is this test-specifiable?" is for
test work.
test work. Its bar is *decidability*, not *pre-settlement*: a fork the
orchestrator can ground an answer for is resolved by deciding it; only a
fork that turns on the user's own preference, with no derivable better,
is the one that bounces.
## The Process
@@ -107,56 +113,86 @@ Enumerate the **load-bearing design decisions** the spec must encode
— the choices that, decided differently, would change what ships.
For each, ask the honest question:
> Do the sources **resolve** this, or am I about to **pick** it
> myself?
> Can I **derive** an answer for this fork — from the sources, the
> code, consistency with the existing design, or relative risk — or
> does it turn on a **preference only the user holds**?
If every load-bearing decision is resolved by the sources, proceed to
Step 2. If even one is unresolved — the sources leave a genuine fork
between plausible designs with real trade-offs — **STOP and bounce to
`brainstorm`** (see Error handling / The Bounce-Back). Do not silently
pick one and bake it into the spec; a guessed design ratified into a
spec is harder to dislodge than an open question, and it is exactly
the reactive-deference failure the gate exists to prevent.
This is a *decidability* test, not a *pre-settlement* test, and the
default is **decide, not bounce**:
Genuine doubt about whether the sources resolve a decision *is itself*
the fork signal — it resolves to a bounce, not to a guess.
- **Derivable fork → decide it boldly.** If there is a groundable
reason to prefer one option — it fits the existing design better, it
is simpler, it carries less risk, the sources lean that way — the
orchestrator decides it and proceeds. It does **not** bounce a fork it
can actually answer. When unsure whether its own leaning is bias
rather than judgement, it pulls **one** independent opinion — an
ad-hoc `spec-skeptic` lens (see Step 6) — and weighs it; the lens
informs the decision, it does not veto. Every derived decision is
**recorded in the reference issue** (below) with its rationale.
- **Pure-preference fork → bounce / pause.** Only when the fork turns on
something the orchestrator cannot derive — roadmap order, product
taste, two genuinely equivalent paths whose trade-off only the user
can weigh — is it a real bounce. Then **STOP and bounce to
`brainstorm`** (see The Bounce-Back), or under `/boss` route to the
human sign-off pause. A guessed *preference* baked into a spec is the
reactive-overreach this gate still prevents.
**Reconciliation comment (lagging-issue case).** A load-bearing fork can
be resolved by the in-context design discussion yet still be listed
*open* by the tracker issue that seeded the cycle — the issue lags the
conversation. When that holds (entry is in-context **and** a tracker
issue still carries a now-resolved fork as open), record the resolution
as an auditable issue comment **before** writing the spec, via the
project's issue-tracker comment command (`tea comment <idx>` for Gitea;
a heredoc body per the `issue` skill's conventions). One comment, this
exact shape:
The old "genuine doubt *is* the fork signal → bounce" reflex is
**retired**: doubt about whether you can derive an answer resolves to an
honest attempt to derive it (with a bias-breaker if needed), not to a
reflexive bounce. Being wrong on a derived call is allowed — the net is
the user's veto and, under `/boss`, the rollback sandbox
(`../boss/SKILL.md` § Direction freedom). What is *not* allowed is
dressing a pure-preference guess as a derived decision.
**Every `/boss` run carries a reference issue.** The run is always
anchored to a tracker issue — an existing one that seeded the cycle, or
one the orchestrator creates when the work was settled in pure chat with
no prior issue (the mechanic below). That issue is the run's **decision
log**: as the orchestrator derives fork answers, it comments each one
there, so the user can audit and veto after the fact and the orchestrator
has a cross-run "issue memory". Because a `/boss` dead-end is recovered
by hard-dropping the autonomous commits (no attempt branch survives), the
issue's comment thread is also the **only surviving trace** of an
abandoned attempt — which is the second reason it is mandatory, not
optional.
A comment records one of two distinct things, and the distinction is
load-bearing:
- A **user decision** the conversation settled — minuted with
**provenance** (a verbatim user statement, or explicit attribution +
date). Legitimate: you are recording what the user chose.
- A **derived orchestrator decision** — recorded with its **rationale**
(why one option is groundably better). Legitimate under the new stance,
but it must be labelled as what it is: a derived call, never disguised
as a user decision. If you cannot state a rationale, the fork was not
derivable — that is a pure-preference bounce, not a comment.
**Reconciliation comment (lagging-issue case).** A fork can be resolved
— by a user decision in the discussion, or derived by the orchestrator —
yet still be listed *open* by the tracker issue that seeded the cycle.
When that holds, record the resolution as an auditable issue comment
**before** writing the spec, via the project's issue-tracker comment
command (`tea comment <idx>` for Gitea; a heredoc body per the `issue`
skill's conventions). One comment, this shape:
```
## Design reconciliation (specify, in-context entry)
## Design reconciliation (specify)
Spec: <spec-name>. The fork(s) below are still listed open on this
issue; they were resolved in the in-context design discussion.
issue; this records their resolution.
- **Fork: <name>** → <decision>.
Provenance: <verbatim user statement, or explicit attribution + date>.
Basis: <verbatim user statement + attribution, OR "derived:" + the
groundable rationale>.
```
The comment must carry **provenance**, and the distinction is
load-bearing: it is the *record of a user decision* (legitimate — the
user settled the fork in discussion, you are minuting it), never a *new
orchestrator decision* dressed as settled (illegitimate). A bare
`decision: B` with no provenance is the latter. If you cannot point to a
real user statement, that is the signal that *you* picked the fork, not
the user — which is a Step-1.5 bounce, not a comment. The comment is
*persistent and auditable* (author + timestamp, in the forward queue) —
which is exactly why it, and not your ephemeral confidence, is what a
downstream `scope-fork` juror reads to confirm the fork is settled (see
Step 6). Recording it keeps the forward queue honest in either run mode;
under `/boss` it is *load-bearing* — without it the `scope-fork` juror
sees only the stale issue body and (correctly) blocks.
Provenance enforcement is not yours to self-certify: you *write* the
comment, the adversarial `scope-fork` juror *checks* it carries
provenance (Step 6). Writing a provenance-less comment does not buy a
pass — the re-dispatched juror blocks on it.
`Basis:` carries either provenance (user decision) or rationale (derived
decision) — both are auditable, neither is a bare `decision: X` with no
support. The comment is *persistent and auditable* (author + timestamp,
in the forward queue) — which is exactly why it, and not your ephemeral
confidence, is what the user reads to understand and veto a call made
without them. Recording it keeps the forward queue honest in either run
mode.
Gitea writes are pre-authorized (per `~/.claude/CLAUDE.md`); post the
comment without a confirmation prompt.
@@ -164,8 +200,8 @@ comment without a confirmation prompt.
**Issue-less in-context entry.** If the cycle has *no* tracker issue —
the design was settled in pure chat — there is no existing artefact to
comment on, so *create one*: open a new tracker issue that records the
resolved fork(s) with provenance, under the same discipline as the
reconciliation comment above. The body takes this shape:
resolved fork(s) under the same discipline as the reconciliation comment
above. The body takes this shape:
```
## Design record (specify, issue-less in-context entry)
@@ -173,38 +209,26 @@ This issue seeds <one-line identity of the work>. The design was settled
in an in-context discussion with no prior tracker issue; this records it.
Load-bearing fork(s) resolved:
- **Fork: <name>** → <decision>.
Provenance: <verbatim user statement, or explicit attribution + date>.
Basis: <verbatim user statement + attribution, OR "derived:" + rationale>.
```
The body names the *work*, not the spec file — the spec does not exist
yet at Step 1.5 (its number is assigned only in Step 3). This new issue
is the cycle's seeding issue from here on: the `scope-fork` juror reads
it via the project's issue show command — its index handed to the juror
in the Step-6 carrier (the `seeding_issue` field, below) — exactly as it
reads any seeding issue. A *new issue* is the right artefact where a spec-note
is the wrong one — a provenance note *inside the spec* is
self-referential (the spec asserting its own sources settle it) and
stays banned, but a separate issue is independent (author + timestamp,
in the forward queue, juror-readable without opening the spec), as
auditable as a reconciliation comment. Creating it is what lets the
`scope-fork` juror ratify the resolution here, instead of blocking for
lack of an auditable source.
The provenance gate is unchanged and load-bearing: the new issue records
*user* decisions, never fresh orchestrator ones. No real user statement
for a fork means *you* picked it — a Step-1.5 bounce, not an issue to
manufacture. You *write* the issue; the adversarial `scope-fork` juror
*checks* the provenance, and a provenance-less body buys no pass.
is the run's reference issue from here on: the orchestrator comments
later derived decisions on it as the run proceeds, and a downstream
ad-hoc `spec-skeptic` can read it via the project's issue show command if
the orchestrator pulls one. A *new issue* is the right artefact where a
provenance note *inside the spec* would be the wrong one — a note inside
the spec is self-referential (the spec asserting its own sources settle
it) and stays banned, but a separate issue is independent (author +
timestamp, in the forward queue, readable without opening the spec).
Create it via the project's issue-create command (the `issue` skill's
mechanics — `tea issues create` for Gitea), title and label per the
project's vocabulary as in the Step-5 failure-mode procedure. Gitea
writes are pre-authorized, so create it without a confirmation prompt.
**Capture the new issue's index** the moment it is created and carry it
into the Step-6 panel as the `seeding_issue` field — it is the only way
the `scope-fork` juror knows which issue to read. An issue created but
never handed to the panel is invisible to the juror, which leaves the
fork looking picked and the creation pointless.
project's vocabulary. Gitea writes are pre-authorized, so create it
without a confirmation prompt. **Capture the new issue's index** the
moment it is created — it is the run's reference issue, the target for
every later decision comment.
### Step 2 — Apply the project's feature-acceptance criterion
@@ -347,7 +371,10 @@ report and requires a fresh dispatch of `grounding-check` before the
Step-6 sign-off and commit. Step 5 is the *last* step that touches the
spec file; nothing slips between it and the commit.
### Step 6 — User-review gate
### Step 6 — Sign-off gate
Outside `/boss` the signature is the user's; under `/boss` it is the
Step-5 `grounding-check` `PASS` (the `/boss` subsection below).
The spec sits in the working tree as an unstaged file. Tell the user:
@@ -366,94 +393,54 @@ the commit itself. The spec is an ephemeral active-cycle artefact: it
lives in the repo only for its own cycle and is `git rm`'d at cycle
close (see `docs/conventions.md` § Lifecycle).
**In `/boss` (autonomous):** `specify` is dispatched autonomously —
it is bounded (no interview). It runs the criterion, parse, and
grounding-check gates without a checkpoint. At *this* gate the
orchestrator signs in the user's place, but only through the
**auto-sign gate** below — never on its own confidence. The gate
exists because the orchestrator that wrote the spec is the worst-placed
party to judge it, so it is built to *not* rely on that confidence. The
human sign-off pause is not the default here; it is where the gate
**escalates** — a spec that cannot clear the gate routes to a
problem-state notify ("spec X ready, please sign off") and waits for the
user, exactly as a plain pause would. Run the gate in two stages:
**In `/boss` (autonomous):** `specify` is dispatched autonomously — it
is bounded (no interview), so there is no user at this gate to take the
signature. The signature comes from the **Step-5 `grounding-check`
`PASS`**: an independent fresh-context agent's verdict against
currently-green tests, not the producing orchestrator's confidence — and
that is what the bold stance trusts in the user's place. There is **no
adversarial panel**, no objective-gates-then-panel staging, no
self-correction loop; those were retired in favour of decide-upstream +
recover-downstream (baseline tag `pre-autosign-rework`).
1. **Objective gates (all must already hold — these are facts, not
judgements):**
- Step 1.5 precondition gate passed cleanly — no load-bearing
fork was silently picked (if one had been, `specify` would have
bounced, so reaching here is the evidence; do not re-decide it).
- Step 4 self-review clean, **including the placeholder scan** (no
`TBD` / `TODO` / incomplete section).
- Step 5 `grounding-check` returned **`PASS`** — *not* an
overridden `BLOCK`. An override needs a human; with no human in
the loop a `BLOCK` can never be overridden, so any non-`PASS`
grounding result denies auto-sign outright.
- On a Step-5 **`PASS`**: commit the spec yourself (subject `spec:
<cycle> <topic> (boss-signed)`), fire the auto-sign notify (see `boss`
§ Notifications — it names the capability in user terms and states the
plan is proceeding and a reply vetoes), then proceed to Step 7. No
pause.
- On a Step-5 no-override **`BLOCK`** or **`INFRA_ERROR`**: there is no
human to override it, so it routes to the human sign-off pause — a
problem-state notify ("spec X needs your sign-off") — exactly as the
Step-5 failure mode describes. A no-override grounding `BLOCK` is the
one objective denial of an autonomous sign and is never signed over.
If any objective gate is not green, **do not run the panel**
fall straight to the human sign-off pause (the default-slot
behaviour above), carrying the failing gate in the notify.
The judgement that used to live in the panel now lives **upstream**, in
Step 1.5's bold-decide stance: a load-bearing fork is *decided* there
(with an ad-hoc `spec-skeptic` bias-breaker if the orchestrator wants
one) and recorded in the reference issue, or — if it is a pure-preference
fork — bounced before the spec is ever written. By the time a spec
reaches this gate the only thing left to verify is the objective one the
`grounding-check` already verifies. Editorial defects the old panel would
have caught (a clumsy worked example, a two-way-readable sentence) are
allowed through **deliberately**: they are cheap to patch inline
downstream, and catching them was never worth an obligatory five-juror
panel on every spec.
2. **Adversarial panel (unanimous-or-nothing):** dispatch the
read-only `spec-skeptic` agent **five times in parallel**, once
per lens — `criterion`, `grounding`, `scope-fork`, `ambiguity`,
`plan-readiness`. Carrier per dispatch: `spec_path` (absolute),
`iteration_scope`, the single `lens`, and `seeding_issue` the
index of the cycle's seeding issue (the lagging issue reconciled in
Step 1.5, the issue Step 1.5 created for an otherwise issue-less
cycle, or `none` if the cycle has neither) for the `scope-fork`
juror to read; the other four lenses ignore it. Each juror tries to
*refute* the spec along its lens and returns `SOUND` / `BLOCK` /
`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.
**Bias-breaker (optional, the orchestrator's call).** When deciding a
fork in Step 1.5 the orchestrator MAY dispatch the read-only
`spec-skeptic` agent ad hoc — one lens, or a few, whichever fork it is
unsure about — for an independent refutation to weigh against its own
leaning. This is a *tool it reaches for*, not a gate it must clear: the
lens returns a finding, the orchestrator decides. Carrier and lenses are
defined in `agents/spec-skeptic.md`.
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 `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. |
**Re-loop limit: ≤ 2 self-correction rounds. The 3rd unresolved
editorial `BLOCK` escalates** — same shape as the per-task re-loop
limit in the implement loop (`../implement/workflows/implement-loop.js`).
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
spec / `git checkout` of downstream working-tree work), never a
history rewind — same discipline as any other orchestrator call (see
`boss` § Direction freedom).
**Auto-sign commits on `main` are forward-only — with one `/boss`
exception.** A later user veto is normally a forward correction (revert /
revised spec / `git checkout` of downstream work). The `/boss` rollback
sandbox is the exception: within an autonomous run the orchestrator may
hard-reset its own *unpushed* autonomous commits back toward (never
below) the session anchor when it has run into a dead end — its own work
only. See `boss` § Direction freedom for the exact boundary.
### Step 7 — Hand off to `planner`
@@ -468,11 +455,13 @@ Hand off carries:
## The Bounce-Back
`specify` returns to `brainstorm` whenever the precondition fails — the
sources do not resolve a load-bearing design decision (Step 1.5), or a
`grounding-check` `BLOCK` reveals an unratified mechanism the user does
not override (Step 5 failure-mode routes to a re-design). On a
precondition bounce:
`specify` returns to `brainstorm` whenever the precondition fails — a
load-bearing fork turns on **pure user preference** the orchestrator
cannot derive (Step 1.5), or a `grounding-check` `BLOCK` reveals an
unratified mechanism the user does not override (Step 5 failure-mode
routes to a re-design). A fork the orchestrator *can* derive an answer
for is **not** a bounce — it is decided and recorded in the reference
issue. On a precondition bounce:
1. Nothing was written yet (the gate fires *before* Step 3), so there
is no working-tree artefact to discard.
@@ -481,8 +470,9 @@ precondition bounce:
planner` path runs.
The bounce-back is not a failure of this skill; it is the skill
correctly refusing to ratify an un-chosen design into a spec — the same
discipline `tdd` applies when behaviour is not test-specifiable.
correctly refusing to ratify a *pure-preference guess* into a spec — the
same discipline `tdd` applies when behaviour is not test-specifiable. It
is narrow by design: only the user's-call forks bounce, not every fork.
## Handoff Contract
@@ -491,9 +481,10 @@ discipline `tdd` applies when behaviour is not test-specifiable.
| user / issue (design settled) → `specify` | the sources: issue body, in-context design discussion, design docs |
| `brainstorm` → `specify` | ratified design narrative (approaches chosen, constraints, sections approved) — in-context; brainstorm writes no spec file |
| `specify` → `grounding-check` (Step 5) | `spec_path` (absolute) + `iteration_scope` |
| `specify``spec-skeptic` panel (Step 6, `/boss` only) | `spec_path` (absolute) + `iteration_scope` + one `lens` per dispatch (five in parallel) + `seeding_issue` (index for the `scope-fork` juror to read, or `none`) |
| `specify` → `spec-skeptic` bias-breaker (Step 1.5, optional, orchestrator's call) | `spec_path` (absolute) + `iteration_scope` + one `lens` + `seeding_issue` (or `none`); dispatched ad hoc for a fork the orchestrator is deciding, never as a gate |
| `specify` → reference issue (Step 1.5) | a derived-decision comment (rationale) or user-decision comment (provenance); a freshly created seeding issue if the cycle had none |
| `specify` → `planner` (Step 7, on PASS or overridden BLOCK) | path to spec + iteration scope |
| `specify``brainstorm` (precondition bounce) | the unresolved design question as a cycle request; no spec written |
| `specify` → `brainstorm` (precondition bounce, pure-preference fork) | the unresolved design question as a cycle request; no spec written |
| `specify` → issue backlog (Step 5, no-override BLOCK) | new issue naming the unratified dependency; spec file deleted from the working tree |
## Common Rationalisations
@@ -501,17 +492,22 @@ discipline `tdd` applies when behaviour is not test-specifiable.
| Excuse | Reality |
|--------|---------|
| "The issue looks exhaustive — skip the grounding-check / criterion too" | The interview is what `specify` skips, by design. The production gates are not the interview; they are what keeps the spec honest no matter how it entered. Skipping a gate because the sources look thorough is the exact reactive-deference failure this skill must not reintroduce. |
| "There's a small open fork but the sources mostly decide it — I'll pick the rest" | "Mostly decided" with one real fork left is an unresolved design, and a guessed design baked into a spec is harder to dislodge than an open question. That is the bounce-back trigger — stop and go to `brainstorm`. |
| "Genuine doubt whether the sources resolve this — but bouncing feels like overkill" | Doubt about whether the sources resolve a decision *is* the fork signal. It resolves to a bounce, not a guess. The bounce is cheap (nothing is written yet); a wrong guess in a ratified spec is not. |
| "There's an open fork left — I should bounce to `brainstorm` to be safe" | Only if it is a *pure-preference* fork. If you can derive an answer — sources, code, consistency, risk — decide it boldly, record the rationale in the reference issue, and proceed. Bouncing a fork you can actually decide is the reactive-deference reflex the new stance retired. |
| "I can derive an answer but I'm not 100% sure it's right — safer to bounce" | Being wrong on a *derived* call is allowed: the net is the user's veto and the `/boss` rollback sandbox. If you're unsure your leaning is judgement not bias, pull one ad-hoc `spec-skeptic` lens and weigh it — then decide. Reserve the bounce for forks only the user's preference can settle. |
| "This is pure product taste but one option seems fine — I'll just pick it" | A fork with no derivable better is the user's call; picking it is the pure-preference guess the gate still forbids. Bounce it, or under `/boss` route to the human sign-off pause. The line is *derivable vs. preference*, not *small vs. large*. |
| "The shape is clear from the prose, I don't need to paste the code" | If it's clear, pasting it is free; if pasting it is hard, it wasn't clear. The acceptance criterion is unjudgeable without the worked code. |
| "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. Flag code lifted from a `status: aspirational` source as such; it is not ratified contract. |
| "In `/boss` 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. 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. |
| "In `/boss` this spec looks solid — I'll sign and move on" | Your sense that it looks solid is not the signature. Under `/boss` the signature is the Step-5 `grounding-check` `PASS` — an independent agent's verdict, not your confidence. Run the grounding-check; a no-override `BLOCK` routes to the human, never auto-signed over. |
| "A grounding `BLOCK` looks like a false alarm — I'll sign over it" | With no human in the loop there is no override. A no-override grounding `BLOCK` is the one objective denial of an autonomous sign; it routes to the human sign-off pause. The whole point of trusting grounding-PASS as the signature is that you also honour its `BLOCK`. |
## Red Flags — STOP
- An unresolved design fork silently picked instead of bounced (any flavour)
- A *pure-preference* fork silently picked instead of bounced — or its
inverse: a *derivable* fork bounced to `brainstorm` instead of decided
(the reactive-deference reflex the new stance retired)
- A derived fork decision made but **not recorded** in the reference
issue (no rationale comment, or no reference issue at all on a `/boss` run)
- "The sources are thorough, skip a gate" thoughts (any flavour)
- A load-bearing change described in prose with no before → after code
block; a surface cycle with no worked user-facing example
@@ -519,14 +515,14 @@ discipline `tdd` applies when behaviour is not test-specifiable.
flagging it as a target, not verified fact
- 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; 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 outside a `/boss` session (interactive runs take the
user's signature directly)
rather than on the Step-5 `grounding-check` `PASS` that is the
autonomous signature; or signing over a no-override grounding `BLOCK`
/ `INFRA_ERROR` instead of routing it to the human sign-off pause
- (auto-sign) Treating the retired `spec-skeptic` panel as still a gate
— re-introducing a unanimous five-juror pass as a sign precondition,
rather than using `spec-skeptic` only as the optional Step-1.5
bias-breaker; or running any auto-sign path outside a `/boss` session
(interactive runs take the user's signature directly)
- Jumping straight from spec to `implement` (must go via `planner`)
- Running an interview here (that is `brainstorm`'s job — if the work
needs one, bounce)
@@ -544,15 +540,15 @@ discipline `tdd` applies when behaviour is not test-specifiable.
- **Agent dispatched:** `agents/grounding-check.md` — dispatched in
Step 5 as a hard-gate. Reads the spec with fresh context and reports
PASS / BLOCK / INFRA_ERROR.
- **Agent dispatched (auto-sign):** `agents/spec-skeptic.md`
dispatched in Step 6 five times in parallel (one per lens) under
a `/boss` session. Each
juror tries to refute the spec along its lens; a unanimous `SOUND` is
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.
- **Agent dispatched (optional bias-breaker):** `agents/spec-skeptic.md`
— an ad-hoc tool, **not** a gate. Under `/boss`, when the orchestrator
is deciding a load-bearing fork in Step 1.5 and is unsure whether its
leaning is judgement or bias, it MAY dispatch one (or a few)
`spec-skeptic` lens(es) for an independent refutation to weigh. The
lens returns a finding; the orchestrator decides. The former obligatory
unanimous five-lens auto-sign panel is retired (baseline tag
`pre-autosign-rework`); the autonomous signature is now the Step-5
`grounding-check` `PASS`.
- **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; discretionary, not part of the