feat(specify): record in-context fork resolutions as auditable issue comments

The `spec-skeptic` `scope-fork` juror reads only the seeding issue plus
the spec. On the legitimate `specify` direct-entry path — a fork settled
in a long in-context design discussion — that resolution lives only in
ephemeral chat the juror cannot replay. When the issue body lags the
discussion (still lists the fork open), the juror correctly blocks, and
a design BLOCK escalates without self-correction. The result: auto-sign
was structurally almost unreachable for the in-context entry path.

Close the blind spot by giving the juror an auditable source instead of
weakening the gate. When `specify` enters in-context and a tracker issue
still lists a now-resolved fork as open, the orchestrator posts a
reconciliation comment recording each fork's resolution WITH provenance
(a record of the user's decision, never a fresh orchestrator one) before
writing the spec. The comment is persistent and audit-able — unlike a
carrier digest — so it, not the orchestrator's confidence, is what the
juror checks.

Separation of powers keeps it honest: the orchestrator writes the
comment, the adversarial juror enforces the provenance requirement. A
bare `decision: X` with no provenance does not resolve the fork — the
re-dispatched juror blocks on it. The escalation rule and the
three-field carrier are untouched; only the juror's information changes.

Mechanics:
- specify Step 1.5: reconciliation-comment sub-step, provenance format,
  issue-less fallback (auto-sign -> human sign-off, no weak spec-note).
- spec-skeptic: replace the "quoted in the dispatch" drift; juror reads
  the issue WITH comments via `issue_tracker.show_cmd`; provenance check.
- new profile slot `issue_tracker.show_cmd` (must render comments);
  documented in schema + template.
- issue skill: `tea issues <idx>` is body-only; `--comments` required
  (verified against tea 0.14.1 and Aura #55 — 180 vs 144 lines).
- consistency: design.md out-of-scope, README, pipeline.md, boss skill.
This commit is contained in:
2026-06-12 15:54:47 +02:00
parent a87916e578
commit 268ee705f4
9 changed files with 101 additions and 14 deletions
+48
View File
@@ -121,6 +121,54 @@ the reactive-deference failure the gate exists to prevent.
Genuine doubt about whether the sources resolve a decision *is itself*
the fork signal — it resolves to a bounce, not to a guess.
**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:
```
## Design reconciliation (specify, in-context entry)
Spec: <spec-name>. The fork(s) below are still listed open on this
issue; they were resolved in the in-context design discussion.
- **Fork: <name>** → <decision>.
Provenance: <verbatim user statement, or explicit attribution + date>.
```
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 auto-sign 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.
Gitea writes are pre-authorized (per `~/.claude/CLAUDE.md`); post the
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 independent artefact to
comment on, and a provenance note *inside the spec* would be
self-referential (the spec asserting its own sources settle it). Do not
manufacture a weak spec-note path: under auto-sign this case falls back
to the human sign-off (the safe default), because no auditable source
exists for the juror to check against.
### Step 2 — Apply the project's feature-acceptance criterion
The project's `CLAUDE.md` declares the criterion a feature must
+21 -5
View File
@@ -72,10 +72,22 @@ In addition, every dispatch:
`paths.code_roots` and the project's examples / fixtures directory —
you grep these to confirm or refute ratification.
- For the `scope-fork` lens additionally: the sources the spec was
built from, when the carrier names them (an issue body is reachable
via the issue-tracker show command; an in-context design narrative is
quoted in the dispatch). You judge "resolved vs picked" against what
the sources actually say, not against what reads plausibly.
built from. The seeding issue is reachable via the profile's
`issue_tracker.show_cmd` (issue index appended last) — invoke it so
you read the issue **with its comment thread**, not the body alone.
You judge "resolved vs picked" against what the sources actually say,
not against what reads plausibly. A fork the issue *body* still lists
open may have been resolved in an in-context discussion the dispatch
cannot replay to you; the legitimate channel for that resolution is a
**reconciliation comment** on the issue (`specify` Step 1.5). A
reconciliation comment counts as the sources resolving the fork **only
if it carries provenance** — a record of the user's decision (a
verbatim statement, or explicit attribution + date). A bare
`decision: X` with no provenance is an orchestrator self-assertion
dressed as settled, not a source: it does **not** resolve the fork, and
a load-bearing decision whose only support is such a comment is a
refutation. If `show_cmd` is unset, you have only the body — a fork the
body lists open is unresolved to you.
You do NOT read files under `paths.plan_dir` (the plan does not yet
exist). You do NOT read other specs unless the spec under review
@@ -117,7 +129,11 @@ YOU DO NOT RUN THE FULL TEST SUITE. (TEST LIST, TYPE-CHECK, PER-BLOCK PARSER RUN
`#[ignore]`/`xfail`/disabled does not pin; code presence does not
pin; your own recall does not pin). For `scope-fork`, this means
listing the load-bearing decisions and checking each against the
sources. For the others, read for the specific failure.
sources read via `show_cmd` (issue **with comments**); a fork the
issue body lists open is resolved only by a reconciliation comment
that carries provenance (see the standing reading list) — a
provenance-less one does not count. For the others, read for the
specific failure.
4. If you find a genuine defect along your lens → `BLOCK`, and state
it concretely (section ref, the offending text, why it is a defect).
5. If, after an honest search, you cannot find one → `SOUND`. Saying