cclaw-cli 0.51.30 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -18
- package/dist/artifact-linter/brainstorm.d.ts +2 -0
- package/dist/artifact-linter/brainstorm.js +289 -0
- package/dist/artifact-linter/design.d.ts +2 -0
- package/dist/artifact-linter/design.js +354 -0
- package/dist/artifact-linter/plan.d.ts +2 -0
- package/dist/artifact-linter/plan.js +183 -0
- package/dist/artifact-linter/review-army.d.ts +24 -0
- package/dist/artifact-linter/review-army.js +365 -0
- package/dist/artifact-linter/review.d.ts +2 -0
- package/dist/artifact-linter/review.js +99 -0
- package/dist/artifact-linter/scope.d.ts +2 -0
- package/dist/artifact-linter/scope.js +125 -0
- package/dist/artifact-linter/shared.d.ts +247 -0
- package/dist/artifact-linter/shared.js +1517 -0
- package/dist/artifact-linter/ship.d.ts +2 -0
- package/dist/artifact-linter/ship.js +82 -0
- package/dist/artifact-linter/spec.d.ts +2 -0
- package/dist/artifact-linter/spec.js +130 -0
- package/dist/artifact-linter/tdd.d.ts +2 -0
- package/dist/artifact-linter/tdd.js +198 -0
- package/dist/artifact-linter.d.ts +4 -76
- package/dist/artifact-linter.js +56 -2949
- package/dist/cli.d.ts +1 -6
- package/dist/cli.js +4 -159
- package/dist/codex-feature-flag.d.ts +1 -1
- package/dist/codex-feature-flag.js +1 -1
- package/dist/config.d.ts +3 -2
- package/dist/config.js +67 -3
- package/dist/constants.d.ts +1 -7
- package/dist/constants.js +10 -15
- package/dist/content/cancel-command.js +2 -2
- package/dist/content/closeout-guidance.d.ts +1 -1
- package/dist/content/closeout-guidance.js +15 -13
- package/dist/content/core-agents.d.ts +46 -29
- package/dist/content/core-agents.js +216 -82
- package/dist/content/decision-protocol.d.ts +1 -1
- package/dist/content/decision-protocol.js +1 -1
- package/dist/content/diff-command.js +1 -1
- package/dist/content/examples.d.ts +0 -3
- package/dist/content/examples.js +197 -752
- package/dist/content/harness-doc.js +20 -2
- package/dist/content/hook-manifest.d.ts +2 -2
- package/dist/content/hook-manifest.js +2 -2
- package/dist/content/hooks.d.ts +1 -0
- package/dist/content/hooks.js +32 -137
- package/dist/content/idea.d.ts +60 -0
- package/dist/content/idea.js +404 -0
- package/dist/content/iron-laws.d.ts +0 -1
- package/dist/content/iron-laws.js +31 -16
- package/dist/content/learnings.d.ts +2 -4
- package/dist/content/learnings.js +11 -27
- package/dist/content/meta-skill.js +7 -7
- package/dist/content/node-hooks.d.ts +10 -0
- package/dist/content/node-hooks.js +163 -95
- package/dist/content/opencode-plugin.js +15 -29
- package/dist/content/reference-patterns.js +2 -2
- package/dist/content/runtime-shared-snippets.d.ts +8 -0
- package/dist/content/runtime-shared-snippets.js +80 -0
- package/dist/content/session-hooks.js +1 -1
- package/dist/content/skills.d.ts +1 -0
- package/dist/content/skills.js +69 -7
- package/dist/content/stage-schema.js +147 -61
- package/dist/content/stages/_lint-metadata/index.js +26 -2
- package/dist/content/stages/brainstorm.js +13 -7
- package/dist/content/stages/design.js +16 -11
- package/dist/content/stages/plan.js +7 -4
- package/dist/content/stages/review.js +12 -12
- package/dist/content/stages/schema-types.d.ts +2 -2
- package/dist/content/stages/scope.js +15 -12
- package/dist/content/stages/ship.js +3 -3
- package/dist/content/stages/spec.js +9 -3
- package/dist/content/stages/tdd.js +14 -4
- package/dist/content/start-command.js +11 -10
- package/dist/content/status-command.js +5 -5
- package/dist/content/subagent-context-skills.js +156 -1
- package/dist/content/subagents.d.ts +0 -5
- package/dist/content/subagents.js +65 -81
- package/dist/content/templates.d.ts +1 -1
- package/dist/content/templates.js +187 -154
- package/dist/content/tree-command.js +2 -2
- package/dist/content/utility-skills.d.ts +2 -2
- package/dist/content/utility-skills.js +28 -99
- package/dist/content/view-command.js +4 -2
- package/dist/delegation.d.ts +2 -0
- package/dist/delegation.js +2 -1
- package/dist/early-loop.d.ts +66 -0
- package/dist/early-loop.js +275 -0
- package/dist/flow-state.d.ts +5 -6
- package/dist/flow-state.js +4 -6
- package/dist/gate-evidence.d.ts +0 -23
- package/dist/gate-evidence.js +111 -153
- package/dist/harness-adapters.d.ts +2 -2
- package/dist/harness-adapters.js +48 -19
- package/dist/install.js +190 -32
- package/dist/internal/advance-stage/advance.d.ts +50 -0
- package/dist/internal/advance-stage/advance.js +479 -0
- package/dist/internal/advance-stage/cancel-run.d.ts +8 -0
- package/dist/internal/advance-stage/cancel-run.js +19 -0
- package/dist/internal/advance-stage/flow-state-coercion.d.ts +3 -0
- package/dist/internal/advance-stage/flow-state-coercion.js +81 -0
- package/dist/internal/advance-stage/helpers.d.ts +14 -0
- package/dist/internal/advance-stage/helpers.js +145 -0
- package/dist/internal/advance-stage/hook.d.ts +8 -0
- package/dist/internal/advance-stage/hook.js +40 -0
- package/dist/internal/advance-stage/parsers.d.ts +54 -0
- package/dist/internal/advance-stage/parsers.js +307 -0
- package/dist/internal/advance-stage/review-loop.d.ts +7 -0
- package/dist/internal/advance-stage/review-loop.js +161 -0
- package/dist/internal/advance-stage/rewind.d.ts +14 -0
- package/dist/internal/advance-stage/rewind.js +108 -0
- package/dist/internal/advance-stage/start-flow.d.ts +11 -0
- package/dist/internal/advance-stage/start-flow.js +136 -0
- package/dist/internal/advance-stage/verify.d.ts +29 -0
- package/dist/internal/advance-stage/verify.js +225 -0
- package/dist/internal/advance-stage.js +21 -1470
- package/dist/internal/compound-readiness.d.ts +1 -1
- package/dist/internal/compound-readiness.js +2 -2
- package/dist/internal/early-loop-status.d.ts +7 -0
- package/dist/internal/early-loop-status.js +90 -0
- package/dist/internal/runtime-integrity.d.ts +7 -0
- package/dist/internal/runtime-integrity.js +288 -0
- package/dist/internal/tdd-red-evidence.js +1 -1
- package/dist/knowledge-store.d.ts +5 -28
- package/dist/knowledge-store.js +57 -84
- package/dist/managed-resources.js +24 -2
- package/dist/policy.js +7 -9
- package/dist/retro-gate.js +8 -90
- package/dist/run-archive.d.ts +1 -1
- package/dist/run-archive.js +13 -16
- package/dist/run-persistence.js +20 -15
- package/dist/runtime/run-hook.entry.d.ts +3 -0
- package/dist/runtime/run-hook.entry.js +5 -0
- package/dist/runtime/run-hook.mjs +9477 -0
- package/dist/tdd-cycle.d.ts +3 -3
- package/dist/tdd-cycle.js +1 -1
- package/dist/types.d.ts +18 -10
- package/package.json +4 -2
- package/dist/content/hook-inline-snippets.d.ts +0 -83
- package/dist/content/hook-inline-snippets.js +0 -302
- package/dist/content/ideate-command.d.ts +0 -8
- package/dist/content/ideate-command.js +0 -315
- package/dist/content/ideate-frames.d.ts +0 -31
- package/dist/content/ideate-frames.js +0 -140
- package/dist/content/ideate-ranking.d.ts +0 -25
- package/dist/content/ideate-ranking.js +0 -65
- package/dist/content/next-command.d.ts +0 -20
- package/dist/content/next-command.js +0 -298
- package/dist/content/seed-shelf.d.ts +0 -36
- package/dist/content/seed-shelf.js +0 -301
- package/dist/content/stage-common-guidance.d.ts +0 -1
- package/dist/content/stage-common-guidance.js +0 -106
- package/dist/doctor-registry.d.ts +0 -10
- package/dist/doctor-registry.js +0 -186
- package/dist/doctor.d.ts +0 -17
- package/dist/doctor.js +0 -2201
- package/dist/internal/hook-manifest.d.ts +0 -16
- package/dist/internal/hook-manifest.js +0 -77
- package/dist/trace-matrix.d.ts +0 -27
- package/dist/trace-matrix.js +0 -226
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
+--------+ +------+
|
|
17
17
|
|
|
|
18
18
|
v
|
|
19
|
-
|
|
19
|
+
post_ship_review -> archive
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
The promise is simple: at any point you can ask **where are we, what is blocked, what evidence exists, and what should run next?**
|
|
@@ -33,18 +33,22 @@ npx cclaw-cli
|
|
|
33
33
|
Then work from your coding harness:
|
|
34
34
|
|
|
35
35
|
```text
|
|
36
|
-
/cc <idea> start a tracked flow
|
|
37
|
-
/cc-
|
|
38
|
-
/cc-
|
|
36
|
+
/cc <idea> start, resume, or continue a tracked flow
|
|
37
|
+
/cc-idea generate or refresh an idea backlog
|
|
38
|
+
/cc-cancel end the current run cleanly with a reason
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
+
`/cc-idea` is a utility command for backlog discovery. It is distinct from
|
|
42
|
+
`.cclaw/artifacts/00-idea.md`, which is the stage artifact created by
|
|
43
|
+
`/cc <idea>`.
|
|
44
|
+
|
|
41
45
|
For scripted setup:
|
|
42
46
|
|
|
43
47
|
```bash
|
|
44
48
|
npx cclaw-cli init --harnesses=claude,cursor --no-interactive
|
|
45
49
|
```
|
|
46
50
|
|
|
47
|
-
If generated files or hooks look stale, run `
|
|
51
|
+
If generated files or hooks look stale, run `npx cclaw-cli sync`.
|
|
48
52
|
|
|
49
53
|
## Why cclaw
|
|
50
54
|
|
|
@@ -57,34 +61,36 @@ AI coding sessions fail when decisions live only in chat. cclaw puts the operati
|
|
|
57
61
|
state/delegation-log.json
|
|
58
62
|
subagent dispatches, waivers, evidence refs
|
|
59
63
|
knowledge.jsonl reusable lessons harvested from stage artifacts
|
|
60
|
-
|
|
64
|
+
archive/ archived run snapshots (durable closeout proof)
|
|
61
65
|
```
|
|
62
66
|
|
|
67
|
+
Legacy `.cclaw/runs/` directories are only auto-removed when empty. If the directory still contains data, migrate it manually to `.cclaw/archive/`.
|
|
68
|
+
|
|
63
69
|
That gives you:
|
|
64
70
|
|
|
65
71
|
- **One path** from idea to ship, with `quick`, `medium`, and `standard` tracks.
|
|
66
72
|
- **Real gates** for evidence, tests, review, delegation, stale-stage recovery, and closeout.
|
|
67
73
|
- **Subagents with accountability**: controller owns state, workers do bounded tasks, overseers validate, evidence lands in `delegation-log.json`.
|
|
68
|
-
- **Recovery instead of confusion**:
|
|
74
|
+
- **Recovery instead of confusion**: `npx cclaw-cli sync` tells you blockers and next fixes.
|
|
69
75
|
- **Portable harness behavior** across Claude Code, Cursor, OpenCode, and Codex.
|
|
70
76
|
|
|
71
77
|
## The Daily Loop
|
|
72
78
|
|
|
73
79
|
```text
|
|
74
80
|
1. Start or resume
|
|
75
|
-
/cc <idea>
|
|
81
|
+
/cc <idea>
|
|
76
82
|
|
|
77
83
|
2. Work the current stage
|
|
78
|
-
The agent writes/updates .cclaw/artifacts
|
|
84
|
+
The agent writes/updates per-stage files like .cclaw/artifacts/00-idea.md, 01-brainstorm-<slug>.md, 02-scope-<slug>.md
|
|
79
85
|
|
|
80
86
|
3. Prove the gate
|
|
81
87
|
stage-complete records evidence in flow-state.json
|
|
82
88
|
|
|
83
89
|
4. Inspect when stuck
|
|
84
|
-
|
|
90
|
+
npx cclaw-cli sync
|
|
85
91
|
|
|
86
92
|
5. Close out after ship
|
|
87
|
-
/cc
|
|
93
|
+
/cc continues post_ship_review -> archive
|
|
88
94
|
```
|
|
89
95
|
|
|
90
96
|
Tracks keep the flow proportional:
|
|
@@ -95,14 +101,14 @@ medium brainstorm -> spec -> plan -> tdd -> review -> ship
|
|
|
95
101
|
standard brainstorm -> scope -> design -> spec -> plan -> tdd -> review -> ship
|
|
96
102
|
```
|
|
97
103
|
|
|
98
|
-
Track selection is **model-guided and advisory** during `/cc`. Runtime enforcement begins after state is written: `/cc
|
|
104
|
+
Track selection is **model-guided and advisory** during `/cc`. Runtime enforcement begins after state is written: subsequent `/cc` turns follow the selected track, required gates, delegation rules, stale-stage markers, and `closeout.shipSubstate`.
|
|
99
105
|
|
|
100
106
|
## When Blocked
|
|
101
107
|
|
|
102
108
|
Start here:
|
|
103
109
|
|
|
104
110
|
```text
|
|
105
|
-
|
|
111
|
+
npx cclaw-cli sync
|
|
106
112
|
```
|
|
107
113
|
|
|
108
114
|
A useful status should read like an operator note, not a raw dump:
|
|
@@ -110,7 +116,7 @@ A useful status should read like an operator note, not a raw dump:
|
|
|
110
116
|
```text
|
|
111
117
|
Current: tdd (standard)
|
|
112
118
|
Blocked by: NO_SOURCE_CONTEXT
|
|
113
|
-
Next: cclaw internal rewind plan "add bootstrap slice", then /cc
|
|
119
|
+
Next: cclaw internal rewind plan "add bootstrap slice", then /cc
|
|
114
120
|
Evidence needed: fresh RED/GREEN/REFACTOR slice and verification output
|
|
115
121
|
```
|
|
116
122
|
|
|
@@ -119,14 +125,14 @@ Common exits:
|
|
|
119
125
|
|
|
120
126
|
| Situation | Next action |
|
|
121
127
|
| ------------------------------------------------- | ------------------------------------------------------------------------------------ |
|
|
122
|
-
| Missing gates | Run `/cc
|
|
128
|
+
| Missing gates | Run `/cc`, finish the stage, then complete with evidence. |
|
|
123
129
|
| Mandatory delegation missing evidence | Dispatch the worker/overseer or waive explicitly with rationale. |
|
|
124
130
|
| `NO_SOURCE_CONTEXT` or `NO_TEST_SURFACE` | Rewind to `plan`/`spec`, define the source or test surface, then resume TDD. |
|
|
125
131
|
| `NO_IMPLEMENTABLE_SLICE` or `RED_NOT_EXPRESSIBLE` | Rework `design`/`spec`/`plan` until one vertical slice is testable. |
|
|
126
132
|
| `NO_VCS_MODE` | Restore git, set `vcs: none` with hash evidence, or configure `tdd.verificationRef`. |
|
|
127
133
|
| Review blocked | `cclaw internal rewind tdd "review_blocked_by_critical <finding-ids>"`. |
|
|
128
134
|
| Stale stage after rewind | Redo the marked stage, then `cclaw internal rewind --ack <stage>`. |
|
|
129
|
-
| Broken hooks or generated files | `cclaw
|
|
135
|
+
| Broken hooks or generated files | `npx cclaw-cli sync`, then follow the fail-fast error guidance if it still fails. |
|
|
130
136
|
|
|
131
137
|
|
|
132
138
|
## Subagents Without Theater
|
|
@@ -158,7 +164,7 @@ Enforced by generated helpers and state checks:
|
|
|
158
164
|
- Mandatory delegations need terminal evidence or explicit waiver.
|
|
159
165
|
- Stale stages block until redone and acknowledged.
|
|
160
166
|
- Review criticals route back to TDD.
|
|
161
|
-
- Ship continues through `
|
|
167
|
+
- Ship continues through `post_ship_review -> archive` with `closeout.shipSubstate`.
|
|
162
168
|
|
|
163
169
|
Advisory/model-guided:
|
|
164
170
|
|
|
@@ -182,7 +188,7 @@ npx cclaw-cli # interactive setup or installed status hint
|
|
|
182
188
|
npx cclaw-cli init --harnesses=<list> --no-interactive
|
|
183
189
|
npx cclaw-cli sync # regenerate managed runtime files
|
|
184
190
|
npx cclaw-cli upgrade # refresh generated files while preserving config
|
|
185
|
-
npx cclaw-cli archive # explicit archive/reset; normal closeout uses /cc
|
|
191
|
+
npx cclaw-cli archive # explicit archive/reset; normal closeout uses /cc
|
|
186
192
|
npx cclaw-cli uninstall # remove .cclaw and generated harness shims
|
|
187
193
|
npx cclaw-cli --version
|
|
188
194
|
```
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { checkCriticPredictionsContract, sectionBodyByName, validateApproachesTaxonomy, headingLineIndex, meaningfulLineCount, parseShortCircuitStatus, validateCalibratedSelfReview, markdownFieldRegex } from "./shared.js";
|
|
4
|
+
export async function lintBrainstormStage(ctx) {
|
|
5
|
+
const { projectRoot, track, raw, absFile, sections, findings, parsedFrontmatter, brainstormShortCircuitBody, brainstormShortCircuitActivated, staleDiagramAuditEnabled, isTrivialOverride } = ctx;
|
|
6
|
+
// Brainstorm Iron Law: "NO ARTIFACT IS COMPLETE WITHOUT AN EXPLICITLY
|
|
7
|
+
// APPROVED DIRECTION — SILENCE IS NOT APPROVAL." Previously this was
|
|
8
|
+
// prose-only — nothing failed when the Selected Direction section
|
|
9
|
+
// omitted an approval marker, or when the Approaches table collapsed
|
|
10
|
+
// to a single row (defeating the "2-3 distinct approaches" gate).
|
|
11
|
+
const tierBody = sectionBodyByName(sections, "Approach Tier");
|
|
12
|
+
if (tierBody !== null) {
|
|
13
|
+
// Token vocabulary covers `lite`, `Lightweight`, `Standard`, and
|
|
14
|
+
// `Deep` (case-insensitive). A line that lists ≥2 distinct tokens is
|
|
15
|
+
// the unfilled template placeholder (`Tier: lite | standard | deep`)
|
|
16
|
+
// and must not silently pass; we look for at least one decision line
|
|
17
|
+
// with exactly one token, while ignoring placeholder lines.
|
|
18
|
+
const cleanedLines = tierBody
|
|
19
|
+
.split("\n")
|
|
20
|
+
.map((line) => line.replace(/[*_`]/gu, ""));
|
|
21
|
+
const lineTokenCounts = cleanedLines.map((line) => {
|
|
22
|
+
const tokens = line.match(/\b(?:lite|lightweight|light|standard|deep)\b/giu) ?? [];
|
|
23
|
+
return new Set(tokens.map((token) => token.toLowerCase())).size;
|
|
24
|
+
});
|
|
25
|
+
const hasDecisionLine = lineTokenCounts.some((count) => count === 1);
|
|
26
|
+
const hasPlaceholderLine = lineTokenCounts.some((count) => count >= 2);
|
|
27
|
+
const ok = hasDecisionLine;
|
|
28
|
+
findings.push({
|
|
29
|
+
section: "Approach Tier Classification",
|
|
30
|
+
required: true,
|
|
31
|
+
rule: "Approach Tier must explicitly classify depth as one of `lite` (a.k.a. `Lightweight`), `Standard`, or `Deep`.",
|
|
32
|
+
found: ok,
|
|
33
|
+
details: ok
|
|
34
|
+
? "Approach Tier includes a single recognized depth token."
|
|
35
|
+
: hasPlaceholderLine
|
|
36
|
+
? "Approach Tier still lists multiple tier tokens (template placeholder); pick exactly one of `lite`/`Lightweight`, `Standard`, or `Deep`."
|
|
37
|
+
: "Approach Tier is missing a recognized depth token (`lite`/`Lightweight`, `Standard`, or `Deep`)."
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
const approachesBody = sectionBodyByName(sections, "Approaches");
|
|
41
|
+
if (approachesBody !== null) {
|
|
42
|
+
const approachesTaxonomy = validateApproachesTaxonomy(approachesBody);
|
|
43
|
+
findings.push({
|
|
44
|
+
section: "Distinct Approaches Enforcement",
|
|
45
|
+
required: true,
|
|
46
|
+
rule: "Approaches section must document at least 2 distinct approaches so the Iron Law comparison is meaningful.",
|
|
47
|
+
found: approachesTaxonomy.rowCount >= 2,
|
|
48
|
+
details: approachesTaxonomy.rowCount >= 2
|
|
49
|
+
? `Detected ${approachesTaxonomy.rowCount} approach row(s).`
|
|
50
|
+
: `Detected ${approachesTaxonomy.rowCount} approach row(s); at least 2 required.`
|
|
51
|
+
});
|
|
52
|
+
findings.push({
|
|
53
|
+
section: "Approaches Role/Upside Taxonomy",
|
|
54
|
+
required: true,
|
|
55
|
+
rule: "Approaches table must use canonical Role and Upside enum values.",
|
|
56
|
+
found: approachesTaxonomy.roleUpsideOk,
|
|
57
|
+
details: approachesTaxonomy.details
|
|
58
|
+
});
|
|
59
|
+
findings.push({
|
|
60
|
+
section: "Challenger Alternative Enforcement",
|
|
61
|
+
required: true,
|
|
62
|
+
rule: "Approaches must include one challenger option with explicit high/higher upside.",
|
|
63
|
+
found: approachesTaxonomy.challengerOk,
|
|
64
|
+
details: approachesTaxonomy.details
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
const reactionIndex = headingLineIndex(raw, "Approach Reaction");
|
|
68
|
+
const directionIndex = headingLineIndex(raw, "Selected Direction");
|
|
69
|
+
if (directionIndex >= 0 && !brainstormShortCircuitActivated) {
|
|
70
|
+
const orderOk = reactionIndex >= 0 && reactionIndex < directionIndex;
|
|
71
|
+
findings.push({
|
|
72
|
+
section: "Approach Reaction Ordering",
|
|
73
|
+
required: true,
|
|
74
|
+
rule: "Approach Reaction must appear before Selected Direction (propose -> react -> recommend).",
|
|
75
|
+
found: orderOk,
|
|
76
|
+
details: orderOk
|
|
77
|
+
? "Approach Reaction appears before Selected Direction."
|
|
78
|
+
: "Approach Reaction must be present before Selected Direction."
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
const directionBody = sectionBodyByName(sections, "Selected Direction");
|
|
82
|
+
if (directionBody !== null) {
|
|
83
|
+
const approvalMarker = /\bapprov(?:ed|al)\b/iu.test(directionBody);
|
|
84
|
+
findings.push({
|
|
85
|
+
section: "Direction Approval Marker",
|
|
86
|
+
required: true,
|
|
87
|
+
rule: "Selected Direction section must state an explicit approval marker (for example `Approval: approved` or `Approved by: user`).",
|
|
88
|
+
found: approvalMarker,
|
|
89
|
+
details: approvalMarker
|
|
90
|
+
? "Approval marker present in Selected Direction."
|
|
91
|
+
: "No explicit `approved`/`approval` marker found in Selected Direction."
|
|
92
|
+
});
|
|
93
|
+
if (!brainstormShortCircuitActivated) {
|
|
94
|
+
const reactionBody = sectionBodyByName(sections, "Approach Reaction");
|
|
95
|
+
const reactionTrace = /\b(?:reaction|feedback|concern(?:s)?)\b/iu.test(directionBody) ||
|
|
96
|
+
(reactionIndex >= 0 && reactionIndex < directionIndex && meaningfulLineCount(reactionBody ?? "") > 0);
|
|
97
|
+
findings.push({
|
|
98
|
+
section: "Direction Reaction Trace",
|
|
99
|
+
required: true,
|
|
100
|
+
rule: "Selected Direction must be traceable to a prior Approach Reaction section or explicitly reference user reaction/feedback/concerns.",
|
|
101
|
+
found: reactionTrace,
|
|
102
|
+
details: reactionTrace
|
|
103
|
+
? "Selected Direction is traceable to prior user reaction."
|
|
104
|
+
: "Selected Direction is not traceable to user reaction. Add `## Approach Reaction` before it, or mention the user's reaction/concerns in the rationale."
|
|
105
|
+
});
|
|
106
|
+
// Track-aware handoff: standard track goes to `scope`; medium track
|
|
107
|
+
// goes directly to `spec`; the quick track skips brainstorm entirely.
|
|
108
|
+
// We accept either canonical successor token plus a generic
|
|
109
|
+
// `next-stage` / `handoff` phrase to preserve i18n flexibility.
|
|
110
|
+
const handoffTrace = /(?:`(?:scope|spec)`|\bscope\b|\bspec\b|next[-\s_]stage|next stage|\bhandoff\b|hand[-\s]off)/iu.test(directionBody);
|
|
111
|
+
findings.push({
|
|
112
|
+
section: "Direction Next-Stage Handoff",
|
|
113
|
+
required: true,
|
|
114
|
+
rule: "Selected Direction must record the track-aware next-stage handoff (mention `scope` for standard, `spec` for medium, or include a `Next-stage handoff:` line).",
|
|
115
|
+
found: handoffTrace,
|
|
116
|
+
details: handoffTrace
|
|
117
|
+
? "Selected Direction names the next-stage handoff."
|
|
118
|
+
: "Selected Direction is missing a next-stage handoff token. Mention `scope` (standard) or `spec` (medium), or add a `Next-stage handoff:` line so downstream stages can trace the contract."
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
const shortCircuitBody = brainstormShortCircuitBody;
|
|
123
|
+
if (shortCircuitBody !== null) {
|
|
124
|
+
const statusValue = parseShortCircuitStatus(shortCircuitBody);
|
|
125
|
+
const hasStatus = statusValue.length > 0;
|
|
126
|
+
findings.push({
|
|
127
|
+
section: "Short-Circuit Status",
|
|
128
|
+
required: true,
|
|
129
|
+
rule: "Short-Circuit Decision must include a `Status:` line (`activated` or `bypassed`).",
|
|
130
|
+
found: hasStatus,
|
|
131
|
+
details: hasStatus
|
|
132
|
+
? `Short-circuit status declared as "${statusValue}".`
|
|
133
|
+
: "Short-Circuit Decision is missing a `Status:` line."
|
|
134
|
+
});
|
|
135
|
+
if (brainstormShortCircuitActivated) {
|
|
136
|
+
const artifactLines = meaningfulLineCount(raw);
|
|
137
|
+
const withinStubLimit = artifactLines <= 30;
|
|
138
|
+
const hasScopeHandoff = /\bscope\b/iu.test(shortCircuitBody);
|
|
139
|
+
findings.push({
|
|
140
|
+
section: "Short-Circuit Stub Size",
|
|
141
|
+
required: true,
|
|
142
|
+
rule: "When short-circuit is activated, brainstorm artifact must remain a <=30 meaningful-line stub.",
|
|
143
|
+
found: withinStubLimit,
|
|
144
|
+
details: withinStubLimit
|
|
145
|
+
? `Short-circuit stub size within limit (${artifactLines} meaningful lines).`
|
|
146
|
+
: `Short-circuit stub too large (${artifactLines} meaningful lines); expected <= 30.`
|
|
147
|
+
});
|
|
148
|
+
findings.push({
|
|
149
|
+
section: "Short-Circuit Scope Handoff",
|
|
150
|
+
required: true,
|
|
151
|
+
rule: "When short-circuit is activated, the section must explicitly hand off to scope.",
|
|
152
|
+
found: hasScopeHandoff,
|
|
153
|
+
details: hasScopeHandoff
|
|
154
|
+
? "Short-circuit section includes explicit scope handoff."
|
|
155
|
+
: "Short-circuit section is missing explicit scope handoff guidance."
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
const selfReviewBody = sectionBodyByName(sections, "Self-Review Notes");
|
|
160
|
+
if (selfReviewBody !== null) {
|
|
161
|
+
const selfReview = validateCalibratedSelfReview(selfReviewBody);
|
|
162
|
+
findings.push({
|
|
163
|
+
section: "Calibrated Self-Review Format",
|
|
164
|
+
required: true,
|
|
165
|
+
rule: "When Self-Review Notes are present, they must use the calibrated review prompt output shape.",
|
|
166
|
+
found: selfReview.ok,
|
|
167
|
+
details: selfReview.details
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
const criticPredictions = checkCriticPredictionsContract(sections);
|
|
171
|
+
if (criticPredictions !== null) {
|
|
172
|
+
findings.push({
|
|
173
|
+
section: "critic.predictions_missing",
|
|
174
|
+
required: true,
|
|
175
|
+
rule: "[P2] critic.predictions_missing — pre-commitment predictions block missing or empty",
|
|
176
|
+
found: criticPredictions.found,
|
|
177
|
+
details: criticPredictions.details
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
// Universal structural checks (Layer 2.1). Each fires only when the
|
|
181
|
+
// matching section is present so legacy fixtures keep their current
|
|
182
|
+
// shape, while artifacts emitted from the v3 template have to satisfy
|
|
183
|
+
// them. Content is never inspected — only the shape required by the
|
|
184
|
+
// reference patterns (gstack mode, forcing questions, premise list,
|
|
185
|
+
// approach detail cards, anti-sycophancy stamp).
|
|
186
|
+
const modeBody = sectionBodyByName(sections, "Mode Block");
|
|
187
|
+
if (modeBody !== null) {
|
|
188
|
+
const modeTokens = ["STARTUP", "BUILDER", "ENGINEERING", "OPS", "RESEARCH"];
|
|
189
|
+
const modeRegex = markdownFieldRegex("Mode", modeTokens.join("|"), "u");
|
|
190
|
+
const tokenMatches = new Set();
|
|
191
|
+
const lineRegex = new RegExp(modeRegex.source, "gu");
|
|
192
|
+
for (const match of modeBody.matchAll(lineRegex)) {
|
|
193
|
+
const token = (match[0].match(/STARTUP|BUILDER|ENGINEERING|OPS|RESEARCH/u) ?? [""])[0];
|
|
194
|
+
if (token)
|
|
195
|
+
tokenMatches.add(token);
|
|
196
|
+
}
|
|
197
|
+
const placeholderLine = modeBody
|
|
198
|
+
.split("\n")
|
|
199
|
+
.find((line) => /\bMode\b\s*[*_]{0,2}\s*:/iu.test(line) && (line.match(/STARTUP|BUILDER|ENGINEERING|OPS|RESEARCH/giu) ?? []).length >= 2);
|
|
200
|
+
const isPlaceholder = Boolean(placeholderLine);
|
|
201
|
+
const ok = tokenMatches.size === 1 && !isPlaceholder;
|
|
202
|
+
findings.push({
|
|
203
|
+
section: "Mode Block Token",
|
|
204
|
+
required: true,
|
|
205
|
+
rule: "Mode Block must declare exactly one mode token: STARTUP, BUILDER, ENGINEERING, OPS, or RESEARCH.",
|
|
206
|
+
found: ok,
|
|
207
|
+
details: ok
|
|
208
|
+
? `Recognized mode token detected: ${[...tokenMatches][0] ?? ""}.`
|
|
209
|
+
: isPlaceholder
|
|
210
|
+
? "Mode Block still lists multiple mode tokens (template placeholder); pick exactly one of STARTUP/BUILDER/ENGINEERING/OPS/RESEARCH."
|
|
211
|
+
: "Mode Block is missing a recognized mode token (STARTUP/BUILDER/ENGINEERING/OPS/RESEARCH)."
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
// Approach Detail Cards: structural sub-section under Approaches, one
|
|
215
|
+
// bullet block per approach with the canonical fields.
|
|
216
|
+
const approachCardsRegex = /####\s+APPROACH\s+[A-Z]\b[\s\S]*?(?:^-\s*Summary:[\s\S]*?^-\s*Effort:[\s\S]*?^-\s*Risk:[\s\S]*?^-\s*Pros:[\s\S]*?^-\s*Cons:[\s\S]*?^-\s*Reuses:)/gimu;
|
|
217
|
+
const matches = raw.match(approachCardsRegex);
|
|
218
|
+
const cardCount = matches ? matches.length : 0;
|
|
219
|
+
if (/####\s+APPROACH\s+[A-Z]\b/iu.test(raw) ||
|
|
220
|
+
/^RECOMMENDATION:/imu.test(raw)) {
|
|
221
|
+
findings.push({
|
|
222
|
+
section: "Approach Detail Cards",
|
|
223
|
+
required: true,
|
|
224
|
+
rule: "Approach Detail Cards must include ≥2 `#### APPROACH <letter>` blocks each with Summary/Effort/Risk/Pros/Cons/Reuses.",
|
|
225
|
+
found: cardCount >= 2,
|
|
226
|
+
details: cardCount >= 2
|
|
227
|
+
? `Detected ${cardCount} valid approach detail card(s).`
|
|
228
|
+
: `Detected ${cardCount} valid approach detail card(s); at least 2 required with all fields present.`
|
|
229
|
+
});
|
|
230
|
+
const recommendationLine = raw.match(/^RECOMMENDATION:\s*(.+)$/imu);
|
|
231
|
+
const hasRecommendation = recommendationLine !== null && recommendationLine[1] !== undefined && recommendationLine[1].trim().length > 0;
|
|
232
|
+
findings.push({
|
|
233
|
+
section: "Approach Recommendation Marker",
|
|
234
|
+
required: true,
|
|
235
|
+
rule: "Approach Detail Cards must conclude with a single `RECOMMENDATION:` line citing the chosen letter and rationale.",
|
|
236
|
+
found: hasRecommendation,
|
|
237
|
+
details: hasRecommendation
|
|
238
|
+
? "Recommendation marker present."
|
|
239
|
+
: "Missing or empty `RECOMMENDATION:` line after approach detail cards."
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
const outsideVoiceBody = sectionBodyByName(sections, "Outside Voice");
|
|
243
|
+
if (outsideVoiceBody !== null) {
|
|
244
|
+
const required = ["source:", "prompt:", "tension:", "resolution:"];
|
|
245
|
+
const missing = required.filter((key) => !new RegExp(`(?:^|\\n)\\s*-?\\s*${key.replace(":", "\\s*:")}`, "iu").test(outsideVoiceBody));
|
|
246
|
+
const optedOut = /\bnot used\b|\bn\/a\b|\bnone\b/iu.test(outsideVoiceBody);
|
|
247
|
+
findings.push({
|
|
248
|
+
section: "Outside Voice Slot Shape",
|
|
249
|
+
required: true,
|
|
250
|
+
rule: "Outside Voice section must either declare opt-out (`not used`/`none`) or include `source:`, `prompt:`, `tension:`, `resolution:`.",
|
|
251
|
+
found: optedOut || missing.length === 0,
|
|
252
|
+
details: optedOut || missing.length === 0
|
|
253
|
+
? "Outside Voice slot is well-formed."
|
|
254
|
+
: `Outside Voice section is missing field(s): ${missing.join(", ")}.`
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
const wavePlansDir = path.join(projectRoot, ".cclaw", "wave-plans");
|
|
258
|
+
let wavePlanEntries = [];
|
|
259
|
+
try {
|
|
260
|
+
wavePlanEntries = (await fs.readdir(wavePlansDir))
|
|
261
|
+
.filter((entry) => entry !== ".gitkeep" && !entry.startsWith("."));
|
|
262
|
+
}
|
|
263
|
+
catch {
|
|
264
|
+
wavePlanEntries = [];
|
|
265
|
+
}
|
|
266
|
+
const multiWaveDetected = wavePlanEntries.length >= 2;
|
|
267
|
+
if (multiWaveDetected) {
|
|
268
|
+
const carryForwardBody = sectionBodyByName(sections, "Wave Carry-forward");
|
|
269
|
+
const hasCarryForwardSection = carryForwardBody !== null;
|
|
270
|
+
const hasCarryForwardContent = carryForwardBody !== null && meaningfulLineCount(carryForwardBody) > 0;
|
|
271
|
+
const hasDriftAuditMarkers = carryForwardBody !== null &&
|
|
272
|
+
/\bcarrying\s+forward\b/iu.test(carryForwardBody) &&
|
|
273
|
+
/\bdrift\s+detected\b/iu.test(carryForwardBody);
|
|
274
|
+
const waveDriftAddressed = hasCarryForwardSection && hasCarryForwardContent && hasDriftAuditMarkers;
|
|
275
|
+
findings.push({
|
|
276
|
+
section: "wave.drift_unaddressed",
|
|
277
|
+
required: true,
|
|
278
|
+
rule: "[P1] wave.drift_unaddressed — when `.cclaw/wave-plans/` has >=2 entries, brainstorm must include `## Wave Carry-forward` with carry-forward and drift audit markers.",
|
|
279
|
+
found: waveDriftAddressed,
|
|
280
|
+
details: waveDriftAddressed
|
|
281
|
+
? `Multi-wave context detected (${wavePlanEntries.length} wave-plan entries); Wave Carry-forward audit is present.`
|
|
282
|
+
: !hasCarryForwardSection
|
|
283
|
+
? "Multi-wave context detected but `## Wave Carry-forward` section is missing."
|
|
284
|
+
: !hasCarryForwardContent
|
|
285
|
+
? "`## Wave Carry-forward` exists but has no meaningful content."
|
|
286
|
+
: "Wave Carry-forward section must include both `Carrying forward` and `Drift detected` markers."
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
}
|