cclaw-cli 7.7.1 → 8.1.1
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 +211 -134
- package/dist/artifact-frontmatter.d.ts +51 -0
- package/dist/artifact-frontmatter.js +131 -0
- package/dist/artifact-paths.d.ts +7 -27
- package/dist/artifact-paths.js +20 -249
- package/dist/cancel.d.ts +16 -0
- package/dist/cancel.js +66 -0
- package/dist/cli.d.ts +2 -27
- package/dist/cli.js +107 -511
- package/dist/compound.d.ts +26 -0
- package/dist/compound.js +96 -0
- package/dist/config.d.ts +14 -51
- package/dist/config.js +23 -359
- package/dist/constants.d.ts +11 -18
- package/dist/constants.js +19 -106
- package/dist/content/antipatterns.d.ts +1 -0
- package/dist/content/antipatterns.js +109 -0
- package/dist/content/artifact-templates.d.ts +10 -0
- package/dist/content/artifact-templates.js +550 -0
- package/dist/content/cancel-command.d.ts +2 -2
- package/dist/content/cancel-command.js +25 -17
- package/dist/content/core-agents.d.ts +9 -233
- package/dist/content/core-agents.js +39 -768
- package/dist/content/decision-protocol.d.ts +1 -12
- package/dist/content/decision-protocol.js +27 -20
- package/dist/content/examples.d.ts +8 -42
- package/dist/content/examples.js +293 -425
- package/dist/content/idea-command.d.ts +2 -0
- package/dist/content/idea-command.js +38 -0
- package/dist/content/iron-laws.d.ts +4 -138
- package/dist/content/iron-laws.js +18 -197
- package/dist/content/meta-skill.d.ts +1 -3
- package/dist/content/meta-skill.js +57 -134
- package/dist/content/node-hooks.d.ts +12 -8
- package/dist/content/node-hooks.js +188 -838
- package/dist/content/recovery.d.ts +8 -0
- package/dist/content/recovery.js +179 -0
- package/dist/content/reference-patterns.d.ts +4 -13
- package/dist/content/reference-patterns.js +260 -389
- package/dist/content/research-playbooks.d.ts +8 -8
- package/dist/content/research-playbooks.js +108 -121
- package/dist/content/review-loop.d.ts +6 -192
- package/dist/content/review-loop.js +29 -731
- package/dist/content/skills.d.ts +8 -38
- package/dist/content/skills.js +681 -732
- package/dist/content/specialist-prompts/architect.d.ts +1 -0
- package/dist/content/specialist-prompts/architect.js +225 -0
- package/dist/content/specialist-prompts/brainstormer.d.ts +1 -0
- package/dist/content/specialist-prompts/brainstormer.js +168 -0
- package/dist/content/specialist-prompts/index.d.ts +2 -0
- package/dist/content/specialist-prompts/index.js +14 -0
- package/dist/content/specialist-prompts/planner.d.ts +1 -0
- package/dist/content/specialist-prompts/planner.js +182 -0
- package/dist/content/specialist-prompts/reviewer.d.ts +1 -0
- package/dist/content/specialist-prompts/reviewer.js +193 -0
- package/dist/content/specialist-prompts/security-reviewer.d.ts +1 -0
- package/dist/content/specialist-prompts/security-reviewer.js +133 -0
- package/dist/content/specialist-prompts/slice-builder.d.ts +1 -0
- package/dist/content/specialist-prompts/slice-builder.js +232 -0
- package/dist/content/stage-playbooks.d.ts +8 -0
- package/dist/content/stage-playbooks.js +404 -0
- package/dist/content/start-command.d.ts +2 -12
- package/dist/content/start-command.js +221 -207
- package/dist/flow-state.d.ts +21 -178
- package/dist/flow-state.js +67 -170
- package/dist/fs-utils.d.ts +6 -26
- package/dist/fs-utils.js +29 -162
- package/dist/gitignore.d.ts +2 -1
- package/dist/gitignore.js +51 -34
- package/dist/harness-detect.d.ts +10 -0
- package/dist/harness-detect.js +29 -0
- package/dist/harness-prompt.d.ts +26 -0
- package/dist/harness-prompt.js +142 -0
- package/dist/install.d.ts +35 -15
- package/dist/install.js +238 -1347
- package/dist/knowledge-store.d.ts +19 -163
- package/dist/knowledge-store.js +56 -590
- package/dist/logger.d.ts +8 -3
- package/dist/logger.js +13 -4
- package/dist/orchestrator-routing.d.ts +29 -0
- package/dist/orchestrator-routing.js +156 -0
- package/dist/run-persistence.d.ts +7 -118
- package/dist/run-persistence.js +29 -845
- package/dist/runtime/run-hook.entry.d.ts +1 -3
- package/dist/runtime/run-hook.entry.js +19 -4
- package/dist/runtime/run-hook.mjs +13 -1024
- package/dist/types.d.ts +25 -261
- package/dist/types.js +8 -36
- package/package.json +6 -3
- package/dist/artifact-linter/brainstorm.d.ts +0 -2
- package/dist/artifact-linter/brainstorm.js +0 -353
- package/dist/artifact-linter/design.d.ts +0 -18
- package/dist/artifact-linter/design.js +0 -444
- package/dist/artifact-linter/findings-dedup.d.ts +0 -56
- package/dist/artifact-linter/findings-dedup.js +0 -232
- package/dist/artifact-linter/plan.d.ts +0 -2
- package/dist/artifact-linter/plan.js +0 -826
- package/dist/artifact-linter/review-army.d.ts +0 -49
- package/dist/artifact-linter/review-army.js +0 -520
- package/dist/artifact-linter/review.d.ts +0 -2
- package/dist/artifact-linter/review.js +0 -113
- package/dist/artifact-linter/scope.d.ts +0 -2
- package/dist/artifact-linter/scope.js +0 -158
- package/dist/artifact-linter/shared.d.ts +0 -637
- package/dist/artifact-linter/shared.js +0 -2163
- package/dist/artifact-linter/ship.d.ts +0 -2
- package/dist/artifact-linter/ship.js +0 -250
- package/dist/artifact-linter/spec.d.ts +0 -2
- package/dist/artifact-linter/spec.js +0 -176
- package/dist/artifact-linter/tdd.d.ts +0 -118
- package/dist/artifact-linter/tdd.js +0 -1404
- package/dist/artifact-linter.d.ts +0 -15
- package/dist/artifact-linter.js +0 -517
- package/dist/codex-feature-flag.d.ts +0 -58
- package/dist/codex-feature-flag.js +0 -193
- package/dist/content/closeout-guidance.d.ts +0 -14
- package/dist/content/closeout-guidance.js +0 -44
- package/dist/content/diff-command.d.ts +0 -1
- package/dist/content/diff-command.js +0 -43
- package/dist/content/harness-doc.d.ts +0 -1
- package/dist/content/harness-doc.js +0 -65
- package/dist/content/hook-events.d.ts +0 -9
- package/dist/content/hook-events.js +0 -23
- package/dist/content/hook-manifest.d.ts +0 -81
- package/dist/content/hook-manifest.js +0 -156
- package/dist/content/hooks.d.ts +0 -11
- package/dist/content/hooks.js +0 -1972
- package/dist/content/idea.d.ts +0 -60
- package/dist/content/idea.js +0 -416
- package/dist/content/language-policy.d.ts +0 -2
- package/dist/content/language-policy.js +0 -13
- package/dist/content/learnings.d.ts +0 -6
- package/dist/content/learnings.js +0 -141
- package/dist/content/observe.d.ts +0 -19
- package/dist/content/observe.js +0 -86
- package/dist/content/opencode-plugin.d.ts +0 -1
- package/dist/content/opencode-plugin.js +0 -635
- package/dist/content/review-prompts.d.ts +0 -1
- package/dist/content/review-prompts.js +0 -104
- package/dist/content/runtime-shared-snippets.d.ts +0 -8
- package/dist/content/runtime-shared-snippets.js +0 -80
- package/dist/content/session-hooks.d.ts +0 -7
- package/dist/content/session-hooks.js +0 -107
- package/dist/content/skills-elicitation.d.ts +0 -1
- package/dist/content/skills-elicitation.js +0 -167
- package/dist/content/stage-command.d.ts +0 -2
- package/dist/content/stage-command.js +0 -17
- package/dist/content/stage-schema.d.ts +0 -117
- package/dist/content/stage-schema.js +0 -955
- package/dist/content/stages/_lint-metadata/index.d.ts +0 -2
- package/dist/content/stages/_lint-metadata/index.js +0 -97
- package/dist/content/stages/brainstorm.d.ts +0 -2
- package/dist/content/stages/brainstorm.js +0 -184
- package/dist/content/stages/design.d.ts +0 -2
- package/dist/content/stages/design.js +0 -288
- package/dist/content/stages/index.d.ts +0 -8
- package/dist/content/stages/index.js +0 -11
- package/dist/content/stages/plan.d.ts +0 -2
- package/dist/content/stages/plan.js +0 -191
- package/dist/content/stages/review.d.ts +0 -2
- package/dist/content/stages/review.js +0 -240
- package/dist/content/stages/schema-types.d.ts +0 -203
- package/dist/content/stages/schema-types.js +0 -1
- package/dist/content/stages/scope.d.ts +0 -2
- package/dist/content/stages/scope.js +0 -254
- package/dist/content/stages/ship.d.ts +0 -2
- package/dist/content/stages/ship.js +0 -159
- package/dist/content/stages/spec.d.ts +0 -2
- package/dist/content/stages/spec.js +0 -170
- package/dist/content/stages/tdd.d.ts +0 -4
- package/dist/content/stages/tdd.js +0 -273
- package/dist/content/state-contracts.d.ts +0 -1
- package/dist/content/state-contracts.js +0 -63
- package/dist/content/status-command.d.ts +0 -4
- package/dist/content/status-command.js +0 -109
- package/dist/content/subagent-context-skills.d.ts +0 -4
- package/dist/content/subagent-context-skills.js +0 -279
- package/dist/content/subagents.d.ts +0 -3
- package/dist/content/subagents.js +0 -997
- package/dist/content/templates.d.ts +0 -26
- package/dist/content/templates.js +0 -1692
- package/dist/content/track-render-context.d.ts +0 -18
- package/dist/content/track-render-context.js +0 -53
- package/dist/content/tree-command.d.ts +0 -1
- package/dist/content/tree-command.js +0 -64
- package/dist/content/utility-skills.d.ts +0 -30
- package/dist/content/utility-skills.js +0 -160
- package/dist/content/view-command.d.ts +0 -2
- package/dist/content/view-command.js +0 -92
- package/dist/delegation.d.ts +0 -649
- package/dist/delegation.js +0 -1539
- package/dist/early-loop.d.ts +0 -70
- package/dist/early-loop.js +0 -302
- package/dist/execution-topology.d.ts +0 -44
- package/dist/execution-topology.js +0 -95
- package/dist/gate-evidence.d.ts +0 -85
- package/dist/gate-evidence.js +0 -631
- package/dist/harness-adapters.d.ts +0 -151
- package/dist/harness-adapters.js +0 -756
- package/dist/harness-selection.d.ts +0 -31
- package/dist/harness-selection.js +0 -214
- package/dist/hook-schema.d.ts +0 -6
- package/dist/hook-schema.js +0 -114
- package/dist/hook-schemas/claude-hooks.v1.json +0 -10
- package/dist/hook-schemas/codex-hooks.v1.json +0 -10
- package/dist/hook-schemas/cursor-hooks.v1.json +0 -13
- package/dist/init-detect.d.ts +0 -2
- package/dist/init-detect.js +0 -50
- package/dist/internal/advance-stage/advance.d.ts +0 -89
- package/dist/internal/advance-stage/advance.js +0 -655
- package/dist/internal/advance-stage/cancel-run.d.ts +0 -8
- package/dist/internal/advance-stage/cancel-run.js +0 -19
- package/dist/internal/advance-stage/flow-state-coercion.d.ts +0 -3
- package/dist/internal/advance-stage/flow-state-coercion.js +0 -81
- package/dist/internal/advance-stage/helpers.d.ts +0 -14
- package/dist/internal/advance-stage/helpers.js +0 -145
- package/dist/internal/advance-stage/hook.d.ts +0 -8
- package/dist/internal/advance-stage/hook.js +0 -40
- package/dist/internal/advance-stage/parsers.d.ts +0 -72
- package/dist/internal/advance-stage/parsers.js +0 -357
- package/dist/internal/advance-stage/proactive-delegation-trace.d.ts +0 -24
- package/dist/internal/advance-stage/proactive-delegation-trace.js +0 -56
- package/dist/internal/advance-stage/review-loop.d.ts +0 -16
- package/dist/internal/advance-stage/review-loop.js +0 -199
- package/dist/internal/advance-stage/rewind.d.ts +0 -14
- package/dist/internal/advance-stage/rewind.js +0 -108
- package/dist/internal/advance-stage/start-flow.d.ts +0 -13
- package/dist/internal/advance-stage/start-flow.js +0 -241
- package/dist/internal/advance-stage/verify.d.ts +0 -21
- package/dist/internal/advance-stage/verify.js +0 -185
- package/dist/internal/advance-stage.d.ts +0 -7
- package/dist/internal/advance-stage.js +0 -138
- package/dist/internal/cohesion-contract-stub.d.ts +0 -24
- package/dist/internal/cohesion-contract-stub.js +0 -148
- package/dist/internal/compound-readiness.d.ts +0 -23
- package/dist/internal/compound-readiness.js +0 -102
- package/dist/internal/detect-public-api-changes.d.ts +0 -5
- package/dist/internal/detect-public-api-changes.js +0 -45
- package/dist/internal/detect-supply-chain-changes.d.ts +0 -6
- package/dist/internal/detect-supply-chain-changes.js +0 -138
- package/dist/internal/early-loop-status.d.ts +0 -7
- package/dist/internal/early-loop-status.js +0 -93
- package/dist/internal/envelope-validate.d.ts +0 -7
- package/dist/internal/envelope-validate.js +0 -66
- package/dist/internal/flow-state-repair.d.ts +0 -20
- package/dist/internal/flow-state-repair.js +0 -104
- package/dist/internal/plan-split-waves.d.ts +0 -190
- package/dist/internal/plan-split-waves.js +0 -764
- package/dist/internal/runtime-integrity.d.ts +0 -7
- package/dist/internal/runtime-integrity.js +0 -268
- package/dist/internal/slice-commit.d.ts +0 -7
- package/dist/internal/slice-commit.js +0 -619
- package/dist/internal/tdd-loop-status.d.ts +0 -14
- package/dist/internal/tdd-loop-status.js +0 -68
- package/dist/internal/tdd-red-evidence.d.ts +0 -7
- package/dist/internal/tdd-red-evidence.js +0 -153
- package/dist/internal/waiver-grant.d.ts +0 -62
- package/dist/internal/waiver-grant.js +0 -294
- package/dist/internal/wave-status.d.ts +0 -74
- package/dist/internal/wave-status.js +0 -506
- package/dist/managed-resources.d.ts +0 -53
- package/dist/managed-resources.js +0 -313
- package/dist/policy.d.ts +0 -10
- package/dist/policy.js +0 -167
- package/dist/retro-gate.d.ts +0 -9
- package/dist/retro-gate.js +0 -47
- package/dist/run-archive.d.ts +0 -61
- package/dist/run-archive.js +0 -391
- package/dist/runs.d.ts +0 -2
- package/dist/runs.js +0 -2
- package/dist/stack-detection.d.ts +0 -116
- package/dist/stack-detection.js +0 -489
- package/dist/streaming/event-stream.d.ts +0 -31
- package/dist/streaming/event-stream.js +0 -114
- package/dist/tdd-cycle.d.ts +0 -107
- package/dist/tdd-cycle.js +0 -289
- package/dist/tdd-verification-evidence.d.ts +0 -17
- package/dist/tdd-verification-evidence.js +0 -122
- package/dist/track-heuristics.d.ts +0 -27
- package/dist/track-heuristics.js +0 -154
- package/dist/util/slice-id.d.ts +0 -58
- package/dist/util/slice-id.js +0 -89
- package/dist/worktree-manager.d.ts +0 -20
- package/dist/worktree-manager.js +0 -108
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { FlowStage, FlowTrack } from "./types.js";
|
|
2
|
-
import { type LintResult } from "./artifact-linter/shared.js";
|
|
3
|
-
export { validateReviewArmy, checkReviewVerdictConsistency, checkReviewSecurityNoChangeAttestation, checkReviewTddNoCrossArtifactDuplication, type ReviewVerdictConsistencyResult, type ReviewSecurityNoChangeAttestationResult, type ReviewTddDuplicationConflict, type ReviewTddDuplicationResult } from "./artifact-linter/review-army.js";
|
|
4
|
-
export { type LintFinding, type LintResult, type LearningEntryType, type LearningConfidence, type LearningSeverity, type LearningSource, type LearningSeedEntry, type LearningsParseResult, extractAuthoredBody, formatLearningsErrorsBullets, learningsParseFailureHumanSummary, extractMarkdownSectionBody, parseLearningsSection } from "./artifact-linter/shared.js";
|
|
5
|
-
export interface LintArtifactOptions {
|
|
6
|
-
/**
|
|
7
|
-
* Stage-level flags supplied by the caller (typically `advance-stage`)
|
|
8
|
-
* that augment whatever flow-state.json says. Used so the linter sees
|
|
9
|
-
* `--skip-questions` even before flow-state is updated for the current
|
|
10
|
-
* stage (advance-stage applies the hint to the successor stage only,
|
|
11
|
-
* but the linter must respect the current-call intent).
|
|
12
|
-
*/
|
|
13
|
-
extraStageFlags?: string[];
|
|
14
|
-
}
|
|
15
|
-
export declare function lintArtifact(projectRoot: string, stage: FlowStage, track?: FlowTrack, options?: LintArtifactOptions): Promise<LintResult>;
|
package/dist/artifact-linter.js
DELETED
|
@@ -1,517 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { resolveArtifactPath as resolveStageArtifactPath } from "./artifact-paths.js";
|
|
4
|
-
import { exists } from "./fs-utils.js";
|
|
5
|
-
import { stageSchema } from "./content/stage-schema.js";
|
|
6
|
-
import { readFlowState } from "./run-persistence.js";
|
|
7
|
-
import { duplicateH2Headings, extractEvidencePointers, extractH2Sections, extractRequirementIdsFromMarkdown, isShortCircuitActivated, normalizeHeadingTitle, parseFrontmatter, parseLearningsSection, sectionBodyByAnyName, sectionBodyByHeadingPrefix, sectionBodyByName, validateSectionBody, formatLearningsErrorsBullets } from "./artifact-linter/shared.js";
|
|
8
|
-
import { shouldDemoteArtifactValidationByTrack } from "./content/stage-schema.js";
|
|
9
|
-
import { readDelegationLedger, recordArtifactValidationDemotedByTrack } from "./delegation.js";
|
|
10
|
-
import { classifyAndPersistFindings } from "./artifact-linter/findings-dedup.js";
|
|
11
|
-
import { lintBrainstormStage } from "./artifact-linter/brainstorm.js";
|
|
12
|
-
import { lintDesignStage } from "./artifact-linter/design.js";
|
|
13
|
-
import { lintPlanStage } from "./artifact-linter/plan.js";
|
|
14
|
-
import { lintScopeStage } from "./artifact-linter/scope.js";
|
|
15
|
-
import { lintSpecStage } from "./artifact-linter/spec.js";
|
|
16
|
-
import { lintTddStage } from "./artifact-linter/tdd.js";
|
|
17
|
-
import { lintReviewStage } from "./artifact-linter/review.js";
|
|
18
|
-
import { lintShipStage } from "./artifact-linter/ship.js";
|
|
19
|
-
export { validateReviewArmy, checkReviewVerdictConsistency, checkReviewSecurityNoChangeAttestation, checkReviewTddNoCrossArtifactDuplication } from "./artifact-linter/review-army.js";
|
|
20
|
-
export { extractAuthoredBody, formatLearningsErrorsBullets, learningsParseFailureHumanSummary, extractMarkdownSectionBody, parseLearningsSection } from "./artifact-linter/shared.js";
|
|
21
|
-
const FRONTMATTER_REQUIRED_KEYS = [
|
|
22
|
-
"stage",
|
|
23
|
-
"schema_version",
|
|
24
|
-
"version",
|
|
25
|
-
"locked_decisions",
|
|
26
|
-
"inputs_hash"
|
|
27
|
-
];
|
|
28
|
-
export async function lintArtifact(projectRoot, stage, track = "standard", options = {}) {
|
|
29
|
-
const schema = stageSchema(stage, track);
|
|
30
|
-
const { absPath: absFile, relPath: relFile } = await resolveStageArtifactPath(stage, {
|
|
31
|
-
projectRoot,
|
|
32
|
-
track,
|
|
33
|
-
intent: "read"
|
|
34
|
-
});
|
|
35
|
-
const findings = [];
|
|
36
|
-
if (!(await exists(absFile))) {
|
|
37
|
-
for (const v of schema.artifactValidation) {
|
|
38
|
-
findings.push({
|
|
39
|
-
section: v.section,
|
|
40
|
-
required: v.required,
|
|
41
|
-
rule: v.validationRule,
|
|
42
|
-
found: false,
|
|
43
|
-
details: `Artifact file missing: ${relFile}`
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
return {
|
|
47
|
-
stage,
|
|
48
|
-
file: relFile,
|
|
49
|
-
passed: schema.artifactValidation.every((v) => !v.required),
|
|
50
|
-
findings
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
const raw = await fs.readFile(absFile, "utf8");
|
|
54
|
-
const sections = extractH2Sections(raw);
|
|
55
|
-
const duplicateHeadings = duplicateH2Headings(raw);
|
|
56
|
-
if (duplicateHeadings.length > 0) {
|
|
57
|
-
findings.push({
|
|
58
|
-
section: "duplicate_h2_heading",
|
|
59
|
-
required: false,
|
|
60
|
-
rule: "[P3] keep each `##` heading unique within an artifact; append updates to the existing section instead of cloning headings.",
|
|
61
|
-
found: false,
|
|
62
|
-
details: `Duplicate H2 heading(s): ${duplicateHeadings.join(", ")}. Merge edits into the existing heading to avoid split contracts.`
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
const parsedFrontmatter = parseFrontmatter(raw);
|
|
66
|
-
const frontmatterMissingKeys = FRONTMATTER_REQUIRED_KEYS.filter((key) => {
|
|
67
|
-
const value = parsedFrontmatter.values[key];
|
|
68
|
-
return typeof value !== "string" || value.trim().length === 0;
|
|
69
|
-
});
|
|
70
|
-
if (parsedFrontmatter.hasFrontmatter &&
|
|
71
|
-
typeof parsedFrontmatter.values.run !== "string" &&
|
|
72
|
-
typeof parsedFrontmatter.values.feature !== "string") {
|
|
73
|
-
frontmatterMissingKeys.push("run");
|
|
74
|
-
}
|
|
75
|
-
const frontmatterStage = parsedFrontmatter.values.stage?.replace(/^['"]|['"]$/gu, "");
|
|
76
|
-
const frontmatterSchemaVersion = parsedFrontmatter.values.schema_version?.replace(/^['"]|['"]$/gu, "");
|
|
77
|
-
const frontmatterInputsHash = parsedFrontmatter.values.inputs_hash?.replace(/^['"]|['"]$/gu, "");
|
|
78
|
-
const frontmatterValid = parsedFrontmatter.hasFrontmatter &&
|
|
79
|
-
frontmatterMissingKeys.length === 0 &&
|
|
80
|
-
frontmatterStage === stage &&
|
|
81
|
-
frontmatterSchemaVersion === "1" &&
|
|
82
|
-
/^sha256:(?:pending|[a-f0-9]{64})$/iu.test(frontmatterInputsHash ?? "");
|
|
83
|
-
const requireFrontmatter = parsedFrontmatter.hasFrontmatter;
|
|
84
|
-
findings.push({
|
|
85
|
-
section: "Frontmatter",
|
|
86
|
-
required: requireFrontmatter,
|
|
87
|
-
rule: "Artifact must include frontmatter keys (stage, schema_version=1, version, run, locked_decisions, inputs_hash=sha256:pending|sha256:<64hex>). Legacy feature is accepted during migration.",
|
|
88
|
-
found: parsedFrontmatter.hasFrontmatter ? frontmatterValid : true,
|
|
89
|
-
details: !parsedFrontmatter.hasFrontmatter
|
|
90
|
-
? "Legacy artifact without YAML frontmatter (allowed for backward compatibility)."
|
|
91
|
-
: frontmatterMissingKeys.length > 0
|
|
92
|
-
? `Frontmatter missing required key(s): ${frontmatterMissingKeys.join(", ")}.`
|
|
93
|
-
: frontmatterStage !== stage
|
|
94
|
-
? `Frontmatter stage must be "${stage}" (found "${frontmatterStage ?? "(missing)"}").`
|
|
95
|
-
: frontmatterSchemaVersion !== "1"
|
|
96
|
-
? `Frontmatter schema_version must be "1" (found "${frontmatterSchemaVersion ?? "(missing)"}").`
|
|
97
|
-
: !/^sha256:(?:pending|[a-f0-9]{64})$/iu.test(frontmatterInputsHash ?? "")
|
|
98
|
-
? "Frontmatter inputs_hash must be sha256:pending or sha256:<64 hex chars>."
|
|
99
|
-
: "Frontmatter integrity checks passed."
|
|
100
|
-
});
|
|
101
|
-
const brainstormShortCircuitBody = stage === "brainstorm" ? sectionBodyByName(sections, "Short-Circuit Decision") : null;
|
|
102
|
-
const brainstormShortCircuitActivated = stage === "brainstorm" && isShortCircuitActivated(brainstormShortCircuitBody);
|
|
103
|
-
const scopePreAuditEnabled = true;
|
|
104
|
-
const staleDiagramAuditEnabled = true;
|
|
105
|
-
const isTrivialOverride = Boolean(schema.trivialOverrideSections &&
|
|
106
|
-
schema.trivialOverrideSections.length > 0 &&
|
|
107
|
-
(/trivial.change|mini.design|escape.hatch/iu.test(raw) ||
|
|
108
|
-
brainstormShortCircuitActivated));
|
|
109
|
-
const overrideSet = isTrivialOverride
|
|
110
|
-
? new Set(schema.trivialOverrideSections.map((s) => normalizeHeadingTitle(s).toLowerCase()))
|
|
111
|
-
: null;
|
|
112
|
-
// Precompute the lite-tier signal so the per-section
|
|
113
|
-
// validators (Interaction Edge Case matrix today, others tomorrow)
|
|
114
|
-
// can relax network-dependent mandatory rows for lite/quick/bugfix
|
|
115
|
-
// runs without each validator having to re-derive the predicate.
|
|
116
|
-
// Same flow-state read powers the post-loop demotion + audit log
|
|
117
|
-
// below; we cache the result here to avoid two disk reads.
|
|
118
|
-
let activeStageFlags = [];
|
|
119
|
-
let discoveryMode = "guided";
|
|
120
|
-
let taskClass = null;
|
|
121
|
-
let packageVersion;
|
|
122
|
-
let activeRunId = null;
|
|
123
|
-
let completedStagesForAudit = [];
|
|
124
|
-
let completedStageMetaForAudit;
|
|
125
|
-
try {
|
|
126
|
-
const flowState = await readFlowState(projectRoot);
|
|
127
|
-
const hint = flowState.interactionHints?.[stage];
|
|
128
|
-
if (hint?.skipQuestions === true)
|
|
129
|
-
activeStageFlags.push("--skip-questions");
|
|
130
|
-
discoveryMode = flowState.discoveryMode ?? "guided";
|
|
131
|
-
taskClass = flowState.taskClass ?? null;
|
|
132
|
-
packageVersion = flowState.packageVersion;
|
|
133
|
-
activeRunId = flowState.activeRunId ?? null;
|
|
134
|
-
completedStagesForAudit = flowState.completedStages;
|
|
135
|
-
completedStageMetaForAudit = flowState.completedStageMeta;
|
|
136
|
-
}
|
|
137
|
-
catch {
|
|
138
|
-
activeStageFlags = [];
|
|
139
|
-
discoveryMode = "guided";
|
|
140
|
-
taskClass = null;
|
|
141
|
-
packageVersion = undefined;
|
|
142
|
-
activeRunId = null;
|
|
143
|
-
completedStagesForAudit = [];
|
|
144
|
-
completedStageMetaForAudit = undefined;
|
|
145
|
-
}
|
|
146
|
-
for (const extra of options.extraStageFlags ?? []) {
|
|
147
|
-
if (typeof extra === "string" && extra.length > 0 && !activeStageFlags.includes(extra)) {
|
|
148
|
-
activeStageFlags.push(extra);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
const liteTierForValidators = shouldDemoteArtifactValidationByTrack(track, taskClass);
|
|
152
|
-
// pre-resolve RED/GREEN Evidence pointers AND
|
|
153
|
-
// delegation phase events so `validateSectionBody` (sync) can
|
|
154
|
-
// short-circuit. The Evidence: pointer mode (T3) stays as a
|
|
155
|
-
// fallback alongside legacy markdown content; phase events with a
|
|
156
|
-
// `phase=red`/`phase=green` row plus non-empty evidenceRefs auto-pass
|
|
157
|
-
// the corresponding markdown validator.
|
|
158
|
-
const tddEvidenceContext = stage === "tdd"
|
|
159
|
-
? await resolveTddEvidencePointerContext({
|
|
160
|
-
projectRoot,
|
|
161
|
-
sections
|
|
162
|
-
})
|
|
163
|
-
: { red: {}, green: {} };
|
|
164
|
-
for (const v of schema.artifactValidation) {
|
|
165
|
-
const sectionKey = normalizeHeadingTitle(v.section).toLowerCase();
|
|
166
|
-
const scopeBoundaryAlias = stage === "scope" && sectionKey === "in scope / out of scope";
|
|
167
|
-
const body = scopeBoundaryAlias
|
|
168
|
-
? sectionBodyByAnyName(sections, ["In Scope / Out of Scope", "In Scope", "Out of Scope"])
|
|
169
|
-
: sectionBodyByName(sections, v.section);
|
|
170
|
-
const hasHeading = body !== null;
|
|
171
|
-
const effectiveRequiredFromOverride = overrideSet
|
|
172
|
-
? overrideSet.has(sectionKey) ? true : false
|
|
173
|
-
: v.required;
|
|
174
|
-
const effectiveRequired = stage === "design" && sectionKey === "data flow" && hasHeading
|
|
175
|
-
? true
|
|
176
|
-
: stage === "scope" && sectionKey === "pre-scope system audit" && scopePreAuditEnabled
|
|
177
|
-
? true
|
|
178
|
-
: effectiveRequiredFromOverride;
|
|
179
|
-
const validation = body === null
|
|
180
|
-
? { ok: false, details: `No ## heading matching required section "${v.section}".` }
|
|
181
|
-
: validateSectionBody(body, v.validationRule, v.section, {
|
|
182
|
-
sections,
|
|
183
|
-
liteTier: liteTierForValidators,
|
|
184
|
-
tddEvidence: stage === "tdd" ? tddEvidenceContext : undefined
|
|
185
|
-
});
|
|
186
|
-
const found = hasHeading && validation.ok;
|
|
187
|
-
findings.push({
|
|
188
|
-
section: v.section,
|
|
189
|
-
required: effectiveRequired,
|
|
190
|
-
rule: v.validationRule,
|
|
191
|
-
found,
|
|
192
|
-
details: found
|
|
193
|
-
? validation.details
|
|
194
|
-
: validation.details
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
const learningsBody = sectionBodyByName(sections, "Learnings");
|
|
198
|
-
const requireLearnings = parsedFrontmatter.hasFrontmatter;
|
|
199
|
-
if (learningsBody === null) {
|
|
200
|
-
findings.push({
|
|
201
|
-
section: "Learnings",
|
|
202
|
-
required: requireLearnings,
|
|
203
|
-
rule: "Required for schema-v1 artifacts: include `## Learnings` with bullets of strict JSON objects compatible with knowledge.jsonl schema, or a single `- None this stage.` sentinel.",
|
|
204
|
-
found: false,
|
|
205
|
-
details: "No ## heading matching required section \"Learnings\"."
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
else {
|
|
209
|
-
const learnings = parseLearningsSection(learningsBody);
|
|
210
|
-
const meaningfulStageNoneWarning = learnings.ok && learnings.none && ["design", "tdd", "review"].includes(stage)
|
|
211
|
-
? " Warning: design/tdd/review usually produce reusable decisions, test patterns, or review lessons; keep `None this stage` only for truly mechanical work."
|
|
212
|
-
: "";
|
|
213
|
-
const learningsErrorBlock = !learnings.ok && learnings.errors.length > 0
|
|
214
|
-
? `\n${formatLearningsErrorsBullets(learnings.errors)}`
|
|
215
|
-
: "";
|
|
216
|
-
findings.push({
|
|
217
|
-
section: "Learnings",
|
|
218
|
-
required: requireLearnings,
|
|
219
|
-
rule: "`## Learnings` must contain either a single `- None this stage.` bullet or JSON bullets compatible with knowledge.jsonl fields (type/trigger/action/confidence required).",
|
|
220
|
-
found: learnings.ok,
|
|
221
|
-
details: `${learnings.details}${learningsErrorBlock}${meaningfulStageNoneWarning}`
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
for (const doneStage of completedStagesForAudit) {
|
|
225
|
-
const completionIso = completedStageMetaForAudit?.[doneStage]?.completedAt;
|
|
226
|
-
if (!completionIso)
|
|
227
|
-
continue;
|
|
228
|
-
const completedMs = Date.parse(completionIso);
|
|
229
|
-
if (!Number.isFinite(completedMs))
|
|
230
|
-
continue;
|
|
231
|
-
try {
|
|
232
|
-
const resolvedDone = await resolveStageArtifactPath(doneStage, {
|
|
233
|
-
projectRoot,
|
|
234
|
-
track,
|
|
235
|
-
intent: "read"
|
|
236
|
-
});
|
|
237
|
-
if (!(await exists(resolvedDone.absPath)))
|
|
238
|
-
continue;
|
|
239
|
-
const artifactStat = await fs.stat(resolvedDone.absPath);
|
|
240
|
-
if (artifactStat.mtimeMs <= completedMs)
|
|
241
|
-
continue;
|
|
242
|
-
const priorRaw = await fs.readFile(resolvedDone.absPath, "utf8");
|
|
243
|
-
const priorSections = extractH2Sections(priorRaw);
|
|
244
|
-
const amendBody = sectionBodyByName(priorSections, "Amendments");
|
|
245
|
-
const trimmedAmend = amendBody === null
|
|
246
|
-
? ""
|
|
247
|
-
: amendBody.replace(/<!--[\s\S]*?-->/gu, "").replace(/\s+/gu, " ").trim();
|
|
248
|
-
if (trimmedAmend.length > 0)
|
|
249
|
-
continue;
|
|
250
|
-
findings.push({
|
|
251
|
-
section: "stage_artifact_post_closure_mutation",
|
|
252
|
-
required: false,
|
|
253
|
-
rule: "stage_artifact_post_closure_mutation — substantive post-closure edit without `## Amendments` (advisory)",
|
|
254
|
-
found: false,
|
|
255
|
-
details: `Completed stage "${doneStage}" snapshot closed at ${completionIso}, but ${resolvedDone.relPath} has a newer mtime without nonempty \`## Amendments\`. ` +
|
|
256
|
-
"Append dated bullets describing each drift fix, or restore the archived copy."
|
|
257
|
-
});
|
|
258
|
-
}
|
|
259
|
-
catch {
|
|
260
|
-
continue;
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
const stageContext = {
|
|
264
|
-
projectRoot,
|
|
265
|
-
stage,
|
|
266
|
-
track,
|
|
267
|
-
discoveryMode,
|
|
268
|
-
raw,
|
|
269
|
-
absFile,
|
|
270
|
-
sections,
|
|
271
|
-
findings,
|
|
272
|
-
parsedFrontmatter,
|
|
273
|
-
brainstormShortCircuitBody,
|
|
274
|
-
brainstormShortCircuitActivated,
|
|
275
|
-
scopePreAuditEnabled,
|
|
276
|
-
staleDiagramAuditEnabled,
|
|
277
|
-
isTrivialOverride,
|
|
278
|
-
overrideSet,
|
|
279
|
-
activeStageFlags,
|
|
280
|
-
taskClass,
|
|
281
|
-
packageVersion
|
|
282
|
-
};
|
|
283
|
-
switch (stage) {
|
|
284
|
-
case "brainstorm":
|
|
285
|
-
await lintBrainstormStage(stageContext);
|
|
286
|
-
break;
|
|
287
|
-
case "design":
|
|
288
|
-
await lintDesignStage(stageContext);
|
|
289
|
-
break;
|
|
290
|
-
case "plan":
|
|
291
|
-
await lintPlanStage(stageContext);
|
|
292
|
-
break;
|
|
293
|
-
case "scope":
|
|
294
|
-
await lintScopeStage(stageContext);
|
|
295
|
-
break;
|
|
296
|
-
case "spec":
|
|
297
|
-
await lintSpecStage(stageContext);
|
|
298
|
-
break;
|
|
299
|
-
case "tdd":
|
|
300
|
-
await lintTddStage(stageContext);
|
|
301
|
-
break;
|
|
302
|
-
case "review":
|
|
303
|
-
await lintReviewStage(stageContext);
|
|
304
|
-
break;
|
|
305
|
-
case "ship":
|
|
306
|
-
await lintShipStage(stageContext);
|
|
307
|
-
break;
|
|
308
|
-
default:
|
|
309
|
-
break;
|
|
310
|
-
}
|
|
311
|
-
if (["design", "spec", "plan", "review"].includes(stage)) {
|
|
312
|
-
const scopeArtifact = await resolveStageArtifactPath("scope", {
|
|
313
|
-
projectRoot,
|
|
314
|
-
track,
|
|
315
|
-
intent: "read"
|
|
316
|
-
});
|
|
317
|
-
if (await exists(scopeArtifact.absPath)) {
|
|
318
|
-
const scopeRaw = await fs.readFile(scopeArtifact.absPath, "utf8");
|
|
319
|
-
const scopeSections = extractH2Sections(scopeRaw);
|
|
320
|
-
const requirementsBody = sectionBodyByHeadingPrefix(scopeSections, "Requirements") ?? "";
|
|
321
|
-
const lockedDecisionsBody = sectionBodyByHeadingPrefix(scopeSections, "Locked Decisions") ?? "";
|
|
322
|
-
const requirementIds = extractRequirementIdsFromMarkdown(requirementsBody);
|
|
323
|
-
const decisionIds = Array.from(new Set((lockedDecisionsBody.match(/\bD-\d+\b/giu) ?? []).map((id) => id.toUpperCase())));
|
|
324
|
-
const missingRequirementRefs = requirementIds.filter((id) => !raw.includes(id));
|
|
325
|
-
const missingDecisionRefs = decisionIds.filter((id) => !raw.toUpperCase().includes(id));
|
|
326
|
-
findings.push({
|
|
327
|
-
section: "Scope Requirement Reference Integrity",
|
|
328
|
-
required: requirementIds.length > 0,
|
|
329
|
-
rule: "Every R# requirement ID from scope must be referenced by downstream artifacts.",
|
|
330
|
-
found: missingRequirementRefs.length === 0,
|
|
331
|
-
details: requirementIds.length === 0
|
|
332
|
-
? "No R# requirement IDs found in scope artifact; reference check skipped."
|
|
333
|
-
: missingRequirementRefs.length === 0
|
|
334
|
-
? `All ${requirementIds.length} scope requirement ID(s) are referenced.`
|
|
335
|
-
: `Missing scope requirement reference(s): ${missingRequirementRefs.join(", ")}.`
|
|
336
|
-
});
|
|
337
|
-
findings.push({
|
|
338
|
-
section: "Locked Decision Reference Integrity",
|
|
339
|
-
required: decisionIds.length > 0,
|
|
340
|
-
rule: "Every D-XX locked decision ID from scope must be referenced by downstream artifacts.",
|
|
341
|
-
found: missingDecisionRefs.length === 0,
|
|
342
|
-
details: decisionIds.length === 0
|
|
343
|
-
? "No D-XX decision IDs found in scope artifact; reference check skipped."
|
|
344
|
-
: missingDecisionRefs.length === 0
|
|
345
|
-
? `All ${decisionIds.length} locked decision ID(s) are referenced.`
|
|
346
|
-
: `Missing locked decision reference(s): ${missingDecisionRefs.join(", ")}.`
|
|
347
|
-
});
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
try {
|
|
351
|
-
const delegationLedger = await readDelegationLedger(projectRoot);
|
|
352
|
-
const legacyWaivers = delegationLedger.entries.filter((entry) => entry.status === "waived" &&
|
|
353
|
-
entry.mode === "proactive" &&
|
|
354
|
-
entry.stage === stage &&
|
|
355
|
-
(typeof entry.approvalToken !== "string" || entry.approvalToken.trim().length === 0));
|
|
356
|
-
if (legacyWaivers.length > 0) {
|
|
357
|
-
const descriptors = legacyWaivers
|
|
358
|
-
.map((entry) => [entry.agent, entry.spanId].filter((value) => typeof value === "string").join("@"))
|
|
359
|
-
.filter((value) => value.length > 0);
|
|
360
|
-
findings.push({
|
|
361
|
-
section: "waiver_legacy_provenance",
|
|
362
|
-
required: false,
|
|
363
|
-
rule: "waiver_legacy_provenance — proactive waiver(s) without approvalToken. Issue new waivers via `cclaw-cli internal waiver-grant --stage <stage> --reason <slug>` so the provenance trail is signed. Legacy waivers remain valid (advisory).",
|
|
364
|
-
found: false,
|
|
365
|
-
details: `Found ${legacyWaivers.length} proactive waiver(s) on stage="${stage}" without approvalToken` +
|
|
366
|
-
(descriptors.length > 0 ? ` (${descriptors.join(", ")})` : "") +
|
|
367
|
-
". Next waiver should be issued with `cclaw-cli internal waiver-grant` and consumed via `--accept-proactive-waiver=<token>`."
|
|
368
|
-
});
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
catch {
|
|
372
|
-
// Ledger absent or unreadable: no advisory to emit.
|
|
373
|
-
}
|
|
374
|
-
const demote = shouldDemoteArtifactValidationByTrack(track, taskClass);
|
|
375
|
-
const demotedSections = [];
|
|
376
|
-
if (demote) {
|
|
377
|
-
for (const finding of findings) {
|
|
378
|
-
if (!ARTIFACT_VALIDATION_LITE_DEMOTE_SECTIONS.has(finding.section))
|
|
379
|
-
continue;
|
|
380
|
-
if (finding.found)
|
|
381
|
-
continue;
|
|
382
|
-
if (!finding.required)
|
|
383
|
-
continue;
|
|
384
|
-
finding.required = false;
|
|
385
|
-
finding.details =
|
|
386
|
-
`${finding.details} (demoted to advisory by track="${track}"` +
|
|
387
|
-
(taskClass ? `, taskClass="${taskClass}"` : "") +
|
|
388
|
-
").";
|
|
389
|
-
demotedSections.push(finding.section);
|
|
390
|
-
}
|
|
391
|
-
if (demotedSections.length > 0 && activeRunId) {
|
|
392
|
-
await recordArtifactValidationDemotedByTrack(projectRoot, {
|
|
393
|
-
stage,
|
|
394
|
-
track,
|
|
395
|
-
taskClass: taskClass ?? null,
|
|
396
|
-
runId: activeRunId,
|
|
397
|
-
sections: demotedSections
|
|
398
|
-
}).catch(() => { });
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
const passed = findings.every((f) => !f.required || f.found);
|
|
402
|
-
let dedup;
|
|
403
|
-
try {
|
|
404
|
-
const dedupResult = await classifyAndPersistFindings(projectRoot, stage, findings);
|
|
405
|
-
const statusByFingerprint = new Map(dedupResult.classified.map(({ fingerprint, status }) => [fingerprint, status]));
|
|
406
|
-
const statuses = dedupResult.classified.map(({ status }) => status);
|
|
407
|
-
void statusByFingerprint;
|
|
408
|
-
dedup = {
|
|
409
|
-
newCount: dedupResult.summary.newCount,
|
|
410
|
-
repeatCount: dedupResult.summary.repeatCount,
|
|
411
|
-
resolvedCount: dedupResult.summary.resolvedCount,
|
|
412
|
-
header: dedupResult.header,
|
|
413
|
-
statuses
|
|
414
|
-
};
|
|
415
|
-
}
|
|
416
|
-
catch {
|
|
417
|
-
dedup = undefined;
|
|
418
|
-
}
|
|
419
|
-
return { stage, file: relFile, passed, findings, ...(dedup ? { dedup } : {}) };
|
|
420
|
-
}
|
|
421
|
-
/**
|
|
422
|
-
* section names whose required-finding outcome is
|
|
423
|
-
* demoted from blocking → advisory when
|
|
424
|
-
* `shouldDemoteArtifactValidationByTrack(track, taskClass)` returns
|
|
425
|
-
* `true`. Mirrors the user-reported quick-tier failure modes:
|
|
426
|
-
*
|
|
427
|
-
* - `Architecture Diagram` — sync/async + failure-edge enforcement
|
|
428
|
-
* - `Data Flow` — Interaction Edge Case mandatory rows
|
|
429
|
-
* - `Stale Diagram Drift Check` — blast-radius file mtime audit
|
|
430
|
-
* - `Product Discovery Delegation (Strategist Mode)` — product-discovery delegation
|
|
431
|
-
*
|
|
432
|
-
* Findings remain in the result so the caller can surface them as
|
|
433
|
-
* advisory hints; only `required` flips to `false`.
|
|
434
|
-
*/
|
|
435
|
-
const ARTIFACT_VALIDATION_LITE_DEMOTE_SECTIONS = new Set([
|
|
436
|
-
"Architecture Diagram",
|
|
437
|
-
"Data Flow",
|
|
438
|
-
"Stale Diagram Drift Check",
|
|
439
|
-
"Product Discovery Delegation (Strategist Mode)"
|
|
440
|
-
]);
|
|
441
|
-
/**
|
|
442
|
-
* pre-resolve `Evidence:` pointers and delegation
|
|
443
|
-
* phase-event auto-satisfy state for the TDD stage's RED/GREEN
|
|
444
|
-
* Evidence rows so `validateSectionBody` (sync) can short-circuit.
|
|
445
|
-
*
|
|
446
|
-
* - `<path>` pointer is satisfied when the path exists on disk relative
|
|
447
|
-
* to the project root.
|
|
448
|
-
* - `spanId:<id>` pointer is satisfied when any delegation ledger row
|
|
449
|
-
* carries that span id.
|
|
450
|
-
* - Phase-event auto-satisfy fires when `delegation-events.jsonl`
|
|
451
|
-
* carries at least one slice-tagged event for the active run with
|
|
452
|
-
* `phase=red`/`phase=green` and non-empty `evidenceRefs`. This is the
|
|
453
|
-
replacement for the sidecar auto-satisfy hook —
|
|
454
|
-
* slice events are now the source of truth, the RED/GREEN markdown
|
|
455
|
-
* tables are auto-rendered from them, and the validators MUST NOT
|
|
456
|
-
* demand pasted stdout when the events already prove RED/GREEN.
|
|
457
|
-
*/
|
|
458
|
-
async function resolveTddEvidencePointerContext(input) {
|
|
459
|
-
const { projectRoot, sections } = input;
|
|
460
|
-
const redSection = sectionBodyByName(sections, "RED Evidence") ?? "";
|
|
461
|
-
const greenSection = sectionBodyByName(sections, "GREEN Evidence") ?? "";
|
|
462
|
-
const redPointers = extractEvidencePointers(redSection);
|
|
463
|
-
const greenPointers = extractEvidencePointers(greenSection);
|
|
464
|
-
let knownSpanIds = new Set();
|
|
465
|
-
let phaseEventsAutoSatisfy = { red: false, green: false };
|
|
466
|
-
try {
|
|
467
|
-
const ledger = await readDelegationLedger(projectRoot);
|
|
468
|
-
knownSpanIds = new Set(ledger.entries
|
|
469
|
-
.map((entry) => entry.spanId)
|
|
470
|
-
.filter((id) => typeof id === "string" && id.length > 0));
|
|
471
|
-
const runId = ledger.runId;
|
|
472
|
-
const slicePhaseRows = ledger.entries.filter((entry) => entry.runId === runId &&
|
|
473
|
-
entry.stage === "tdd" &&
|
|
474
|
-
typeof entry.sliceId === "string" &&
|
|
475
|
-
entry.sliceId.length > 0 &&
|
|
476
|
-
typeof entry.phase === "string");
|
|
477
|
-
const redOk = slicePhaseRows.some((entry) => entry.phase === "red" &&
|
|
478
|
-
Array.isArray(entry.evidenceRefs) &&
|
|
479
|
-
entry.evidenceRefs.some((ref) => typeof ref === "string" && ref.trim().length > 0));
|
|
480
|
-
const greenOk = slicePhaseRows.some((entry) => entry.phase === "green" &&
|
|
481
|
-
Array.isArray(entry.evidenceRefs) &&
|
|
482
|
-
entry.evidenceRefs.some((ref) => typeof ref === "string" && ref.trim().length > 0));
|
|
483
|
-
phaseEventsAutoSatisfy = { red: redOk, green: greenOk };
|
|
484
|
-
}
|
|
485
|
-
catch {
|
|
486
|
-
knownSpanIds = new Set();
|
|
487
|
-
phaseEventsAutoSatisfy = { red: false, green: false };
|
|
488
|
-
}
|
|
489
|
-
async function pointerResolves(value) {
|
|
490
|
-
const trimmed = value.replace(/[`*_]/gu, "").trim();
|
|
491
|
-
if (trimmed.length === 0)
|
|
492
|
-
return false;
|
|
493
|
-
if (/^spanid\s*:/iu.test(trimmed)) {
|
|
494
|
-
const id = trimmed.replace(/^spanid\s*:\s*/iu, "").trim();
|
|
495
|
-
return id.length > 0 && knownSpanIds.has(id);
|
|
496
|
-
}
|
|
497
|
-
const candidate = path.isAbsolute(trimmed) ? trimmed : path.join(projectRoot, trimmed);
|
|
498
|
-
return exists(candidate);
|
|
499
|
-
}
|
|
500
|
-
async function anyResolved(values) {
|
|
501
|
-
for (const value of values) {
|
|
502
|
-
if (await pointerResolves(value))
|
|
503
|
-
return true;
|
|
504
|
-
}
|
|
505
|
-
return false;
|
|
506
|
-
}
|
|
507
|
-
return {
|
|
508
|
-
red: {
|
|
509
|
-
pointerSatisfied: await anyResolved(redPointers),
|
|
510
|
-
phaseEventsSatisfied: phaseEventsAutoSatisfy.red
|
|
511
|
-
},
|
|
512
|
-
green: {
|
|
513
|
-
pointerSatisfied: await anyResolved(greenPointers),
|
|
514
|
-
phaseEventsSatisfied: phaseEventsAutoSatisfy.green
|
|
515
|
-
}
|
|
516
|
-
};
|
|
517
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Manage the `codex_hooks` feature flag in `~/.codex/config.toml`.
|
|
3
|
-
*
|
|
4
|
-
* Codex CLI ≥ v0.114 (Mar 2026) exposes lifecycle hooks via
|
|
5
|
-
* `.codex/hooks.json`, but the hooks engine is inert unless the user has
|
|
6
|
-
* opted into it with:
|
|
7
|
-
*
|
|
8
|
-
* ```toml
|
|
9
|
-
* [features]
|
|
10
|
-
* codex_hooks = true
|
|
11
|
-
* ```
|
|
12
|
-
*
|
|
13
|
-
* in `$CODEX_HOME/config.toml` (default: `~/.codex/config.toml`).
|
|
14
|
-
* cclaw init/sync can prompt the user to flip this flag for them; sync/runtime diagnostics report the concrete repair when it is missing;
|
|
15
|
-
* this module owns the detection / mutation code so the prompt logic in
|
|
16
|
-
* `cli.ts` stays small and testable.
|
|
17
|
-
*
|
|
18
|
-
* The TOML mutations here are intentionally surgical — we never reparse
|
|
19
|
-
* or rewrite the whole document. A deliberately narrow regex based
|
|
20
|
-
* approach lets the function stay dependency-free and preserves the
|
|
21
|
-
* user's comments, whitespace, and custom key ordering.
|
|
22
|
-
*/
|
|
23
|
-
/**
|
|
24
|
-
* Absolute path of the Codex config file. Respects `$CODEX_HOME` when
|
|
25
|
-
* present (the only override Codex CLI documents); falls back to
|
|
26
|
-
* `~/.codex/config.toml` otherwise.
|
|
27
|
-
*/
|
|
28
|
-
export declare function codexConfigPath(env?: NodeJS.ProcessEnv): string;
|
|
29
|
-
export type CodexHooksFlagState = "enabled" | "disabled" | "missing-key" | "missing-section" | "missing-file";
|
|
30
|
-
/**
|
|
31
|
-
* Inspect a TOML document and decide which of the five canonical states
|
|
32
|
-
* it represents. Comments and blank lines are ignored. Only the first
|
|
33
|
-
* `[features]` section is considered — duplicates are technically invalid
|
|
34
|
-
* TOML and Codex rejects them, so cclaw does not try to be clever there.
|
|
35
|
-
*/
|
|
36
|
-
export declare function classifyCodexHooksFlag(toml: string | null): CodexHooksFlagState;
|
|
37
|
-
/**
|
|
38
|
-
* Return a TOML document with `[features] codex_hooks = true` set.
|
|
39
|
-
* Preserves all other content verbatim:
|
|
40
|
-
* - If the document lacks a `[features]` section, we append one at the
|
|
41
|
-
* end of the file (separated by a blank line).
|
|
42
|
-
* - If `[features]` exists without `codex_hooks`, we insert the key
|
|
43
|
-
* immediately after the header.
|
|
44
|
-
* - If `codex_hooks` exists with any non-`true` value, we rewrite
|
|
45
|
-
* just that line.
|
|
46
|
-
* - If the flag is already `true`, the input is returned unchanged.
|
|
47
|
-
*/
|
|
48
|
-
export declare function patchCodexHooksFlag(toml: string | null): {
|
|
49
|
-
updated: string;
|
|
50
|
-
changed: boolean;
|
|
51
|
-
};
|
|
52
|
-
/**
|
|
53
|
-
* Read the Codex config, return `null` when the file does not exist.
|
|
54
|
-
* All other read errors propagate so callers can surface a useful
|
|
55
|
-
* message instead of silently degrading.
|
|
56
|
-
*/
|
|
57
|
-
export declare function readCodexConfig(configPath: string): Promise<string | null>;
|
|
58
|
-
export declare function writeCodexConfig(configPath: string, content: string): Promise<void>;
|