freeharness 0.4.1 → 0.4.2

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 (194) hide show
  1. package/README.md +8 -7
  2. package/canonical/HARVEST.yaml +25 -14
  3. package/canonical/PROVENANCE.yaml +1 -1
  4. package/canonical/agents/README.md +77 -36
  5. package/canonical/agents/agentic-architect.md +2 -1
  6. package/canonical/agents/database-reviewer.md +6 -8
  7. package/canonical/agents/explorer-ai-engineer-analyst.md +2 -6
  8. package/canonical/agents/implementer.md +30 -0
  9. package/canonical/agents/pr-reviewer.md +9 -11
  10. package/canonical/agents/python-reviewer.md +7 -9
  11. package/canonical/agents/report-formatter.md +7 -3
  12. package/canonical/agents/security-reviewer.md +7 -8
  13. package/canonical/agents/test-writer.md +5 -2
  14. package/canonical/hooks/cost-prices.json +11 -9
  15. package/canonical/hooks/freeharness-config-guard.mjs +8072 -87
  16. package/canonical/hooks/freeharness-context-monitor.mjs +88 -54
  17. package/canonical/hooks/freeharness-converge.mjs +78 -23
  18. package/canonical/hooks/freeharness-cost-tracker.mjs +1129 -218
  19. package/canonical/hooks/freeharness-memory-guard.mjs +20 -23
  20. package/canonical/hooks/freeharness-precompact.mjs +217 -87
  21. package/canonical/hooks/freeharness-quota-tap.mjs +173 -0
  22. package/canonical/hooks/freeharness-reload-context.mjs +197 -0
  23. package/canonical/hooks/freeharness-repeat-tool.mjs +1 -1
  24. package/canonical/hooks/freeharness-sdd-context.mjs +970 -0
  25. package/canonical/hooks/freeharness-session-start +297 -59
  26. package/canonical/hooks/hooks.claude-code.json +1 -1
  27. package/canonical/hooks/hooks.config-guard.claude-code.json +3 -2
  28. package/canonical/hooks/hooks.context-monitor.claude-code.json +1 -1
  29. package/canonical/hooks/hooks.converge.claude-code.json +1 -1
  30. package/canonical/hooks/hooks.memory-guard.claude-code.json +3 -2
  31. package/canonical/hooks/hooks.reload-context.claude-code.json +15 -0
  32. package/canonical/openspec/schema.spec-driven.yaml +29 -8
  33. package/canonical/openspec/templates/follow-ups.md +3 -2
  34. package/canonical/openspec/templates/proposal.md +1 -1
  35. package/canonical/openspec/templates/tasks.md +8 -0
  36. package/canonical/rules/CLAUDE.md +5 -4
  37. package/canonical/rules/routing.md +10 -9
  38. package/canonical/skills/agent-evaluation/SKILL.md +17 -6
  39. package/canonical/skills/agent-evaluation/evals/cases.yaml +38 -23
  40. package/canonical/skills/agentic-architecture/SKILL.md +4 -0
  41. package/canonical/skills/agentic-architecture/evals/cases.yaml +27 -0
  42. package/canonical/skills/code-review/SKILL.md +9 -2
  43. package/canonical/skills/code-review/evals/cases.yaml +1118 -27
  44. package/canonical/skills/git-workflow/SKILL.md +2 -0
  45. package/canonical/skills/local-code/SKILL.md +2 -2
  46. package/canonical/skills/local-code/evals/cases.yaml +1 -1
  47. package/canonical/skills/report-formatting/SKILL.md +10 -2
  48. package/canonical/skills/report-formatting/reference/ARCHITECTURE.md +3 -3
  49. package/canonical/skills/report-formatting/reference/HELPERS.md +10 -5
  50. package/canonical/skills/report-formatting/reference/RELEASE.md +1 -2
  51. package/canonical/skills/report-formatting/reference/TEMPLATES.md +3 -2
  52. package/canonical/skills/report-formatting/reference/authoring.md +25 -37
  53. package/canonical/skills/report-formatting/scripts/build-architecture.js +1 -1
  54. package/canonical/skills/report-formatting/scripts/build-md-claro.js +5 -8
  55. package/canonical/skills/report-formatting/scripts/check-env.js +0 -4
  56. package/canonical/skills/report-formatting/scripts/docmodel.js +3 -11
  57. package/canonical/skills/report-formatting/scripts/example-build.js +8 -10
  58. package/canonical/skills/report-formatting/scripts/example_build_ArchitectureReview_docx.js +8 -10
  59. package/canonical/skills/report-formatting/scripts/example_build_ExecutiveReport_docx.js +8 -10
  60. package/canonical/skills/report-formatting/scripts/theme-claro.js +39 -10
  61. package/canonical/skills/subagent-driven-development/SKILL.md +35 -28
  62. package/canonical/skills/subagent-driven-development/evals/cases.yaml +3496 -19
  63. package/canonical/skills/subagent-driven-development/reference/brief.md +185 -35
  64. package/canonical/skills/subagent-driven-development/reference/handoff.md +82 -2
  65. package/canonical/skills/subagent-driven-development/reference/loop.md +57 -0
  66. package/canonical/skills/subagent-driven-development/reference/trees.md +40 -0
  67. package/canonical/skills/subagent-driven-development/reference/workspace.md +31 -0
  68. package/canonical/skills/subagent-driven-development/scripts/validate-handoff.mjs +4771 -326
  69. package/dist/adapters/harness/claude-code.d.ts +19 -3
  70. package/dist/adapters/harness/claude-code.js +204 -39
  71. package/dist/adapters/harness/opencode.js +16 -2
  72. package/dist/adapters/harness/plugin.js +28 -17
  73. package/dist/adapters/memory/doctor.d.ts +174 -29
  74. package/dist/adapters/memory/doctor.js +758 -134
  75. package/dist/adapters/memory/migrate.d.ts +95 -0
  76. package/dist/adapters/memory/migrate.js +497 -0
  77. package/dist/adapters/memory/paths.d.ts +22 -0
  78. package/dist/adapters/memory/paths.js +85 -0
  79. package/dist/adapters/memory/vendored.d.ts +56 -29
  80. package/dist/adapters/memory/vendored.js +191 -154
  81. package/dist/adapters/sync/diff.js +4 -1
  82. package/dist/cli/cleanup-prompt.d.ts +35 -0
  83. package/dist/cli/cleanup-prompt.js +68 -0
  84. package/dist/cli/cleanup.d.ts +22 -24
  85. package/dist/cli/cleanup.js +182 -68
  86. package/dist/cli/cost-quota.d.ts +158 -0
  87. package/dist/cli/cost-quota.js +645 -0
  88. package/dist/cli/cost.d.ts +408 -46
  89. package/dist/cli/cost.js +1731 -57
  90. package/dist/cli/doctor.d.ts +52 -13
  91. package/dist/cli/doctor.js +193 -44
  92. package/dist/cli/hard-cleanup.d.ts +93 -0
  93. package/dist/cli/hard-cleanup.js +552 -0
  94. package/dist/cli/install-facts.d.ts +28 -0
  95. package/dist/cli/install-facts.js +66 -0
  96. package/dist/cli/json-key-edit.d.ts +18 -0
  97. package/dist/cli/json-key-edit.js +118 -0
  98. package/dist/cli/memory-import.d.ts +74 -0
  99. package/dist/cli/memory-import.js +469 -0
  100. package/dist/cli/plugin-validate.d.ts +12 -0
  101. package/dist/cli/plugin-validate.js +43 -0
  102. package/dist/cli/uninstall.d.ts +1 -1
  103. package/dist/cli/uninstall.js +2 -2
  104. package/dist/cli/upstream-launchers.d.ts +49 -0
  105. package/dist/cli/upstream-launchers.js +287 -0
  106. package/dist/index.js +247 -70
  107. package/dist/infra/fsutil.d.ts +31 -3
  108. package/dist/infra/fsutil.js +69 -6
  109. package/dist/infra/manifest.d.ts +2 -1
  110. package/dist/infra/manifest.js +3 -2
  111. package/dist/infra/prices.d.ts +18 -0
  112. package/dist/infra/prices.js +64 -0
  113. package/node_modules/@freeharness/core/dist/tiers.d.ts +10 -1
  114. package/node_modules/@freeharness/core/dist/tiers.js +24 -2
  115. package/node_modules/@freeharness/core/package.json +1 -1
  116. package/node_modules/@freeharness/sdd/PROVENANCE.yaml +279 -2
  117. package/node_modules/@freeharness/sdd/dist/src/bin/openspec.js +34 -8
  118. package/node_modules/@freeharness/sdd/dist/src/shim/archive.d.ts +7 -6
  119. package/node_modules/@freeharness/sdd/dist/src/shim/archive.js +72 -64
  120. package/node_modules/@freeharness/sdd/dist/src/shim/follow-up.d.ts +48 -16
  121. package/node_modules/@freeharness/sdd/dist/src/shim/follow-up.js +168 -70
  122. package/node_modules/@freeharness/sdd/dist/src/shim/instructions.d.ts +36 -6
  123. package/node_modules/@freeharness/sdd/dist/src/shim/instructions.js +110 -49
  124. package/node_modules/@freeharness/sdd/dist/src/shim/list.d.ts +1 -1
  125. package/node_modules/@freeharness/sdd/dist/src/shim/list.js +57 -14
  126. package/node_modules/@freeharness/sdd/dist/src/shim/nested-change.d.ts +7 -0
  127. package/node_modules/@freeharness/sdd/dist/src/shim/nested-change.js +118 -0
  128. package/node_modules/@freeharness/sdd/dist/src/shim/new-change.js +3 -1
  129. package/node_modules/@freeharness/sdd/dist/src/shim/proposal-capabilities.d.ts +12 -0
  130. package/node_modules/@freeharness/sdd/dist/src/shim/proposal-capabilities.js +94 -0
  131. package/node_modules/@freeharness/sdd/dist/src/shim/shared.d.ts +17 -0
  132. package/node_modules/@freeharness/sdd/dist/src/shim/shared.js +182 -1
  133. package/node_modules/@freeharness/sdd/dist/src/shim/status.d.ts +2 -2
  134. package/node_modules/@freeharness/sdd/dist/src/shim/status.js +34 -5
  135. package/node_modules/@freeharness/sdd/dist/src/shim/validate.d.ts +9 -0
  136. package/node_modules/@freeharness/sdd/dist/src/shim/validate.js +49 -41
  137. package/node_modules/@freeharness/sdd/dist/src/shim/warnings.d.ts +21 -0
  138. package/node_modules/@freeharness/sdd/dist/src/shim/warnings.js +66 -0
  139. package/node_modules/@freeharness/sdd/dist/vendor/openspec/apply/line-endings.d.ts +4 -0
  140. package/node_modules/@freeharness/sdd/dist/vendor/openspec/apply/line-endings.js +17 -0
  141. package/node_modules/@freeharness/sdd/dist/vendor/openspec/apply/specs-apply.js +95 -22
  142. package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/change-status-policy.d.ts +6 -0
  143. package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/change-status-policy.js +14 -6
  144. package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/index.d.ts +1 -1
  145. package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/index.js +1 -1
  146. package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/outputs.d.ts +3 -3
  147. package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/outputs.js +24 -4
  148. package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/task-progress.js +4 -7
  149. package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/types.js +13 -1
  150. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/apply-change.js +21 -6
  151. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/archive-change.js +80 -19
  152. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/explore.js +42 -22
  153. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/project-root.d.ts +32 -0
  154. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/project-root.js +39 -0
  155. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/propose.js +16 -9
  156. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/sync-specs.js +36 -1
  157. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/update-change.js +65 -30
  158. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/constants.d.ts +1 -1
  159. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/constants.js +1 -1
  160. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/change-parser.d.ts +8 -13
  161. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/change-parser.js +79 -102
  162. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/markdown-parser.js +4 -9
  163. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/requirement-blocks.d.ts +50 -10
  164. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/requirement-blocks.js +165 -37
  165. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/requirement-text.d.ts +24 -3
  166. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/requirement-text.js +55 -8
  167. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/spec-structure.js +4 -1
  168. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/purpose-placeholder.js +19 -9
  169. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/spec-discovery.d.ts +10 -0
  170. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/spec-discovery.js +115 -0
  171. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/task-lines.d.ts +3 -3
  172. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/task-lines.js +20 -20
  173. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/validator.d.ts +6 -0
  174. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/validator.js +75 -14
  175. package/node_modules/@freeharness/sdd/package.json +1 -1
  176. package/node_modules/@freeharness/sdd/vendor/openspec/assets/schema.upstream.yaml +47 -7
  177. package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-apply-change/SKILL.md +25 -7
  178. package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-archive-change/SKILL.md +50 -11
  179. package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-bulk-archive-change/SKILL.md +48 -10
  180. package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-continue-change/SKILL.md +27 -7
  181. package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-explore/SKILL.md +25 -12
  182. package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-ff-change/SKILL.md +17 -5
  183. package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-new-change/SKILL.md +12 -1
  184. package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-onboard/SKILL.md +47 -33
  185. package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-propose/SKILL.md +17 -5
  186. package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-sync-specs/SKILL.md +29 -1
  187. package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-update-change/SKILL.md +31 -13
  188. package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-verify-change/SKILL.md +75 -27
  189. package/package.json +6 -4
  190. package/provenance/canonical.PROVENANCE.yaml +1 -1
  191. package/provenance/memory.PROVENANCE.yaml +458 -6
  192. package/provenance/sdd.PROVENANCE.yaml +279 -2
  193. package/canonical/hooks/freeharness-auto-memory.mjs +0 -158
  194. package/canonical/hooks/hooks.auto-memory.claude-code.json +0 -16
@@ -1,36 +1,169 @@
1
1
  # Brief template
2
2
 
3
- One brief per task (or per batch of small same-shape tasks) at
4
- `.freeharness/sdd/<change>/<task>.brief.md`. The brief is the single source of
5
- requirements for the implementer: exact values, signatures and test names live here,
6
- never in the dispatch prompt. The implementer reads the brief, the files it names and
7
- nothing else; never the whole plan, never the session history.
8
-
9
- Header lines are `Key: value`, one per line, in this order. `Spec` is the OpenSpec
10
- change id (the implementer runs `openspec status --change <id>` and reads the delta
11
- specs it needs); never a copied spec, never a path. `Write scope` lists every file the
12
- implementer may create or modify, one entry per line in one of three forms: an exact
13
- file path; a directory with a trailing `/`, matching everything under it; or a glob,
14
- `*` matching within one path segment and `**` matching across segments. A path under
15
- `.freeharness/` is never charged to a worker, in or out of the write scope. Briefs
16
- dispatched together have disjoint scopes, and `scripts/validate-handoff.mjs briefs
17
- <dir>` refuses the batch on overlap. `Class` is
18
- optional, right after `Tier`: a freeform token for the cost tracker's `task_class` field
19
- (e.g. `feature`, `bugfix`, `refactor`, `spike`); a brief without it tags the session
20
- `untagged`. `Ceiling` is optional, right after `Class`: a positive integer, the per-task
21
- cache-read token ceiling summed across every dispatch of the task (impl, review and fix
22
- rounds alike); it overrides the `budget` subcommand's default ceiling of 25,000,000 for
23
- that task, and a brief without it uses that default.
3
+ One brief per task at `.freeharness/sdd/<change>/<task>.brief.md`; a task above the
4
+ sizing bounds is split into sub-tasks first, each with its own brief. The brief is the
5
+ single source of requirements for the implementer: exact values, signatures and test
6
+ names live here, never in the dispatch prompt. The implementer reads the brief, the files
7
+ it names and nothing else; never the whole plan, never the session history.
8
+
9
+ Header lines are `Key: value`, one per line, in this order. `Task` names exactly one id
10
+ (`2.4`, a sub-task `2.4.1`, or `final`); `validate-handoff.mjs briefs` refuses a list, a
11
+ range or a batch. `Spec` is the OpenSpec change id (the implementer runs `openspec status
12
+ --change <id>` and reads the delta specs it needs); never a copied spec, never a path.
13
+ `Write scope` lists every file the implementer may create or modify, one entry per line in
14
+ one of three forms: an exact file path; a directory with a trailing `/`, matching
15
+ everything under it; or a glob, `*` matching within one path segment and `**` matching
16
+ across segments. A path under `.freeharness/` is never charged to a worker, in or out of
17
+ the write scope. A source-file entry may carry a range, `path:120-340`, or a symbol,
18
+ `path#symbolName` (counted as 200 lines), which narrows the reading-set estimate. A
19
+ `path:append` entry names a file the task only appends to: its estimate counts the file's
20
+ last 200 lines; it takes no range or symbol (`path:10-20:append` is refused) and no
21
+ directory or glob; `handoff --brief` fails `append-only <path>` when the file's diff against
22
+ the base removes, changes or inserts a line above the base file's last 5 lines, so
23
+ replacing a closing line and appending entries pass. Briefs
24
+ dispatched together have disjoint scopes, and `scripts/validate-handoff.mjs briefs` refuses
25
+ the batch on overlap. The lead names the new brief files plus every brief file still in
26
+ flight, never the workspace directory: a directory argument skips the set checks, since the
27
+ workspace also holds landed briefs.
28
+
29
+ `Tier` and `Class` route the dispatch. `docs`, `extraction`, `exploration` and
30
+ `mechanical` (documentation, extraction, exploration, mechanical edits such as matrix
31
+ rows, renames, one-line values and transcription of complete plan text) run on `standard`
32
+ or `strong`; `summary` runs on `fast`, `standard` or `strong`, and `extraction` also on
33
+ `fast`; `security` (a task that changes a security boundary: a guard, a hook verdict, an
34
+ auth or secret path, input validation of untrusted payloads) and `complex` (a parser or
35
+ state machine, concurrency, a cross-file contract, or a task that reached the round cap
36
+ before) run on `strong` with `Effort: high` only; `code` (a code change that is neither
37
+ `security` nor `complex`) runs on `strong`, or on `standard` only with a `Route:` header
38
+ (below); any other class, or none, runs on `strong` only (the implementer's own tier). A
39
+ fix round runs on the tier and effort `validate-handoff.mjs route <brief> --round <n>
40
+ [--p0] [--security]` prints, and any rewrite of a standard `code` brief to `strong` (fix
41
+ round 2, or a fix round carrying a P0 or a security finding) drops the `Route:` line. A `fast` brief keeps
42
+ its reading set under 100,000 tokens at 4 bytes per token; the validator checks the class, never that bound. `validate-handoff.mjs briefs`
43
+ refuses a class the tier does not allow, naming the brief and the allowed classes.
44
+ `Class` is optional, right after `Tier`: the class token also fills the cost tracker's
45
+ `task_class` field; a brief without it tags the session `untagged` and runs on `strong`.
46
+ `Effort` is optional, right after `Class`: `medium` (the default) or `high`; `high` runs
47
+ on `strong` only and is passed as the per-dispatch effort. `Route` comes right after `Effort`, on
48
+ a standard `code` brief only (below). `Split` is optional: `Split: refused - <reason>` waives the size bounds
49
+ when the work cannot be divided, and is the only accepted value. `Read` is optional: a
50
+ comma-separated list of files (with ranges or symbols) the worker reads beyond its write
51
+ scope; its entries count in the reading set. The size bounds are at most 3 write-scope
52
+ files outside test and fixture directories and a reading set under 60,000 tokens at 4
53
+ bytes per token. `Ceiling` is optional, right after `Split` and `Read`: a positive
54
+ integer, the per-task cache-read token ceiling summed across every dispatch of the task
55
+ (impl, review and fix rounds alike), at or below the task's class ceiling: 60,000,000 for
56
+ `security` and `complex`, 50,000,000 for task `final` and 25,000,000 for any other task,
57
+ or the owner's `Ceiling <class>:` value in the change's run authorization (reference/loop.md).
58
+ The header only lowers the class ceiling; `briefs` and `budget` refuse one above it.
59
+
60
+ `Route: C1-C7 - <reason>` is required on, and allowed only on, a brief with `Tier:
61
+ standard` and `Class: code`; it states that the task meets every standard-code
62
+ criterion: C1 at most 2 write-scope files outside test and fixture directories; C2 no
63
+ parser, state machine, concurrency, guard or hook verdict, auth or secret path, or
64
+ cross-file contract; C3 an existing pattern in the repository the task follows, named in
65
+ the `Read:` header; C4 at least 2 acceptance probes, each stating its expected outcome;
66
+ C5 no manifest, lockfile or generated file in the write scope; C6 no edit of a test
67
+ fixture or helper shared with other tests; C7 the task never came back `partial` or
68
+ `blocked`, and never opened fix round 2, on `standard`. The reason is non-empty and names
69
+ the pattern followed. `validate-handoff.mjs briefs` refuses a standard `code` brief
70
+ without the header, a value not starting with `C1-C7 - ` or with an empty reason, and a
71
+ `Route:` on any other brief; the validator checks C1 to C5; C6 and C7 live in the reason.
72
+ For C1 a directory or glob entry outside test and fixture directories is refused, and a
73
+ `*.test.*` file counts as a test entry. Example: `Route: C1-C7 - adds one retry option following
74
+ src/http/timeout.ts; no shared fixture; first dispatch`. The template carries the line
75
+ as a comment, like `Unaffected:`; a standard `code` brief writes it uncommented.
76
+
77
+ The implementer type is the pair of `Tier` and `Effort`; there are four:
78
+
79
+ | Type | Classes |
80
+ |---|---|
81
+ | fast/medium | extraction, summary |
82
+ | standard/medium | docs, extraction, exploration, mechanical, summary, code with `Route:` |
83
+ | strong/medium | every class but security and complex, any other token or none |
84
+ | strong/high | security, complex, and any strong class the lead raises to high |
85
+
86
+ `Changes` is required, right after `Ceiling`: the identifiers and literal values the task
87
+ changes (a field, a constructor, a flag, a message, a verdict, a declared effort or model),
88
+ comma-separated, or `none - <reason>` when the task changes none. `briefs` searches the
89
+ text of every tracked test file for each term and refuses the brief when a file naming one
90
+ is not covered. A test file is one under a `test`, `tests`, `__tests__` or `fixtures`
91
+ directory, one named `*.test.*`, `*.spec.*`, `*_test.*` or `test_*.py`, or a
92
+ `conftest.py`; a binary file and a file over 4 MiB are skipped. A file is covered when a write-scope entry matches it, an
93
+ `Unaffected:` line names it, or a `## Verify` command names its path directly or relative
94
+ to the command's leading `cd <dir> &&`, as a whole path: never as part of a longer one.
95
+ `Unaffected: <path> - <reason>` is repeatable, one
96
+ line per test file that names a term and stays untouched; the reason is required. The
97
+ failures are `changes-unscoped <term> <path>` (a test names the term and nothing covers
98
+ it), `changes-missing` (no `Changes:` line), `unaffected-reason <path>` (an `Unaffected:`
99
+ line without a reason), `changes-short <term>` (a term under 3 characters) and
100
+ `changes-broad <term> (...)` (more than 40 test files name it; name a narrower term). The
101
+ check covers tests only, so a production file the plan missed is still found by reading
102
+ the design.
103
+
104
+ `Key-list` is optional, right after `Changes`: `Key-list: <key>[, <key>...]`, one sibling
105
+ key per JSON object the task adds a key to. `briefs` also searches each `Changes` term
106
+ regex-escaped, and fails `changes-unscoped key-list:<key> <path>` for an uncovered test that
107
+ pins an exact key list naming the sibling (`Object.keys(` then a `deepEqual` array quoting it).
108
+
109
+ `Suite` is required, right after `Changes` and its `Unaffected:` lines: the project's
110
+ full-suite command. The worker never runs it and runs only the `## Verify` commands;
111
+ `briefs` refuses a brief without it (`suite-missing`).
112
+
113
+ `Tree`, `Setup` and `Resources` come right after `Suite`; reference/trees.md holds the
114
+ tree lifecycle. `Tree: <root>/.worktrees/<change>/<task>` names the task's worktree, the
115
+ absolute path under the main project root; `Tree: none - <reason>`, or no `Tree:` line,
116
+ runs the task in the main tree. A brief may run in the main tree only when it is the one
117
+ implementer in flight: `briefs` fails `tree-required <brief>` when it names more than one
118
+ brief and any of them runs in the main tree, `tree-path <brief>` for a header that differs
119
+ from the derived path, and `tree-absent <brief>` when the path is not a linked worktree
120
+ (run `validate-handoff.mjs tree <brief.md>` first). `Setup: <command> | none - <reason>`
121
+ is required on a tree brief (`setup-missing <brief>`): the command the lead runs in the
122
+ tree before the dispatch, such as an install. `Resources: <name>[, <name>...] | none`
123
+ names the shared ports, databases and GPUs the task uses; two named briefs listing the same
124
+ name, compared case-insensitively, fail `resource-overlap <name> <brief> <brief>`. A write
125
+ scope naming a dependency manifest or lockfile (the C5 set) fails `manifest-parallel
126
+ <path>` when the run names more than one brief. A tree brief's write-scope entry under
127
+ `.freeharness/`, or an untracked one git ignores in the main tree, fails `scope-ignored
128
+ <entry>`, since no tree diff can show a change to it; keep such
129
+ paths in a brief without `Tree:`. The template carries the three lines as comments, like
130
+ `Route:`; a tree brief writes them uncommented.
131
+
132
+ A line range or a symbol on a write-scope entry bounds the reading-set estimate only: it
133
+ never narrows what the worker may edit, and an edit elsewhere in the same file never
134
+ fails `scope`.
135
+
136
+ The `## Acceptance probes` section holds 1 to 12 lines `P<n>: <command, input or
137
+ observable> -> <expected verdict or outcome>`, written by the lead from the spec
138
+ scenarios and the design's edge cases before the dispatch. A `security` or `complex`
139
+ brief carries a probe whose expected outcome is an allow or a pass, and a probe that runs
140
+ a corpus, fixture replay or generator command over the changed behavior. A probe of a
141
+ guard or hook verdict runs the hook through node on a payload file (the hook reading the
142
+ payload on stdin), never the probed command typed into Bash, so the session's own
143
+ permission layer never judges the probe: `node <hook>.mjs < payload.json -> <verdict>`. The
144
+ `## Project rules` section holds 1 to 20 lines, since the workers run without CLAUDE.md:
145
+ the project CLAUDE.md invariants and protected boundaries that bear on the write scope,
146
+ plus the artifact rules every worker follows.
24
147
 
25
148
  ```markdown
26
149
  # Brief <task>: <title>
27
150
 
28
151
  Change: <change-id>
29
- Task: <task number, or "1.3, 1.4, 1.5 (batch)">
152
+ Task: <one task number, "2.4.1" for a sub-task, or "final" for the final branch review>
30
153
  Spec: <change-id>
31
154
  Tier: strong | standard | fast
32
- Class: <optional freeform task-class tag>
33
- Ceiling: <optional cache-read token ceiling for the task, integer; default 25000000>
155
+ Class: <optional routing class: docs, extraction, exploration, mechanical, summary, code, security, complex, or a freeform token that runs on strong>
156
+ Effort: <optional medium | high; high requires Tier: strong and is passed as the per-dispatch effort>
157
+ <!-- Route: <only for Tier: standard Class: code: C1-C7 - <reason>> -->
158
+ Split: <optional "refused - <reason>" when the work cannot be divided>
159
+ Read: <optional comma-separated files, ranges or symbols read beyond the write scope>
160
+ Ceiling: <optional cache-read ceiling at or below the class ceiling; default the class ceiling>
161
+ Changes: <term>[, <term>...] or none - <reason>
162
+ <!-- Unaffected: <test path that names a term and stays untouched> - <reason> -->
163
+ Suite: <the project's full-suite command; the lead runs it, the worker never does>
164
+ <!-- Tree: <root>/.worktrees/<change-id>/<task> | none - <reason> -->
165
+ <!-- Setup: <command the lead runs in the tree> | none - <reason> -->
166
+ <!-- Resources: <shared port, database or GPU>[, <name>...] | none -->
34
167
  Base: <git rev-parse HEAD at dispatch>
35
168
  Report: .freeharness/sdd/<change-id>/<task>.review.md
36
169
 
@@ -61,31 +194,48 @@ decisions; a stale digest is a defect, not a shortcut.>
61
194
  ## Depends
62
195
  <task numbers whose output this task needs, or "none">
63
196
 
197
+ ## Acceptance probes
198
+ P1: <command, input or observable> -> <expected verdict or outcome>
199
+ P2: <command, input or observable> -> <expected verdict or outcome>
200
+
201
+ ## Project rules
202
+ - <invariant or protected boundary from the project CLAUDE.md that bears on the write scope>
203
+ - Artifacts (code, comments, docs, tests, reports) in English; no bold markdown anywhere.
204
+
64
205
  ## Verify
65
- <the commands whose output proves the task: test target, mypy, lint, eval>
206
+ <the commands whose output proves the task: test target, mypy, lint, eval; a Verify item
207
+ testing a bundled or built file is preceded by its rebuild command, which the worker runs;
208
+ name test files, never a package's `npm test`, which the handoff guard reads as the Suite command>
66
209
 
67
210
  ## Run rules
68
- - Build before tests (`npm run build` at the repo root, or `-w freeharness` for the CLI package).
211
+ - Build before tests (`npm run build` at the repo root, or `-w freeharness` for the CLI package; after a canonical/ edit, `npm run bundle:canonical -w freeharness` first); a Verify item testing a bundled or built file is preceded by its rebuild command, which the worker runs.
69
212
  - Run tests in the foreground and wait for the exit code; give any run that may exceed two minutes an explicit Bash timeout (up to 600000 ms), since the tool moves a longer foreground command to the background, and never use run_in_background for tests.
70
213
  - A command denied by a permission prompt or a guard ends the step: reply blocked with `needs-confirm`; never retry it verbatim and never reach the same effect through another tool.
71
214
  - Never pipe test output through `tail` or `head`; keep the full output.
72
215
  - One idle notification ends a wait; never wait on a finished background run.
73
216
  - Never run the CLI, a hook or a worker against the real HOME; tests use a fake HOME.
74
217
  - The reply is the JSON block plus the report path, under 8,000 characters.
218
+ - The reply and the report's first fenced json block follow this skeleton exactly: `{"task":"...","status":"completed|partial|blocked","files_changed":[...],"tests_run":[{"command":"...","result":"pass|fail|unavailable","summary":"..."}],"evidence":[...],"partial_output":null,"blocked_reason":null,"concerns":[]}`; the report file opens with it inside a ```json fence.
219
+ - Cite every acceptance probe id (`P1:` ...) in `evidence` with its observed outcome; the first dispatch runs and cites every probe, and a fix round re-runs the probes its open findings name plus every corpus, fixture replay or generator probe and cites each other probe as `P<n>: carried from round <n-1>`.
220
+ - An environment override (HOME, XDG_*) is set in the same shell command as the program it applies to; a separate export does not persist between tool calls.
221
+ - Edit files only with the Edit and Write tools, never through a Bash heredoc, `sed -i` or an interpreter script.
222
+ - Write a helper or probe script with the Write tool and run it as `node <file>`; a probe that makes a fake HOME write its `.claude/settings.json` or hooks runs from such a script, since the config guard refuses a Bash line naming those paths.
223
+ - The handoff guard measures the comment share of every edited source file and fails one over 30% (grown-only when the file was over at the base); keep comments to the why and the share in line with the surrounding code.
75
224
  - Reply and report in English; every comment, docstring, test title and doc passage passes the HEAD-reader test: no task numbers, ruling ids, round or review references (the `commit` field of a PROVENANCE row is the one convention that names the task).
76
225
  - Never `git stash`, `git checkout --` or `git reset` in the shared working tree; prove RED by writing the tests first or against a temp copy of the pre-change file.
226
+ - Never write the git index: no `git add`, `git rm`, `git mv`, `git commit`; delete a file with a plain `rm` and claim it in `files_changed`.
227
+ - Never run the `Suite:` command; run only the `## Verify` commands (the lead runs the full suite).
77
228
  - A failing test or a regression the task did not expect is handled with the systematic-debugging skill (reproduce, isolate, root fix, verify) before any code change.
229
+ - The report ends with `## Checkpoint` holding six subsections with these exact headings in this order: `### Intent`, `### Files and code`, `### Decisions` (decisions with the approaches tried and rejected), `### Errors and fixes`, `### Open work`, `### Next step`; terse `- ` lines, `(none)` when empty, at most 8,000 characters (target 4,000); on a fix round it is the complete merged state, never an appended delta.
78
230
 
79
231
  ## Handoff
80
232
  Write the review package to the Report path: the JSON handoff block first
81
- (reference/handoff.md), then the report. Return only the JSON block and the report path
82
- to the caller. Never dispatch subagents. Other implementers write in the same tree, so
83
- never `git stash`, `git checkout --` or `git reset` here: prove a failing test by
84
- writing it first, or against a temp copy of the file. On a stop condition, return
85
- `blocked` with the reason.
233
+ (reference/handoff.md), then the report body, then the checkpoint. Return only the JSON
234
+ block and the report path to the caller. Never dispatch subagents. Other implementers
235
+ write in the same tree, so never `git stash`, `git checkout --` or `git reset` here: prove
236
+ a failing test by writing it first, or against a temp copy of the file. On a stop
237
+ condition, return `blocked` with the stop token and one line of detail.
86
238
  ```
87
239
 
88
- Batching: several tasks that are each the same small edit repeated across files (a
89
- constant, a one-line fix, a field addition) share one brief whose Task section lists
90
- every file and its change; the review package covers the batch as one diff. A task that
91
- needs its own tests, its own judgment or its own review surface gets its own brief.
240
+ Every task gets its own brief, even a small same-shape edit: the review package covers one
241
+ diff, and a task above the sizing bounds is split before any brief is written.
@@ -59,6 +59,49 @@ one; it never widens the scope, never edits outside it, never asks a subagent.
59
59
  - Deviations from the brief and the reason.
60
60
  - Fix rounds append `## Fix round <n>` sections with the findings addressed, the
61
61
  covering tests re-run and their output; the JSON block is rewritten to the new state.
62
+ - The report ends with `## Checkpoint`.
63
+
64
+ ## Checkpoint
65
+
66
+ The last section of every report is the task's bounded working state, so a fresh fix
67
+ implementer starts from it instead of a transcript. Six subsections with these exact
68
+ headings, in this order, terse `- ` lines, `(none)` when a subsection is empty:
69
+
70
+ ```markdown
71
+ ## Checkpoint
72
+
73
+ ### Intent
74
+ - Make `doctor` name the file and line of a gerund fixture failure.
75
+
76
+ ### Files and code
77
+ - src/cli/doctor.ts: `checkFixture()` returns `{file, line}`; test/doctor.test.ts covers it.
78
+
79
+ ### Decisions
80
+ - Report the first failing line only; rejected: collecting every line (output too long).
81
+
82
+ ### Errors and fixes
83
+ - Off-by-one in the line number (counted from 0); fixed in `checkFixture()`.
84
+
85
+ ### Open work
86
+ - (none)
87
+
88
+ ### Next step
89
+ - Re-run the Verify commands and write the handoff.
90
+ ```
91
+
92
+ The checkpoint holds at most 8,000 code points (target 4,000); the validator never
93
+ truncates it. On a fix round it is the complete merged state, stale lines dropped, never an
94
+ appended delta. Two writers turn it into the task state file `<task>.state.md`, replaced and
95
+ never appended: the SDD context hook at SubagentStop when it is installed, and otherwise
96
+ `scripts/validate-handoff.mjs state <report> <task>.state.md`; the lead's fix brief names
97
+ `<task>.state.md`, and the next fix implementer reads it and confirms it against the tree.
98
+
99
+ `handoff <file> --brief <brief.md>` checks the checkpoint and the probes, and prints
100
+ `FAIL <outcome> <detail>` for:
101
+ - `checkpoint-missing <subsection>`: the report has no `## Checkpoint` (detail `section`), or a
102
+ subsection is absent, empty or out of order; the detail names it.
103
+ - `checkpoint-oversized <n>`: the checkpoint holds `<n>` code points, more than 8,000.
104
+ - `probe-missing <id>`: a brief probe (`P1`, `P2`, ...) that no `evidence` line cites.
62
105
 
63
106
  ## Guard commands
64
107
 
@@ -86,6 +129,40 @@ reported as `peer-scope`. The `claimed-unchanged` check keeps the whole tree, so
86
129
  a path that did not change is still reported. Without the option a guard run while peers are
87
130
  writing returns their files as this worker's findings.
88
131
 
132
+ A task that runs in its own worktree (a `Tree:` path, reference/trees.md) is guarded with
133
+ `scripts/validate-handoff.mjs handoff <file> --base <sha> --brief <brief.md> --agent
134
+ <agent_id> --tree <Tree>`. Every diff, scope and transcript check then runs against the
135
+ tree, while the brief, the report and the ledger stay under the main root's
136
+ `.freeharness/sdd/<change>/`. A path that is not a linked worktree of the repository under
137
+ `<root>/.worktrees/` fails `tree-path`. Two more outcomes apply:
138
+ - `tree-leak <path>`: the worker wrote the main tree instead of its own. Reported for a
139
+ write-scope path that differs in the main tree from its HEAD (an untracked file counts;
140
+ `--lead` paths are excepted), and with `--agent` for an Edit, MultiEdit, Write or
141
+ NotebookEdit path or a Bash write target under the main root outside the tree and outside
142
+ `<root>/.freeharness/`, or naming the report's change directory `<root>/.freeharness/sdd/<change>/`
143
+ itself or a path in it (a Bash removal there included; only a tool write to the report is
144
+ spared), and for a git command that writes the main tree's index. It returns to the worker
145
+ as a round.
146
+ - `NOTE tree-cwd <command>`: with `--agent`, a Bash command that ran in a directory
147
+ outside the tree, typically one missing the `cd <Tree> &&` prefix. A note, not a
148
+ failure: the lead reads the command and judges whether it touched the main tree.
149
+
150
+ The change directory holds the ledger, the findings and the round copies the guard trusts,
151
+ so with `--agent` and without `--tree` a write there fails too. Only an Edit, MultiEdit,
152
+ Write or NotebookEdit write to the report is spared, and only while the report is a regular
153
+ file with one link at scan time: a symlink or hard link there would spare the file it shares.
154
+ A Bash write target (the report included), a link created at the report name, a copy or move
155
+ naming the directory itself, or a Bash removal of the directory or a path in it fails as
156
+ `bash-write <path>`, and a tool path as
157
+ - `workspace-write <path>`: an Edit, MultiEdit, Write or NotebookEdit path in the report's
158
+ change directory other than a spared report. The path prints from the main root; every other
159
+ `.freeharness/` path, another change's directory included, stays free.
160
+
161
+ A command behind `env -C`, `env --chdir`, `sudo -D` or `sudo --chdir` is judged from the
162
+ shell's own directory; the option's directory is noted, never resolved, so a relative write
163
+ target behind it also prints `NOTE bash-write-unresolved <target>` and the option can only add
164
+ lines.
165
+
89
166
  `scripts/validate-handoff.mjs recover <transcript.jsonl> <report> [--force]` rebuilds the handoff
90
167
  when a dispatch ends without a clean reply. Recovery reads only assistant-authored text:
91
168
  the joined `text` blocks of an `assistant` transcript line, and the `input.message` of
@@ -114,5 +191,8 @@ is dispatched the lead appends `## Lead evaluation`: the diff reviewed
114
191
  (`git diff <base>..HEAD --stat`), the Verify commands the lead ran itself with their
115
192
  summary lines, and the verdict `accepted for review | returned` with the reason. A task
116
193
  whose review package carries only the worker's claim is not done. The task reviewer's
117
- findings, each fix round line (`round <n>/5: <addressed>/<open>`) and, at the cap, the
118
- `## Controller ruling` (finding, decision, cost if wrong) follow in the same file.
194
+ findings, each fix round's tally line and, at the round cap or the class ceiling, the
195
+ `## Controller ruling` (finding, decision, cost if wrong) follow in the same file. The tally
196
+ is `round <n>/<cap>: <addressed>/<open blocking>`: `<n>` the round, `<cap>` the task's round
197
+ cap (3, or the run authorization's `Rounds:`), `<addressed>` the findings the round fixed and
198
+ `<open blocking>` the blocking findings still open after its re-review.
@@ -0,0 +1,57 @@
1
+ # Loop detail
2
+
3
+ Brief sizing and the budget, dispatch names, the handoff guards, fix rounds, the commit and the final branch review of the subagent-driven-development loop. SKILL.md names this file at the step that needs each part; every line binds like a SKILL.md line.
4
+
5
+ ## Brief
6
+
7
+ - Sizing, before every brief: a brief covers one behavior (the scenarios of one requirement, or one cohesive part), with at most 3 write-scope files outside test and fixture directories and a reading set (write-scope files plus the `Read:` header's entries) under 60,000 tokens at 4 bytes per token. A file over 1,500 lines is named by range (`path:120-340`) or symbol (`path#symbolName`, counted as 200 lines) in the write scope or `Read:`. A task above a bound is split into sub-tasks `<task>.<n>` (`2.4.1`, `2.4.2`), each with its own brief, dispatch, review and acceptance; the first sub-task's review package carries a `## Split` note naming the sub-tasks and the behavior each owns, and the parent is ticked in `tasks.md` only when every sub-task is accepted. `Split: refused - <reason>` is written only when the work cannot be divided; `validate-handoff.mjs briefs` refuses an oversized brief without it.
8
+ - Pin grep: `briefs` searches each `Changes:` term plain and regex-escaped and, with `Key-list:`, finds exact key-list pins; the lead still greps by hand for a pinned count (a bare number) and puts each hit in the write scope or an `Unaffected:` line; a brief that changes a USAGE line or skill text runs content.test.ts in its Verify.
9
+ - Changed terms: `Changes:` names every identifier and literal value the task changes, and `validate-handoff.mjs briefs` refuses a brief whose write scope, Verify commands or `Unaffected:` lines miss a test that names one. The check covers tests only, so the lead still adds the production files a plan missed by reading the design.
10
+ - Class ceilings: `budget` stops a task at its class ceiling of cache-read tokens: 60,000,000 for a `security` or `complex` task, 50,000,000 for task `final` and 25,000,000 for any other task. The class is `security` or `complex` when a brief declaring the task carries that `Class:` (the higher ceiling when both appear). It trips when the total plus a reserve, the median of the task's dispatches (the lower middle value for an even count), reaches the ceiling, so a typical next dispatch never crosses it. The output names the source of the ceiling: `class <class> default`, `class <class> authorized`, `brief <file>` or `flag`.
11
+ - Run authorization: the owner may set the ceilings and the round cap in one section of the change's `proposal.md` under the exact heading `## Run authorization`, approved with the proposal: `Ceiling <class>: <n>` (`<class>` one of `security`, `complex`, `final`, `other`; `<n>` a positive integer) and `Rounds: <1-9>`, each key at most once. Without it the class defaults and a cap of 3 rounds apply; a malformed section fails `budget`, `briefs` and `round-base` naming proposal.md and the line: `duplicate-section` (a second such heading), `near-miss-heading` (a `## Run authorization` heading in another case or with more words), `unknown-line` (any other non-blank line in the section), `duplicate-key` or `bad-value`. Only the owner edits it; the lead never raises a ceiling or the cap.
12
+ - A brief's `Ceiling:` header only lowers the class ceiling: the highest one among the task's briefs applies at or below it, and one above it fails `budget` and `briefs`. `--ceiling` on `budget` overrides every other source and is the owner's to pass.
13
+
14
+ ## Dispatch names
15
+
16
+ - Write `_` for `.` because the Agent tool rejects a dot in a name; the ledger stores the dotted id and reads role, task and round through the slug.
17
+ - The lens slug is lowercase letters, digits and hyphens (`review-final-r1-lenses-b`); any other character leaves role and task null in the ledger.
18
+
19
+ ## Guards before the reviewer
20
+
21
+ - Run `validate-handoff.mjs handoff <report> --base <Base> --brief <brief.md> --agent <agent_id>`, where `<agent_id>` is the id the Agent tool result returns, adding `--in-flight <brief.md>` for every other brief still running so a peer's files are not charged to this worker, and `--lead <path>` for the lead's own paths (the change's `tasks.md`, any review package outside `.freeharness/`) so they are not charged either.
22
+ - A `claimed-unchanged`, `changed-unclaimed`, `scope`, `peer-scope` or `lead-path` failure, a `probe-missing`, or a `checkpoint-missing` or `checkpoint-oversized` failure returns to the worker as findings (a round), never straight to the reviewer; `peer-scope` means the worker claimed a file another dispatch owns, `lead-path` means it claimed one of the lead's own paths, neither ever worth passing on.
23
+ - With `--agent`, the command also scans the worker's transcript and fails `bash-write <path>` (a Bash command wrote a file in the tree) or `bash-write <interpreter>` (an interpreter body wrote in the tree), `workspace-write <path>` (a tool write into the change directory in a shared-tree run), `index-write <command>` (a git command wrote the index or HEAD) and `suite-run` (the worker ran the brief's `Suite:` command). It prints these NOTE lines: `NOTE bash-write-unresolved <target>` (a write target the scan could not resolve), `NOTE transcript-unavailable <no-ledger|no-line|outside-projects|foreign-path|unreadable>` (a ledger path for the agent was not scanned; `foreign-path` names a file other than `agent-<agent_id>.jsonl` in a `subagents` directory, and every accepted path is still scanned), `NOTE transcript-truncated` (only part of the transcript was read), `NOTE transcript-tree-unresolved <error>` (git named no working tree, so the current directory stands in for it) and `NOTE bash-write-budget` (the scan's filesystem or path budget ran out, so later targets were placed by their text alone or left unresolved).
24
+ - Each of these failures returns to the worker as a round, like the other guard failures. The lead may rule a false positive (for example a fixture generator run inside the tree on purpose): it reads the command in the transcript and writes a ruling line in the review package before the reviewer runs, never a silent pass.
25
+ - Threat model: the transcript scan is a visibility layer over the diff guards (`claimed-unchanged`, `changed-unclaimed`, `scope`, `tree-leak`) for the owner's own subagents; tree isolation and the lead's diff read stay the controls. Padding, symlink and quoting or tokenizer shapes against the scan close as wontfix, a follow-up and never a round. The brief, the findings ledger and the review package are written by the lead and are trusted input: a parser that reads only them owes no hostile-input size or timing test; the change ledger and the transcripts stay untrusted.
26
+ - Evidence test: on a `security` or `complex` task a P1 blocks only when it fails a brief probe, a spec scenario, or the corpus probe against the base (a deny the base prints that the task's code no longer prints); a shape the reviewer constructs is `hardening` with its reproducer, recorded as a follow-up.
27
+ - Guard false positive: a worker that returns `blocked` with `needs-confirm` because the config or git guard denied a Verify or probe line that names no guarded path is answered by the lead rerunning that line itself in the task's tree, then re-dispatching the same round only to rerun Verify and write a completed report, with no code change, no new ruling id and no `round-base` call; the denied shape is recorded once as a follow-up. A line that names a guarded path keeps the stop: the owner opens the config-edit window or the task is re-scoped.
28
+ - `transcript-unavailable` with `no-ledger` or `no-line` happens when the dispatch has no ledger line (the other reasons are listed above): the lead dispatches from the repository root so the hooks find the project.
29
+
30
+ ## Fix rounds
31
+
32
+ - Fix prompt order: the brief path, `Round <n> of <cap>` (`<cap>` the task's round cap), the line "The working tree is the memory and the source of truth: run round-diff and inspect the files before acting; treat the task state in `<task>.state.md` as a bounded handoff and confirm it against the tree", then the ledger path. The lead's fix brief names `<task>.state.md`, the task state the SDD context hook or `validate-handoff.mjs state` wrote from the last checkpoint; the fix checkpoint is the complete updated state, never an appended delta.
33
+ - Round cap: `round-base <brief.md> <n>` refuses `round-cap <n> over <cap>` before copying anything; the cap is 3, or the run authorization's `Rounds:`. The round's tier is what `route` prints (strong from round 3 on, whatever the cap).
34
+ - Controller ruling at the round cap or the class ceiling, one entry per open finding: real and load-bearing gets the smallest unblocking change, carried into the next brief; real and deferred, or contested, is parked with the reason and the cost if wrong. A structural defect that leaves every path a guess stops the run. A ruling carried into the next brief narrows the task's own decisions only; a `[base]`, `hardening` or follow-up finding never becomes a carried ruling.
35
+ - Remove the addition: at the round cap a blocking finding on a mid-task addition (code no brief scenario or design decision asked for, added by the worker or by a digest ruling) is ruled "remove the addition", handled as a sub-task and recorded as a follow-up, without an owner stop; every other round-cap finding keeps the stop.
36
+
37
+ ## Commit by path
38
+
39
+ - Commit every accepted task with `validate-handoff.mjs commit <report> [<report>...] -F <message-file> [--lead <path>]...`, after the tick of `tasks.md` (pass it as `--lead`), listing every report of the task (each round's review package): the reports must declare the same task, the last one must be `completed`, and the claimed set is the union of their `files_changed`, so an earlier round's path is never dropped. Write the message file yourself; the worker never writes it.
40
+ - The subcommand commits with `--only` over the reports' files plus each `--lead` path. A commit after an explicit `git add` takes the whole index, so a peer's staged deletion rides along in the task commit; `--only` commits the named paths and leaves every other staged entry out.
41
+ - A refusal prints its reason and commits nothing; fix the cause and run the subcommand again. Hooks run; never bypass them. A plain `commit` run from inside a task tree fails `tree-path`, naming `--tree <path> --base <sha>`.
42
+ - A `staged` guard failure names a stray staged entry. Read the entry, attribute it to its dispatch, and unstage it with `git restore --staged <path>` before the commit.
43
+ - Tree landing (`--tree <Tree> --base <sha>`, reference/trees.md) labels a failure by its step: `tree-add-failed`, `tree-commit-failed`, `cherry-pick-failed`, `lead-add-failed`, `main-commit-failed`, and `commit-hook` only for a non-128 exit while a commit hook is installed. Every failure after the tree commit also prints `NOTE tree-commit <sha>` (the tree commit to recover from), `NOTE tree-head-moved <sha>` (the tree's HEAD now sits on it) and one `NOTE main-entry <XY> <path>` per staged or unmerged main-tree entry to clear before a retry.
44
+ - `--tree-only` skips `land-conflict` and `tree-leak`, since it never touches the main tree.
45
+
46
+ ## Test tiers and waves
47
+
48
+ - A wave is the set of tasks dispatched together before the lead waits.
49
+ - The lead's full-suite run (the briefs' `Suite:` command) happens after the last task of the wave is accepted and committed, and before the next wave's dispatch; the lead runs it alone, one run at a time. Implementers run only their `Verify` commands: the task's tests plus the affected tests the changed-term check found.
50
+ - A suite failure is attributed by running the failing test files at each task commit of the wave; each task is its own commit, so the first commit at which they fail names the task.
51
+ - A regression in a test that names nothing the task changed surfaces at the wave run, never at the worker's.
52
+
53
+ ## Final branch review
54
+
55
+ - Reviewers: domain reviewers + `pr-reviewer`, each dispatch on the tier `route --review` prints over `final.brief.md`, the spec-compliance pass with `--final-spec`.
56
+ - Probes: a probe of a guard or hook verdict runs the hook through node on a payload file (the hook reading the payload on stdin), never the probed command typed into Bash, so the session's own permission layer never judges the probe.
57
+ - The review runs under the same cap of 3 reviewer dispatches per round unless a user ask for more is recorded in the review package; one fix dispatch for the whole findings list, one scoped re-review, rulings for residuals.
@@ -0,0 +1,40 @@
1
+ # Trees for parallel implementers
2
+
3
+ The worktree lifecycle of the subagent-driven-development loop. SKILL.md names this file at dispatch rule 5, read when more than one implementer is in flight; every line binds like a SKILL.md line.
4
+
5
+ ## When a tree is needed
6
+
7
+ - One implementer in flight runs in the main tree: its brief carries `Tree: none - <reason>` (or no `Tree:` line) and its task is committed with the plain `commit`.
8
+ - Whenever more than one implementer is in flight, every brief of the set names its own tree, `Tree: <root>/.worktrees/<change>/<task>`, where `<root>` is the main project root and `<task>` the dotted task id. `briefs` fails `tree-required` otherwise.
9
+ - Workers in a tree are plain subagents: no Agent tool isolation, no `name`.
10
+
11
+ ## Band
12
+
13
+ - The band caps the implementers in flight: small 4 (the default), medium 8, large 12. `briefs --band <small|medium|large>` fails `band <count> over <cap>` when the brief files it names outnumber the cap.
14
+ - Write `Band: <small|medium|large> - <reason>[ - quota <n>%]` in the review package of the first task dispatched under a band other than small.
15
+ - Large needs the user's ask, recorded as `Band: large - <ask> - quota <n>%`, with the newest reading in `~/.claude/freeharness/quota-readings.jsonl` under 50%. The validator never reads the quota and never sees the ask.
16
+ - Medium or large only when the median accepted-task duration in the ledger exceeds four times the lead's service time per task. Record that service time per task as `Service time: <task> <seconds>s`: the tree, the setup, the guards and the landing.
17
+ - At most 16 dispatches of any role in flight: implementers, test-writers and reviewers together. With 12 implementers in flight, 4 slots remain for reviewers.
18
+ - Before a medium or large band, check the free space on the project's volume (`df -h <root>`) against the band times the size of one set-up tree (`du -sh <Tree>`); a filesystem without copy-on-write clones pays the full copy for every tree.
19
+
20
+ ## Lifecycle
21
+
22
+ 1. Create: `validate-handoff.mjs tree <brief.md>` adds the tree detached at the main tree's HEAD and prints `OK tree <path> <sha>` (`OK tree reused <path> <sha>` when it already exists); record the sha as the brief's `Base:`. It refuses, creating nothing, with `tree-path` (the header differs from the derived path), `tree-unignored` (add `.worktrees/` to `.gitignore`), `change-untracked` (commit the change directory first), `tree-exists` (a non-worktree path is in the way) and `tree-stale` (run `git worktree prune`).
23
+ 2. Setup: run the brief's `Setup:` command in a subshell, `(cd <Tree> && <Setup>)`, so the lead shell stays at the main root, and record `Setup time: <task> <seconds>s` in the review package.
24
+ 3. Check: run `validate-handoff.mjs briefs <new and in-flight brief files> --band <b>`, naming the new briefs plus every brief still in flight as files; a directory argument skips the set checks (`NOTE briefs-dir <count>`). On a refusal, remove the clean trees just created for the refused set with `git worktree remove <Tree>`, fix the briefs and start again.
25
+ 4. Dispatch: the prompt carries the tree path and these worker lines: use absolute paths under `<Tree>` for every Read, Edit and Write; start every Bash command with `cd <Tree> &&`. The brief, the report and the ledger stay under the main root's `.freeharness/sdd/<change>/`.
26
+ 5. Guards: `validate-handoff.mjs handoff <report> --base <Base> --brief <brief.md> --agent <agent_id> --tree <Tree>`, with the `--in-flight` and `--lead` paths as in the main tree. `tree-leak <path>` (a write in the main tree) and `NOTE tree-cwd <command>` (a command run outside the tree) are listed in `reference/handoff.md`.
27
+ 6. Fix rounds run in the same tree on the same base: `round-base <brief.md> <n> --tree <Tree>` and `round-diff <brief.md> <n> --tree <Tree>`. `handoff`, `round-base` and `round-diff` accept only the brief's own tree, `<root>/.worktrees/<change>/<task>` for the brief's change and task; a sibling task's tree, another repository's tree or any other path fails `tree-path`.
28
+ 7. Land: `validate-handoff.mjs commit <report> [<report>...] -F <message-file> --tree <Tree> --base <sha> --lead <tasks.md>`, listing every report of the task, lands the task as one commit on the change branch and removes the tree without force. It refuses, writing nothing, with every refusal of the main-tree commit plus `tree-path`, `tree-moved` (the worker committed in the tree), `base-not-ancestor`, `staged`, `tree-leak`, `land-conflict` (the branch changed a claimed path since the base) and `lead-claimed`. A plain `commit` run from inside a task tree is refused with `tree-path`: land a tree task only from the main root with `--tree`. A failure after the tree commit names its step and prints the recovery NOTE lines (reference/loop.md, "Commit by path").
29
+ 8. `NOTE tree-kept <path> <reason>`: the task landed, but git refused the removal (an untracked file is left in the tree); the exit status stays 0. Read the leftover, then remove the tree with `git worktree remove <Tree>` once it is clean, or leave it for the user.
30
+ 9. `FAIL commit-hook`: a repository hook rejected the main commit; the output lists the staged paths and the tree is kept. Never bypass the hook; the hook's output is a finding for a ruling before the task is committed.
31
+ 10. `--tree-only`, for a `land-conflict` or an abandoned tree: `commit <report> -F <message-file> --tree <Tree> --base <sha> --tree-only` commits the claimed paths in the tree alone, prints `OK tree-commit <sha>` and leaves the main tree untouched, so it skips `land-conflict` and `tree-leak`. Record the sha in the review package for the re-brief, then remove the clean tree with `git worktree remove <Tree>`.
32
+
33
+ Never `git worktree remove --force`: a tree that refuses removal holds work nobody has read.
34
+
35
+ ## Setup, resources and manifests
36
+
37
+ - `Setup: <command> | none - <reason>` is required on every tree brief; the lead runs it, never the validator (`setup-missing` otherwise). A Node project uses its install command (`npm ci --prefer-offline`).
38
+ - A uv project builds its own environment per tree with `uv sync --frozen`, which links from the uv cache. Never share the main environment of a project installed into it: the editable install points at the main tree's sources, so tests in the tree would import the main tree's code, and a sync from the tree would repoint the install. Sharing is allowed only for a project not installed into its environment, and the `Setup:` reason states it.
39
+ - `Resources:` names the shared ports, databases and GPUs a task uses, comma-separated (`none` when it uses none). Two briefs in flight naming the same resource, compared case-insensitively, fail `resource-overlap`; run them one after the other.
40
+ - A task whose write scope names a dependency manifest or lockfile (`package.json`, `uv.lock`, `Cargo.lock` and the rest of the route rule's C5 set) never runs in parallel: `briefs` fails `manifest-parallel <path>`. Run it alone, in the main tree.
@@ -0,0 +1,31 @@
1
+ # Workspace detail
2
+
3
+ The run-start step and the validator inventory of the subagent-driven-development loop. SKILL.md names this file at its `<workspace>` step; every line binds like a SKILL.md line.
4
+
5
+ ## Workspace files
6
+
7
+ - `.freeharness/sdd/<change>/` (gitignored; `init --project` creates it) holds `<task>.brief.md` and `<task>.review.md`, plus `<task>.findings.md` (the findings ledger), `<task>.state.md` (the working state) and `rounds/<task>-r<n>/` (round bases). `handoffs.jsonl` is the append-only record of handoff outcomes, one line per stopped implementer dispatch; the SDD context hook writes it on SubagentStop and `ledger` reads it for the per-task handoff and stop counts.
8
+
9
+ ## Run start
10
+
11
+ - Before task 1: read `tasks.md` once, list the tasks, note Global Constraints; scan pairs of tasks that share a file or an interface and rule on conflicts (spec is the authority) in a `## Preflight` note of the first review package.
12
+
13
+ ## Validator
14
+
15
+ The validator `scripts/validate-handoff.mjs` has fourteen subcommands:
16
+
17
+ - `brief-init <change> <task>`: writes `.freeharness/sdd/<change>/<task>.brief.md` from the task's line and annotation lines in `openspec/changes/<change>/tasks.md` and the template in reference/brief.md, with `Change:`, `Spec:`, `Task:`, `Base:` and `Report:` set, `## Run rules` and `## Handoff` copied, and one `<fill: ...>` line for every other header and section, then prints `OK brief-init <path>`; refuses `task-shape`, `tasks-missing`, `task-unknown`, `template-missing`, `workspace-escape` (the workspace directory resolves outside the project root), `brief-exists` and `brief-unwritable`, and `briefs` fails `placeholder-left <name>` while a placeholder remains.
18
+ - `briefs <dir|file...> [--band small|medium|large]`: headers, one task id, routing class and `Effort:`, size bounds, probes, `## Project rules`, digest budget, write-scope overlap; over the named brief files also the set checks `tree-required`, `tree-path`, `tree-absent`, `setup-missing`, `resource-overlap`, `manifest-parallel` and `band <count> over <cap>` (small when `--band` is absent); a directory argument skips the set checks with `NOTE briefs-dir <count>`.
19
+ - `handoff <file> [--base <sha>] [--brief <brief.md>] [--in-flight <brief.md>]... [--lead <path>]... [--agent <agent_id>] [--tree <path>]`: schema plus the diff guards; `--base` adds `staged` and `base-not-ancestor`; `--lead` excludes the lead's own paths; `--brief` adds `probe-missing`, `checkpoint-missing` and `checkpoint-oversized`; `--agent` scans the worker's transcript for `bash-write`, `workspace-write`, `index-write` and `suite-run`; `--tree` reads the task's tree and adds `tree-leak`.
20
+ - `budget <ledger.jsonl> <task> [--ceiling <n>]`: the total, the median reserve and the class ceiling with its source (reference/loop.md).
21
+ - `ledger <change-dir>`.
22
+ - `recover <transcript.jsonl> <report> [--force]`.
23
+ - `name <dispatch-name>`: checks a dispatch name and prints the corrected form.
24
+ - `gate <findings.md>`.
25
+ - `route <brief.md> [--round <n>] [--p0] [--security]`: prints `Tier: <tier>` and `Effort: <effort>` for the brief's next dispatch: the brief's own (`medium` effort when absent), `strong` for a standard `code` brief from round 2 and for every brief from round 3, `strong` with `--p0`, `strong` and `high` with `--security`, never lower than the brief.
26
+ - `route <brief.md> --review --base <sha> [--round <n>] [--p0] [--final-spec] [--probed]`: prints `Review tier: <tier> - S<n> <reason>` by the first criterion that holds: S1 `strong` (class `security` or `complex`, effort `high`, or a changed guard, hook, auth, secret or parser file, or a file under a `hooks` or `guards` directory), S2 `strong` (round 2 or later, `--p0`, `--final-spec`), S3 `standard` (class `docs`, `mechanical`, `extraction`, `exploration` or `summary`, or at most 200 changed lines in at most 3 files since `<sha>`, untracked files included), S4 `standard` with `--probed`, else S1 `strong`; refuses `route-base` outside a git work tree or for an unknown base.
27
+ - `round-base <brief.md> <n> [--tree <path>] [--p0] [--security]`: refuses `round-cap <n> over <cap>` past the round cap, and `route-climb <tier>/<effort>` when the brief's tier or effort is below what `route --round <n>` with the same flags prints; `--tree` accepts only the brief's own tree.
28
+ - `round-diff <brief.md> <n> [--tree <path>]`: `--tree` accepts only the brief's own tree.
29
+ - `state <report> <state.md>`.
30
+ - `tree <brief.md>`: creates the brief's `Tree:` worktree detached at HEAD and prints `OK tree <path> <sha>`, or `OK tree reused <path> <sha>`; refuses with `tree-path`, `tree-unignored`, `change-untracked`, `tree-exists` or `tree-stale` (reference/trees.md).
31
+ - `commit <report> [<report>...] -F <message-file> [--lead <path>]... [--tree <path> --base <sha> [--tree-only]]`: commits the union of the reports' files (every report of one task, the last `completed`; `handoff-invalid`, `task-mismatch`) plus each `--lead` path by path; run from inside a task tree without `--tree` it fails `tree-path`. With `--tree` it lands the tree task as one commit on the main branch and removes the tree without force (`tree-moved`, `land-conflict`, `lead-claimed`, `tree-leak`, `staged`; `NOTE tree-kept`); a landing failure names its step (`tree-add-failed`, `tree-commit-failed`, `cherry-pick-failed`, `lead-add-failed`, `main-commit-failed`, or `commit-hook` for a non-128 exit with a commit hook installed), and every failure after the tree commit prints the recovery lines `NOTE tree-commit <sha>`, `NOTE tree-head-moved <sha>` and `NOTE main-entry <XY> <path>` (reference/loop.md). `--tree-only` commits in the tree alone and skips `land-conflict` and `tree-leak`.