@accidentally-awesome-labs/opencode-bestest 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +2 -2
  2. package/bin/init.ts +1 -1
  3. package/opencode-assets/agents/plan-checker.md +1 -18
  4. package/opencode-assets/agents/researcher.md +1 -10
  5. package/opencode-assets/agents/reviewer.md +1 -14
  6. package/package.json +1 -1
  7. package/payload/VERSION +1 -1
  8. package/payload/agents/plan-checker.md +1 -1
  9. package/payload/hooks/lock-guard.sh +9 -4
  10. package/payload/hooks/session-start.sh +44 -0
  11. package/payload/scripts/trace.sh +228 -16
  12. package/payload/skills/bestest-go/SKILL.md +9 -6
  13. package/payload/skills/bestest-go/references/quick.md +29 -0
  14. package/payload/skills/bestest-status/SKILL.md +4 -3
  15. package/payload/skills/build/SKILL.md +6 -6
  16. package/payload/skills/interrogate/SKILL.md +3 -3
  17. package/payload/skills/plan/SKILL.md +2 -2
  18. package/payload/skills/research/SKILL.md +5 -2
  19. package/payload/skills/research/references/findings.md +20 -0
  20. package/payload/skills/ship/SKILL.md +5 -5
  21. package/payload/skills/using-bestest/references/claude-code.md +2 -1
  22. package/payload/skills/using-bestest/references/codex.md +2 -1
  23. package/payload/skills/using-bestest/references/opencode.md +2 -1
  24. package/payload/templates/CODEBASE.md +26 -0
  25. package/payload/templates/DECISIONS.md +3 -1
  26. package/payload/templates/DISCOVERED.md +6 -0
  27. package/payload/templates/FINDINGS.md +14 -0
  28. package/payload/templates/LESSONS.md +5 -0
  29. package/payload/templates/PLAN.md +18 -0
  30. package/payload/templates/ROADMAP.md +21 -0
  31. package/payload/templates/RQ.md +19 -0
  32. package/payload/templates/SPEC.md +3 -1
  33. package/payload/templates/STATE.md +1 -0
  34. package/payload/templates/quick-card.md +22 -0
  35. package/payload/templates/report.md +5 -0
package/README.md CHANGED
@@ -12,8 +12,8 @@ bunx @accidentally-awesome-labs/opencode-bestest init # or npx
12
12
  `init` is idempotent (re-run it to update) and supports `--dry-run` and `--global` (skills/agents/commands into `~/.config/opencode/`). It:
13
13
 
14
14
  - copies the lifecycle skills into `.opencode/skills/` (prefixed `bestest-*`)
15
- - installs the plan-checker / researcher / reviewer agents and `/bestest-go` + `/bestest-status` commands
16
- - copies the runtime payload (guard scripts, `trace.sh`, templates) to `.opencode/bestest/`
15
+ - installs the plan-checker / researcher / reviewer agents (pointer stubs — their canonical bodies ride the payload, so `--global` installs need a project `init` before the roles resolve) and `/bestest-go` + `/bestest-status` commands
16
+ - copies the runtime payload (guard scripts, `trace.sh`, templates, canonical agent bodies) to `.opencode/bestest/`
17
17
  - wires `opencode.json`: the `@accidentally-awesome-labs/opencode-bestest` plugin (enforcement) and an `instructions` entry that passively injects `.bestest/.context.md` — the plugin-maintained digest of STATE + LOCKS + active card + DRIFT notice
18
18
 
19
19
  Then open opencode and run `/bestest-go`.
package/bin/init.ts CHANGED
@@ -60,7 +60,7 @@ for (const kind of ["agents", "commands"]) {
60
60
 
61
61
  // 4. Runtime payload for project sessions: hooks (shim targets), trace.sh, templates.
62
62
  if (!GLOBAL) {
63
- for (const part of ["hooks", "scripts", "templates"]) {
63
+ for (const part of ["agents", "hooks", "scripts", "templates"]) {
64
64
  doCopy(join(src, part), join(cwd, ".opencode", "bestest", part))
65
65
  }
66
66
  const version = existsSync(join(src, "VERSION")) ? readFileSync(join(src, "VERSION"), "utf8").trim() : "dev"
@@ -7,21 +7,4 @@ permission:
7
7
  webfetch: deny
8
8
  ---
9
9
 
10
- You are the adversarial checker for a bestest project. You are read-only; you report, you never fix. Your job is to be the reviewer who finds the hole *before* it costs a build day. A clean PASS you didn't earn is a failure of yours, not a success of theirs.
11
-
12
- **Spec mode** (given SPEC.md): grade completeness, clarity, consistency, measurability, coverage, traceability. Every finding typed and cited:
13
- - `[Gap]` — something the build will need that no section provides (asset, actor, failure path, data lifecycle).
14
- - `[Ambiguity]` — a sentence two reasonable builders would implement differently. Quote it; say the two readings.
15
- - `[Conflict]` — two statements that cannot both hold. Cite both R-IDs/sections.
16
- - `[Assumption]` — a load-bearing `[assumed]` tag or an untagged claim with no `[user]`/`[cited:]` evidence.
17
- Check every R-N.M is real EARS (trigger + observable behavior, one sentence, testable), anti-goals ≥3, out_of_scope non-empty, user-facing docs dispositioned (R-IDs or out_of_scope), doneness function falsifiable, autonomy contract present. Gaps and Conflicts are freeze-blockers.
18
-
19
- **Plan mode** (given PLAN.md + cards + SPEC): hunt, in order:
20
- 1. R-IDs with no covering card; cards citing phantom R-IDs; orphan cards serving no R-ID.
21
- 2. **Recompute complexity** per card from its own fields (`1 + 2/extra file + 2/verify cmd + 3 if title needs "and" + 2/new interface`); declared < recomputed, or >6 unsplit → blocker.
22
- 3. Zero-context violations: placeholders, "as discussed", missing signatures/paths, a card a fresh stranger couldn't execute.
23
- 4. Assets consumed but never produced (check `blocked_by:` ordering too).
24
- 5. Complexity Justification table: any structure beyond the simplest satisfying design without a row → blocker.
25
- 6. TDD pairing: implementer cards with `may_edit_tests: true`, or no failing-test card ahead of them.
26
-
27
- Verdict format, both modes: `PASS` or `FAIL` + numbered blockers, each with file/R-ID cite and the one-line fix you'd accept. Nits go in a separate short list — never pad blockers.
10
+ Read the file `agents/plan-checker.md` under the bestest plugin root the `plugin:` line in this project's `.bestest/STATE.md` names that root (this harness: `.opencode/bestest`) and follow it exactly for the task you were given. You are read-only: never modify any file.
@@ -8,13 +8,4 @@ permission:
8
8
  bash: deny
9
9
  ---
10
10
 
11
- You research exactly one question (RQ) for a bestest project. Your training data is presumed stale: every claim you return must come from a page you fetched **this session**.
12
-
13
- Method:
14
- 1. Read the RQ file you were given; note the answer shape it needs and which decision it feeds.
15
- 2. Search broadly, then fetch primary sources — official docs, changelogs, pricing pages, GitHub issues/releases — over blogs and search snippets. For version- or price-sensitive facts, find the date on the page.
16
- For version-sensitive TOOL behavior, docs may describe an unreleased major: prefer running the pinned tool itself (`--help`, a scratch project) over reading about it, and cite the run.
17
- 3. Adversarially check each load-bearing claim: one honest attempt to refute it via a second independent source. Disagreement is a finding — report both sides, don't pick silently.
18
- 4. For dependency candidates, run SLOPCHECK: last release, issue trend, bus factor, install weight, license, "does the platform already do this?".
19
-
20
- Write findings into the RQ file: each claim on its own line, tagged `[cited:URL]` (the URL you fetched, not the search result), or `[assumed]` if you genuinely could not verify — with what you tried. Finish with a ≤5-line summary: the answer, confidence, and what would change it. Your final message is just the RQ file path and that summary.
11
+ Read the file `agents/researcher.md` under the bestest plugin root — the `plugin:` line in this project's `.bestest/STATE.md` names that root (this harness: `.opencode/bestest`) and follow it exactly for the research question you were given. Write only into `.bestest/research/`.
@@ -6,17 +6,4 @@ permission:
6
6
  webfetch: deny
7
7
  ---
8
8
 
9
- You review one completed task card for a bestest project. Read-only: you may run read-only commands (tests, linters, `git diff`) but change nothing. You are reviewing the *diff against the card*, not the codebase's life story.
10
-
11
- Inputs: the card, the diff range, the report, SPEC.md, relevant D-IDs.
12
-
13
- **Depth is risk-scaled by card `weight:`**
14
- - **weight ≥ 4 — full adversarial:** actively try to break it. Trace the R-IDs' EARS triggers through the code by hand; probe edge/failure paths the tests skip; check the claim→evidence table by re-running what's cheap; hunt silent contract drift (behavior the SPEC forbids, error paths swallowed, security footguns in the diff).
15
- - **weight < 4 — blockers-only:** correctness of the R-ID behavior, verify commands genuinely proving it, diff staying inside `files:`. No style opinions.
16
-
17
- Always check, any weight:
18
- 1. Tests: do they *assert the criterion* or just execute the code? A test the implementation can't fail is a blocker at weight≥4, a flag below.
19
- 2. Report honesty: any claim whose evidence doesn't hold up = blocker, regardless of code quality.
20
- 3. `Bestest-Req:` trailer present and citing the card's R-IDs.
21
-
22
- Verdict: `APPROVED` or numbered findings, each: severity (blocker/flag), file:line, what breaks, the smallest fix you'd accept. A finding you raised closes only when you re-confirm the fix — say explicitly what you'd need to see. Do not approve out of fatigue; do not block on taste.
9
+ Read the file `agents/reviewer.md` under the bestest plugin root the `plugin:` line in this project's `.bestest/STATE.md` names that root (this harness: `.opencode/bestest`) and follow it exactly for the card you were given. Read-only on product code: run verify commands, never edit.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@accidentally-awesome-labs/opencode-bestest",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "OpenCode head for the bestest plugin: guard/lock/syntax enforcement shelling into the shared bash hooks, passive state injection, and an installer for skills/agents/commands.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/payload/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.0
1
+ 0.6.0
@@ -12,7 +12,7 @@ You are the adversarial checker for a bestest project. You are read-only; you re
12
12
  - `[Ambiguity]` — a sentence two reasonable builders would implement differently. Quote it; say the two readings.
13
13
  - `[Conflict]` — two statements that cannot both hold. Cite both R-IDs/sections.
14
14
  - `[Assumption]` — a load-bearing `[assumed]` tag or an untagged claim with no `[user]`/`[cited:]` evidence.
15
- Check every R-N.M is real EARS (trigger + observable behavior, one sentence, testable), anti-goals ≥3, out_of_scope non-empty, user-facing docs dispositioned (R-IDs or out_of_scope), doneness function falsifiable, autonomy contract present. Gaps and Conflicts are freeze-blockers.
15
+ Check every R-N.M is real EARS (trigger + observable behavior, one sentence, testable), anti-goals ≥3, out_of_scope non-empty, user-facing docs dispositioned (R-IDs or out_of_scope), doneness function falsifiable, autonomy contract present. `deliverable: findings` SPECs: R-IDs are research questions — require a named verdict shape and evidence bar per R-ID instead of EARS. Gaps and Conflicts are freeze-blockers.
16
16
 
17
17
  **Plan mode** (given PLAN.md + cards + SPEC): hunt, in order:
18
18
  1. R-IDs with no covering card; cards citing phantom R-IDs; orphan cards serving no R-ID.
@@ -95,10 +95,15 @@ esac
95
95
  may_edit_tests="false"
96
96
  card="$(grep -m1 -E '^card:' "$root/.bestest/STATE.md" 2>/dev/null | sed -E 's/^card:[[:space:]]*//; s/[[:space:]]*#.*$//; s/[[:space:]]+$//')"
97
97
  if [ -n "${card:-}" ] && [ -f "$root/$card" ]; then
98
- # frontmatter only — a mention of may_edit_tests in the card body must not count
99
- if awk '/^---$/{n++; next} n==1' "$root/$card" | grep -m1 -Eq '^may_edit_tests:[[:space:]]*true'; then
100
- may_edit_tests="true"
101
- fi
98
+ case "${card##*/}" in
99
+ Q-*) ;; # quick lane: may_edit_tests is always false a tampered grant must not count
100
+ *)
101
+ # frontmatter only — a mention of may_edit_tests in the card body must not count
102
+ if awk '/^---$/{n++; next} n==1' "$root/$card" | grep -m1 -Eq '^may_edit_tests:[[:space:]]*true'; then
103
+ may_edit_tests="true"
104
+ fi
105
+ ;;
106
+ esac
102
107
  fi
103
108
 
104
109
  check_one_path() {
@@ -51,6 +51,12 @@ if [ -f "$b/SPEC.md" ]; then
51
51
  echo "Autonomy contract in force: SPEC.md section 9 (PROCEED / INTERRUPT / GATED)."
52
52
  fi
53
53
 
54
+ phase_cur="$(grep -m1 -E '^phase:' "$b/STATE.md" 2>/dev/null | sed -E 's/^phase:[[:space:]]*//; s/[[:space:]]*#.*$//; s/[[:space:]]+$//')"
55
+ if [ "$phase_cur" = "done" ] || [ "$phase_cur" = "quick" ]; then
56
+ echo
57
+ echo "Post-ship contract: phase is done — this project's audit chain (gate log, Bestest-Req trailers) requires every new change, any size, to land through the quick lane (a Q-card per the router skill's references/quick.md; template and trace.sh --quick under the STATE plugin: root) or a milestone rollover. A direct untracked edit breaks the chain the shipped milestone was accepted on."
58
+ fi
59
+
54
60
  if [ -f "$snap" ]; then
55
61
  old_head="$(grep -m1 '^HEAD=' "$snap" | cut -d= -f2-)"
56
62
  old_dirty="$(grep -m1 '^DIRTY=' "$snap" | cut -d= -f2-)"
@@ -60,4 +66,42 @@ if [ -f "$snap" ]; then
60
66
  fi
61
67
  fi
62
68
 
69
+ # STALE-STATE: the injected STATE is only as good as its last rewrite. If product
70
+ # code moved >= STALE_N commits past it, the phase/card/next-action above may lie.
71
+ # Every probe fails silent (no git, never-committed STATE, dirty STATE = just touched).
72
+ STALE_N=5
73
+ state_c="$(git -C "$root" log -1 --format=%H -- .bestest/STATE.md 2>/dev/null || true)"
74
+ state_dirty="$(git -C "$root" status --porcelain -- .bestest/STATE.md 2>/dev/null || true)"
75
+ if [ -n "$state_c" ] && [ -z "$state_dirty" ]; then
76
+ behind="$(git -C "$root" rev-list --count "$state_c..HEAD" -- . ':(exclude).bestest' 2>/dev/null || echo 0)"
77
+ if [ "${behind:-0}" -ge "$STALE_N" ] 2>/dev/null; then
78
+ echo
79
+ echo "STALE-STATE: $behind product commits since STATE.md was last rewritten. The injected phase/card/next-action above may be stale — re-derive them (and Open interrupts) from git log, plans/*.md status, and reports/, rewrite STATE.md, commit, then continue."
80
+ fi
81
+ fi
82
+
83
+ # DECISIONS revisit scan: surface (never act on) waiver/risk lines whose
84
+ # 'revisit' date has arrived. Quieted by an appended 'D-0NN revisited <date>: ...'
85
+ # line — DECISIONS stays append-only. ISO dates compare lexicographically.
86
+ if [ -f "$b/DECISIONS.md" ]; then
87
+ today="$(date +%Y-%m-%d 2>/dev/null || true)"
88
+ if [ -n "$today" ]; then
89
+ due=""
90
+ while IFS= read -r ln; do
91
+ d="$(printf '%s' "$ln" | grep -oE '[0-9]{4}-[0-9]{2}-[0-9]{2}' | head -1)"
92
+ [ -n "$d" ] || continue
93
+ [ "$d" \> "$today" ] && continue
94
+ did="$(printf '%s' "$ln" | grep -oE '(D|RISK)-[0-9]+' | head -1)"
95
+ if [ -n "$did" ] && grep -i 'revisited' "$b/DECISIONS.md" 2>/dev/null | grep -qE "(^|[^0-9])${did}([^0-9]|$)"; then continue; fi
96
+ due="${due}${ln}
97
+ "
98
+ done < <(grep -iE 'revisit' "$b/DECISIONS.md" 2>/dev/null | grep -E '[0-9]{4}-[0-9]{2}-[0-9]{2}' || true)
99
+ if [ -n "$due" ]; then
100
+ echo
101
+ echo "### DECISIONS revisit due (surface only — no auto-action; quiet each with an appended 'D-0NN revisited $today: <outcome>' line)"
102
+ printf '%s' "$due"
103
+ fi
104
+ fi
105
+ fi
106
+
63
107
  exit 0
@@ -12,18 +12,195 @@ b="$root/.bestest"
12
12
  fails=0
13
13
  fail() { fails=$((fails+1)); printf 'FAIL %02d: %s\n' "$fails" "$1"; }
14
14
  front() { awk '/^---$/{n++; next} n==1' "$1"; } # frontmatter body of a card
15
+ rid_re() { printf '(^|[^0-9.])%s([^0-9]|$)' "$(printf '%s' "$1" | sed 's/\./\\./g')"; } # R-1.1 must not match R-1.10
16
+ section_filled() { awk -v s="## $2" '$0 ~ "^"s{f=1;next} /^## /{f=0} f&&/^[[:space:]]*<!--/{next} f&&NF{c++} END{exit c?0:1}' "$1"; }
17
+ check_spikes() { # every spike: block in a research RQ file must carry a disposition:
18
+ for rq in "$b"/research/RQ-*.md; do
19
+ [ -f "$rq" ] || continue
20
+ if grep -qE '^spike:' "$rq" && ! grep -qE '^disposition:' "$rq"; then
21
+ fail "$(basename "$rq") has a spike: block without a disposition: line"
22
+ fi
23
+ done
24
+ }
25
+ check_discovered() { # every DISCOVERED entry needs a disposition by ship
26
+ [ -f "$b/DISCOVERED.md" ] || return 0
27
+ while IFS= read -r line; do
28
+ printf '%s' "$line" | grep -q 'disposition:' \
29
+ || fail "DISCOVERED entry lacks disposition: ${line%%|*}"
30
+ done < <(grep -E '^- ' "$b/DISCOVERED.md")
31
+ }
32
+ check_roadmap() { # check_roadmap <1 to also require a verdict on the current milestone's ledger line>
33
+ ms_cur="$(grep -m1 -E '^milestone:' "$b/STATE.md" 2>/dev/null | sed -E 's/^milestone:[[:space:]]*//; s/[[:space:]]*#.*$//; s/[[:space:]]+$//')"
34
+ if [ ! -f "$b/ROADMAP.md" ]; then
35
+ fail "no $b/ROADMAP.md — copy templates/ROADMAP.md and append this milestone's intent line"
36
+ return 0
37
+ fi
38
+ [ "$(wc -l < "$b/ROADMAP.md" | tr -d ' ')" -le 40 ] || fail "ROADMAP.md exceeds its 40-line cap — compress old ledger lines"
39
+ [ -n "${ms_cur:-}" ] || return 0
40
+ ml="$(grep -E "^[[:space:]]*$ms_cur[[:space:]]*\|" "$b/ROADMAP.md" | head -1)"
41
+ if [ -z "$ml" ]; then
42
+ fail "ROADMAP.md has no ledger line for milestone $ms_cur (append: '$ms_cur | intent: <one clause>')"
43
+ elif [ "$1" = 1 ]; then
44
+ printf '%s' "$ml" | grep -q 'verdict:' || fail "ROADMAP.md ledger line for $ms_cur lacks a verdict — complete it before go/no-go"
45
+ fi
46
+ }
15
47
 
16
48
  [ -f "$b/SPEC.md" ] || { echo "FAIL 01: no $b/SPEC.md — nothing to trace"; exit 1; }
17
49
 
18
50
  rids="$(grep -oE 'R-[0-9]+\.[0-9]+' "$b/SPEC.md" | sort -u)"
19
51
  [ -n "$rids" ] || fail "SPEC.md defines no R-N.M acceptance criteria"
20
52
 
53
+ # --- quick-lane checks (--quick): Q-cards only, then exit --------------------
54
+ if [ "$mode" = "--quick" ]; then
55
+ # tamper canary: the quick lane never amends the SPEC
56
+ lh="$(grep -m1 -E '^hash [0-9a-f]+ \.bestest/SPEC\.md' "$b/LOCKS" 2>/dev/null | awk '{print $2}')"
57
+ if [ -n "${lh:-}" ]; then
58
+ if command -v sha256sum >/dev/null 2>&1; then ch="$(sha256sum "$b/SPEC.md" | cut -d' ' -f1)"
59
+ else ch="$(shasum -a 256 "$b/SPEC.md" | cut -d' ' -f1)"; fi
60
+ [ "$lh" = "$ch" ] || fail "SPEC.md no longer matches its LOCKS hash — the quick lane never amends the SPEC"
61
+ fi
62
+
63
+ qcards="$(ls "$b"/plans/Q-*.md 2>/dev/null || true)"
64
+ [ -n "$qcards" ] || fail "no quick cards in $b/plans/"
65
+ qn="$(printf '%s\n' "$qcards" | grep -c . || true)"
66
+ [ "$qn" -gt 5 ] && fail "$qn quick cards since last rollover — this is a milestone; run rollover"
67
+ manifests='package.json package-lock.json pnpm-lock.yaml yarn.lock bun.lockb Cargo.toml Cargo.lock go.mod go.sum pyproject.toml requirements.txt requirements-dev.txt Gemfile Gemfile.lock pom.xml build.gradle build.gradle.kts composer.json composer.lock'
68
+
69
+ for qc in $qcards; do
70
+ qid="$(basename "$qc" .md)"
71
+ qfm="$(front "$qc")"
72
+ rlist=""
73
+
74
+ printf '%s' "$qfm" | grep -Eq '^may_edit_tests:[[:space:]]*false' \
75
+ || fail "$qid may_edit_tests must be false on the quick lane"
76
+
77
+ qfiles="$(printf '%s' "$qfm" | grep -E '^files:' | head -1 | sed 's/^files:[[:space:]]*//; s/[[:space:]]*#.*$//' | tr ',' '\n' | sed 's/^[[:space:]]*//; s/[[:space:]]*$//' | grep -v '^$' || true)"
78
+ nq="$(printf '%s\n' "$qfiles" | grep -c . || true)"
79
+ { [ "$nq" -ge 1 ] && [ "$nq" -le 3 ]; } || fail "$qid files: must list 1-3 paths (found $nq)"
80
+
81
+ qreq="$(printf '%s' "$qfm" | grep -E '^requirements:' | head -1 | sed 's/^requirements:[[:space:]]*//; s/[[:space:]]*#.*$//; s/[[:space:]]*$//')"
82
+ case "$qreq" in
83
+ none|refactor) : ;;
84
+ *) rlist="$(printf '%s' "$qreq" | grep -oE 'R-[0-9]+\.[0-9]+' || true)"
85
+ [ -n "$rlist" ] || fail "$qid requirements: must be none, refactor, or the R-IDs it restores"
86
+ for r in $rlist; do
87
+ printf '%s\n' "$rids" | grep -qx "$r" || fail "$qid cites $r which is not in SPEC.md"
88
+ done ;;
89
+ esac
90
+
91
+ qdid="$(printf '%s' "$qfm" | grep -E '^decision:' | grep -oE 'D-[0-9]+' | head -1)"
92
+ if [ -n "${qdid:-}" ]; then
93
+ grep -q "$qdid" "$b/DECISIONS.md" 2>/dev/null || fail "$qid decision $qdid not found in DECISIONS.md"
94
+ else
95
+ fail "$qid missing decision: D-### field"
96
+ fi
97
+
98
+ qstart="$(printf '%s' "$qfm" | grep -E '^start:' | head -1 | sed 's/^start:[[:space:]]*//; s/[[:space:]]*#.*$//; s/[[:space:]]*$//')"
99
+ changed=""
100
+ if git -C "$root" rev-parse -q --verify "$qstart^{commit}" >/dev/null 2>&1; then
101
+ changed="$( { git -C "$root" diff --name-only "$qstart"..HEAD; git -C "$root" status --porcelain | awk '{print $NF}'; } 2>/dev/null | grep -v '^\.bestest/' | sort -u || true)"
102
+ while IFS= read -r chp; do
103
+ [ -n "$chp" ] || continue
104
+ printf '%s\n' "$qfiles" | grep -qxF -- "$chp" || fail "$qid touched $chp outside its files: list — escalate (status: escalated + DISCOVERED line)"
105
+ done <<QEOF
106
+ $changed
107
+ QEOF
108
+ else
109
+ fail "$qid start: sha does not resolve"
110
+ fi
111
+
112
+ # neither declared files nor actual changes may hit locked paths or manifests
113
+ for p in $(printf '%s\n%s\n' "$qfiles" "$changed" | grep -v '^$' | sort -u); do
114
+ pb="$(basename "$p")"
115
+ for m in $manifests; do
116
+ [ "$pb" = "$m" ] && fail "$qid touches dependency manifest $p — that is a milestone, not a quick fix"
117
+ done
118
+ [ "$p" = ".bestest/LOCKS" ] && fail "$qid touches the lock registry"
119
+ if [ -f "$b/LOCKS" ]; then
120
+ while IFS= read -r line; do
121
+ kind="${line%% *}"
122
+ case "$kind" in
123
+ hash) hp="$(printf '%s' "$line" | awk '{print $3}')"
124
+ [ "$p" = "$hp" ] && fail "$qid touches hash-locked $p" ;;
125
+ glob) pat="$(printf '%s' "$line" | cut -d' ' -f2-)"
126
+ case "$p" in $pat) fail "$qid touches glob-locked $p — a fix needing test edits is a milestone" ;; esac ;;
127
+ esac
128
+ done < "$b/LOCKS"
129
+ fi
130
+ done
131
+
132
+ qstatus="$(printf '%s' "$qfm" | grep -E '^status:' | head -1 | grep -oE 'doing|done|escalated' | head -1)"
133
+ case "${qstatus:-}" in
134
+ doing) : ;;
135
+ done)
136
+ qrep="$b/reports/$qid.md"
137
+ if [ ! -f "$qrep" ]; then
138
+ fail "$qid is done but report $qrep is missing"
139
+ else
140
+ section_filled "$qrep" FRICTION || fail "$qid report has empty FRICTION section (write 'None' if truly none)"
141
+ section_filled "$qrep" Lessons || fail "$qid report has empty Lessons section (list the LESSONS.md lines appended, or 'None')"
142
+ if [ "$qreq" = refactor ]; then
143
+ section_filled "$qrep" 'Verify output' || fail "$qid refactor card needs the green incumbent-suite run pasted in Verify output"
144
+ fi
145
+ fi
146
+ git -C "$root" log -E --grep="Bestest-Req:.*$qid([^0-9]|\$)" --oneline -1 2>/dev/null | grep -q . \
147
+ || fail "$qid has no commit with trailer 'Bestest-Req: $qid'"
148
+ if [ -n "$rlist" ]; then
149
+ for r in $rlist; do
150
+ grep -E "$(rid_re "$r")" "$b/UAT.md" 2>/dev/null | grep -Eq "requick|$qid" \
151
+ || fail "$r restored by $qid needs a requick row in UAT.md"
152
+ done
153
+ fi
154
+ ;;
155
+ escalated)
156
+ grep -q "discovered-from: $qid" "$b/DISCOVERED.md" 2>/dev/null \
157
+ || fail "$qid escalated without a DISCOVERED.md line (discovered-from: $qid)"
158
+ ;;
159
+ *) fail "$qid status must be doing | done | escalated" ;;
160
+ esac
161
+ done
162
+
163
+ if [ "$fails" -eq 0 ]; then
164
+ echo "PASS: $qn quick cards, chain clean (--quick)"
165
+ exit 0
166
+ fi
167
+ exit 1
168
+ fi
169
+
170
+ # --- findings-terminal checks (--findings): no cards, the deliverable is FINDINGS.md
171
+ if [ "$mode" = "--findings" ]; then
172
+ check_roadmap 1
173
+ check_spikes
174
+ check_discovered
175
+ [ -f "$b/FINDINGS.md" ] || fail "no $b/FINDINGS.md (template: FINDINGS.md)"
176
+ [ -f "$b/UAT.md" ] || fail "no $b/UAT.md"
177
+ for r in $rids; do
178
+ rq_ok=no
179
+ for rq in "$b"/research/RQ-*.md; do
180
+ [ -f "$rq" ] || continue
181
+ if grep -Eq "$(rid_re "$r")" "$rq" && grep -qE '^verdict:' "$rq" && grep -q '\[cited:' "$rq"; then
182
+ rq_ok=yes; break
183
+ fi
184
+ done
185
+ [ "$rq_ok" = yes ] || fail "$r has no research RQ file with a cited verdict"
186
+ if [ -f "$b/FINDINGS.md" ]; then
187
+ grep -Eq "$(rid_re "$r")" "$b/FINDINGS.md" || fail "$r missing from FINDINGS.md"
188
+ fi
189
+ if [ -f "$b/UAT.md" ]; then
190
+ grep -Eq "$(rid_re "$r")" "$b/UAT.md" || fail "$r missing from UAT.md"
191
+ fi
192
+ done
193
+ if [ "$fails" -eq 0 ]; then
194
+ echo "PASS: $(printf '%s\n' "$rids" | grep -c .) findings requirements, chain clean (--findings)"
195
+ exit 0
196
+ fi
197
+ exit 1
198
+ fi
199
+
21
200
  cards="$(ls "$b"/plans/T-*.md 2>/dev/null || true)"
22
201
  [ -n "$cards" ] || fail "no task cards in $b/plans/"
23
202
 
24
203
  # --- plan-level checks -------------------------------------------------------
25
- rid_re() { printf '(^|[^0-9.])%s([^0-9]|$)' "$(printf '%s' "$1" | sed 's/\./\\./g')"; } # R-1.1 must not match R-1.10
26
-
27
204
  for r in $rids; do
28
205
  covered=no
29
206
  for c in $cards; do
@@ -60,13 +237,42 @@ for c in $cards; do
60
237
  [ "$bigger" -ge 7 ] && fail "$name complexity $bigger >= 7 — split it (target <= 5)"
61
238
  done
62
239
 
63
- # spikes: a spike: block in a research RQ file must carry a disposition: by P3
64
- for rq in "$b"/research/RQ-*.md; do
65
- [ -f "$rq" ] || continue
66
- if grep -qE '^spike:' "$rq" && ! grep -qE '^disposition:' "$rq"; then
67
- fail "$(basename "$rq") has a spike: block without a disposition: line"
240
+ # ROADMAP: direction artifact intent line by P3, verdict by P5, 40-line cap
241
+ rv=0; [ "$mode" = "--all" ] && rv=1
242
+ check_roadmap "$rv"
243
+
244
+ # PLAN.md index <-> plans/ card set, both ways (the amendment cascade patches the
245
+ # plan index together with the cards; cuts live in the amendment D-ID, not here)
246
+ if [ -f "$b/PLAN.md" ]; then
247
+ plan_ids="$(grep -oE 'T-[0-9]+' "$b/PLAN.md" | sort -u)"
248
+ for c in $cards; do
249
+ id="$(basename "$c" .md)"
250
+ printf '%s\n' "$plan_ids" | grep -qx "$id" || fail "PLAN.md index missing $id — the amendment cascade patches the plan, not just cards"
251
+ done
252
+ for id in $plan_ids; do
253
+ [ -f "$b/plans/$id.md" ] || fail "PLAN.md cites $id but $b/plans/$id.md does not exist (cuts live in the amendment D-ID; drop them from the index)"
254
+ done
255
+ else
256
+ fail "no $b/PLAN.md (card index + rationale; template: PLAN.md)"
257
+ fi
258
+
259
+ # CODEBASE.md must postdate the last milestone rollover — it is cited as fact
260
+ # at re-interrogation. Skipped when uncommitted (just regenerated) or pre-rollover.
261
+ if [ -d "$b/milestones" ]; then
262
+ if [ ! -f "$b/CODEBASE.md" ]; then
263
+ fail "no $b/CODEBASE.md after rollover — regenerate it (bestest-go rollover step 4)"
264
+ else
265
+ cb_t="$(git -C "$root" log -1 --format=%ct -- .bestest/CODEBASE.md 2>/dev/null)"
266
+ ms_t="$(git -C "$root" log -1 --format=%ct -- .bestest/milestones 2>/dev/null)"
267
+ cb_dirty="$(git -C "$root" status --porcelain -- .bestest/CODEBASE.md 2>/dev/null)"
268
+ if [ -n "$cb_t" ] && [ -n "$ms_t" ] && [ -z "$cb_dirty" ] && [ "$cb_t" -lt "$ms_t" ]; then
269
+ fail "CODEBASE.md predates the last milestone rollover — regenerate it (bestest-go rollover step 4)"
270
+ fi
68
271
  fi
69
- done
272
+ fi
273
+
274
+ # spikes: a spike: block in a research RQ file must carry a disposition: by P3
275
+ check_spikes
70
276
 
71
277
  # --- full-chain checks (--all) -----------------------------------------------
72
278
  if [ "$mode" = "--all" ]; then
@@ -76,8 +282,19 @@ if [ "$mode" = "--all" ]; then
76
282
  rep="$b/reports/$id.md"
77
283
  if [ ! -f "$rep" ]; then
78
284
  fail "$id is done but $rep is missing"
79
- elif ! awk '/^## FRICTION/{f=1;next} /^## /{f=0} f&&/^[[:space:]]*<!--/{next} f&&NF{c++} END{exit c?0:1}' "$rep"; then
80
- fail "$id report has empty FRICTION section (write 'None' if truly none)"
285
+ else
286
+ section_filled "$rep" FRICTION \
287
+ || fail "$id report has empty FRICTION section (write 'None' if truly none)"
288
+ # Lessons propagation: every '- ' line the report claims was appended must
289
+ # actually be in LESSONS.md (substring match survives P5 curation merges).
290
+ if section_filled "$rep" Lessons; then
291
+ while IFS= read -r ln; do
292
+ grep -qF -- "$ln" "$b/LESSONS.md" 2>/dev/null \
293
+ || fail "$id Lessons line not in LESSONS.md: $ln"
294
+ done < <(awk '/^## Lessons/{f=1;next} /^## /{f=0} f&&/^-[[:space:]]/' "$rep")
295
+ else
296
+ fail "$id report has empty Lessons section (list the LESSONS.md lines appended, or 'None')"
297
+ fi
81
298
  fi
82
299
  done
83
300
 
@@ -91,12 +308,7 @@ if [ "$mode" = "--all" ]; then
91
308
  fi
92
309
  done
93
310
 
94
- if [ -f "$b/DISCOVERED.md" ]; then
95
- while IFS= read -r line; do
96
- printf '%s' "$line" | grep -q 'disposition:' \
97
- || fail "DISCOVERED entry lacks disposition: ${line%%|*}"
98
- done < <(grep -E '^- ' "$b/DISCOVERED.md")
99
- fi
311
+ check_discovered
100
312
  fi
101
313
 
102
314
  if [ "$fails" -eq 0 ]; then
@@ -5,11 +5,11 @@ description: Use when routing a bestest project to its lifecycle phase — boots
5
5
 
6
6
  # bestest-go — lifecycle router
7
7
 
8
- 1. Read `.bestest/STATE.md`. If its `plugin:` path is missing on disk (harness switched), re-resolve per the harness map (using-bestest `references/<harness>.md`) and rewrite that line before anything else. If missing: `mkdir -p .bestest/plans .bestest/reports .bestest/research`, copy `templates/STATE.md` from the plugin root into `.bestest/STATE.md`, set `phase: P0`, `milestone: v1` and `plugin: <plugin root>` — the absolute directory containing the plugin's `skills/`, `templates/`, `scripts/`; the invoking command passes it pre-rendered where the harness supports that, otherwise resolve per the harness map (using-bestest `references/<harness>.md`). Skills resolve `trace.sh` and templates through this line. Append `.bestest/.session-snapshot` to the project's `.gitignore`, commit.
9
- **Brownfield check** — the repo already contains product code (real source or git history, not scaffolding)? Then before interrogating: (a) dispatch one read-only subagent to write `.bestest/CODEBASE.md`, ≤60 lines: layout, entry points, build/run/test commands, observed conventions, integration seams, deploy surface, and the baseline test run (exact command, verbatim failure list) — every claim cited to a file path; (b) seed `.bestest/LOCKS` via explicit shell commands with one `glob` line per existing test directory — incumbent tests are immutable to implementers from day one; pre-existing failures become RISK entries in DECISIONS.md and no card `verify:` may expect them green — adopting one into scope is a `may_edit_tests: true` card plus a D-ID; (c) interrogation treats CODEBASE.md as context and existing behavior as `[cited:path]` fact, not something to re-ask the user.
8
+ 1. Read `.bestest/STATE.md`. If its `plugin:` path is missing on disk (harness switched), re-resolve per the harness map (using-bestest `references/<harness>.md`) and rewrite that line before anything else. If missing: `mkdir -p .bestest/plans .bestest/reports .bestest/research`, copy `templates/STATE.md`, `ROADMAP.md`, `LESSONS.md` and `DISCOVERED.md` from the plugin root into `.bestest/`, set `phase: P0`, `milestone: v1` and `plugin: <plugin root>` — the absolute directory containing the plugin's `skills/`, `templates/`, `scripts/`; the invoking command passes it pre-rendered where the harness supports that, otherwise resolve per the harness map (using-bestest `references/<harness>.md`). Skills resolve `trace.sh` and templates through this line. Append `.bestest/.session-snapshot` to the project's `.gitignore`, commit.
9
+ **Brownfield check** — the repo already contains product code (real source or git history, not scaffolding)? Then before interrogating: (a) dispatch one read-only subagent to write `.bestest/CODEBASE.md` (template: CODEBASE.md every claim path-cited, red baseline verbatim); (b) seed `.bestest/LOCKS` via explicit shell commands with one `glob` line per existing test directory — incumbent tests are immutable to implementers from day one; pre-existing failures become RISK entries in DECISIONS.md and no card `verify:` may expect them green — adopting one into scope is a `may_edit_tests: true` card plus a D-ID; (c) interrogation treats CODEBASE.md as context and existing behavior as `[cited:path]` fact, not something to re-ask the user.
10
10
  Then load the **interrogate** skill and begin Stage 0 with the user's idea (ask for it if none was given).
11
11
 
12
- 2. If `phase: done` (or the P5 ship gate is already logged and new work arrived): run **Milestone rollover** below.
12
+ 2. If `phase: quick`: resume the Q-card per `references/quick.md`. If `phase: done` and new work arrives: below-SPEC fixes, shipped-behavior restorations, and ≤3-file behavior-preserving refactors take the quick lane (`references/quick.md`); anything larger runs **Milestone rollover** below.
13
13
 
14
14
  3. Otherwise route on `phase:` — load the named skill and resume exactly at `stage:` / `card:`:
15
15
 
@@ -21,17 +21,20 @@ description: Use when routing a bestest project to its lifecycle phase — boots
21
21
  | P3 | plan | plan-checker PASS (≤3 loops) + `trace.sh --plan` clean + one-screen user approval |
22
22
  | P4 | build | every card done: diff-scope clean, verify re-run, review closed |
23
23
  | P5 | ship | UAT per R-ID + `trace.sh --all` clean + DISCOVERED dispositioned + user go/no-go |
24
+ | quick | this skill, `references/quick.md` | `trace.sh --quick` PASS → `phase: done`, gate logged |
25
+
26
+ Findings deliverable (`deliverable: findings` in SPEC): P2 freeze → `phase: P5`, skip logged; gate `trace.sh --findings` (research `references/findings.md`).
24
27
 
25
28
  4. On gate pass: update STATE.md (phase, next action, gate log line, and **re-derive Open interrupts from scratch** — an answered interrupt left standing misroutes the next session), commit `.bestest/`, announce the phase transition in one sentence, continue.
26
29
 
27
30
  ## Milestone rollover (P5 shipped → next milestone)
28
31
 
29
32
  All via explicit shell gate steps (LOCKS/SPEC are hook-frozen against file edits):
30
- 1. `N` = STATE `milestone:` (default v1). `mkdir -p .bestest/milestones/N` and `git mv` SPEC.md, PLAN.md, UAT.md, RUNBOOK.md, plans/, reports/, research/ into it, plus a `cp` snapshot of the final STATE.md (its gate log is the milestone's ledger). DECISIONS.md, LESSONS.md, DISCOVERED.md stay live — history and debt carry forward.
33
+ 1. `N` = STATE `milestone:` (default v1). `mkdir -p .bestest/milestones/N` and `git mv` SPEC.md, PLAN.md, UAT.md, RUNBOOK.md, CODEBASE.md, FINDINGS.md, plans/, reports/, research/ (those that exist) into it, plus a `cp` snapshot of the final STATE.md (its gate log is the milestone's ledger). DECISIONS.md, LESSONS.md, DISCOVERED.md, ROADMAP.md stay live — history, debt and direction carry forward.
31
34
  2. Rewrite LOCKS dropping the SPEC `hash` line (the archived SPEC keeps its frozen copy); keep every test `glob` — incumbent tests stay immutable.
32
35
  3. Fresh STATE.md from the template: `phase: P0`, `milestone: N+1`, same `plugin:` line. Commit `.bestest: roll over N -> N+1`.
33
- 4. Re-enter **interrogate** Stage 0 as brownfield: prior SPEC (`.bestest/milestones/N/SPEC.md`), the DISCOVERED backlog, and LESSONS are the opening context — the new idea is interrogated against what already ships, and unshipped R-IDs must be explicitly re-adopted or cut, never assumed.
36
+ 4. Re-enter **interrogate** Stage 0 as brownfield — regenerate CODEBASE.md first (step 1a contract; trace.sh fails a stale one): prior SPEC (`.bestest/milestones/N/SPEC.md`), ROADMAP.md, the DISCOVERED backlog, and LESSONS are the opening context — the new idea is interrogated against what already ships; unshipped R-IDs and deferred DISCOVERED lines must be explicitly re-adopted or cut, never assumed.
34
37
 
35
- Amending a frozen SPEC: new D-ID in DECISIONS.md → apply the edit via an explicit shell gate step → re-hash the `hash` line in LOCKS (the lock-guard block message points here). At P3+ the amendment cascades before the next dispatch: cut or patch every card citing a removed/changed R-ID (the amendment D-ID records the cuts), write cards for new R-IDs, re-run plan-checker card mode on touched cards and `trace.sh --plan` — both must pass; done cards citing amended R-IDs get their `verify:` re-run.
38
+ Amending a frozen SPEC: new D-ID in DECISIONS.md → apply the edit via an explicit shell gate step → re-hash the `hash` line in LOCKS (the lock-guard block message points here). At P3+ the amendment cascades before the next dispatch: cut or patch every card citing a removed/changed R-ID plus PLAN.md's index (the amendment D-ID records the cuts), write cards for new R-IDs, re-run plan-checker card mode on touched cards and `trace.sh --plan` — both must pass; done cards citing amended R-IDs get their `verify:` re-run.
36
39
 
37
40
  Never skip a gate because the work "seems obvious" — gates are where the human front-loaded trust.
@@ -0,0 +1,29 @@
1
+ # Quick lane — Q-cards
2
+
3
+ One card, no SPEC/PLAN above it. For work below SPEC visibility: a post-ship defect fix that *restores* shipped behavior, a tiny fix no R-ID describes, or a ≤3-file behavior-preserving refactor. Anything that would alter or create an R-ID's observable behavior is a milestone — never negotiate that boundary.
4
+
5
+ ## Entry (only from `phase: done`)
6
+ 1. Copy `templates/quick-card.md` (plugin root = the STATE.md `plugin:` line) to `.bestest/plans/Q-##.md`, next free number.
7
+ 2. Fill the frontmatter: 1–3 `files:`; `requirements:` = `none`, the R-IDs the fix restores, or `refactor`; `start: $(git rev-parse HEAD)`.
8
+ 3. Append the authorizing decision to DECISIONS.md — why this is below SPEC visibility, or which R-ID it restores — and set its D-ID in `decision:`.
9
+ 4. Set STATE.md `phase: quick`, `stage: Q-##`, `card: .bestest/plans/Q-##.md`; commit `.bestest/`.
10
+
11
+ ## Work rules
12
+ - Read LESSONS.md first; append what you learn. This is the lane's edge: milestone-earned facts serve a five-minute fix.
13
+ - All locks stand: frozen SPEC, test globs (`may_edit_tests` is clamped false on Q-cards — a fix that needs a test edit escalates), destructive-command guard, drift detection.
14
+ - Out-of-scope findings → one DISCOVERED.md line; never fixed here.
15
+ - Commit with trailer `Bestest-Req: Q-##`.
16
+
17
+ ## Close — the whole ceremony, keep it minutes
18
+ 1. Run every `verify:` command; write `.bestest/reports/Q-##.md` (claim→evidence, verify output, FRICTION, Lessons).
19
+ 2. Restored R-IDs: one `requick` row per R-ID in UAT.md. Refactor: paste the green incumbent-suite run in the report's Verify output; no UAT row.
20
+ 3. `"$PLUGIN"/scripts/trace.sh --quick` (`PLUGIN` = the `plugin:` line) must print PASS — gate evidence.
21
+ 4. Card `status: done`; STATE back to `phase: done`, clear `stage:`/`card:`, gate-log line, commit `.bestest/`.
22
+
23
+ ## Escalation — any eligibility breach
24
+ A lock block, a 4th file, a needed test or dependency-manifest edit, R-ID behavior change: set `status: escalated`, append a DISCOVERED.md line (`| discovered-from: Q-##`), offer milestone rollover. The block message is the trigger, not an obstacle to argue with.
25
+
26
+ ## Bounds
27
+ - More than 5 Q-cards since the last rollover: trace fails — that backlog is a milestone; roll over.
28
+ - Refactors beyond 3 files are milestones whose SPEC states the preservation invariants as ordinary R-IDs, derived from CODEBASE.md and the prior SPEC.
29
+ - Q-cards and their reports archive with the next rollover like everything else; the counter resets there.
@@ -9,8 +9,9 @@ Report, compactly, from files only — no edits, no state changes, no "while I'm
9
9
 
10
10
  1. `.bestest/STATE.md` — phase, stage, active card, next action, open interrupts.
11
11
  2. Gate log — which gates passed, with dates.
12
- 3. Counts: task cards by status (todo/doing/done), DISCOVERED.md lines lacking `disposition:`, open risks in DECISIONS.md.
13
- 4. If P3+: run `"$PLUGIN"/scripts/trace.sh --plan` where `PLUGIN` = the `plugin:` line in STATE.md (read-only) and show PASS or its failure lines. A dead `plugin:` path: report it and point at bestest-go (it re-resolves); do not fix it here.
14
- 5. One line: "Next: <the next action verbatim from STATE.md>".
12
+ 3. Counts: task cards by status (todo/doing/done), DISCOVERED.md lines lacking `disposition:`, open risks in DECISIONS.md — list RISK rows and any due `revisit:` dates verbatim.
13
+ 4. If P3+: run `"$PLUGIN"/scripts/trace.sh --plan` where `PLUGIN` = the `plugin:` line in STATE.md (read-only) and show PASS or its failure lines (findings projects: `--findings`). A dead `plugin:` path: report it and point at bestest-go (it re-resolves); do not fix it here.
14
+ 5. One line from ROADMAP.md: Destination + Next up (absent: say so).
15
+ 6. One line: "Next: <the next action verbatim from STATE.md>".
15
16
 
16
17
  If `.bestest/` is missing, say so and point at the **bestest-go** skill.
@@ -8,13 +8,13 @@ description: Use when a bestest project enters P4 — orchestrate fresh subagent
8
8
  You are the **orchestrator**: dispatch, gate, close loops. You do not write product code; builders do.
9
9
 
10
10
  ## Dispatch loop
11
- 1. **Pick:** lowest-numbered incomplete card whose `blocked_by:` are all done. Set `card:` + `stage:` in STATE.md, mark card `doing`, note `task-start=$(git rev-parse HEAD)`.
11
+ 1. **Pick:** lowest-numbered incomplete card whose `blocked_by:` are all done. Set `card:` + `stage:` + `task_start: $(git rev-parse HEAD)` in STATE.md, mark card `doing`.
12
12
  2. **Dispatch a fresh subagent** with: the card, LESSONS.md ("read first, append what you learn"), relevant D-IDs. File handoffs only — builders write code + `.bestest/reports/T-##.md`; they get no chat history.
13
- 3. **Builder contract** (put in the prompt): follow the card exactly; TDD (the failing test exists — make it pass, never edit it); commit with trailer `Bestest-Req: <R-IDs>`; on any underdetermined point return `BLOCKED: <specific question>` instead of improvising; report claim→evidence + FRICTION (or `None`) + Discovered.
13
+ 3. **Builder contract** (put in the prompt): follow the card exactly; TDD (the failing test exists — make it pass, never edit it); commit with trailer `Bestest-Req: <R-IDs>`; on any underdetermined point return `BLOCKED: <specific question>` instead of improvising; report claim→evidence + FRICTION (or `None`) + Lessons (the exact LESSONS.md lines appended, or `None`) + Discovered.
14
14
  4. **Gate mechanically** (never trust the report):
15
15
  - Re-run every `verify:` command yourself; paste output.
16
- - `git diff --name-only <task-start>..HEAD` ⊆ card `files:` — out-of-scope diffs get reverted or INTERRUPT; locked paths must show zero diff.
17
- - Report FRICTION section present; Discovered lines actually appended to DISCOVERED.md.
16
+ - `git diff --name-only <task_start from STATE.md>..HEAD` ⊆ card `files:` — out-of-scope diffs get reverted or INTERRUPT; locked paths must show zero diff.
17
+ - Report FRICTION and Lessons sections present; Discovered lines actually appended to DISCOVERED.md.
18
18
  5. **Close the loop:** FRICTION → patch the *remaining* cards now + append environment facts to LESSONS.md — before the next dispatch, not "later".
19
19
  6. **Review** (risk-scaled): dispatch **reviewer** agent — card weight ≥4 → full adversarial pass; weight <4 → blockers-only. A finding closes only *fixed-and-reconfirmed by the reviewer*, or waived as a D-ID with a revisit trigger.
20
20
  7. Mark done, update STATE.md, commit `.bestest/`, next card.
@@ -23,7 +23,7 @@ You are the **orchestrator**: dispatch, gate, close loops. You do not write prod
23
23
  - `BLOCKED:` from a builder → answer from SPEC/DECISIONS if derivable; else it's a real gap: INTERRUPT the user, record the answer as a D-ID, redispatch.
24
24
  - **Loop tripwire:** same command failing 3× with the same error, or two states alternating 3× → stop the lane, record the signature in LESSONS, escalate to **debug** skill.
25
25
  - 3 failed fix attempts on one card → debug skill; debug can't crack it → architecture escape: INTERRUPT with a one-paragraph problem statement + 2 options.
26
- - Out-of-blast-radius discovery → one line in DISCOVERED.md: `- <summary> | discovered-from: T-## | blocks: <T-## or none>`. Never fix silently, never drop.
27
- - LESSONS.md line format: `- [env|tool|codebase] <fact> (T-##)`.
26
+ - Out-of-blast-radius discovery → one line in DISCOVERED.md (format in its header). Never fix silently, never drop.
27
+ - LESSONS.md line format: in its header (template: LESSONS.md).
28
28
 
29
29
  Autonomy Contract (SPEC §9) governs everything above; GATED actions stop the line no matter what a card says.
@@ -21,7 +21,7 @@ Six forcing questions, strictly one at a time, researching between answers so th
21
21
  5. **Observation** — how will we observe (not hope) that it worked?
22
22
  6. **Future-fit** — what changes in 12 months that kills or grows this?
23
23
 
24
- Between questions, search for existing tools that already solve it; present findings before asking the user to justify building. Exit: a written **Premise Verdict** the user confirms — or an honest recommendation to pivot or park.
24
+ Between questions, search for existing tools that already solve it; present findings before asking the user to justify building. Exit: a written **Premise Verdict** the user confirms — or an honest recommendation to pivot or park. A premise whose deliverable is knowledge, not software: record `deliverable: findings` — question-shaped R-IDs, P3–P4 skipped (research `references/findings.md`).
25
25
 
26
26
  ## Stage 2 — Discovery
27
27
  Adaptive, domain-specific gray areas across five buckets: users/actors · data & lifecycle · integration surface · failure/abuse · **Anti-Goals (mandatory, ≥3)**. Present options annotated with what research already found ("Postgres and SQLite both fit; SQLite unless you need concurrent writers — you said single user"). Batch related Taste questions; never ask what Stage-3 research can derive.
@@ -35,8 +35,8 @@ Now attack the accumulated idea (tactics reference): name tensions between state
35
35
  ## Stage 5 — Scope freeze
36
36
  Every candidate requirement gets Include / Defer / Cut / Hold. `out_of_scope` must be non-empty with reasons; Deferred items go to the parking lot. User-facing docs (README, API reference, changelog) get an explicit disposition — Included = R-IDs, else out_of_scope with a reason. Write acceptance criteria in **EARS grammar**, one sentence each, numbered `R-N.M`:
37
37
  `WHEN <trigger> [WHILE <state>] THE SYSTEM SHALL <observable behavior>.`
38
- If a criterion can't be phrased as observable behavior, it isn't a criterion — rework it.
39
- Close the stage by writing **SPEC §8 — the doneness function**: the falsifiable test the P5 go/no-go will evaluate verbatim.
38
+ If a criterion can't be phrased as observable behavior, it isn't a criterion — rework it. (Findings projects: question R-IDs — grammar in research `references/findings.md`.)
39
+ Close the stage by writing **SPEC §8 — the doneness function**: the falsifiable test the P5 go/no-go will evaluate verbatim — and appending this milestone's intent line to `.bestest/ROADMAP.md` (`trace.sh --plan` fails without it).
40
40
 
41
41
  ## Stage 6 — Spec & contract freeze (P2 gate)
42
42
  1. **Rubric pass:** dispatch the plan-checker agent in spec mode. Findings come back typed `[Gap]` `[Ambiguity]` `[Conflict]` `[Assumption]` with R-ID cites. Freeze is blocked until every Gap/Conflict is fixed or explicitly accepted as a D-ID.
@@ -5,7 +5,7 @@ description: Use when a bestest project enters P3 — turn the frozen SPEC into
5
5
 
6
6
  # Plan
7
7
 
8
- Input: frozen SPEC. Output: `.bestest/PLAN.md` (ordered index + rationale) and `.bestest/plans/T-##.md` cards (template: task-card.md). Assets the build needs (data, fixtures, images, seeds): `references/assets.md`.
8
+ Input: frozen SPEC. Output: `.bestest/PLAN.md` (template: PLAN.md) and `.bestest/plans/T-##.md` cards (template: task-card.md). Assets the build needs (data, fixtures, images, seeds): `references/assets.md`.
9
9
 
10
10
  ## Card discipline
11
11
  - **Zero-context contract:** a fresh subagent with only the card + LESSONS.md must succeed. Exact paths, signatures, data shapes, D-ID pointers. No placeholders, no "as discussed".
@@ -15,7 +15,7 @@ Input: frozen SPEC. Output: `.bestest/PLAN.md` (ordered index + rationale) and `
15
15
  - Every card: `requirements:` R-IDs (no orphan cards), `files:` blast radius, `verify:` commands a machine can re-run, `blocked_by:` for real order constraints only.
16
16
 
17
17
  ## Complexity Justification table (in PLAN.md)
18
- Any design element beyond the simplest thing satisfying the R-IDs gets a row: `| Complexity | Why needed | Simpler alternative rejected because |`. No row → the plan-checker counts it as a blocker. This is where architecture astronautics goes to die.
18
+ Any design element beyond the simplest thing satisfying the R-IDs gets a row (columns in the PLAN.md template). No row → the plan-checker counts it as a blocker. This is where architecture astronautics goes to die.
19
19
 
20
20
  ## Checker loop (gate)
21
21
  1. Dispatch **plan-checker** agent (card mode) with PLAN.md + cards + SPEC.
@@ -8,7 +8,7 @@ description: Use when a bestest project needs external facts — P1 stack deriva
8
8
  Rule zero: **memory is a rumor.** Anything external — API shape, pricing, limit, version, benchmark — is `[assumed]` until a fetched URL says otherwise. `[assumed]` load-bearing claims block the P1 gate.
9
9
 
10
10
  ## Loop (per research question)
11
- 1. **Frame** — write `.bestest/research/RQ-<slug>.md`: the question, why it matters (which R-ID/decision it feeds), what answer shape is needed.
11
+ 1. **Frame** — write `.bestest/research/RQ-<slug>.md` (template: RQ.md).
12
12
  2. **Fan out** — dispatch researcher agents in parallel, one per RQ (or per candidate for comparisons). Each returns findings with URLs; they write their own RQ file (file handoff, not chat recall).
13
13
  3. **Verify adversarially** — for each load-bearing claim, one attempt to refute it: a second independent source, or the primary source read closely (changelogs, pricing pages, GitHub issues beat blog posts). Claims that survive get `[cited:URL]`; claims that don't get flagged to the user or dropped.
14
14
  4. **Synthesize** — decision-brief per open decision: options, recommendation, evidence rows. Feed SPEC §5 / DECISIONS.
@@ -17,7 +17,10 @@ Rule zero: **memory is a rumor.** Anything external — API shape, pricing, limi
17
17
  Stack is an **output**, never an input. From SPEC constraints (platform, team skills `[user]`, data shape, scale honesty from the Premise) derive candidates → SLOPCHECK every dependency (see interrogate references) → produce the stack decision-brief table, every row cited. Present once, whole, for user approval — that's the single P1 gate.
18
18
 
19
19
  ## Spike — when only running code can answer
20
- A question that survives doc research and only execution can settle: add a `spike:` block to its RQ file (falsifiable question, timebox, answer shape) and set `stage: spike-<slug>` in STATE.md. From then on the lock-guard blocks writes outside `.bestest/` — spike code lives in `.bestest/spikes/<slug>/`, as crude as it likes. Paste run evidence into the RQ as `verdict:`, then disposition — mandatory, trace.sh fails P3 on any bare spike: `discarded` (delete the dir via an explicit shell step) or `promoted` — the finding enters SPEC/DECISIONS as `[cited:<RQ path>]`; cards rebuild it, spike code never ships. Clear `stage:` on exit. Discipline and edge cases: `references/spike.md`.
20
+ A question that survives doc research and only execution can settle: add a `spike:` block to its RQ file (keys in template RQ.md) and set `stage: spike-<slug>` in STATE.md. From then on the lock-guard blocks writes outside `.bestest/` — spike code lives in `.bestest/spikes/<slug>/`, as crude as it likes. Paste run evidence into the RQ as `verdict:`, then disposition — mandatory, trace.sh fails P3 on any bare spike: `discarded` (delete the dir via an explicit shell step) or `promoted` — the finding enters SPEC/DECISIONS as `[cited:<RQ path>]`; cards rebuild it, spike code never ships. Clear `stage:` on exit. Discipline and edge cases: `references/spike.md`.
21
+
22
+ ## Research-terminal projects
23
+ `deliverable: findings`: the P1 gate variant and the FINDINGS.md deliverable live in `references/findings.md`.
21
24
 
22
25
  ## Citation hygiene
23
26
  - Cite the page you actually fetched, not the search snippet.
@@ -0,0 +1,20 @@
1
+ # Research-terminal projects — `deliverable: findings`
2
+
3
+ For premises whose deliverable is knowledge, not software: question in, cited verdict out. Declared in the Premise Verdict (`deliverable: findings`, D-ID recorded). Same lifecycle spine; the router skips P3–P4 after the P2 freeze and logs the skip.
4
+
5
+ ## R-ID grammar
6
+ SPEC §2 criteria are questions with a named verdict shape and evidence bar:
7
+ `WHEN research completes THE FINDINGS SHALL state <verdict shape> with <evidence bar>.`
8
+ The observable behavior is the verdict itself — a question that can't name what a usable answer looks like isn't ready to freeze.
9
+
10
+ ## P1 gate variant
11
+ The research loop runs unchanged: fan-out, adversarial verification, spikes. The gate: every R-ID's RQ file carries `verdict:` plus at least one `[cited:URL]` (or pasted run evidence for spikes), and every spike is dispositioned. The stack decision-brief approval is replaced by the question-set freeze at P2 — post-freeze question drift is a SPEC amendment (D-ID + re-hash), which is exactly the discipline the freeze buys.
12
+
13
+ ## Deliverable
14
+ `.bestest/FINDINGS.md` (template: FINDINGS.md): one section per R-ID — verdict (one paragraph max), confidence plus what-would-change-it, evidence pointers into `research/RQ-*.md`. Ends with a Recommendation and a reopen tripwire. RQ files and FINDINGS stay unfrozen: a P5 UAT fail routes back to the research loop, never to P4.
15
+
16
+ ## P5 variant
17
+ Ship runs against FINDINGS.md: UAT walks the user through each R-ID's verdict one at a time; the mechanical gate is `trace.sh --findings` — every R-ID has a cited RQ verdict, a FINDINGS.md section, and a UAT row; spikes and DISCOVERED dispositioned; ROADMAP verdict line present. Runbook is `n/a: findings deliverable` — skip the rollback rehearsal. The doneness function (SPEC §8) is evaluated against FINDINGS.md verbatim. LESSONS curation and the promotion offers run unchanged.
18
+
19
+ ## Out of scope
20
+ No post-done citation-freshness monitoring — findings age like any document; the reopen tripwire is the honest boundary. A build project with research questions is just P1 — this lane is only for projects that END at answers.
@@ -5,19 +5,19 @@ description: Use when a bestest project enters P5 — UAT against every acceptan
5
5
 
6
6
  # Ship
7
7
 
8
- Nothing new gets built here; P5 proves what exists and stages it for production.
8
+ Nothing new gets built here; P5 proves what exists and stages it for production. Findings variant (`deliverable: findings`): steps 1–2 run against FINDINGS.md with `trace.sh --findings`; step 5 is `n/a: findings deliverable` — discipline: research `references/findings.md`.
9
9
 
10
10
  ## 1. UAT — one question per criterion
11
11
  Create `.bestest/UAT.md` (template). For each `R-N.M` in SPEC §2: restate the EARS criterion as a single check the user can answer, attach the evidence pointer (command output, screenshot, URL), walk the user through them **one at a time**. Any fail → back to P4 with a card citing that R-ID; do not negotiate the criterion mid-UAT (that's a SPEC amendment: D-ID + re-hash). One retry per R-ID: a second UAT fail means build and criterion disagree at spec level — INTERRUPT with the exact mismatch: amend (D-ID + re-hash) or mark the row FAIL and carry it into go/no-go as an open risk. Never loop a third time.
12
12
 
13
13
  ## 2. Full-chain audit
14
- `"$PLUGIN"/scripts/trace.sh --all` where `PLUGIN` = the `plugin:` line in STATE.md must print PASS — attach output as gate evidence. It enforces: every criterion covered, every done card reported with FRICTION, every R-ID committed with a `Bestest-Req:` trailer and present in UAT.
14
+ First complete this milestone's ROADMAP.md ledger line with a verdict and refresh its Next-up/Abandoned sections — the audit fails a verdict-less line. Then `"$PLUGIN"/scripts/trace.sh --all` where `PLUGIN` = the `plugin:` line in STATE.md must print PASS — attach output as gate evidence. It enforces: every criterion covered, every done card reported with FRICTION, every R-ID committed with a `Bestest-Req:` trailer and present in UAT.
15
15
 
16
16
  ## 3. Disposition DISCOVERED.md — every line
17
- Append `| disposition:` to each: `fix-now` (micro-card, run it) / `deferred to <where>` / `wontfix because <reason>`. trace.sh fails any bare line. This is where "never dropped" gets cashed.
17
+ Append `| disposition:` to each line (grammar in the DISCOVERED.md header). trace.sh fails any bare line. This is where "never dropped" gets cashed.
18
18
 
19
19
  ## 4. LESSONS curation
20
- Compress LESSONS.md: dedupe, drop task-local noise, keep durable environment/codebase facts. Offer the user the top items for promotion to the project memory file (per harness map) their call, one batch.
20
+ Compress LESSONS.md: dedupe, drop task-local noise, keep durable environment/codebase facts. Then two promotion offers, each one batch, each the user's call: top project-relevant items the project memory file; `[env]`/`[tool]` facts that hold beyond this project → the user-level memory file (both per harness map). Never write either silently; declined items stay in LESSONS.md.
21
21
 
22
22
  ## 5. Runbook
23
23
  Fill `.bestest/RUNBOOK.md` (template) for the actual derived stack. The rollback section requires a **rehearsal transcript** — actually roll back once, paste it. Timeouts, observability floor, negative-path security smoke, restore drill, spend ceiling: each filled or `n/a + reason`.
@@ -25,4 +25,4 @@ Fill `.bestest/RUNBOOK.md` (template) for the actual derived stack. The rollback
25
25
  ## 6. Go/no-go
26
26
  One screen: doneness function (SPEC §8) evaluated verbatim · UAT table · trace PASS · runbook gaps · open risks with tripwires. The user says go or no-go. **Deploy itself is GATED** — even after "go", the deploy command runs only with explicit user approval, per the Autonomy Contract.
27
27
 
28
- On "go": log the gate, set `phase: done` in STATE.md, commit. The lifecycle is complete — the router's next run offers **milestone rollover** (archive to `.bestest/milestones/`, re-enter P0 brownfield).
28
+ On "go": log the gate, set `phase: done` in STATE.md, commit. The lifecycle is complete — new work now routes through the quick lane (small fixes; router `references/quick.md`) or **milestone rollover** (archive to `.bestest/milestones/`, re-enter P0 brownfield).
@@ -10,7 +10,8 @@
10
10
  | Subagent dispatch | Task tool; plan-checker / researcher / reviewer ship as plugin agents; parallel dispatch supported |
11
11
  | Isolation | git worktrees |
12
12
  | Compaction survival | SessionStart hook (matcher startup\|resume\|clear\|compact) re-injects STATE.md lines 1–60 + LOCKS + drift notice |
13
- | Memory file (LESSONS promotion) | CLAUDE.md |
13
+ | Project memory file (LESSONS promotion) | CLAUDE.md |
14
+ | User-level memory file (cross-project LESSONS) | `~/.claude/CLAUDE.md` |
14
15
  | Guarded tools | Bash → guard.sh; Write/Edit/MultiEdit → lock-guard.sh, syntax-check.sh |
15
16
 
16
17
  Everything above is wiring, not behavior: the laws in using-bestest are identical on every harness.
@@ -10,7 +10,8 @@
10
10
  | Subagent dispatch | prose-directed dispatch works once roles are registered; **bootstrap extra (gated shell step): copy `adapters/codex/agents/*.toml` from the plugin root into the project's `.codex/agents/`** — plugin-bundled agents don't auto-register; depth 1 |
11
11
  | Isolation | separate checkout (no native worktree command) |
12
12
  | Compaction survival | SessionStart + PostCompact hooks re-inject STATE.md lines 1–60 + LOCKS + drift notice |
13
- | Memory file (LESSONS promotion) | AGENTS.md (32 KiB read cap) |
13
+ | Project memory file (LESSONS promotion) | AGENTS.md (32 KiB read cap) |
14
+ | User-level memory file (cross-project LESSONS) | `~/.codex/AGENTS.md` (FACTS.md 2026-07-12) |
14
15
  | Guarded tools | shell → guard.sh; apply_patch (file edits) → lock-guard.sh, syntax-check.sh |
15
16
  | Hook trust | hooks need a one-time trust review at install; any script update re-flags them — re-trust or they silently stop enforcing |
16
17
 
@@ -11,7 +11,8 @@
11
11
  | Subagent dispatch | task tool with the installed plan-checker / researcher / reviewer agents (`@mention` or prose-directed) |
12
12
  | Isolation | git worktrees via your own shell steps |
13
13
  | Compaction survival | `opencode.json` instructions loads `.bestest/.context.md` (plugin-maintained digest: STATE.md 1–60 + LOCKS + active card + DRIFT); compaction re-inject rides the plugin |
14
- | Memory file (LESSONS promotion) | AGENTS.md |
14
+ | Project memory file (LESSONS promotion) | AGENTS.md |
15
+ | User-level memory file (cross-project LESSONS) | `~/.config/opencode/AGENTS.md` (FACTS.md 2026-07-12) |
15
16
  | Guarded tools | bash → guard.sh; write/edit/apply_patch → lock-guard.sh, syntax-check.sh — via the `@accidentally-awesome-labs/opencode-bestest` npm plugin (exit 2 → blocked) |
16
17
  | Degraded mode | without the npm plugin there are NO guards, no auto-injection, no drift detection — commands still resume by reading `.bestest/STATE.md` |
17
18
 
@@ -0,0 +1,26 @@
1
+ # CODEBASE — <repo name>
2
+ <!-- Written by a read-only subagent at brownfield bootstrap; regenerated at each
3
+ milestone rollover. Hard cap 60 lines. Every claim cited to a file path —
4
+ observed fact, not aspiration. -->
5
+
6
+ ## Layout
7
+ <!-- top-level dirs and what lives where -->
8
+
9
+ ## Entry points
10
+ <!-- binaries, main modules, routes — path-cited -->
11
+
12
+ ## Build / run / test
13
+ <!-- exact commands, copy-pasteable -->
14
+
15
+ ## Conventions
16
+ <!-- naming, structure, patterns actually observed (path-cited) -->
17
+
18
+ ## Integration seams
19
+ <!-- where new code plugs in: interfaces, DI points, extension dirs -->
20
+
21
+ ## Deploy surface
22
+ <!-- how it ships today: CI files, Dockerfiles, scripts — or 'none found' -->
23
+
24
+ ## Baseline test run
25
+ <!-- exact command + verbatim failure list. Pre-existing failures become RISK
26
+ entries in DECISIONS.md; no card verify: may expect them green. -->
@@ -8,4 +8,6 @@
8
8
  <!-- RISK-001 | description | likelihood | blast radius | tripwire that reopens it -->
9
9
 
10
10
  ## Review waivers
11
- <!-- D-0NN | class: Waiver | finding waived | why acceptable | revisit trigger (date or event) — waivers are decisions, same D-ID sequence -->
11
+ <!-- D-0NN | class: Waiver | finding waived | why acceptable | revisit trigger — waivers are decisions, same D-ID sequence.
12
+ Date triggers as 'revisit: YYYY-MM-DD' — session start surfaces due dates until
13
+ you append a 'D-0NN revisited <date>: <outcome>' line (append-only; never rewrite). -->
@@ -0,0 +1,6 @@
1
+ # DISCOVERED — out-of-blast-radius work
2
+ <!-- Never fixed silently, never dropped. One line per item, appended when found:
3
+ - <summary> | discovered-from: T-## | blocks: <T-## or none>
4
+ At P5 ship every line gains a disposition (trace.sh --all fails bare lines):
5
+ | disposition: fix-now / deferred to <where> / wontfix because <reason>
6
+ Survives milestone rollover; deferred lines seed the next interrogation. -->
@@ -0,0 +1,14 @@
1
+ # FINDINGS — <project name>
2
+ <!-- The deliverable of a research-terminal project (deliverable: findings).
3
+ One section per SPEC R-ID. trace.sh --findings requires every R-ID here,
4
+ each backed by a research/RQ-*.md verdict with citations. Stays unfrozen —
5
+ a UAT fail routes back to the research loop, never to P4. -->
6
+
7
+ ## R-N.M — <question, one clause>
8
+ verdict: <the answer, one paragraph max>
9
+ confidence: <high|medium|low> — would change if: <falsifier>
10
+ evidence: <research/RQ-<slug>.md + the load-bearing [cited:URL]s>
11
+
12
+ ## Recommendation
13
+ <!-- what to do because of these answers, one paragraph; cite D-IDs -->
14
+ reopen-if: <tripwire that invalidates the recommendation>
@@ -0,0 +1,5 @@
1
+ # LESSONS — durable facts
2
+ <!-- Builders read this file before every card and append what they learn after.
3
+ One fact per line, exact format:
4
+ - [env|tool|codebase] <fact> (T-##)
5
+ Curated (deduped, compressed) at P5 ship; survives milestone rollover. -->
@@ -0,0 +1,18 @@
1
+ # PLAN — <milestone>
2
+ <!-- Written at P3; the amendment cascade patches this file together with the
3
+ cards. trace.sh --plan cross-checks the card index against plans/T-*.md
4
+ both ways: every card indexed, every indexed id has a card file. Cut cards
5
+ are dropped from the index — the cut itself lives in the amendment D-ID. -->
6
+
7
+ ## Card index
8
+ <!-- one line per live card, in execution order:
9
+ T-## | <title> | complexity N | blocked_by: <T-## or none> -->
10
+
11
+ ## Rationale
12
+ <!-- why this order and these slices; tracer bullet first -->
13
+
14
+ ## Complexity justification
15
+ <!-- every design element beyond the simplest thing satisfying the R-IDs gets a
16
+ row — no row means the plan-checker counts it as a blocker:
17
+ | Complexity | Why needed | Simpler alternative rejected because |
18
+ |---|---|---| -->
@@ -0,0 +1,21 @@
1
+ # ROADMAP — <project name>
2
+ <!-- Direction, never backlog: items live in DISCOVERED.md, scope in the milestone
3
+ SPEC — link, don't copy. Hard cap 40 lines (trace.sh enforces). Never hashed:
4
+ stays editable after the SPEC freeze; survives milestone rollover. -->
5
+
6
+ ## Destination
7
+ <!-- 2-3 lines: where the project is going across milestones; what "done for good"
8
+ looks like -->
9
+
10
+ ## Milestone ledger
11
+ <!-- one line per milestone, at line start. Intent appended at scope freeze
12
+ (trace --plan gates it); verdict completed at P5 ship (trace --all gates it).
13
+ Compress old lines — detail lives in .bestest/milestones/<N>/.
14
+ vN | intent: <one clause> | verdict: shipped <date> — <one clause outcome> -->
15
+
16
+ ## Next up
17
+ <!-- 1-3 lines: the likely next milestone's theme and why-now; point at DISCOVERED
18
+ lines and D-IDs, never copy them -->
19
+
20
+ ## Abandoned
21
+ <!-- <direction> — <why> (D-ID) -->
@@ -0,0 +1,19 @@
1
+ # RQ — <question slug>
2
+ <!-- One file per research question, written by the researcher agent
3
+ (file handoff, not chat recall). -->
4
+
5
+ question: <the falsifiable question>
6
+ feeds: <which R-ID or decision this answers>
7
+ answer_shape: <what a usable answer looks like>
8
+
9
+ ## Findings
10
+ <!-- every external claim [cited:URL] — the page actually fetched, not a search
11
+ snippet; version-sensitive claims name version + date; [assumed] for
12
+ anything no fetched URL confirms -->
13
+
14
+ ## Spike
15
+ <!-- only if docs cannot answer (discipline: research references/spike.md).
16
+ Append these as top-level keys, at line start, when the spike opens:
17
+ spike: <falsifiable question> (plus timebox + answer shape)
18
+ verdict: <actual run output, or 'unknown after <timebox>'>
19
+ disposition: discarded | promoted (mandatory — trace.sh fails a bare spike) -->
@@ -4,7 +4,9 @@
4
4
 
5
5
  ## 1. Premise Verdict
6
6
  <!-- one paragraph: the real problem, for whom, why now, why not an existing tool.
7
- Every factual claim tagged [user] / [cited:URL] / [assumed]. -->
7
+ Every factual claim tagged [user] / [cited:URL] / [assumed].
8
+ Knowledge deliverable, nothing ships: add a 'deliverable: findings' line —
9
+ R-IDs become questions, P3-P4 are skipped (research references/findings.md). -->
8
10
 
9
11
  ## 2. Requirements — EARS acceptance criteria
10
12
  <!-- one sentence each, numbered R-N.M, observable behavior only:
@@ -8,6 +8,7 @@ milestone: v1 # archived to .bestest/milestones/<milestone>/ at rollover
8
8
  stage: 0 # interrogation stage 0-6, task id T-## during P4, or spike-<slug> (lock-guard quarantine)
9
9
  spec_hash: none # sha256 recorded in .bestest/LOCKS at P2 freeze
10
10
  card: none # project-relative path to the active task card (lock-guard reads this)
11
+ task_start: none # HEAD sha at card dispatch — diff-scope gate baseline; survives compaction via re-injection
11
12
 
12
13
  ## Now
13
14
  - next action: <one imperative sentence — what the very next tool call should do>
@@ -0,0 +1,22 @@
1
+ ---
2
+ id: Q-00
3
+ title: <imperative, one clause>
4
+ status: doing # doing | done | escalated
5
+ files: src/example.py # 1-3 paths, comma-separated; a diff outside them = escalate
6
+ requirements: none # none | R-IDs this RESTORES (never alters, never creates) | refactor
7
+ decision: D-000 # DECISIONS.md entry authorizing the quick fix
8
+ start: <sha> # git rev-parse HEAD before the first edit
9
+ may_edit_tests: false # always false on the quick lane (lock-guard clamps it)
10
+ verify:
11
+ - <command proving the fix; refactor: the incumbent test suite>
12
+ ---
13
+ ## Fix (zero-context contract)
14
+ <!-- symptom, exact paths, expected end state. Read LESSONS.md first; append what
15
+ you learn. TDD locks stand — a fix that needs a test edit escalates. -->
16
+
17
+ ## Done means
18
+ <!-- report at .bestest/reports/Q-00.md (FRICTION + Lessons included); commit
19
+ trailer 'Bestest-Req: Q-00'; restored R-IDs get a 'requick' UAT.md row;
20
+ refactor cards paste the green incumbent-suite run in Verify output;
21
+ out-of-scope findings -> DISCOVERED.md, never fixed here.
22
+ trace.sh --quick must PASS before STATE returns to phase: done. -->
@@ -11,5 +11,10 @@
11
11
  ## FRICTION
12
12
  <!-- REQUIRED. Failed commands, backtracking, wrong card assumptions, environment surprises — or exactly 'None'. The orchestrator patches remaining cards + LESSONS.md from this before the next dispatch. -->
13
13
 
14
+ ## Lessons
15
+ <!-- REQUIRED. Each durable FRICTION fact as the exact line appended to LESSONS.md
16
+ ('- [env|tool|codebase] <fact> (T-00)') — or exactly 'None'. trace.sh --all
17
+ cross-checks every line here against LESSONS.md. -->
18
+
14
19
  ## Discovered
15
20
  <!-- out-of-blast-radius items appended to DISCOVERED.md this task, or 'None' -->