cclaw-cli 0.48.0 → 0.48.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/dist/artifact-linter.d.ts +2 -2
- package/dist/artifact-linter.js +4 -10
- package/dist/constants.d.ts +6 -0
- package/dist/constants.js +11 -0
- package/dist/content/core-agents.d.ts +53 -1
- package/dist/content/core-agents.js +6 -0
- package/dist/content/observe.js +22 -1
- package/dist/content/opencode-plugin.js +5 -1
- package/dist/content/stage-schema.js +2 -0
- package/dist/content/stages/ship.js +2 -5
- package/dist/content/templates.js +13 -15
- package/dist/content/utility-skills.d.ts +7 -1
- package/dist/content/utility-skills.js +5 -0
- package/dist/delegation.d.ts +15 -1
- package/dist/delegation.js +24 -8
- package/dist/doctor.js +101 -18
- package/dist/feature-system.d.ts +11 -4
- package/dist/feature-system.js +52 -8
- package/dist/flow-state.d.ts +3 -1
- package/dist/flow-state.js +34 -3
- package/dist/fs-utils.d.ts +9 -0
- package/dist/fs-utils.js +46 -3
- package/dist/gate-evidence.d.ts +2 -0
- package/dist/gate-evidence.js +13 -4
- package/dist/gitignore.js +6 -3
- package/dist/harness-adapters.js +11 -1
- package/dist/install.js +41 -5
- package/dist/internal/advance-stage.js +45 -8
- package/dist/knowledge-store.js +2 -2
- package/dist/retro-gate.js +23 -14
- package/dist/run-archive.js +164 -93
- package/dist/run-persistence.d.ts +8 -1
- package/dist/run-persistence.js +13 -5
- package/dist/tdd-cycle.js +6 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type FlowStage } from "./types.js";
|
|
1
|
+
import { type FlowStage, type FlowTrack } from "./types.js";
|
|
2
2
|
export interface LintFinding {
|
|
3
3
|
section: string;
|
|
4
4
|
required: boolean;
|
|
@@ -46,7 +46,7 @@ export interface LearningsParseResult {
|
|
|
46
46
|
details: string;
|
|
47
47
|
}
|
|
48
48
|
export declare function parseLearningsSection(sectionBody: string): LearningsParseResult;
|
|
49
|
-
export declare function lintArtifact(projectRoot: string, stage: FlowStage): Promise<LintResult>;
|
|
49
|
+
export declare function lintArtifact(projectRoot: string, stage: FlowStage, track?: FlowTrack): Promise<LintResult>;
|
|
50
50
|
export declare function validateReviewArmy(projectRoot: string): Promise<{
|
|
51
51
|
valid: boolean;
|
|
52
52
|
errors: string[];
|
package/dist/artifact-linter.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { RUNTIME_ROOT } from "./constants.js";
|
|
3
|
+
import { RUNTIME_ROOT, SHIP_FINALIZATION_MODES } from "./constants.js";
|
|
4
4
|
import { exists } from "./fs-utils.js";
|
|
5
5
|
import { stageSchema } from "./content/stage-schema.js";
|
|
6
6
|
import { FLOW_STAGES } from "./types.js";
|
|
@@ -137,13 +137,7 @@ function tokensFromRule(rule) {
|
|
|
137
137
|
return [...new Set(allCaps)];
|
|
138
138
|
}
|
|
139
139
|
if (/finalization enum token/iu.test(rule)) {
|
|
140
|
-
return [
|
|
141
|
-
"FINALIZE_MERGE_LOCAL",
|
|
142
|
-
"FINALIZE_OPEN_PR",
|
|
143
|
-
"FINALIZE_KEEP_BRANCH",
|
|
144
|
-
"FINALIZE_DISCARD_BRANCH",
|
|
145
|
-
"FINALIZE_NO_VCS"
|
|
146
|
-
];
|
|
140
|
+
return [...SHIP_FINALIZATION_MODES];
|
|
147
141
|
}
|
|
148
142
|
if (/final verdict/iu.test(rule)) {
|
|
149
143
|
return ["APPROVED", "APPROVED_WITH_CONCERNS", "BLOCKED"];
|
|
@@ -795,8 +789,8 @@ function validateSectionBody(sectionBody, rule, sectionName) {
|
|
|
795
789
|
details: "Section heading and content satisfy lint heuristics."
|
|
796
790
|
};
|
|
797
791
|
}
|
|
798
|
-
export async function lintArtifact(projectRoot, stage) {
|
|
799
|
-
const schema = stageSchema(stage);
|
|
792
|
+
export async function lintArtifact(projectRoot, stage, track = "standard") {
|
|
793
|
+
const schema = stageSchema(stage, track);
|
|
800
794
|
const { absPath: absFile, relPath: relFile } = await resolveArtifactPath(projectRoot, schema.artifactFile);
|
|
801
795
|
const findings = [];
|
|
802
796
|
if (!(await exists(absFile))) {
|
package/dist/constants.d.ts
CHANGED
|
@@ -3,6 +3,12 @@ import type { FlowStage, HarnessId } from "./types.js";
|
|
|
3
3
|
export declare const RUNTIME_ROOT = ".cclaw";
|
|
4
4
|
export declare const CCLAW_VERSION: string;
|
|
5
5
|
export declare const FLOW_VERSION = "1.0.0";
|
|
6
|
+
/**
|
|
7
|
+
* Canonical ship finalization enums used across stage schema, linting, and
|
|
8
|
+
* runtime gate evidence checks.
|
|
9
|
+
*/
|
|
10
|
+
export declare const SHIP_FINALIZATION_MODES: readonly ["FINALIZE_MERGE_LOCAL", "FINALIZE_OPEN_PR", "FINALIZE_KEEP_BRANCH", "FINALIZE_DISCARD_BRANCH", "FINALIZE_NO_VCS"];
|
|
11
|
+
export type ShipFinalizationMode = (typeof SHIP_FINALIZATION_MODES)[number];
|
|
6
12
|
export declare const DEFAULT_HARNESSES: HarnessId[];
|
|
7
13
|
/**
|
|
8
14
|
* Evals subtree. Scaffolds the directory layout and a default config.yaml; the
|
package/dist/constants.js
CHANGED
|
@@ -35,6 +35,17 @@ function readPackageVersion() {
|
|
|
35
35
|
}
|
|
36
36
|
export const CCLAW_VERSION = readPackageVersion();
|
|
37
37
|
export const FLOW_VERSION = "1.0.0";
|
|
38
|
+
/**
|
|
39
|
+
* Canonical ship finalization enums used across stage schema, linting, and
|
|
40
|
+
* runtime gate evidence checks.
|
|
41
|
+
*/
|
|
42
|
+
export const SHIP_FINALIZATION_MODES = [
|
|
43
|
+
"FINALIZE_MERGE_LOCAL",
|
|
44
|
+
"FINALIZE_OPEN_PR",
|
|
45
|
+
"FINALIZE_KEEP_BRANCH",
|
|
46
|
+
"FINALIZE_DISCARD_BRANCH",
|
|
47
|
+
"FINALIZE_NO_VCS"
|
|
48
|
+
];
|
|
38
49
|
export const DEFAULT_HARNESSES = [
|
|
39
50
|
"claude",
|
|
40
51
|
"cursor",
|
|
@@ -24,8 +24,60 @@ export interface AgentDefinition {
|
|
|
24
24
|
}
|
|
25
25
|
/**
|
|
26
26
|
* Canonical specialist roster (core-5) materialized under `.cclaw/agents/`.
|
|
27
|
+
*
|
|
28
|
+
* Declared with `satisfies` so the array retains literal `name` types for
|
|
29
|
+
* downstream type-level consumers (e.g. `AgentName`), while still being
|
|
30
|
+
* checked against the `AgentDefinition` shape at compile time. Do not add
|
|
31
|
+
* an explicit `AgentDefinition[]` annotation here — it would widen `name`
|
|
32
|
+
* to `string` and break the compile-time drift guard.
|
|
33
|
+
*/
|
|
34
|
+
export declare const CCLAW_AGENTS: readonly [{
|
|
35
|
+
readonly name: "planner";
|
|
36
|
+
readonly description: "MANDATORY for scope/design/plan and PROACTIVE for high-ambiguity work. MUST BE USED when sequencing, dependency mapping, or risk trade-offs are required before coding.";
|
|
37
|
+
readonly tools: ["Read", "Grep", "Glob", "WebSearch"];
|
|
38
|
+
readonly model: "deep";
|
|
39
|
+
readonly activation: "mandatory";
|
|
40
|
+
readonly relatedStages: ["brainstorm", "scope", "design", "spec", "plan"];
|
|
41
|
+
readonly body: string;
|
|
42
|
+
}, {
|
|
43
|
+
readonly name: "reviewer";
|
|
44
|
+
readonly description: "MANDATORY during review. MUST BE USED to run a two-pass audit: spec compliance first, then correctness/maintainability/performance/architecture.";
|
|
45
|
+
readonly tools: ["Read", "Grep", "Glob"];
|
|
46
|
+
readonly model: "balanced";
|
|
47
|
+
readonly activation: "mandatory";
|
|
48
|
+
readonly relatedStages: ["spec", "review", "ship"];
|
|
49
|
+
readonly body: string;
|
|
50
|
+
}, {
|
|
51
|
+
readonly name: "security-reviewer";
|
|
52
|
+
readonly description: "MANDATORY during review; PROACTIVE during design/ship for trust-boundary changes. Always produce an explicit no-change attestation when no security-relevant surface moved.";
|
|
53
|
+
readonly tools: ["Read", "Grep", "Glob"];
|
|
54
|
+
readonly model: "balanced";
|
|
55
|
+
readonly activation: "mandatory";
|
|
56
|
+
readonly relatedStages: ["design", "review", "ship"];
|
|
57
|
+
readonly body: string;
|
|
58
|
+
}, {
|
|
59
|
+
readonly name: "test-author";
|
|
60
|
+
readonly description: "MANDATORY in TDD stage. MUST BE USED for RED -> GREEN -> REFACTOR with evidence-first discipline.";
|
|
61
|
+
readonly tools: ["Read", "Write", "Edit", "Grep", "Glob", "Bash"];
|
|
62
|
+
readonly model: "balanced";
|
|
63
|
+
readonly activation: "mandatory";
|
|
64
|
+
readonly relatedStages: ["tdd"];
|
|
65
|
+
readonly body: string;
|
|
66
|
+
}, {
|
|
67
|
+
readonly name: "doc-updater";
|
|
68
|
+
readonly description: "MANDATORY at ship and PROACTIVE when behavior/config/public API changes. Keep docs and runbooks in lockstep with shipped behavior.";
|
|
69
|
+
readonly tools: ["Read", "Write", "Edit", "Grep", "Glob"];
|
|
70
|
+
readonly model: "fast";
|
|
71
|
+
readonly activation: "mandatory";
|
|
72
|
+
readonly relatedStages: ["tdd", "ship"];
|
|
73
|
+
readonly body: string;
|
|
74
|
+
}];
|
|
75
|
+
/**
|
|
76
|
+
* Union of known agent names (compile-time). Use this in content that
|
|
77
|
+
* references agents by name so the TypeScript compiler catches renames
|
|
78
|
+
* and typos instead of letting them slip into generated artifacts.
|
|
27
79
|
*/
|
|
28
|
-
export
|
|
80
|
+
export type AgentName = (typeof CCLAW_AGENTS)[number]["name"];
|
|
29
81
|
/**
|
|
30
82
|
* Render a complete cclaw agent markdown file (YAML frontmatter + body).
|
|
31
83
|
*/
|
|
@@ -15,6 +15,12 @@ function yamlFlowSequence(values) {
|
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
17
17
|
* Canonical specialist roster (core-5) materialized under `.cclaw/agents/`.
|
|
18
|
+
*
|
|
19
|
+
* Declared with `satisfies` so the array retains literal `name` types for
|
|
20
|
+
* downstream type-level consumers (e.g. `AgentName`), while still being
|
|
21
|
+
* checked against the `AgentDefinition` shape at compile time. Do not add
|
|
22
|
+
* an explicit `AgentDefinition[]` annotation here — it would widen `name`
|
|
23
|
+
* to `string` and break the compile-time drift guard.
|
|
18
24
|
*/
|
|
19
25
|
export const CCLAW_AGENTS = [
|
|
20
26
|
{
|
package/dist/content/observe.js
CHANGED
|
@@ -1094,11 +1094,20 @@ export function claudeHooksJsonWithObservation() {
|
|
|
1094
1094
|
}]
|
|
1095
1095
|
}],
|
|
1096
1096
|
PreToolUse: [{
|
|
1097
|
+
// `prompt-guard.sh` inspects tool inputs across all tool calls;
|
|
1098
|
+
// it has to stay on `*` so it sees MCP/Edit/Write/WebSearch
|
|
1099
|
+
// traffic too. `workflow-guard.sh`, however, only checks TDD
|
|
1100
|
+
// ordering on write-like operations — it is a no-op for reads.
|
|
1101
|
+
// Splitting the two matchers cuts Claude's per-read hook
|
|
1102
|
+
// overhead in half without reducing coverage on write paths.
|
|
1097
1103
|
matcher: "*",
|
|
1098
1104
|
hooks: [{
|
|
1099
1105
|
type: "command",
|
|
1100
1106
|
command: hookDispatcherCommand("prompt-guard.sh")
|
|
1101
|
-
}
|
|
1107
|
+
}]
|
|
1108
|
+
}, {
|
|
1109
|
+
matcher: "Write|Edit|MultiEdit|NotebookEdit|Bash",
|
|
1110
|
+
hooks: [{
|
|
1102
1111
|
type: "command",
|
|
1103
1112
|
command: hookDispatcherCommand("workflow-guard.sh")
|
|
1104
1113
|
}]
|
|
@@ -1196,6 +1205,18 @@ export function codexHooksJsonWithObservation() {
|
|
|
1196
1205
|
hooks: [{
|
|
1197
1206
|
type: "command",
|
|
1198
1207
|
command: hookDispatcherCommand("prompt-guard.sh")
|
|
1208
|
+
}, {
|
|
1209
|
+
// `workflow-guard.sh` also runs here because Codex's PreToolUse
|
|
1210
|
+
// only sees Bash; Write/Edit/MCP writes never reach the hook
|
|
1211
|
+
// surface. Running workflow-guard on UserPromptSubmit catches
|
|
1212
|
+
// TDD-order violations that originate from the user's prompt
|
|
1213
|
+
// text (e.g. "edit X.ts to ..."). Payload is a prompt envelope,
|
|
1214
|
+
// not a tool call, so the script's TOOL extraction falls back
|
|
1215
|
+
// to "unknown" and advisory mode is a no-op by design — the
|
|
1216
|
+
// value is that prompt text is scanned for write-shaped intent
|
|
1217
|
+
// via the existing PAYLOAD_LOWER heuristics.
|
|
1218
|
+
type: "command",
|
|
1219
|
+
command: hookDispatcherCommand("workflow-guard.sh")
|
|
1199
1220
|
}, {
|
|
1200
1221
|
type: "command",
|
|
1201
1222
|
command: "bash -lc 'if command -v cclaw >/dev/null 2>&1; then cclaw internal verify-current-state --quiet >/dev/null || true; else npx -y cclaw-cli internal verify-current-state --quiet >/dev/null || true; fi'"
|
|
@@ -233,11 +233,15 @@ export default function cclawPlugin(ctx) {
|
|
|
233
233
|
eventType === "session.created" ||
|
|
234
234
|
eventType === "session.resumed" ||
|
|
235
235
|
eventType === "session.compacted" ||
|
|
236
|
-
eventType === "session.cleared"
|
|
236
|
+
eventType === "session.cleared" ||
|
|
237
|
+
eventType === "session.updated"
|
|
237
238
|
) {
|
|
238
239
|
// Avoid writing directly to stdout in lifecycle hooks because it can
|
|
239
240
|
// interfere with OpenCode TUI rendering. Bootstrap is injected via
|
|
240
241
|
// the system transform hook instead.
|
|
242
|
+
// session.updated covers config reloads and artifact/rules edits
|
|
243
|
+
// that happen mid-session; without it the cache would stay stale
|
|
244
|
+
// until the next compaction or restart.
|
|
241
245
|
refreshBootstrapCache();
|
|
242
246
|
}
|
|
243
247
|
if (eventType === "session.compacted") {
|
|
@@ -5,6 +5,7 @@ import { tddStageForTrack } from "./stages/tdd.js";
|
|
|
5
5
|
const ARTIFACT_STAGE_BY_PATH = {
|
|
6
6
|
".cclaw/artifacts/01-brainstorm.md": "brainstorm",
|
|
7
7
|
".cclaw/artifacts/02-scope.md": "scope",
|
|
8
|
+
".cclaw/artifacts/02a-research.md": "design",
|
|
8
9
|
".cclaw/artifacts/03-design.md": "design",
|
|
9
10
|
".cclaw/artifacts/04-spec.md": "spec",
|
|
10
11
|
".cclaw/artifacts/05-plan.md": "plan",
|
|
@@ -46,6 +47,7 @@ const REQUIRED_GATE_IDS = {
|
|
|
46
47
|
"tdd_green_full_suite",
|
|
47
48
|
"tdd_refactor_completed",
|
|
48
49
|
"tdd_verified_before_complete",
|
|
50
|
+
"tdd_docs_drift_check",
|
|
49
51
|
...(track === "quick" ? [] : ["tdd_traceable_to_plan"])
|
|
50
52
|
],
|
|
51
53
|
review: (track) => [
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SHIP_FINALIZATION_MODES } from "../../constants.js";
|
|
1
2
|
// ---------------------------------------------------------------------------
|
|
2
3
|
// SHIP — reference: superpowers finishing-a-development-branch + gstack /ship
|
|
3
4
|
// ---------------------------------------------------------------------------
|
|
@@ -93,11 +94,7 @@ export const SHIP = {
|
|
|
93
94
|
"Pre-Ship Checks",
|
|
94
95
|
"Release Notes",
|
|
95
96
|
"Rollback Plan",
|
|
96
|
-
|
|
97
|
-
"FINALIZE_OPEN_PR",
|
|
98
|
-
"FINALIZE_KEEP_BRANCH",
|
|
99
|
-
"FINALIZE_DISCARD_BRANCH",
|
|
100
|
-
"FINALIZE_NO_VCS"
|
|
97
|
+
...SHIP_FINALIZATION_MODES
|
|
101
98
|
],
|
|
102
99
|
artifactFile: "08-ship.md",
|
|
103
100
|
// `done` exits the stage pipeline. Archive semantics are handled by the
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
import { CCLAW_VERSION, SHIP_FINALIZATION_MODES } from "../constants.js";
|
|
1
2
|
import { orderedStageSchemas } from "./stage-schema.js";
|
|
2
3
|
import { FLOW_STAGES } from "../types.js";
|
|
4
|
+
const SHIP_FINALIZATION_ENUM_LINES = SHIP_FINALIZATION_MODES.map((mode) => ` - ${mode}`).join("\n");
|
|
3
5
|
export const ARTIFACT_TEMPLATES = {
|
|
4
6
|
"01-brainstorm.md": `---
|
|
5
7
|
stage: brainstorm
|
|
6
8
|
schema_version: 1
|
|
7
|
-
version:
|
|
9
|
+
version: ${CCLAW_VERSION}
|
|
8
10
|
feature: <feature-id>
|
|
9
11
|
locked_decisions: []
|
|
10
12
|
inputs_hash: sha256:pending
|
|
@@ -52,7 +54,7 @@ inputs_hash: sha256:pending
|
|
|
52
54
|
"02-scope.md": `---
|
|
53
55
|
stage: scope
|
|
54
56
|
schema_version: 1
|
|
55
|
-
version:
|
|
57
|
+
version: ${CCLAW_VERSION}
|
|
56
58
|
feature: <feature-id>
|
|
57
59
|
locked_decisions: []
|
|
58
60
|
inputs_hash: sha256:pending
|
|
@@ -158,7 +160,7 @@ inputs_hash: sha256:pending
|
|
|
158
160
|
"02a-research.md": `---
|
|
159
161
|
stage: design
|
|
160
162
|
schema_version: 1
|
|
161
|
-
version:
|
|
163
|
+
version: ${CCLAW_VERSION}
|
|
162
164
|
feature: <feature-id>
|
|
163
165
|
locked_decisions: []
|
|
164
166
|
inputs_hash: sha256:pending
|
|
@@ -199,7 +201,7 @@ inputs_hash: sha256:pending
|
|
|
199
201
|
"03-design.md": `---
|
|
200
202
|
stage: design
|
|
201
203
|
schema_version: 1
|
|
202
|
-
version:
|
|
204
|
+
version: ${CCLAW_VERSION}
|
|
203
205
|
feature: <feature-id>
|
|
204
206
|
locked_decisions: []
|
|
205
207
|
inputs_hash: sha256:pending
|
|
@@ -303,7 +305,7 @@ inputs_hash: sha256:pending
|
|
|
303
305
|
"04-spec.md": `---
|
|
304
306
|
stage: spec
|
|
305
307
|
schema_version: 1
|
|
306
|
-
version:
|
|
308
|
+
version: ${CCLAW_VERSION}
|
|
307
309
|
feature: <feature-id>
|
|
308
310
|
locked_decisions: []
|
|
309
311
|
inputs_hash: sha256:pending
|
|
@@ -359,7 +361,7 @@ inputs_hash: sha256:pending
|
|
|
359
361
|
"05-plan.md": `---
|
|
360
362
|
stage: plan
|
|
361
363
|
schema_version: 1
|
|
362
|
-
version:
|
|
364
|
+
version: ${CCLAW_VERSION}
|
|
363
365
|
feature: <feature-id>
|
|
364
366
|
locked_decisions: []
|
|
365
367
|
inputs_hash: sha256:pending
|
|
@@ -438,7 +440,7 @@ Execution rule: complete and verify each batch before starting the next batch.
|
|
|
438
440
|
"06-tdd.md": `---
|
|
439
441
|
stage: tdd
|
|
440
442
|
schema_version: 1
|
|
441
|
-
version:
|
|
443
|
+
version: ${CCLAW_VERSION}
|
|
442
444
|
feature: <feature-id>
|
|
443
445
|
locked_decisions: []
|
|
444
446
|
inputs_hash: sha256:pending
|
|
@@ -505,7 +507,7 @@ inputs_hash: sha256:pending
|
|
|
505
507
|
"07-review.md": `---
|
|
506
508
|
stage: review
|
|
507
509
|
schema_version: 1
|
|
508
|
-
version:
|
|
510
|
+
version: ${CCLAW_VERSION}
|
|
509
511
|
feature: <feature-id>
|
|
510
512
|
locked_decisions: []
|
|
511
513
|
inputs_hash: sha256:pending
|
|
@@ -614,7 +616,7 @@ inputs_hash: sha256:pending
|
|
|
614
616
|
"08-ship.md": `---
|
|
615
617
|
stage: ship
|
|
616
618
|
schema_version: 1
|
|
617
|
-
version:
|
|
619
|
+
version: ${CCLAW_VERSION}
|
|
618
620
|
feature: <feature-id>
|
|
619
621
|
locked_decisions: []
|
|
620
622
|
inputs_hash: sha256:pending
|
|
@@ -644,11 +646,7 @@ inputs_hash: sha256:pending
|
|
|
644
646
|
|
|
645
647
|
## Finalization
|
|
646
648
|
- Selected enum (exactly one):
|
|
647
|
-
|
|
648
|
-
- FINALIZE_OPEN_PR
|
|
649
|
-
- FINALIZE_KEEP_BRANCH
|
|
650
|
-
- FINALIZE_DISCARD_BRANCH
|
|
651
|
-
- FINALIZE_NO_VCS
|
|
649
|
+
${SHIP_FINALIZATION_ENUM_LINES}
|
|
652
650
|
- Selected label (A/B/C/D/E):
|
|
653
651
|
- Execution result:
|
|
654
652
|
- PR URL / merge commit / kept branch / discard confirmation:
|
|
@@ -669,7 +667,7 @@ inputs_hash: sha256:pending
|
|
|
669
667
|
"09-retro.md": `---
|
|
670
668
|
stage: retro
|
|
671
669
|
schema_version: 1
|
|
672
|
-
version:
|
|
670
|
+
version: ${CCLAW_VERSION}
|
|
673
671
|
feature: <feature-id>
|
|
674
672
|
locked_decisions: []
|
|
675
673
|
inputs_hash: sha256:pending
|
|
@@ -48,4 +48,10 @@ export declare const LANGUAGE_RULE_PACK_GENERATORS: Record<string, () => string>
|
|
|
48
48
|
*/
|
|
49
49
|
export declare const LEGACY_LANGUAGE_RULE_PACK_FOLDERS: readonly ["language-typescript", "language-python", "language-go"];
|
|
50
50
|
export declare const UTILITY_SKILL_FOLDERS: readonly ["security", "debugging", "performance", "ci-cd", "docs", "executing-plans", "verification-before-completion", "finishing-a-development-branch", "context-engineering", "source-driven-development", "frontend-accessibility", "landscape-check", "adversarial-review", "security-audit", "knowledge-curation", "retrospective", "document-review", "receiving-code-review"];
|
|
51
|
-
export
|
|
51
|
+
export type UtilitySkillFolder = (typeof UTILITY_SKILL_FOLDERS)[number];
|
|
52
|
+
/**
|
|
53
|
+
* One entry per `UTILITY_SKILL_FOLDERS` slot. Typed via the tuple so that
|
|
54
|
+
* adding a folder without a generator (or vice versa) is a TypeScript
|
|
55
|
+
* error — keeps the two sources of truth in lockstep at compile time.
|
|
56
|
+
*/
|
|
57
|
+
export declare const UTILITY_SKILL_MAP: Record<UtilitySkillFolder, () => string>;
|
|
@@ -1735,6 +1735,11 @@ export const UTILITY_SKILL_FOLDERS = [
|
|
|
1735
1735
|
"document-review",
|
|
1736
1736
|
"receiving-code-review"
|
|
1737
1737
|
];
|
|
1738
|
+
/**
|
|
1739
|
+
* One entry per `UTILITY_SKILL_FOLDERS` slot. Typed via the tuple so that
|
|
1740
|
+
* adding a folder without a generator (or vice versa) is a TypeScript
|
|
1741
|
+
* error — keeps the two sources of truth in lockstep at compile time.
|
|
1742
|
+
*/
|
|
1738
1743
|
export const UTILITY_SKILL_MAP = {
|
|
1739
1744
|
security: securityReviewSkill,
|
|
1740
1745
|
debugging: debuggingSkill,
|
package/dist/delegation.d.ts
CHANGED
|
@@ -53,6 +53,8 @@ export type DelegationEntry = {
|
|
|
53
53
|
retryCount?: number;
|
|
54
54
|
/** Optional references to evidence anchors in artifacts. */
|
|
55
55
|
evidenceRefs?: string[];
|
|
56
|
+
/** Optional skill marker used for role-specific mandatory checks. */
|
|
57
|
+
skill?: string;
|
|
56
58
|
/**
|
|
57
59
|
* Fulfillment mode this entry was executed under. Omitted on legacy rows
|
|
58
60
|
* (treated as `"isolated"` for Claude, otherwise inferred from the active
|
|
@@ -66,6 +68,16 @@ export type DelegationLedger = {
|
|
|
66
68
|
runId: string;
|
|
67
69
|
entries: DelegationEntry[];
|
|
68
70
|
};
|
|
71
|
+
/**
|
|
72
|
+
* Heuristic: does a changed file path strongly imply a trust-boundary
|
|
73
|
+
* surface? Used to gate adversarial-reviewer requirements on review.
|
|
74
|
+
*
|
|
75
|
+
* Matches authN/Z, credentials, crypto, policy, or explicit sanitization
|
|
76
|
+
* or injection handling. Intentionally excludes broad terms like `input`
|
|
77
|
+
* and `validation` because they match innocuous paths such as
|
|
78
|
+
* `form-input.ts` or `number-validation.ts` and produce false positives.
|
|
79
|
+
*/
|
|
80
|
+
export declare function isTrustBoundaryPath(filePath: string): boolean;
|
|
69
81
|
export declare function readDelegationLedger(projectRoot: string): Promise<DelegationLedger>;
|
|
70
82
|
export declare function appendDelegation(projectRoot: string, entry: DelegationEntry): Promise<void>;
|
|
71
83
|
/**
|
|
@@ -75,7 +87,9 @@ export declare function appendDelegation(projectRoot: string, entry: DelegationE
|
|
|
75
87
|
* strongest guarantee.
|
|
76
88
|
*/
|
|
77
89
|
export declare function expectedFulfillmentMode(fallbacks: SubagentFallback[]): DelegationFulfillmentMode;
|
|
78
|
-
export declare function checkMandatoryDelegations(projectRoot: string, stage: FlowStage
|
|
90
|
+
export declare function checkMandatoryDelegations(projectRoot: string, stage: FlowStage, options?: {
|
|
91
|
+
repairFeatureSystem?: boolean;
|
|
92
|
+
}): Promise<{
|
|
79
93
|
satisfied: boolean;
|
|
80
94
|
missing: string[];
|
|
81
95
|
waived: string[];
|
package/dist/delegation.js
CHANGED
|
@@ -53,6 +53,18 @@ async function resolveReviewDiffBase(projectRoot) {
|
|
|
53
53
|
return null;
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Heuristic: does a changed file path strongly imply a trust-boundary
|
|
58
|
+
* surface? Used to gate adversarial-reviewer requirements on review.
|
|
59
|
+
*
|
|
60
|
+
* Matches authN/Z, credentials, crypto, policy, or explicit sanitization
|
|
61
|
+
* or injection handling. Intentionally excludes broad terms like `input`
|
|
62
|
+
* and `validation` because they match innocuous paths such as
|
|
63
|
+
* `form-input.ts` or `number-validation.ts` and produce false positives.
|
|
64
|
+
*/
|
|
65
|
+
export function isTrustBoundaryPath(filePath) {
|
|
66
|
+
return /(auth|security|secret|token|credential|permission|acl|policy|oauth|session|encrypt|decrypt|sanitize|untrusted|csrf|xss|injection|taint)/iu.test(filePath);
|
|
67
|
+
}
|
|
56
68
|
async function detectReviewTriggers(projectRoot) {
|
|
57
69
|
const empty = {
|
|
58
70
|
changedFiles: 0,
|
|
@@ -81,7 +93,7 @@ async function detectReviewTriggers(projectRoot) {
|
|
|
81
93
|
.split(/\r?\n/gu)
|
|
82
94
|
.map((line) => line.trim())
|
|
83
95
|
.filter((line) => line.length > 0);
|
|
84
|
-
const trustBoundaryChanged = changedPaths.some((
|
|
96
|
+
const trustBoundaryChanged = changedPaths.some((p) => isTrustBoundaryPath(p));
|
|
85
97
|
const requireAdversarialReviewer = changedLines > 100 || changedFiles > 10 || trustBoundaryChanged;
|
|
86
98
|
return {
|
|
87
99
|
changedFiles,
|
|
@@ -142,6 +154,7 @@ function isDelegationEntry(value) {
|
|
|
142
154
|
(o.tokens === undefined || isDelegationTokenUsage(o.tokens)) &&
|
|
143
155
|
retryOk &&
|
|
144
156
|
(o.evidenceRefs === undefined || (Array.isArray(o.evidenceRefs) && o.evidenceRefs.every((item) => typeof item === "string"))) &&
|
|
157
|
+
(o.skill === undefined || typeof o.skill === "string") &&
|
|
145
158
|
(o.schemaVersion === undefined || o.schemaVersion === 1));
|
|
146
159
|
}
|
|
147
160
|
function parseLedger(raw, runId) {
|
|
@@ -245,9 +258,11 @@ export function expectedFulfillmentMode(fallbacks) {
|
|
|
245
258
|
return "role-switch";
|
|
246
259
|
return "harness-waiver";
|
|
247
260
|
}
|
|
248
|
-
export async function checkMandatoryDelegations(projectRoot, stage) {
|
|
261
|
+
export async function checkMandatoryDelegations(projectRoot, stage, options = {}) {
|
|
249
262
|
const mandatory = stageSchema(stage).mandatoryDelegations;
|
|
250
|
-
const { activeRunId } = await readFlowState(projectRoot
|
|
263
|
+
const { activeRunId } = await readFlowState(projectRoot, {
|
|
264
|
+
repairFeatureSystem: options.repairFeatureSystem
|
|
265
|
+
});
|
|
251
266
|
const ledger = await readDelegationLedger(projectRoot);
|
|
252
267
|
const forStage = ledger.entries.filter((e) => e.stage === stage);
|
|
253
268
|
const forRun = forStage.filter((e) => e.runId === activeRunId);
|
|
@@ -267,14 +282,15 @@ export async function checkMandatoryDelegations(projectRoot, stage) {
|
|
|
267
282
|
const rows = forRun.filter((e) => e.agent === agent);
|
|
268
283
|
const completedRows = rows.filter((e) => e.status === "completed");
|
|
269
284
|
const waivedRows = rows.filter((e) => e.status === "waived");
|
|
270
|
-
const
|
|
285
|
+
const adversarialReviewerRequired = stage === "review" &&
|
|
271
286
|
agent === "reviewer" &&
|
|
272
|
-
reviewTriggers?.requireAdversarialReviewer
|
|
273
|
-
|
|
274
|
-
: 1;
|
|
287
|
+
reviewTriggers?.requireAdversarialReviewer === true;
|
|
288
|
+
const requiredCompletedCount = adversarialReviewerRequired ? 2 : 1;
|
|
275
289
|
const hasCompleted = completedRows.length >= requiredCompletedCount;
|
|
276
290
|
const hasWaived = waivedRows.length > 0;
|
|
277
|
-
const
|
|
291
|
+
const hasAdversarialSkill = !adversarialReviewerRequired ||
|
|
292
|
+
completedRows.some((row) => row.skill === "adversarial-review");
|
|
293
|
+
const ok = hasWaived || (hasCompleted && hasAdversarialSkill);
|
|
278
294
|
if (!ok) {
|
|
279
295
|
missing.push(agent);
|
|
280
296
|
continue;
|