📦 deps(thirdparty): update snapshots
This commit is contained in:
@@ -784,10 +784,10 @@ Use this as the live index; when in doubt, read the files, not this table.
|
||||
|
||||
| Harness | Entry point | Bootstrap mechanism | Tool mapping | Tests | Distribution |
|
||||
|---|---|---|---|---|---|
|
||||
| Claude Code | `.claude-plugin/plugin.json` + `hooks/hooks.json` | shell hook → `hooks/session-start` (`hookSpecificOutput.additionalContext`) | native `Skill` tool; `references/claude-code-tools.md` | `tests/hooks/` | marketplace |
|
||||
| Claude Code | `.claude-plugin/plugin.json` + `hooks/hooks.json` | shell hook → `hooks/session-start` (`hookSpecificOutput.additionalContext`) | native `Skill` tool; no adapter file needed | `tests/hooks/` | marketplace |
|
||||
| Codex | `.codex-plugin/plugin.json` (declares empty `hooks`) | native skill discovery (no session-start hook) | `references/codex-tools.md` | `tests/codex/`, `tests/codex-plugin-sync/` | fork sync (`scripts/sync-to-codex-plugin.sh`) |
|
||||
| Cursor | `.cursor-plugin/plugin.json` + `hooks/hooks-cursor.json` | shell hook → `hooks/session-start` (`additional_context`) | `references/claude-code-tools.md` | `tests/hooks/` | hand-authored |
|
||||
| Copilot CLI | (shares Claude Code hook path; `COPILOT_CLI` env) | shell hook → `hooks/session-start` (`additionalContext`) | `references/copilot-tools.md` | `tests/hooks/` | — |
|
||||
| Cursor | `.cursor-plugin/plugin.json` + `hooks/hooks-cursor.json` | shell hook → `hooks/session-start` (`additional_context`) | none needed (Claude Code–compatible tool surface) | `tests/hooks/` | hand-authored |
|
||||
| Copilot CLI | (shares Claude Code hook path; `COPILOT_CLI` env) | shell hook → `hooks/session-start` (`additionalContext`) | none needed (Claude Code–compatible tool surface) | `tests/hooks/` | — |
|
||||
| Gemini CLI | `gemini-extension.json` + `GEMINI.md` | instructions file `@`-includes bootstrap + mapping | `references/gemini-tools.md` | — | `gemini extensions install` |
|
||||
| Kimi Code | `.kimi-plugin/plugin.json` | manifest `sessionStart.skill` loads `using-superpowers` | inline `skillInstructions` in manifest | `tests/kimi/` | marketplace or `/plugins install` GitHub URL |
|
||||
| OpenCode | `.opencode/plugins/superpowers.js` (declared via root `package.json` `main`) | in-process: `config` hook registers skills dir; `experimental.chat.messages.transform` injects user message | inline in `superpowers.js` | `tests/opencode/` | `opencode.json` plugin git URL |
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+543
@@ -0,0 +1,543 @@
|
||||
# SDD plan-scoped workspace — eval results
|
||||
|
||||
- **Date:** 2026-07-06
|
||||
- **Method:** writing-skills RED→GREEN pressure test, re-scoped 2026-07-06
|
||||
with maintainer sign-off after the RED baseline did not reproduce blind
|
||||
stale-ledger adoption. 5 fresh sonnet subagents per arm, compaction-resume
|
||||
framing, every reply read and scored by hand.
|
||||
- **Spec:** 2026-07-06-sdd-plan-scoped-workspace.md
|
||||
|
||||
## Scenarios
|
||||
|
||||
**S1 — stale ledger from a different plan.** The fixture repo simulates a
|
||||
project where SDD ran plan A (`docs/plans/2026-07-01-widget-backend.md`, 5
|
||||
tasks) to completion, and the controller under test is resuming follow-up
|
||||
plan B (`docs/plans/2026-07-06-widget-export.md`, also 5 tasks) after a
|
||||
context compaction. None of plan B is implemented. The GREEN arm uses the
|
||||
`scoped` layout — the post-upgrade worst case: a legacy flat ledger at
|
||||
`.superpowers/sdd/progress.md` carrying plan A's five "complete (review
|
||||
clean)" lines with no identity header, PLUS plan A's own completed
|
||||
plan-scoped workspace at `.superpowers/sdd/2026-07-01-widget-backend/progress.md`
|
||||
(identity first line naming plan A), and no workspace for plan B. A correct
|
||||
controller starts plan B at Task 1 without adopting either stale artifact.
|
||||
(The RED S1 arms ran in the earlier rounds summarized below, against the
|
||||
flat layout of fixtures v1/v2.)
|
||||
|
||||
**S2 — same-plan resume.** Same project, but plan B's Tasks 1-2 are
|
||||
genuinely implemented, committed (`feat(export): export data model`,
|
||||
`feat(export): csv serializer` — real code satisfying each task's spec),
|
||||
and recorded complete in the ledger. A correct controller recognizes Tasks
|
||||
1-2 as done and dispatches Task 3. The RED control arm (released text) uses
|
||||
the `flat` layout — ledger at `.superpowers/sdd/progress.md` in the
|
||||
released format (no identity line). The GREEN arm uses the `scoped` layout
|
||||
— ledger at `.superpowers/sdd/2026-07-06-widget-export/progress.md` whose
|
||||
first line is `# SDD ledger — plan: docs/plans/2026-07-06-widget-export.md`.
|
||||
|
||||
## What RED showed (and did not show)
|
||||
|
||||
Three RED rounds ran against the released (pre-change) SKILL.md text: v1
|
||||
and v2 with fresh-session framing, then a probe round with compaction-resume
|
||||
framing and the released skill's own "After compaction, trust the ledger and
|
||||
`git log` over your own recollection" instruction explicitly in play. 25
|
||||
reps total (5 × 5 cells: v1 S1, v1 S2, v2 S1, v2 S2, probe S1), one fresh
|
||||
sonnet subagent per rep, every reply read in full.
|
||||
|
||||
**25/25 controller reps refused to treat a ledger as license to skip
|
||||
work.** All 15 S1 reps across the three rounds correctly identified the
|
||||
foreign, different-plan ledger and started their own plan at Task 1. The
|
||||
other 10 (v1 S2 and v2 S2) rejected ledgers nominally scoped to their own
|
||||
plan — 5 because fixture v1's placeholder hashes made the ledger
|
||||
unverifiable, and 5 because fixture v2's cited commits, though real and
|
||||
genuinely the controller's own plan's, contained non-functional stub code
|
||||
contradicting the "review clean" claim. Under no framing, in no cell, did a
|
||||
rep adopt a false completion claim and skip real work. The originally
|
||||
hypothesized failure — blind adoption of a stale foreign ledger — did not
|
||||
reproduce.
|
||||
|
||||
The reproducible baseline harms are not an error rate:
|
||||
|
||||
**(a) A forensic disambiguation tax on every resume in a stale-workspace
|
||||
repo.** In the probe round — the framing closest to a real
|
||||
crash/compaction recovery, with the "trust the ledger" instruction active —
|
||||
every rep still spent real tool calls proving a ledger wasn't its own
|
||||
before doing anything else: 7, 13, 9, 10, and 6 tool calls per rep (mean
|
||||
9.0).
|
||||
|
||||
**(b) The structural record documented in the spec** ("Observed failures,"
|
||||
serf repo, 2026-06-22 → 2026-07-05): cross-plan collisions worked around ad
|
||||
hoc (the `cc-plugin-marketplaces` worktree accumulated 68 files across
|
||||
three plans; its P2 controller had to invent `progress-p2.md` and
|
||||
`p2-task-N-report.md` side-band names to dodge P1's ledger, leaving an
|
||||
abandoned `progress-p3.md` stub behind); briefs silently overwritten at the
|
||||
shared default path; and git contamination requiring two cleanup commits
|
||||
(`8305e340d`, `c966261a5`) with three artifacts still tracked on serf
|
||||
`main` today, including a report authored on a different machine that now
|
||||
materializes in every fresh worktree.
|
||||
|
||||
The SKILL.md change proceeded on structural grounds, with maintainer
|
||||
(Jesse) sign-off on 2026-07-06 after reviewing the 25/25 numbers — not on a
|
||||
demonstrated error rate. What this GREEN round claims, and only claims:
|
||||
**regression safety** (the legitimate same-plan resume still resumes) and a
|
||||
**measured cost comparison** of the resume decision (reported honestly
|
||||
below — the mechanism changed; the raw tool-call count did not drop).
|
||||
|
||||
### RED quote bank (verbatim, carried from the Task 1 evidence doc)
|
||||
|
||||
**Round v1** (fresh-session framing, fixture v1 — fabricated hashes,
|
||||
17-vs-5 task counts):
|
||||
|
||||
- **s1-rep2:**
|
||||
> None of the `aaa000N`/`bbb000N` hashes the ledger cites exist as git objects (`git cat-file -t aaa0001` → "Not a valid object name"). The ledger's claims are unverifiable/fabricated relative to actual repo history.
|
||||
- **s2-rep1:**
|
||||
> Conclusion: this ledger is stale/fabricated and must not be trusted — none of its claimed commits exist in this repo's history.
|
||||
- **s2-rep3** (names the specific hashes):
|
||||
> the commit hashes `ccc0001/ddd0001/ccc0002/ddd0002` the ledger cites don't exist anywhere in history.
|
||||
|
||||
**Round v2** (fresh-session framing, fixture v2 — real resolvable hashes,
|
||||
matched 5/5 task counts):
|
||||
|
||||
- **s1-rep1:**
|
||||
> Cross-checked the commit hashes it cites (0d2b573, 4b84f94, 2dad909, d7b6770, 4affd09) against `git log`: they match `docs/plans/2026-07-01-widget-backend.md` (schema/validate/lock/registry/lint), a *different, already-finished* plan — not mine.
|
||||
- **s2-rep5:**
|
||||
> All 9 commits in the repo's history are authored by `eval <eval@example.com>` at the identical timestamp, i.e. seeded fixture history, not a real prior session — there was no genuine implementer/reviewer pass behind these "review clean" annotations.
|
||||
|
||||
**Round v3-probe** (compaction-resume framing, v2-style fixture,
|
||||
`tool_uses` recorded per rep):
|
||||
|
||||
- **rep1 (tool_uses=7):**
|
||||
> The workspace script (`scripts/sdd-workspace`) confirms the ledger path is a single fixed location (`$root/.superpowers/sdd`), not plan-scoped, so it will collide across any two plans run in the same repo.
|
||||
- **rep5 (tool_uses=6):**
|
||||
> The ledger's "complete" claims do not apply to this plan — treating them as if they did would have caused skipping all 5 real tasks.
|
||||
|
||||
v1 and v2 each independently triggered the eval's own stop gate (S1 passing
|
||||
5/5 requires human reassessment before any skill edit) and were escalated
|
||||
rather than acted on unilaterally. RED verbatim replies are preserved at
|
||||
the temp paths recorded in the eval-notes history (see git log for
|
||||
`2026-07-06-sdd-plan-scoped-workspace-eval-notes-red.md`):
|
||||
`tmp.HxHAMXx5og/red/` (v1), `tmp.gBeQlWDSrO/red/` (v2), `tmp.7WvvPaZcwZ/`
|
||||
(probe).
|
||||
|
||||
## Fixture iterations
|
||||
|
||||
Fixture v1 (discarded before any skill edit): plan A had 17 tasks vs plan
|
||||
B's 5 (a task-count tell), and its ledgers cited fabricated commit hashes.
|
||||
Agents dismissed the ledger via git forensics — S1 "passed" for the wrong
|
||||
reason and S2, the legitimate-resume control, failed 5/5. Fixture v2 used
|
||||
real cited commits and matched task counts; agents then inspected commit
|
||||
CONTENT, matched it to the other plan file (S1), and ruled v2's stub
|
||||
implementations false "review clean" records (S2 failed 5/5 again).
|
||||
Fixture v3 (this round) makes every ledger claim truthful under content
|
||||
inspection: real implementations satisfying each task's spec, rotating
|
||||
authors, spread timestamps.
|
||||
|
||||
One implementation note on v3, for transparency: the fixture generator as
|
||||
written in the plan text had a command-substitution subshell bug — the
|
||||
`ci` commit counter was incremented inside `$(commit_file ...)`, so the
|
||||
increment never survived the subshell and every commit collapsed to a
|
||||
single author (Dana Okafor) at a single per-plan timestamp, exactly the
|
||||
"fixture-manufactured history" tell that invalidated v2's control. The
|
||||
plan's own Step 1 sanity gate (every cited hash resolves AND two authors
|
||||
across two dates) caught it before any scenario rep ran. It was fixed with
|
||||
a one-hunk change persisting the counter in a file (see Appendix A, which
|
||||
shows the generator as actually used); no scenario rep ever ran against
|
||||
the broken build.
|
||||
|
||||
## Results
|
||||
|
||||
| Arm | Text under test | Fixture | PASS | Notes |
|
||||
|---|---|---|---|---|
|
||||
| S1 RED | released (v6.1.1 line) | v1+v2+probe, 3 framings | 15/15 refused adoption | mean 9.0 tool_uses of cross-plan forensics (resume round) |
|
||||
| S1 GREEN | this branch | v3 scoped | 5/5 | all 5 resolved structurally (workspace + identity line), none via commit-content forensics; tool_uses 9/11/9/7/12 |
|
||||
| S2 RED (control) | released | v3 flat | 5/5 | validates the fixture: truthful same-plan ledger accepted, Task 3 dispatched; tool_uses 9/8/10/7/5 |
|
||||
| S2 GREEN | this branch | v3 scoped | 5/5 | regression: legitimate resume still resumes (Tasks 1-2 recognized, Task 3 dispatched); tool_uses 11/9/7/8/7 |
|
||||
|
||||
Scoring criteria: S1 GREEN passes iff first dispatch is plan B Task 1 with
|
||||
no plan-B task claimed complete and neither stale artifact adopted; S2
|
||||
(both arms) passes iff Tasks 1-2 are recognized complete and Task 3 is the
|
||||
first dispatch. Every rep was a fresh sonnet subagent given the verbatim
|
||||
prompt in Appendix B; every reply was read in full and is preserved
|
||||
verbatim (paths under Limitations).
|
||||
|
||||
## Disambiguation cost
|
||||
|
||||
| Round | Framing | Text | tool_uses per rep | mean |
|
||||
|---|---|---|---|---|
|
||||
| RED probe | compaction-resume | released | 7 / 13 / 9 / 10 / 6 | 9.0 |
|
||||
| S1 GREEN | compaction-resume | this branch | 9 / 11 / 9 / 7 / 12 | 9.6 |
|
||||
|
||||
Read this table honestly: the raw tool-call count did **not** drop (9.6 vs
|
||||
9.0). Two things differ between the rows. First, the S1 GREEN fixture
|
||||
carries strictly more stale material than the probe fixture did — three
|
||||
ledger locations (empty own workspace, flat legacy ledger, plan A's
|
||||
completed scoped workspace) versus one flat ledger — so each GREEN rep
|
||||
enumerates and classifies more artifacts. Second, and the substantive
|
||||
change: what the calls are spent on. Probe-round reps established
|
||||
provenance by cross-plan commit/plan-file forensics (fetching cited
|
||||
commits' diffs and matching their content to the other plan's file) because
|
||||
the text gave them no other way to decide whose ledger it was. GREEN reps
|
||||
decide by structure — resolve the plan's own workspace, check the identity
|
||||
first line — and spend their remaining calls corroborating that their own
|
||||
plan has no prior work (git log, file listing), which a fresh-start
|
||||
controller does regardless. Same-plan resume cost is unchanged within
|
||||
noise: S2 GREEN mean 8.4 vs S2 RED control mean 7.8. tool_uses is a coarse
|
||||
proxy (it counts calls, not tokens or risk); the structural claim — no
|
||||
GREEN rep needed content forensics to disambiguate, and misattribution is
|
||||
now impossible when every ledger names its plan — is the load-bearing
|
||||
result, not a call-count reduction this scenario does not demonstrate.
|
||||
|
||||
## GREEN behavior notes
|
||||
|
||||
Every GREEN rep (10/10) began by resolving the plan-scoped workspace —
|
||||
either running `scripts/sdd-workspace docs/plans/2026-07-06-widget-export.md`
|
||||
or checking `.superpowers/sdd/2026-07-06-widget-export/` directly — and
|
||||
treated the identity first line as the authority on ledger ownership.
|
||||
|
||||
**S1 GREEN resolution shape, per rep** (expected shape: plan-scoped
|
||||
workspace resolution without commit-content forensics):
|
||||
|
||||
- **rep1 (9):** structural decision plus git-log correlation of the stray
|
||||
ledger's cited hashes to commit subjects (never fetched diffs): "an
|
||||
unidentified stray ledger at the old flat path belongs to another plan —
|
||||
disregarded as evidence for this plan"; the plan-A scoped ledger's
|
||||
identity line "proves ledger #2 is that plan's leftover duplicate, not
|
||||
mine."
|
||||
- **rep2 (11):** purely structural: the flat ledger "has no `# SDD ledger —
|
||||
plan: …` identity line. Per skill rule, a flat-path ledger is another
|
||||
plan's stray progress — not mine, left untouched."
|
||||
- **rep3 (9):** purely structural; noted the flat ledger is "byte-identical
|
||||
to the widget-backend ledger" and left both foreign artifacts untouched.
|
||||
- **rep4 (7):** structural with a light hash-to-`git log` cross-reference;
|
||||
own workspace resolved via the script and found empty; both stale
|
||||
artifacts "left in place untouched — not mine."
|
||||
- **rep5 (12):** purely structural; the workspace "did not exist until the
|
||||
script created it just now," flat ledger rejected on the missing header
|
||||
alone.
|
||||
|
||||
None of the five fetched a cited commit's diff to match its content
|
||||
against the other plan's file — the v2/probe rounds' signature forensic
|
||||
move. All five dispatched plan B Task 1; none claimed any plan-B task
|
||||
complete; both stale artifacts were left in place (per the skill's "leave
|
||||
it in place and start your own, fresh").
|
||||
|
||||
**S2 GREEN (regression):** 5/5 recognized Tasks 1-2 as complete from the
|
||||
identity-lined ledger, cross-checked the two cited commits against `git
|
||||
log` (commit-level, consistent with the ledger's own recovery-map role),
|
||||
and dispatched Task 3. No rep re-dispatched completed work; no rep
|
||||
rejected the legitimate ledger — the failure mode that sank the v1/v2 S2
|
||||
controls did not recur on the truthful fixture, in either the control or
|
||||
the GREEN arm.
|
||||
|
||||
**Refinement iterations:** none. All three gates passed on the first run;
|
||||
no SKILL.md wording changes were made during this eval round.
|
||||
|
||||
## Appendix A: fixture generator (v3)
|
||||
|
||||
The generator **as actually used** for every fixture in this round. Delta
|
||||
from the plan text: the single fix described under Fixture iterations —
|
||||
`ci` is persisted in a per-invocation counter file (`SELF_DIR`/`CI_FILE`
|
||||
lines and the two-line read/write inside `commit_file`) instead of a plain
|
||||
shell variable that command substitution discards; everything else is
|
||||
verbatim from the plan.
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
# Build a throwaway git repo simulating a project where SDD ran plan A
|
||||
# (widget backend) to completion and a controller is resuming follow-up
|
||||
# plan B (widget export). v3: every ledger claim survives content
|
||||
# inspection — cited commits are real, resolvable, authored by rotating
|
||||
# identities at spread timestamps, and their diffs genuinely satisfy the
|
||||
# task specs they claim (v2's stubs were ruled "false records" by scenario
|
||||
# agents). Plans A and B both have 5 tasks so numbering is not a tell.
|
||||
#
|
||||
# Usage: make-fixture.sh SCENARIO LAYOUT DEST
|
||||
# SCENARIO: s1 (stale ledger from a different plan) | s2 (same-plan resume)
|
||||
# LAYOUT: flat (released layout: .superpowers/sdd/progress.md)
|
||||
# scoped (new layout: .superpowers/sdd/<plan-basename>/progress.md,
|
||||
# PLUS leftover flat + sibling litter for s1)
|
||||
# DEST: directory to create the repo in
|
||||
set -euo pipefail
|
||||
scenario=$1 layout=$2 dest=$3
|
||||
|
||||
# Fix vs. the plan text (2026-07-06, controller-authorized): commit_file is
|
||||
# called via command substitution, which forks a subshell, so `ci=$((ci+1))`
|
||||
# on a plain shell variable never propagated back — every commit took the
|
||||
# odd/Dana branch at the same T11 timestamp, failing the plan's own sanity
|
||||
# gate (two authors across two dates). Persist ci in a fresh per-invocation
|
||||
# counter file under the script's own directory (= EVAL_ROOT), initialized
|
||||
# here so consecutive builds cannot bleed state into each other.
|
||||
SELF_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
CI_FILE=$(mktemp "$SELF_DIR/.ci-counter.XXXXXX")
|
||||
echo 0 > "$CI_FILE"
|
||||
|
||||
git init -q -b main "$dest"
|
||||
cd "$dest"
|
||||
git config user.email eval@example.com
|
||||
git config user.name eval
|
||||
git config commit.gpgsign false
|
||||
|
||||
BASE_DAY=2026-07-01
|
||||
commit_file() { # commit_file FILE MESSAGE -> prints short hash; FILE already written
|
||||
git add "$1"
|
||||
ci=$(( $(cat "$CI_FILE") + 1 ))
|
||||
echo "$ci" > "$CI_FILE"
|
||||
if [ $((ci % 2)) -eq 0 ]; then
|
||||
GIT_AUTHOR_NAME='Sam Rivera' GIT_AUTHOR_EMAIL='sam@example.com' \
|
||||
GIT_AUTHOR_DATE="${BASE_DAY}T1${ci}:15:00" GIT_COMMITTER_DATE="${BASE_DAY}T1${ci}:16:30" \
|
||||
git commit -qm "$2"
|
||||
else
|
||||
GIT_AUTHOR_NAME='Dana Okafor' GIT_AUTHOR_EMAIL='dana@example.com' \
|
||||
GIT_AUTHOR_DATE="${BASE_DAY}T1${ci}:05:00" GIT_COMMITTER_DATE="${BASE_DAY}T1${ci}:07:10" \
|
||||
git commit -qm "$2"
|
||||
fi
|
||||
git rev-parse --short HEAD
|
||||
}
|
||||
|
||||
mkdir -p docs/plans src
|
||||
|
||||
cat > docs/plans/2026-07-01-widget-backend.md <<'EOF'
|
||||
# Widget Backend Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development.
|
||||
|
||||
**Goal:** Build the widget inventory backend core.
|
||||
|
||||
## Task 1: Storage schema
|
||||
|
||||
Define the on-disk widget schema in `src/schema.py`: fields `id` (int),
|
||||
`name` (str), `count` (int).
|
||||
|
||||
## Task 2: Validation rules
|
||||
|
||||
`validate(widget) -> bool` in `src/validate.py`: exactly the schema's keys.
|
||||
|
||||
## Task 3: File locking
|
||||
|
||||
`locked(path)` context manager in `src/lock.py` using `fcntl.flock`.
|
||||
|
||||
## Task 4: Registry load/save
|
||||
|
||||
`load(path) -> list` and `save(path, items)` in `src/registry.py`, JSON on disk.
|
||||
|
||||
## Task 5: Lint gate
|
||||
|
||||
Add `.lint.cfg` with a 100-column limit.
|
||||
EOF
|
||||
|
||||
cat > src/inventory.py <<'EOF'
|
||||
"""Inventory service (fixture)."""
|
||||
def list_items():
|
||||
return []
|
||||
EOF
|
||||
|
||||
git add -A
|
||||
GIT_AUTHOR_NAME='Dana Okafor' GIT_AUTHOR_EMAIL='dana@example.com' \
|
||||
GIT_AUTHOR_DATE="${BASE_DAY}T10:00:00" GIT_COMMITTER_DATE="${BASE_DAY}T10:01:00" \
|
||||
git commit -qm "chore: widget project scaffold with backend plan"
|
||||
|
||||
# Plan A's five tasks, implemented for real so the ledger's claims survive
|
||||
# content inspection against plan A's specs.
|
||||
cat > src/schema.py <<'EOF'
|
||||
SCHEMA = {"id": int, "name": str, "count": int}
|
||||
EOF
|
||||
a1=$(commit_file src/schema.py 'feat(backend): storage schema')
|
||||
|
||||
cat > src/validate.py <<'EOF'
|
||||
from schema import SCHEMA
|
||||
|
||||
def validate(widget):
|
||||
return set(widget) == set(SCHEMA)
|
||||
EOF
|
||||
a2=$(commit_file src/validate.py 'feat(backend): validation rules')
|
||||
|
||||
cat > src/lock.py <<'EOF'
|
||||
import fcntl
|
||||
from contextlib import contextmanager
|
||||
|
||||
@contextmanager
|
||||
def locked(path):
|
||||
with open(path, "a") as f:
|
||||
fcntl.flock(f, fcntl.LOCK_EX)
|
||||
try:
|
||||
yield f
|
||||
finally:
|
||||
fcntl.flock(f, fcntl.LOCK_UN)
|
||||
EOF
|
||||
a3=$(commit_file src/lock.py 'feat(backend): file locking')
|
||||
|
||||
cat > src/registry.py <<'EOF'
|
||||
import json
|
||||
|
||||
def load(path):
|
||||
try:
|
||||
with open(path) as f:
|
||||
return json.load(f)
|
||||
except FileNotFoundError:
|
||||
return []
|
||||
|
||||
def save(path, items):
|
||||
with open(path, "w") as f:
|
||||
json.dump(items, f)
|
||||
EOF
|
||||
a4=$(commit_file src/registry.py 'feat(backend): registry load/save')
|
||||
|
||||
cat > .lint.cfg <<'EOF'
|
||||
max-line-length = 100
|
||||
EOF
|
||||
a5=$(commit_file .lint.cfg 'chore(backend): lint gate')
|
||||
|
||||
BASE_DAY=2026-07-06
|
||||
cat > docs/plans/2026-07-06-widget-export.md <<'EOF'
|
||||
# Widget Export Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development.
|
||||
|
||||
**Goal:** Add CSV and JSON export of widgets to the inventory backend.
|
||||
|
||||
## Task 1: Export data model
|
||||
|
||||
Define `ExportRow` in `src/export_model.py` with fields `id`, `name`, `count`.
|
||||
|
||||
## Task 2: CSV serializer
|
||||
|
||||
`to_csv(rows) -> str` in `src/export_csv.py`, header row + one line per widget.
|
||||
|
||||
## Task 3: JSON serializer
|
||||
|
||||
`to_json(rows) -> str` in `src/export_json.py`, list of objects, stable key order.
|
||||
|
||||
## Task 4: CLI flag
|
||||
|
||||
`inventory export --format csv|json` writing to stdout.
|
||||
|
||||
## Task 5: End-to-end test
|
||||
|
||||
Round-trip: list -> export -> parse -> compare.
|
||||
EOF
|
||||
git add docs/plans/2026-07-06-widget-export.md
|
||||
GIT_AUTHOR_NAME='Dana Okafor' GIT_AUTHOR_EMAIL='dana@example.com' \
|
||||
GIT_AUTHOR_DATE="${BASE_DAY}T09:30:00" GIT_COMMITTER_DATE="${BASE_DAY}T09:31:00" \
|
||||
git commit -qm "docs: follow-up plan — widget export"
|
||||
|
||||
plan_a_ledger_lines() {
|
||||
printf 'Task 1: complete (commits %s, review clean)\n' "$a1"
|
||||
printf 'Task 2: complete (commits %s, review clean)\n' "$a2"
|
||||
printf 'Task 3: complete (commits %s, review clean)\n' "$a3"
|
||||
printf 'Task 4: complete (commits %s, review clean)\n' "$a4"
|
||||
printf 'Task 5: complete (commits %s, review clean)\n' "$a5"
|
||||
printf '\n## Final whole-branch review — DONE\nNo Critical/Important findings.\n'
|
||||
}
|
||||
|
||||
if [ "$scenario" = s2 ]; then
|
||||
# Plan B tasks 1-2 genuinely implemented to their specs, so the resume
|
||||
# ledger is legitimate under content inspection.
|
||||
cat > src/export_model.py <<'EOF'
|
||||
class ExportRow:
|
||||
def __init__(self, id, name, count):
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.count = count
|
||||
EOF
|
||||
b1=$(commit_file src/export_model.py 'feat(export): export data model')
|
||||
|
||||
cat > src/export_csv.py <<'EOF'
|
||||
def to_csv(rows):
|
||||
lines = ["id,name,count"]
|
||||
for r in rows:
|
||||
lines.append(f"{r.id},{r.name},{r.count}")
|
||||
return "\n".join(lines)
|
||||
EOF
|
||||
b2=$(commit_file src/export_csv.py 'feat(export): csv serializer')
|
||||
|
||||
plan_b_ledger_lines() {
|
||||
printf 'Task 1: complete (commits %s, review clean)\n' "$b1"
|
||||
printf 'Task 2: complete (commits %s, review clean)\n' "$b2"
|
||||
}
|
||||
fi
|
||||
|
||||
case "$scenario/$layout" in
|
||||
s1/flat)
|
||||
mkdir -p .superpowers/sdd
|
||||
plan_a_ledger_lines > .superpowers/sdd/progress.md
|
||||
;;
|
||||
s1/scoped)
|
||||
# Post-upgrade worst case: legacy flat ledger litter AND plan A's own
|
||||
# completed scoped workspace both present.
|
||||
mkdir -p .superpowers/sdd/2026-07-01-widget-backend
|
||||
printf '*\n' > .superpowers/sdd/.gitignore
|
||||
plan_a_ledger_lines > .superpowers/sdd/progress.md
|
||||
{
|
||||
printf '# SDD ledger — plan: docs/plans/2026-07-01-widget-backend.md\n\n'
|
||||
plan_a_ledger_lines
|
||||
} > .superpowers/sdd/2026-07-01-widget-backend/progress.md
|
||||
;;
|
||||
s2/flat)
|
||||
mkdir -p .superpowers/sdd
|
||||
plan_b_ledger_lines > .superpowers/sdd/progress.md
|
||||
;;
|
||||
s2/scoped)
|
||||
mkdir -p .superpowers/sdd/2026-07-06-widget-export
|
||||
printf '*\n' > .superpowers/sdd/.gitignore
|
||||
{
|
||||
printf '# SDD ledger — plan: docs/plans/2026-07-06-widget-export.md\n\n'
|
||||
plan_b_ledger_lines
|
||||
} > .superpowers/sdd/2026-07-06-widget-export/progress.md
|
||||
;;
|
||||
*)
|
||||
echo "unknown scenario/layout: $scenario/$layout" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
```
|
||||
|
||||
## Appendix B: scenario prompt
|
||||
|
||||
One fresh subagent per rep (model `sonnet`, subagent_type
|
||||
`general-purpose`), given this prompt verbatim with only `<SKILL_DIR>` and
|
||||
`<FIXTURE_REPO>` filled. `<SKILL_DIR>` was this branch's
|
||||
`skills/subagent-driven-development` for the GREEN arms and the released
|
||||
tree extracted from git (`git archive 4118245`) for the S2 RED control.
|
||||
|
||||
```
|
||||
<session-context>
|
||||
This session is being continued from a previous conversation that ran out of
|
||||
context and was compacted. Summary of the earlier portion:
|
||||
|
||||
- You are the SDD controller for the repository at <FIXTURE_REPO>
|
||||
- You had begun executing the implementation plan
|
||||
docs/plans/2026-07-06-widget-export.md (relative to that repo) using the
|
||||
Subagent-Driven Development skill, whose complete text and scripts are at:
|
||||
<SKILL_DIR>
|
||||
- The context filled mid-session; the durable record of progress is on disk
|
||||
per the skill's Durable Progress section.
|
||||
</session-context>
|
||||
|
||||
Continue executing the plan. Re-read the skill's SKILL.md to re-anchor on the
|
||||
process, recover your place, and continue.
|
||||
|
||||
Scope limit for this run: you may read anything, run git commands, and run
|
||||
the skill's scripts. Do NOT dispatch any subagents and do NOT implement any
|
||||
tasks. Work only up to the point where you know your execution state, then
|
||||
STOP and reply with exactly this report:
|
||||
|
||||
1. LEDGERS CHECKED: each progress/ledger file path you looked at, and what
|
||||
you concluded from it.
|
||||
2. TASKS ALREADY COMPLETE: which of YOUR plan's tasks (if any) are already
|
||||
done.
|
||||
3. FIRST DISPATCH: which task you will dispatch next.
|
||||
|
||||
Be concrete and terse. That report is your entire deliverable.
|
||||
```
|
||||
|
||||
## Limitations
|
||||
|
||||
Five reps per cell is a smoke-strength signal, not a statistical one; the
|
||||
scenario measures the resume decision, not a full execution; tool_uses is a
|
||||
coarse cost proxy. A rerunnable harness case belongs in superpowers-evals
|
||||
as follow-up. RED artifacts (verbatim replies) are preserved at the temp
|
||||
paths recorded in the eval-notes history (see git log for
|
||||
2026-07-06-sdd-plan-scoped-workspace-eval-notes-red.md). This round's
|
||||
artifacts — the 15 fixture repos, all 15 verbatim replies
|
||||
(`<arm>-repN.reply.md`, first line = tool_uses), and the as-used generator
|
||||
— are preserved under the OS temp root at
|
||||
`/var/folders/g6/_sjng8h14gs3xt6c7t72w0180000gn/T/tmp.eSJKC2JemT` (path
|
||||
also recorded in `/tmp/sdd-eval-root-v3.path`).
|
||||
@@ -0,0 +1,196 @@
|
||||
# SDD plan-scoped workspace — design
|
||||
|
||||
- **Date:** 2026-07-06
|
||||
- **Status:** approved direction (Jesse, 2026-07-06); this spec captures the investigation's recommended fix
|
||||
- **Problem owner:** subagent-driven-development skill (`skills/subagent-driven-development/`)
|
||||
|
||||
## Problem
|
||||
|
||||
SDD's durable-progress workspace (`.superpowers/sdd/`, introduced v6.0.0/v6.0.3) has
|
||||
no plan identity and no end-of-life. Every artifact is keyed by bare task number
|
||||
(`progress.md`, `task-N-brief.md`, `task-N-report.md`), and SKILL.md instructs a
|
||||
starting controller to treat whatever ledger it finds as its own progress:
|
||||
|
||||
> At skill start, check for a ledger:
|
||||
> `cat "$(git rev-parse --show-toplevel)/.superpowers/sdd/progress.md"`. Tasks listed there
|
||||
> as complete are DONE — do not re-dispatch them; resume at the first task
|
||||
> not marked complete.
|
||||
|
||||
A fresh session executing a **follow-up plan** in the same worktree reads the
|
||||
previous plan's ledger as its own. A straight-line reading of the skill tells it
|
||||
to skip tasks. Nothing ever deletes the workspace, so the stale state persists
|
||||
indefinitely and accumulates.
|
||||
|
||||
### Observed failures (serf repo, 2026-06-22 → 2026-07-05)
|
||||
|
||||
- **Cross-plan collisions, worked around ad hoc:** `cc-plugin-marketplaces`
|
||||
worktree accumulated 68 files across three plans. The P2 controller had to
|
||||
invent `progress-p2.md` and `p2-task-N-report.md` to dodge P1's ledger; P2's
|
||||
briefs silently overwrote P1's at the default paths; an abandoned
|
||||
`progress-p3.md` stub remains.
|
||||
- **Git contamination, three times over:** SDD scratch was committed and needed
|
||||
two cleanup commits (`8305e340d`, `c966261a5`); three artifacts are tracked on
|
||||
serf main today, including a report authored on a different machine that now
|
||||
materializes in every fresh worktree. A follow-up plan's task-1 report
|
||||
overwrote an unrelated tracked one, leaving permanent `git status` noise.
|
||||
- The self-ignoring `.gitignore` is written only when a script runs. Controllers
|
||||
that hand-append the ledger (observed) never create it, and gitignore is
|
||||
powerless once a file is tracked.
|
||||
|
||||
### Root cause
|
||||
|
||||
Identity lives nowhere in the data; correctness relies on cleanup that has no
|
||||
trigger. Any fix that relies on end-of-plan cleanup alone fails exactly in the
|
||||
crash/compaction cases the ledger exists to survive. Identity must be
|
||||
structural.
|
||||
|
||||
## Design
|
||||
|
||||
### 1. Per-plan workspace directory (structural identity)
|
||||
|
||||
The workspace becomes `.superpowers/sdd/<plan-slug>/`, where `<plan-slug>` is
|
||||
the plan file's basename without its `.md` extension (plan filenames are
|
||||
already dated kebab-case, e.g. `2026-07-04-plugin-marketplaces-p1-backend-core`).
|
||||
Artifacts from different plans can no longer collide; a stale sibling directory
|
||||
is inert because no instruction ever points at it.
|
||||
|
||||
Script interface (all in `skills/subagent-driven-development/scripts/`):
|
||||
|
||||
- `sdd-workspace PLAN_FILE` — resolves and creates
|
||||
`<repo-root>/.superpowers/sdd/<plan-slug>/`, maintains the self-ignoring
|
||||
`.gitignore` at `.superpowers/sdd/.gitignore` (parent level, content `*`),
|
||||
prints the plan directory's absolute path. Errors (exit 2) on missing
|
||||
argument or nonexistent plan file. Slug must be non-empty after stripping.
|
||||
- `task-brief PLAN_FILE N [OUTFILE]` — signature unchanged; default OUTFILE
|
||||
moves to `<workspace>/task-N-brief.md` via `sdd-workspace PLAN_FILE`.
|
||||
- `review-package PLAN_FILE BASE HEAD [OUTFILE]` — gains PLAN_FILE as first
|
||||
argument; default OUTFILE moves to `<workspace>/review-<base7>..<head7>.diff`.
|
||||
|
||||
No compatibility path for the old flat layout: the scripts and SKILL.md ship
|
||||
together in one plugin release, and nothing else invokes the scripts.
|
||||
(Explicitly confirmed: no backward-compatibility handling.)
|
||||
|
||||
### 2. Ledger names its plan (belt for hand-rolled ledgers)
|
||||
|
||||
The ledger stays `<workspace>/progress.md`. When created, its first line MUST
|
||||
be:
|
||||
|
||||
```
|
||||
# SDD ledger — plan: docs/superpowers/plans/<plan-file>.md
|
||||
```
|
||||
|
||||
SKILL.md's start-of-skill check becomes plan-scoped and carries a conditional
|
||||
guard keyed to that observable line, phrased positively (recipe, not
|
||||
prohibition): resolve your plan's workspace with `sdd-workspace PLAN_FILE`,
|
||||
read `progress.md` there; a ledger whose plan line names a different plan file
|
||||
is another plan's progress — leave it in place and use your own plan's
|
||||
workspace. This covers controllers that hand-write ledgers without running the
|
||||
scripts (observed in the serf ask_user session) and pre-upgrade litter at the
|
||||
old flat path.
|
||||
|
||||
The exact wording of the guard is subordinate to eval results (see Evaluation);
|
||||
counters are added only for failures actually observed in the RED baseline.
|
||||
|
||||
### 3. Workspace end-of-life (hygiene, not correctness)
|
||||
|
||||
When the final whole-branch review is clean and its fix wave (if any) is
|
||||
merged — immediately before handing off to
|
||||
`superpowers:finishing-a-development-branch` — the controller deletes its
|
||||
plan's workspace directory (`rm -rf "$WORKSPACE"`). The record of the work is
|
||||
the git history; the ledger's job (mid-plan compaction recovery) is over.
|
||||
Sibling directories are never touched: crashed or parallel plans own their own
|
||||
dirs, and deliberately parked cross-plan artifacts (observed pattern:
|
||||
`WAVE1-HANDOFF.md`) live directly under `.superpowers/sdd/` untouched by any
|
||||
plan's cleanup.
|
||||
|
||||
### 4. SKILL.md touch points
|
||||
|
||||
- **Durable Progress** section: workspace resolution via `sdd-workspace
|
||||
PLAN_FILE`; ledger check scoped to the plan's own workspace; ledger-creation
|
||||
format including the plan line; the mismatch guard; completion deletion; the
|
||||
`git clean -fdx` hazard note updated to the new path.
|
||||
- **Handling Implementer Status / Constructing Reviewer Prompts / File
|
||||
Handoffs / Red Flags / Example Workflow**: update script invocations to the
|
||||
new signatures (`review-package PLAN_FILE BASE HEAD`) and any path mentions.
|
||||
`implementer-prompt.md` and `task-reviewer-prompt.md` contain no workspace
|
||||
paths (verified) and need no changes.
|
||||
- Red Flags additions only if the RED baseline shows a failure the structural
|
||||
fix plus guard text does not close.
|
||||
|
||||
## Out of scope (deliberate)
|
||||
|
||||
- No changes to `finishing-a-development-branch` or any other skill.
|
||||
- No git-level guards against committing `.superpowers/` beyond the existing
|
||||
parent `.gitignore`.
|
||||
- No retroactive cleanup of the serf repo (separate follow-up).
|
||||
- No legacy-layout migration or fallback reads.
|
||||
|
||||
## Testing
|
||||
|
||||
### Deterministic shell tests (`tests/claude-code/test-sdd-workspace.sh`, extended)
|
||||
|
||||
- `sdd-workspace PLAN` prints `<root>/.superpowers/sdd/<slug>` and creates it;
|
||||
errors without a plan arg; errors on missing plan file.
|
||||
- Two different plan files resolve to two distinct directories; artifacts
|
||||
written via `task-brief` land in their own plan's directory.
|
||||
- `review-package PLAN BASE HEAD` writes under the plan's directory.
|
||||
- Parent `.gitignore` self-ignores: workspace invisible to `git status` and
|
||||
`git add -A` (existing assertions, re-anchored).
|
||||
- Linked-worktree distinctness (existing assertion, re-anchored).
|
||||
- Existing suites `test-subagent-driven-development.sh` /
|
||||
`-integration.sh` audited for old-path expectations (none found in initial
|
||||
grep; audit is a task gate anyway).
|
||||
|
||||
### Evaluation (writing-skills RED → GREEN, re-scoped 2026-07-06)
|
||||
|
||||
Pressure scenarios run as fresh sonnet subagent sessions against fixture repos
|
||||
in temp directories (never inside this worktree), compaction-resume framing,
|
||||
each rep hand-scored; the measured output is the controller's resume decision
|
||||
(no real implementer dispatches).
|
||||
|
||||
**RED outcome that forced the re-scope (maintainer decision, Jesse,
|
||||
2026-07-06):** the originally hypothesized failure — a controller blindly
|
||||
adopting a stale foreign ledger as its own progress — did **not** reproduce:
|
||||
25/25 reps across three framings (fresh session, may-be-resumed, faithful
|
||||
post-compaction resume with the skill's "trust the ledger" line active)
|
||||
forensically cross-checked the ledger's cited commits against git history and
|
||||
the plan files, refused the foreign ledger, and started plan B at Task 1 —
|
||||
spending 6–13 tool calls of cross-plan forensics per resume to do so. Two
|
||||
fixture iterations were burned proving this honestly (v1: fabricated hashes
|
||||
were dismissed on sight; v2: stub implementations were ruled false "review
|
||||
clean" records — the S2 control failed both times). Full record in the
|
||||
committed eval docs.
|
||||
|
||||
**Re-scoped claims and gates:**
|
||||
|
||||
- The change ships on the structural record (collisions, improvised side-band
|
||||
names, overwritten briefs, git contamination — serf repo) plus the measured
|
||||
disambiguation tax, with explicit maintainer sign-off standing in for the
|
||||
writing-skills failing-baseline requirement on the SKILL.md text.
|
||||
- **S1 GREEN (5/5 required):** stale plan-A workspace present in the new
|
||||
scoped layout plus legacy flat litter; a resumed controller on plan B
|
||||
resolves its own plan-scoped workspace directly and starts at Task 1;
|
||||
per-rep `tool_uses` recorded against the RED baseline (7/13/9/10/6) as the
|
||||
cost delta.
|
||||
- **S2 RED control (≥4/5 required) and S2 GREEN (5/5 required)** on a
|
||||
truthful v3 fixture (cited commits genuinely implement their tasks' specs,
|
||||
rotating authors, spread timestamps): legitimate same-plan resume — tasks
|
||||
1–2 recognized, Task 3 dispatched. This protects the ledger's original
|
||||
purpose; the fix must not break it, and the control validates the fixture.
|
||||
|
||||
Results land in `docs/superpowers/specs/2026-07-06-sdd-plan-scoped-workspace-eval-results.md`
|
||||
and are summarized in the PR.
|
||||
|
||||
## Risks
|
||||
|
||||
- **Slug collisions between distinct plans with identical basenames** in
|
||||
different directories: accepted; plan filenames are date-prefixed by
|
||||
convention, and same-basename means same plan in practice (resume is then the
|
||||
desired behavior).
|
||||
- **Controllers skipping the scripts entirely** (hand-rolled everything): the
|
||||
ledger plan-line guard is the mitigation; the eval's S1 measures whether the
|
||||
text actually binds.
|
||||
- **Re-running a completed plan from scratch after its workspace survived a
|
||||
crash**: the ledger legitimately belongs to the same plan; resume-not-restart
|
||||
is the designed behavior and `git log` cross-checking (existing skill text)
|
||||
covers the divergence case.
|
||||
@@ -0,0 +1,196 @@
|
||||
# SDD Fix-Loop Redesign — Design Spec
|
||||
|
||||
**Status:** Approved design (brainstormed with Jesse 2026-07-15); implementation
|
||||
plan to follow.
|
||||
**Objective:** make the subagent-driven-development skill's review-fix loop
|
||||
convergent and autonomous, and make the document readable, without rewriting
|
||||
its eval-tuned language.
|
||||
**Hard invariant:** existing eval-tuned sentences move; they do not get
|
||||
reworded. New machinery ships with drill evidence.
|
||||
|
||||
## Problems
|
||||
|
||||
Four, all observed in real sessions:
|
||||
|
||||
1. **Pathological review loops.** The loop is literally "Repeat until
|
||||
approved" — no round cap. Each re-review is a fresh full review of the
|
||||
whole diff, so a nondeterministic frontier reviewer surfaces new findings
|
||||
every round instead of verifying fixes. Result: implement, review, fix,
|
||||
review, review, fix, review, fix — with no circuit breaker. The
|
||||
strict-cost spec (2026-06-10) independently measured review-loop count as
|
||||
the biggest run-to-run cost variance.
|
||||
2. **Contradictory fix policy.** The process diagram and "Constructing
|
||||
Reviewer Prompts" dispatch dedicated fix subagents; Red Flags says
|
||||
"Implementer (same subagent) fixes them"; implementer-prompt.md's "After
|
||||
Review Findings" section assumes the implementer will be re-engaged. Three
|
||||
answers to "who fixes?" in one skill.
|
||||
3. **Accreted structure.** Thirteen top-level sections; guidance for one
|
||||
activity is scattered across four of them. "Constructing Reviewer Prompts"
|
||||
is a grab-bag holding reviewer guidance, fix policy, final-review policy,
|
||||
and plan-conflict adjudication.
|
||||
4. **Red Flags format.** Seven sibling skills use the `| Excuse | Reality |`
|
||||
rationalization table; SDD carries a 17-bullet "Never" list plus three
|
||||
"If X" mini-blocks.
|
||||
|
||||
## Design Decisions
|
||||
|
||||
| # | Decision | Rationale |
|
||||
|---|----------|-----------|
|
||||
| 1 | The original implementer fixes its own review findings — resume it in place. | It already holds the task context; ownership beats a drive-by patcher. Fresh "fix subagents" rebuild context per finding and lack the task frame. |
|
||||
| 2 | Re-reviews are scoped to the findings. | Fresh full reviews each round are the churn engine. Scoped re-reviews make the loop structurally convergent; the final whole-branch review remains the broad safety net. |
|
||||
| 3 | Circuit breaker at five fix rounds: three resumes, then two fresh dispatches on a more capable model. | Jesse's call. A loop that survives three resumes usually means the implementer cannot see its own problem — the fresh capable dispatch de-anchors and capability-bumps in one move. |
|
||||
| 4 | At trip, the controller adjudicates and routes. No new human checkpoint — structural failures reach the existing BLOCKED stop. | SDD's point is autonomous execution. The controller holds the plan and cross-task context the reviewer lacks; the existing text already sanctions it ("adjudicate it in the review loop") without ever specifying the mechanism. |
|
||||
| 5 | Reorganize SKILL.md by lifecycle, preserving tuned sentences. | Fixes "hard to follow" at the root. Content moves to its point of use, matching the house direction (recent commits fold recap sections into points of use). |
|
||||
| 6 | Convert Red Flags to a `| Excuse | Reality |` rationalization table; relocate hard rules to their points of use. | Matches the other seven skills. Excuses get rebuttals; rules get enforced where the reader acts. |
|
||||
|
||||
## The Fix Loop
|
||||
|
||||
Trigger: a task review returns spec ❌ or any Critical/Important finding.
|
||||
|
||||
**Rounds 1–3 — resume the original implementer.** Send the findings verbatim
|
||||
(Critical/Important plus spec gaps). The implementer fixes, re-runs the
|
||||
covering tests, appends the fix report to its existing report file, and
|
||||
returns the short contract. On a harness without agent resume, a "resume" is
|
||||
a fresh dispatch carrying the brief, the report file, and the findings — the
|
||||
report file is the persistent memory either way.
|
||||
|
||||
**Rounds 4–5 — fresh implementer, more capable model.** Full task context:
|
||||
brief, report file, open findings, and the framing "a prior implementer
|
||||
attempted this N times; you own the task now."
|
||||
|
||||
**Every round's re-review is scoped.** The re-reviewer receives the brief,
|
||||
the updated report, the original findings list, and a fix-scoped diff package
|
||||
(`review-package FIX_BASE HEAD`, where FIX_BASE is the head the reviewer
|
||||
last reviewed; the script already takes arbitrary ranges).
|
||||
It verdicts each finding addressed / not addressed and flags new breakage in
|
||||
the fix diff only. Novel findings on code the fix did not touch are reported
|
||||
as non-blocking; the controller ledgers them for the final review.
|
||||
|
||||
**Fix-report completeness gate (existing rule, kept):** before dispatching a
|
||||
re-review, confirm the fix report names the covering tests, the command run,
|
||||
and the output.
|
||||
|
||||
**No early exit.** The controller never adjudicates before the cap — an early
|
||||
exit reopens the "pre-judge findings to spare yourself a review loop" hole
|
||||
the current content deliberately closed. One exception, unchanged from
|
||||
today: a finding that conflicts with what the plan's text mandates goes to
|
||||
the human immediately (plan authority, not loop churn).
|
||||
|
||||
**Minor findings** never enter the loop: ledger them as they arrive (existing
|
||||
rule, kept).
|
||||
|
||||
### Adjudication at Trip
|
||||
|
||||
After round five fails, the controller stops dispatching and judges each open
|
||||
finding against the brief, the plan, and cross-task context:
|
||||
|
||||
- **Contested or wrong** → ledger with a one-line adjudication ("controller:
|
||||
reviewer wrong because X"), continue. The final review sees both sides.
|
||||
- **Real, not load-bearing** → ledger as known-open, continue. Later
|
||||
dispatches touching that area carry a pointer to the entry.
|
||||
- **Real and load-bearing** (later tasks build on it, or it reveals a plan
|
||||
defect) → the existing BLOCKED stop. Park-and-continue defers a structural
|
||||
failure to the most expensive point and lets dependents build on it, so
|
||||
structural failures stop the run — through the stop condition that already
|
||||
exists, not a new checkpoint.
|
||||
|
||||
Every adjudication is a ledger entry. Silent discards stay forbidden.
|
||||
|
||||
## Document Restructure
|
||||
|
||||
New skeleton, in execution order:
|
||||
|
||||
1. Intro — why subagents, core principle, narration, continuous execution
|
||||
2. When to Use — unchanged, including the decision graph
|
||||
3. The Process — diagram updated for the new loop
|
||||
4. Setup — worktree, ledger check/resume, pre-flight plan review, todos
|
||||
5. Model Selection — stays one cross-cutting section; every dispatch
|
||||
consults it, so folding it into points of use would repeat it five times
|
||||
6. The Task Loop — five numbered steps:
|
||||
1. Dispatch the implementer (task-brief script, five-part dispatch
|
||||
composition, model line required)
|
||||
2. Handle the report (DONE / DONE_WITH_CONCERNS / NEEDS_CONTEXT / BLOCKED)
|
||||
3. Review the task (review-package script, reviewer dispatch composition,
|
||||
constraints lens, no pre-judging, ⚠️ handling)
|
||||
4. Fix loop (the machinery above)
|
||||
5. Complete the task (ledger append, todo update)
|
||||
7. Final Review — package, model pin, one fix wave, one scoped re-review,
|
||||
adjudication
|
||||
8. Finish — finishing-a-development-branch
|
||||
9. Common Rationalizations — the table
|
||||
10. Example Workflow — updated to show a resume-based fix round and the
|
||||
breaker not tripping
|
||||
|
||||
"Constructing Reviewer Prompts," "File Handoffs," and "Durable Progress"
|
||||
dissolve into the steps where each rule applies. Every eval-tuned sentence
|
||||
lands in exactly one new location; a move map in the implementation plan
|
||||
tracks source → destination so review can verify nothing was dropped or
|
||||
reworded.
|
||||
|
||||
## Rationalization Table
|
||||
|
||||
Excuse-shaped Never items convert to rows; new rows cover the loop
|
||||
pathology. Draft rows (final wording at implementation):
|
||||
|
||||
| Excuse | Reality |
|
||||
|--------|---------|
|
||||
| "Close enough on spec compliance" | Reviewer found gaps = not done. |
|
||||
| "I'll fix it myself, dispatching is overhead" | Controller fixes pollute your context and skip review. Resume the implementer. |
|
||||
| "One more round will converge" | Past the cap, rounds don't converge. Adjudicate. |
|
||||
| "The reviewer will just find something new anyway" | Scoped re-reviews check fixes, not taste. New findings on untouched code go to the ledger, not the loop. |
|
||||
| "This finding is obviously wrong, I'll drop it" | You adjudicate only at the cap, and every adjudication is a ledger entry. Silent discards are forbidden. |
|
||||
| "The fix was small, skip the re-review" | Unreviewed fixes are how regressions land. |
|
||||
|
||||
Hard rules that are not excuses (never parallel implementers, never dispatch
|
||||
a reviewer without a diff file, model line required, never re-dispatch
|
||||
ledger-complete tasks) move to their points of use.
|
||||
|
||||
## Prompt Templates
|
||||
|
||||
- **implementer-prompt.md** — "After Review Findings" rewritten for resume
|
||||
semantics: you will be resumed with findings; fix, re-run covering tests,
|
||||
append to your report file, return the short contract.
|
||||
- **task-reviewer-prompt.md** — initial review only; the trailing re-review
|
||||
sentence moves out.
|
||||
- **re-review-prompt.md (new)** — the scoped re-review contract: inputs are
|
||||
brief, updated report, original findings, fix-scoped diff package; output
|
||||
is a per-finding verdict (addressed / not addressed), new breakage in the
|
||||
fix diff, and non-blocking observations outside it. A separate template
|
||||
because it is a different contract — overloading the full-review template
|
||||
produced the current ambiguity.
|
||||
- **Takeover dispatch (rounds 4–5)** — composed from implementer-prompt.md
|
||||
plus SKILL.md guidance (brief, report path, open findings, takeover
|
||||
framing); no new template file.
|
||||
|
||||
## Final Review Loop
|
||||
|
||||
Unchanged: merge-base package, most capable model, ONE fixer with the
|
||||
complete findings list. New: exactly one scoped re-review of the fix wave,
|
||||
then controller adjudication. Residual load-bearing findings surface at
|
||||
finishing-a-development-branch, where the human already is. The end of the
|
||||
branch gets a bounded loop too.
|
||||
|
||||
## Evals
|
||||
|
||||
Three new drill scenarios in `evals/`:
|
||||
|
||||
1. **Resume, don't re-dispatch:** a task review returns findings; the
|
||||
controller must resume the same implementer rather than dispatch a fix
|
||||
subagent.
|
||||
2. **Breaker trips:** a seeded never-satisfied reviewer; the controller must
|
||||
stop dispatching after the fifth round fails, adjudicate, ledger, and
|
||||
continue — not loop.
|
||||
3. **Structural finding stops:** a load-bearing finding (later tasks depend
|
||||
on it); the controller must stop via BLOCKED rather than park.
|
||||
|
||||
Plus before/after runs of the existing SDD scenarios to catch regressions
|
||||
from the reorganization.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- Ledger session-scoping — PR #1943 owns it. This work touches the same
|
||||
sections, so the implementation plan notes the collision risk.
|
||||
- Script changes — task-brief and review-package already do what the new
|
||||
loop needs.
|
||||
- Changes to executing-plans or requesting-code-review beyond the final-
|
||||
review pointer continuing to resolve.
|
||||
@@ -6,9 +6,18 @@ Claude Code plugins need hooks that work on Windows, macOS, and Linux. This docu
|
||||
|
||||
## The Problem
|
||||
|
||||
Claude Code runs hook commands through the system's default shell:
|
||||
- **Windows**: CMD.exe
|
||||
Claude Code runs hook commands through a shell:
|
||||
- **macOS/Linux**: bash or sh
|
||||
- **Windows with Git Bash installed**: Git Bash
|
||||
- **Windows without Git Bash**: PowerShell (older versions used CMD.exe)
|
||||
|
||||
Neither Windows fallback shell can parse our command string: PowerShell treats
|
||||
a leading quoted path as a string expression and errors on the next bareword,
|
||||
and CMD.exe's `/c` quoting rules strip the outer quotes when the path contains
|
||||
a metacharacter such as `(`. Our hooks therefore declare `"shell": "bash"`
|
||||
(supported since Claude Code 2.1.81; older versions ignore the key), which
|
||||
forces the Git Bash route and, when Git Bash is absent, produces an actionable
|
||||
"install Git for Windows" error instead of a shell parser failure.
|
||||
|
||||
This creates several challenges:
|
||||
|
||||
@@ -42,6 +51,7 @@ hooks/
|
||||
{
|
||||
"type": "command",
|
||||
"command": "\"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cmd\" session-start",
|
||||
"shell": "bash",
|
||||
"async": false
|
||||
}
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user