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.
- package/README.md +8 -7
- package/canonical/HARVEST.yaml +25 -14
- package/canonical/PROVENANCE.yaml +1 -1
- package/canonical/agents/README.md +77 -36
- package/canonical/agents/agentic-architect.md +2 -1
- package/canonical/agents/database-reviewer.md +6 -8
- package/canonical/agents/explorer-ai-engineer-analyst.md +2 -6
- package/canonical/agents/implementer.md +30 -0
- package/canonical/agents/pr-reviewer.md +9 -11
- package/canonical/agents/python-reviewer.md +7 -9
- package/canonical/agents/report-formatter.md +7 -3
- package/canonical/agents/security-reviewer.md +7 -8
- package/canonical/agents/test-writer.md +5 -2
- package/canonical/hooks/cost-prices.json +11 -9
- package/canonical/hooks/freeharness-config-guard.mjs +8072 -87
- package/canonical/hooks/freeharness-context-monitor.mjs +88 -54
- package/canonical/hooks/freeharness-converge.mjs +78 -23
- package/canonical/hooks/freeharness-cost-tracker.mjs +1129 -218
- package/canonical/hooks/freeharness-memory-guard.mjs +20 -23
- package/canonical/hooks/freeharness-precompact.mjs +217 -87
- package/canonical/hooks/freeharness-quota-tap.mjs +173 -0
- package/canonical/hooks/freeharness-reload-context.mjs +197 -0
- package/canonical/hooks/freeharness-repeat-tool.mjs +1 -1
- package/canonical/hooks/freeharness-sdd-context.mjs +970 -0
- package/canonical/hooks/freeharness-session-start +297 -59
- package/canonical/hooks/hooks.claude-code.json +1 -1
- package/canonical/hooks/hooks.config-guard.claude-code.json +3 -2
- package/canonical/hooks/hooks.context-monitor.claude-code.json +1 -1
- package/canonical/hooks/hooks.converge.claude-code.json +1 -1
- package/canonical/hooks/hooks.memory-guard.claude-code.json +3 -2
- package/canonical/hooks/hooks.reload-context.claude-code.json +15 -0
- package/canonical/openspec/schema.spec-driven.yaml +29 -8
- package/canonical/openspec/templates/follow-ups.md +3 -2
- package/canonical/openspec/templates/proposal.md +1 -1
- package/canonical/openspec/templates/tasks.md +8 -0
- package/canonical/rules/CLAUDE.md +5 -4
- package/canonical/rules/routing.md +10 -9
- package/canonical/skills/agent-evaluation/SKILL.md +17 -6
- package/canonical/skills/agent-evaluation/evals/cases.yaml +38 -23
- package/canonical/skills/agentic-architecture/SKILL.md +4 -0
- package/canonical/skills/agentic-architecture/evals/cases.yaml +27 -0
- package/canonical/skills/code-review/SKILL.md +9 -2
- package/canonical/skills/code-review/evals/cases.yaml +1118 -27
- package/canonical/skills/git-workflow/SKILL.md +2 -0
- package/canonical/skills/local-code/SKILL.md +2 -2
- package/canonical/skills/local-code/evals/cases.yaml +1 -1
- package/canonical/skills/report-formatting/SKILL.md +10 -2
- package/canonical/skills/report-formatting/reference/ARCHITECTURE.md +3 -3
- package/canonical/skills/report-formatting/reference/HELPERS.md +10 -5
- package/canonical/skills/report-formatting/reference/RELEASE.md +1 -2
- package/canonical/skills/report-formatting/reference/TEMPLATES.md +3 -2
- package/canonical/skills/report-formatting/reference/authoring.md +25 -37
- package/canonical/skills/report-formatting/scripts/build-architecture.js +1 -1
- package/canonical/skills/report-formatting/scripts/build-md-claro.js +5 -8
- package/canonical/skills/report-formatting/scripts/check-env.js +0 -4
- package/canonical/skills/report-formatting/scripts/docmodel.js +3 -11
- package/canonical/skills/report-formatting/scripts/example-build.js +8 -10
- package/canonical/skills/report-formatting/scripts/example_build_ArchitectureReview_docx.js +8 -10
- package/canonical/skills/report-formatting/scripts/example_build_ExecutiveReport_docx.js +8 -10
- package/canonical/skills/report-formatting/scripts/theme-claro.js +39 -10
- package/canonical/skills/subagent-driven-development/SKILL.md +35 -28
- package/canonical/skills/subagent-driven-development/evals/cases.yaml +3496 -19
- package/canonical/skills/subagent-driven-development/reference/brief.md +185 -35
- package/canonical/skills/subagent-driven-development/reference/handoff.md +82 -2
- package/canonical/skills/subagent-driven-development/reference/loop.md +57 -0
- package/canonical/skills/subagent-driven-development/reference/trees.md +40 -0
- package/canonical/skills/subagent-driven-development/reference/workspace.md +31 -0
- package/canonical/skills/subagent-driven-development/scripts/validate-handoff.mjs +4771 -326
- package/dist/adapters/harness/claude-code.d.ts +19 -3
- package/dist/adapters/harness/claude-code.js +204 -39
- package/dist/adapters/harness/opencode.js +16 -2
- package/dist/adapters/harness/plugin.js +28 -17
- package/dist/adapters/memory/doctor.d.ts +174 -29
- package/dist/adapters/memory/doctor.js +758 -134
- package/dist/adapters/memory/migrate.d.ts +95 -0
- package/dist/adapters/memory/migrate.js +497 -0
- package/dist/adapters/memory/paths.d.ts +22 -0
- package/dist/adapters/memory/paths.js +85 -0
- package/dist/adapters/memory/vendored.d.ts +56 -29
- package/dist/adapters/memory/vendored.js +191 -154
- package/dist/adapters/sync/diff.js +4 -1
- package/dist/cli/cleanup-prompt.d.ts +35 -0
- package/dist/cli/cleanup-prompt.js +68 -0
- package/dist/cli/cleanup.d.ts +22 -24
- package/dist/cli/cleanup.js +182 -68
- package/dist/cli/cost-quota.d.ts +158 -0
- package/dist/cli/cost-quota.js +645 -0
- package/dist/cli/cost.d.ts +408 -46
- package/dist/cli/cost.js +1731 -57
- package/dist/cli/doctor.d.ts +52 -13
- package/dist/cli/doctor.js +193 -44
- package/dist/cli/hard-cleanup.d.ts +93 -0
- package/dist/cli/hard-cleanup.js +552 -0
- package/dist/cli/install-facts.d.ts +28 -0
- package/dist/cli/install-facts.js +66 -0
- package/dist/cli/json-key-edit.d.ts +18 -0
- package/dist/cli/json-key-edit.js +118 -0
- package/dist/cli/memory-import.d.ts +74 -0
- package/dist/cli/memory-import.js +469 -0
- package/dist/cli/plugin-validate.d.ts +12 -0
- package/dist/cli/plugin-validate.js +43 -0
- package/dist/cli/uninstall.d.ts +1 -1
- package/dist/cli/uninstall.js +2 -2
- package/dist/cli/upstream-launchers.d.ts +49 -0
- package/dist/cli/upstream-launchers.js +287 -0
- package/dist/index.js +247 -70
- package/dist/infra/fsutil.d.ts +31 -3
- package/dist/infra/fsutil.js +69 -6
- package/dist/infra/manifest.d.ts +2 -1
- package/dist/infra/manifest.js +3 -2
- package/dist/infra/prices.d.ts +18 -0
- package/dist/infra/prices.js +64 -0
- package/node_modules/@freeharness/core/dist/tiers.d.ts +10 -1
- package/node_modules/@freeharness/core/dist/tiers.js +24 -2
- package/node_modules/@freeharness/core/package.json +1 -1
- package/node_modules/@freeharness/sdd/PROVENANCE.yaml +279 -2
- package/node_modules/@freeharness/sdd/dist/src/bin/openspec.js +34 -8
- package/node_modules/@freeharness/sdd/dist/src/shim/archive.d.ts +7 -6
- package/node_modules/@freeharness/sdd/dist/src/shim/archive.js +72 -64
- package/node_modules/@freeharness/sdd/dist/src/shim/follow-up.d.ts +48 -16
- package/node_modules/@freeharness/sdd/dist/src/shim/follow-up.js +168 -70
- package/node_modules/@freeharness/sdd/dist/src/shim/instructions.d.ts +36 -6
- package/node_modules/@freeharness/sdd/dist/src/shim/instructions.js +110 -49
- package/node_modules/@freeharness/sdd/dist/src/shim/list.d.ts +1 -1
- package/node_modules/@freeharness/sdd/dist/src/shim/list.js +57 -14
- package/node_modules/@freeharness/sdd/dist/src/shim/nested-change.d.ts +7 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/nested-change.js +118 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/new-change.js +3 -1
- package/node_modules/@freeharness/sdd/dist/src/shim/proposal-capabilities.d.ts +12 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/proposal-capabilities.js +94 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/shared.d.ts +17 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/shared.js +182 -1
- package/node_modules/@freeharness/sdd/dist/src/shim/status.d.ts +2 -2
- package/node_modules/@freeharness/sdd/dist/src/shim/status.js +34 -5
- package/node_modules/@freeharness/sdd/dist/src/shim/validate.d.ts +9 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/validate.js +49 -41
- package/node_modules/@freeharness/sdd/dist/src/shim/warnings.d.ts +21 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/warnings.js +66 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/apply/line-endings.d.ts +4 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/apply/line-endings.js +17 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/apply/specs-apply.js +95 -22
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/change-status-policy.d.ts +6 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/change-status-policy.js +14 -6
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/index.d.ts +1 -1
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/index.js +1 -1
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/outputs.d.ts +3 -3
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/outputs.js +24 -4
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/task-progress.js +4 -7
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/types.js +13 -1
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/apply-change.js +21 -6
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/archive-change.js +80 -19
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/explore.js +42 -22
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/project-root.d.ts +32 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/project-root.js +39 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/propose.js +16 -9
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/sync-specs.js +36 -1
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/update-change.js +65 -30
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/constants.d.ts +1 -1
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/constants.js +1 -1
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/change-parser.d.ts +8 -13
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/change-parser.js +79 -102
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/markdown-parser.js +4 -9
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/requirement-blocks.d.ts +50 -10
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/requirement-blocks.js +165 -37
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/requirement-text.d.ts +24 -3
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/requirement-text.js +55 -8
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/spec-structure.js +4 -1
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/purpose-placeholder.js +19 -9
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/spec-discovery.d.ts +10 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/spec-discovery.js +115 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/task-lines.d.ts +3 -3
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/task-lines.js +20 -20
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/validator.d.ts +6 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/validator.js +75 -14
- package/node_modules/@freeharness/sdd/package.json +1 -1
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/schema.upstream.yaml +47 -7
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-apply-change/SKILL.md +25 -7
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-archive-change/SKILL.md +50 -11
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-bulk-archive-change/SKILL.md +48 -10
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-continue-change/SKILL.md +27 -7
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-explore/SKILL.md +25 -12
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-ff-change/SKILL.md +17 -5
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-new-change/SKILL.md +12 -1
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-onboard/SKILL.md +47 -33
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-propose/SKILL.md +17 -5
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-sync-specs/SKILL.md +29 -1
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-update-change/SKILL.md +31 -13
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-verify-change/SKILL.md +75 -27
- package/package.json +6 -4
- package/provenance/canonical.PROVENANCE.yaml +1 -1
- package/provenance/memory.PROVENANCE.yaml +458 -6
- package/provenance/sdd.PROVENANCE.yaml +279 -2
- package/canonical/hooks/freeharness-auto-memory.mjs +0 -158
- 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
|
|
4
|
-
|
|
5
|
-
requirements for the implementer: exact values, signatures and test
|
|
6
|
-
never in the dispatch prompt. The implementer reads the brief, the files
|
|
7
|
-
nothing else; never the whole plan, never the session history.
|
|
8
|
-
|
|
9
|
-
Header lines are `Key: value`, one per line, in this order. `
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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,
|
|
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
|
|
33
|
-
|
|
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
|
|
82
|
-
to the caller. Never dispatch subagents. Other implementers
|
|
83
|
-
never `git stash`, `git checkout --` or `git reset` here: prove
|
|
84
|
-
writing it first, or against a temp copy of the file. On a stop
|
|
85
|
-
`blocked` with the
|
|
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
|
-
|
|
89
|
-
|
|
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
|
|
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`.
|