cclaw-cli 0.21.1 → 0.21.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/content/skills.js +1 -1
- package/dist/content/stages/brainstorm.js +3 -7
- package/dist/content/stages/design.js +2 -5
- package/dist/content/stages/plan.js +2 -4
- package/dist/content/stages/review.js +2 -4
- package/dist/content/stages/schema-types.d.ts +8 -2
- package/dist/content/stages/scope.js +2 -6
- package/dist/content/stages/ship.js +2 -4
- package/dist/content/stages/spec.js +2 -5
- package/dist/content/stages/tdd.js +2 -4
- package/dist/runs.d.ts +0 -18
- package/dist/runs.js +1 -188
- package/package.json +1 -1
package/dist/content/skills.js
CHANGED
|
@@ -157,7 +157,7 @@ ${rows}
|
|
|
157
157
|
function mergedAntiPatterns(schema) {
|
|
158
158
|
const merged = [];
|
|
159
159
|
const seen = new Set();
|
|
160
|
-
for (const item of [...schema.
|
|
160
|
+
for (const item of [...schema.commonRationalizations, ...schema.blockers]) {
|
|
161
161
|
const key = item.trim().toLowerCase();
|
|
162
162
|
if (seen.has(key))
|
|
163
163
|
continue;
|
|
@@ -94,18 +94,14 @@ export const BRAINSTORM = {
|
|
|
94
94
|
"no implementation action taken",
|
|
95
95
|
"artifact reviewed by user"
|
|
96
96
|
],
|
|
97
|
-
|
|
97
|
+
commonRationalizations: [
|
|
98
98
|
"Asking questions without exploring existing project context first",
|
|
99
99
|
"Asking bundled or purely informational questions that don't change decisions",
|
|
100
100
|
"Proposing cosmetic option variants instead of architecturally distinct approaches",
|
|
101
101
|
"Jumping directly into implementation",
|
|
102
|
-
"Requesting approval without stating what decision is being approved"
|
|
103
|
-
],
|
|
104
|
-
redFlags: [
|
|
105
|
-
"No project context exploration before questions",
|
|
102
|
+
"Requesting approval without stating what decision is being approved",
|
|
106
103
|
"Questions that only gather preferences without design impact",
|
|
107
|
-
"Options that are variants of one approach, not distinct alternatives"
|
|
108
|
-
"Approval requested without explicit decision context"
|
|
104
|
+
"Options that are variants of one approach, not distinct alternatives"
|
|
109
105
|
],
|
|
110
106
|
policyNeedles: [
|
|
111
107
|
"Explore project context",
|
|
@@ -106,20 +106,17 @@ export const DESIGN = {
|
|
|
106
106
|
"completion dashboard present with all review-section statuses",
|
|
107
107
|
"artifact complete for spec handoff"
|
|
108
108
|
],
|
|
109
|
-
|
|
109
|
+
commonRationalizations: [
|
|
110
110
|
"Architecture deferred to implementation phase",
|
|
111
111
|
"Missing data-flow edge cases",
|
|
112
112
|
"No performance budget for critical path",
|
|
113
113
|
"Batching multiple design issues into one question",
|
|
114
114
|
"Skipping review sections because plan seems simple",
|
|
115
115
|
"Agreeing with user's architecture choice without evaluating alternatives",
|
|
116
|
-
"Hedging every recommendation with 'it depends' instead of taking a position"
|
|
117
|
-
],
|
|
118
|
-
redFlags: [
|
|
116
|
+
"Hedging every recommendation with 'it depends' instead of taking a position",
|
|
119
117
|
"No explicit architecture boundary section",
|
|
120
118
|
"No failure recovery strategy",
|
|
121
119
|
"No defined test/perf baseline",
|
|
122
|
-
"Review sections skipped or condensed",
|
|
123
120
|
"No NOT-in-scope output section",
|
|
124
121
|
"No What-already-exists output section",
|
|
125
122
|
"Design decisions made without reading the actual code first"
|
|
@@ -83,14 +83,12 @@ export const PLAN = {
|
|
|
83
83
|
"artifact ready for TDD execution",
|
|
84
84
|
"acceptance mapping complete"
|
|
85
85
|
],
|
|
86
|
-
|
|
86
|
+
commonRationalizations: [
|
|
87
87
|
"Horizontal decomposition without end-to-end slices",
|
|
88
88
|
"Tasks without verification steps",
|
|
89
89
|
"Starting execution before approval",
|
|
90
90
|
"Tasks that touch multiple unrelated areas",
|
|
91
|
-
"Using placeholder tokens or scope-reduction phrases (`v1`, `for now`, `later`) in task definitions"
|
|
92
|
-
],
|
|
93
|
-
redFlags: [
|
|
91
|
+
"Using placeholder tokens or scope-reduction phrases (`v1`, `for now`, `later`) in task definitions",
|
|
94
92
|
"No dependency graph",
|
|
95
93
|
"No WAIT_FOR_CONFIRM marker",
|
|
96
94
|
"No explicit dependency waves",
|
|
@@ -88,14 +88,12 @@ export const REVIEW = {
|
|
|
88
88
|
"critical blockers resolved",
|
|
89
89
|
"ship readiness explicitly stated"
|
|
90
90
|
],
|
|
91
|
-
|
|
91
|
+
commonRationalizations: [
|
|
92
92
|
"Single generic review without layered structure",
|
|
93
93
|
"No severity classification",
|
|
94
94
|
"Shipping with open criticals",
|
|
95
95
|
"Batching multiple findings into one report without individual resolution",
|
|
96
|
-
"Skipping Layer 2 sections because Layer 1 passed"
|
|
97
|
-
],
|
|
98
|
-
redFlags: [
|
|
96
|
+
"Skipping Layer 2 sections because Layer 1 passed",
|
|
99
97
|
"No separate Layer 1/Layer 2 outcomes",
|
|
100
98
|
"No structured review-army reconciliation artifact",
|
|
101
99
|
"No critical bucket",
|
|
@@ -74,8 +74,14 @@ export interface StageSchema {
|
|
|
74
74
|
outputs: string[];
|
|
75
75
|
blockers: string[];
|
|
76
76
|
exitCriteria: string[];
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
/**
|
|
78
|
+
* Consolidated "Common Rationalizations" list — things an agent is likely to
|
|
79
|
+
* talk itself into that should stop the stage. Rendered under the
|
|
80
|
+
* "Anti-Patterns & Red Flags" heading in the generated SKILL.md. Replaces
|
|
81
|
+
* the former split between `antiPatterns` and `redFlags`, which produced
|
|
82
|
+
* near-duplicate entries and forced downstream code to merge them anyway.
|
|
83
|
+
*/
|
|
84
|
+
commonRationalizations: string[];
|
|
79
85
|
policyNeedles: string[];
|
|
80
86
|
artifactFile: string;
|
|
81
87
|
next: FlowStage | "done";
|
|
@@ -96,7 +96,7 @@ export const SCOPE = {
|
|
|
96
96
|
"completion dashboard produced",
|
|
97
97
|
"scope summary produced"
|
|
98
98
|
],
|
|
99
|
-
|
|
99
|
+
commonRationalizations: [
|
|
100
100
|
"Scope silently expanded during discussion",
|
|
101
101
|
"No explicit out-of-scope section",
|
|
102
102
|
"Premise accepted without challenge",
|
|
@@ -104,16 +104,12 @@ export const SCOPE = {
|
|
|
104
104
|
"Hedged recommendations that avoid taking a position",
|
|
105
105
|
"Batching multiple scope issues into one question",
|
|
106
106
|
"Re-arguing for smaller scope after user rejects reduction",
|
|
107
|
-
"Using scope-reduction placeholders (`v1`, `for now`, `we can do later`) instead of explicit user-approved boundaries"
|
|
108
|
-
],
|
|
109
|
-
redFlags: [
|
|
107
|
+
"Using scope-reduction placeholders (`v1`, `for now`, `we can do later`) instead of explicit user-approved boundaries",
|
|
110
108
|
"No selected mode in artifact",
|
|
111
109
|
"Mode selected without heuristic justification",
|
|
112
110
|
"No discretion section (or explicit `None`) in artifact",
|
|
113
111
|
"No deferred/not-in-scope section",
|
|
114
112
|
"No user approval marker",
|
|
115
|
-
"Premise challenge missing or superficial",
|
|
116
|
-
"No implementation alternatives evaluated",
|
|
117
113
|
"Missing Locked Decisions section or decisions without D-XX IDs"
|
|
118
114
|
],
|
|
119
115
|
policyNeedles: ["Scope mode", "In Scope", "Out of Scope", "Discretion Areas", "NOT in scope", "Premise Challenge", "Locked Decisions"],
|
|
@@ -77,14 +77,12 @@ export const SHIP = {
|
|
|
77
77
|
"rollback and release notes complete",
|
|
78
78
|
"finalization action explicitly chosen and executed"
|
|
79
79
|
],
|
|
80
|
-
|
|
80
|
+
commonRationalizations: [
|
|
81
81
|
"Shipping without rollback strategy",
|
|
82
82
|
"Implicit finalization decision",
|
|
83
83
|
"Bypassing preflight due to urgency",
|
|
84
84
|
"Selecting multiple finalization modes",
|
|
85
|
-
"Shipping with BLOCKED review verdict"
|
|
86
|
-
],
|
|
87
|
-
redFlags: [
|
|
85
|
+
"Shipping with BLOCKED review verdict",
|
|
88
86
|
"No rollback trigger/steps",
|
|
89
87
|
"More than one finalization mode implied",
|
|
90
88
|
"No explicit preflight result",
|
|
@@ -77,14 +77,11 @@ export const SPEC = {
|
|
|
77
77
|
"plan-ready acceptance mapping exists",
|
|
78
78
|
"testability confirmed for all criteria"
|
|
79
79
|
],
|
|
80
|
-
|
|
80
|
+
commonRationalizations: [
|
|
81
81
|
"High-level goals without measurable outcomes",
|
|
82
82
|
"Implicit assumptions",
|
|
83
83
|
"Proceeding to plan before approval",
|
|
84
|
-
"Using vague adjectives (fast, intuitive, robust) without thresholds"
|
|
85
|
-
],
|
|
86
|
-
redFlags: [
|
|
87
|
-
"Criteria use vague language (fast, intuitive, robust) without thresholds",
|
|
84
|
+
"Using vague adjectives (fast, intuitive, robust) without thresholds",
|
|
88
85
|
"No explicit assumptions section",
|
|
89
86
|
"No approval record",
|
|
90
87
|
"No testability mapping",
|
|
@@ -88,16 +88,14 @@ export const TDD = {
|
|
|
88
88
|
"required gates marked satisfied",
|
|
89
89
|
"traceability annotated"
|
|
90
90
|
],
|
|
91
|
-
|
|
91
|
+
commonRationalizations: [
|
|
92
92
|
"Writing code before failing test",
|
|
93
93
|
"Asserting implementation details instead of behavior",
|
|
94
94
|
"Big-bang implementation across multiple slices",
|
|
95
95
|
"Partial test runs presented as GREEN",
|
|
96
96
|
"Skipping evidence capture",
|
|
97
97
|
"Undocumented refactor changes",
|
|
98
|
-
"Adding features beyond what RED tests require"
|
|
99
|
-
],
|
|
100
|
-
redFlags: [
|
|
98
|
+
"Adding features beyond what RED tests require",
|
|
101
99
|
"No failing test output (RED missing)",
|
|
102
100
|
"Implementation edits appear before RED evidence",
|
|
103
101
|
"No full-suite GREEN evidence",
|
package/dist/runs.d.ts
CHANGED
|
@@ -52,19 +52,6 @@ export interface ArchiveManifest {
|
|
|
52
52
|
snapshottedStateFiles: string[];
|
|
53
53
|
retro: ArchiveRunResult["retro"];
|
|
54
54
|
}
|
|
55
|
-
export interface RewindRunOptions {
|
|
56
|
-
to: FlowStage;
|
|
57
|
-
reason?: string;
|
|
58
|
-
}
|
|
59
|
-
export interface RewindRunResult {
|
|
60
|
-
rewindId: string;
|
|
61
|
-
from: FlowStage;
|
|
62
|
-
to: FlowStage;
|
|
63
|
-
invalidatedStages: FlowStage[];
|
|
64
|
-
staleArtifacts: string[];
|
|
65
|
-
archivePath: string;
|
|
66
|
-
nextState: FlowState;
|
|
67
|
-
}
|
|
68
55
|
interface EnsureRunSystemOptions {
|
|
69
56
|
createIfMissing?: boolean;
|
|
70
57
|
}
|
|
@@ -82,11 +69,6 @@ export declare function writeFlowState(projectRoot: string, state: FlowState, op
|
|
|
82
69
|
export declare function ensureRunSystem(projectRoot: string, _options?: EnsureRunSystemOptions): Promise<FlowState>;
|
|
83
70
|
export declare function listRuns(projectRoot: string): Promise<CclawRunMeta[]>;
|
|
84
71
|
export declare function archiveRun(projectRoot: string, featureName?: string, options?: ArchiveRunOptions): Promise<ArchiveRunResult>;
|
|
85
|
-
export declare function rewindRun(projectRoot: string, options: RewindRunOptions): Promise<RewindRunResult>;
|
|
86
|
-
export declare function acknowledgeStaleStage(projectRoot: string, stage: FlowStage): Promise<{
|
|
87
|
-
acknowledged: boolean;
|
|
88
|
-
remaining: FlowStage[];
|
|
89
|
-
}>;
|
|
90
72
|
/**
|
|
91
73
|
* Counts entries in the canonical JSONL knowledge store. An "active" entry is one
|
|
92
74
|
* non-empty line that parses as JSON with the required `type` field belonging to the
|
package/dist/runs.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { COMMAND_FILE_ORDER, RUNTIME_ROOT } from "./constants.js";
|
|
4
|
-
import { canTransition, createInitialFlowState, isFlowTrack, skippedStagesForTrack
|
|
4
|
+
import { canTransition, createInitialFlowState, isFlowTrack, skippedStagesForTrack } from "./flow-state.js";
|
|
5
5
|
import { ensureFeatureSystem, readActiveFeature, syncActiveFeatureSnapshot } from "./feature-system.js";
|
|
6
6
|
import { ensureDir, exists, withDirectoryLock, writeFileSafe } from "./fs-utils.js";
|
|
7
|
-
import { stageSchema } from "./content/stage-schema.js";
|
|
8
7
|
export class InvalidStageTransitionError extends Error {
|
|
9
8
|
from;
|
|
10
9
|
to;
|
|
@@ -36,8 +35,6 @@ const FLOW_STATE_REL_PATH = `${RUNTIME_ROOT}/state/flow-state.json`;
|
|
|
36
35
|
const RUNS_DIR_REL_PATH = `${RUNTIME_ROOT}/runs`;
|
|
37
36
|
const ACTIVE_ARTIFACTS_REL_PATH = `${RUNTIME_ROOT}/artifacts`;
|
|
38
37
|
const STATE_DIR_REL_PATH = `${RUNTIME_ROOT}/state`;
|
|
39
|
-
const REWIND_LOG_REL_PATH = `${RUNTIME_ROOT}/state/rewind-log.jsonl`;
|
|
40
|
-
const REWIND_ARCHIVE_DIR_NAME = "_rewind-archive";
|
|
41
38
|
const FLOW_STAGE_SET = new Set(COMMAND_FILE_ORDER);
|
|
42
39
|
/** State filenames explicitly excluded from the archive snapshot. */
|
|
43
40
|
const STATE_SNAPSHOT_EXCLUDE = new Set([
|
|
@@ -59,12 +56,6 @@ function activeArtifactsPath(projectRoot) {
|
|
|
59
56
|
function stateDirPath(projectRoot) {
|
|
60
57
|
return path.join(projectRoot, STATE_DIR_REL_PATH);
|
|
61
58
|
}
|
|
62
|
-
function rewindLogPath(projectRoot) {
|
|
63
|
-
return path.join(projectRoot, REWIND_LOG_REL_PATH);
|
|
64
|
-
}
|
|
65
|
-
function rewindArchivePath(projectRoot, rewindId) {
|
|
66
|
-
return path.join(activeArtifactsPath(projectRoot), REWIND_ARCHIVE_DIR_NAME, rewindId);
|
|
67
|
-
}
|
|
68
59
|
async function snapshotStateDirectory(projectRoot, destinationRoot) {
|
|
69
60
|
const sourceDir = stateDirPath(projectRoot);
|
|
70
61
|
if (!(await exists(sourceDir))) {
|
|
@@ -348,23 +339,6 @@ async function uniqueArchiveId(projectRoot, baseId) {
|
|
|
348
339
|
}
|
|
349
340
|
return candidate;
|
|
350
341
|
}
|
|
351
|
-
function rewindTimestampId(date = new Date()) {
|
|
352
|
-
return date
|
|
353
|
-
.toISOString()
|
|
354
|
-
.replace(/[-:]/gu, "")
|
|
355
|
-
.replace(/\.\d{3}Z$/u, "Z");
|
|
356
|
-
}
|
|
357
|
-
function staleArtifactFileName(fileName) {
|
|
358
|
-
const ext = path.extname(fileName);
|
|
359
|
-
if (!ext) {
|
|
360
|
-
return `${fileName}.stale`;
|
|
361
|
-
}
|
|
362
|
-
const base = fileName.slice(0, -ext.length);
|
|
363
|
-
return `${base}.stale${ext}`;
|
|
364
|
-
}
|
|
365
|
-
function stageIndexMapForTrack(track) {
|
|
366
|
-
return new Map(trackStages(track).map((stage, index) => [stage, index]));
|
|
367
|
-
}
|
|
368
342
|
function retroArtifactPath(projectRoot) {
|
|
369
343
|
return path.join(activeArtifactsPath(projectRoot), "09-retro.md");
|
|
370
344
|
}
|
|
@@ -620,167 +594,6 @@ export async function archiveRun(projectRoot, featureName, options = {}) {
|
|
|
620
594
|
retro: retroSummary
|
|
621
595
|
};
|
|
622
596
|
}
|
|
623
|
-
export async function rewindRun(projectRoot, options) {
|
|
624
|
-
await ensureRunSystem(projectRoot);
|
|
625
|
-
const state = await readFlowState(projectRoot);
|
|
626
|
-
const track = state.track ?? "standard";
|
|
627
|
-
const ordered = trackStages(track);
|
|
628
|
-
const stageToIndex = stageIndexMapForTrack(track);
|
|
629
|
-
const toIndex = stageToIndex.get(options.to);
|
|
630
|
-
const currentIndex = stageToIndex.get(state.currentStage);
|
|
631
|
-
if (toIndex === undefined) {
|
|
632
|
-
throw new Error(`Cannot rewind to "${options.to}" because it is outside track "${track}".`);
|
|
633
|
-
}
|
|
634
|
-
if (currentIndex === undefined) {
|
|
635
|
-
throw new Error(`Current stage "${state.currentStage}" is not part of track "${track}".`);
|
|
636
|
-
}
|
|
637
|
-
if (toIndex > currentIndex) {
|
|
638
|
-
throw new Error(`Cannot rewind forward from "${state.currentStage}" to "${options.to}".`);
|
|
639
|
-
}
|
|
640
|
-
const reason = options.reason?.trim() && options.reason.trim().length > 0
|
|
641
|
-
? options.reason.trim()
|
|
642
|
-
: "manual_rewind";
|
|
643
|
-
const nowIso = new Date().toISOString();
|
|
644
|
-
const rewindId = `rewind-${rewindTimestampId()}`;
|
|
645
|
-
const invalidatedStages = ordered.filter((stage) => {
|
|
646
|
-
const idx = stageToIndex.get(stage);
|
|
647
|
-
if (idx === undefined || idx <= toIndex) {
|
|
648
|
-
return false;
|
|
649
|
-
}
|
|
650
|
-
return state.completedStages.includes(stage) || stage === state.currentStage;
|
|
651
|
-
});
|
|
652
|
-
const nextCompletedStages = state.completedStages.filter((stage) => {
|
|
653
|
-
const idx = stageToIndex.get(stage);
|
|
654
|
-
return typeof idx === "number" && idx < toIndex;
|
|
655
|
-
});
|
|
656
|
-
const freshCatalog = createInitialFlowState({ activeRunId: state.activeRunId, track }).stageGateCatalog;
|
|
657
|
-
const nextCatalog = { ...state.stageGateCatalog };
|
|
658
|
-
for (const stage of ordered) {
|
|
659
|
-
const idx = stageToIndex.get(stage);
|
|
660
|
-
if (idx === undefined)
|
|
661
|
-
continue;
|
|
662
|
-
if (idx >= toIndex) {
|
|
663
|
-
nextCatalog[stage] = {
|
|
664
|
-
...freshCatalog[stage],
|
|
665
|
-
required: [...freshCatalog[stage].required],
|
|
666
|
-
recommended: [...freshCatalog[stage].recommended],
|
|
667
|
-
conditional: [...freshCatalog[stage].conditional],
|
|
668
|
-
triggered: [],
|
|
669
|
-
passed: [],
|
|
670
|
-
blocked: []
|
|
671
|
-
};
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
const nextGuardEvidence = { ...state.guardEvidence };
|
|
675
|
-
for (const stage of ordered) {
|
|
676
|
-
const idx = stageToIndex.get(stage);
|
|
677
|
-
if (idx === undefined || idx < toIndex)
|
|
678
|
-
continue;
|
|
679
|
-
const catalog = state.stageGateCatalog[stage];
|
|
680
|
-
const gateIds = new Set([
|
|
681
|
-
...catalog.required,
|
|
682
|
-
...catalog.recommended,
|
|
683
|
-
...catalog.conditional,
|
|
684
|
-
...catalog.triggered,
|
|
685
|
-
...catalog.passed,
|
|
686
|
-
...catalog.blocked
|
|
687
|
-
]);
|
|
688
|
-
for (const gateId of gateIds) {
|
|
689
|
-
delete nextGuardEvidence[gateId];
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
const nextStale = {};
|
|
693
|
-
for (const [stage, marker] of Object.entries(state.staleStages)) {
|
|
694
|
-
if (!marker)
|
|
695
|
-
continue;
|
|
696
|
-
const idx = stageToIndex.get(stage);
|
|
697
|
-
if (idx === undefined || idx <= toIndex) {
|
|
698
|
-
continue;
|
|
699
|
-
}
|
|
700
|
-
nextStale[stage] = marker;
|
|
701
|
-
}
|
|
702
|
-
for (const stage of invalidatedStages) {
|
|
703
|
-
nextStale[stage] = {
|
|
704
|
-
rewindId,
|
|
705
|
-
reason,
|
|
706
|
-
markedAt: nowIso
|
|
707
|
-
};
|
|
708
|
-
}
|
|
709
|
-
const archivePath = rewindArchivePath(projectRoot, rewindId);
|
|
710
|
-
const staleArtifacts = [];
|
|
711
|
-
for (const stage of invalidatedStages) {
|
|
712
|
-
const artifactFile = stageSchema(stage).artifactFile;
|
|
713
|
-
const artifactPath = path.join(activeArtifactsPath(projectRoot), artifactFile);
|
|
714
|
-
if (!(await exists(artifactPath))) {
|
|
715
|
-
continue;
|
|
716
|
-
}
|
|
717
|
-
await ensureDir(archivePath);
|
|
718
|
-
await ensureDir(path.join(archivePath, path.dirname(artifactFile)));
|
|
719
|
-
await fs.copyFile(artifactPath, path.join(archivePath, artifactFile));
|
|
720
|
-
const staleName = staleArtifactFileName(artifactFile);
|
|
721
|
-
const stalePath = path.join(activeArtifactsPath(projectRoot), staleName);
|
|
722
|
-
await fs.rm(stalePath, { force: true });
|
|
723
|
-
await fs.rename(artifactPath, stalePath);
|
|
724
|
-
staleArtifacts.push(staleName);
|
|
725
|
-
}
|
|
726
|
-
const rewindRecord = {
|
|
727
|
-
id: rewindId,
|
|
728
|
-
fromStage: state.currentStage,
|
|
729
|
-
toStage: options.to,
|
|
730
|
-
reason,
|
|
731
|
-
timestamp: nowIso,
|
|
732
|
-
invalidatedStages
|
|
733
|
-
};
|
|
734
|
-
const nextState = {
|
|
735
|
-
...state,
|
|
736
|
-
currentStage: options.to,
|
|
737
|
-
completedStages: nextCompletedStages,
|
|
738
|
-
guardEvidence: nextGuardEvidence,
|
|
739
|
-
stageGateCatalog: nextCatalog,
|
|
740
|
-
staleStages: nextStale,
|
|
741
|
-
rewinds: [...state.rewinds, rewindRecord]
|
|
742
|
-
};
|
|
743
|
-
await writeFlowState(projectRoot, nextState, { allowReset: true });
|
|
744
|
-
const rewindLogEntry = {
|
|
745
|
-
...rewindRecord,
|
|
746
|
-
track,
|
|
747
|
-
runId: state.activeRunId,
|
|
748
|
-
staleArtifacts
|
|
749
|
-
};
|
|
750
|
-
await ensureDir(path.dirname(rewindLogPath(projectRoot)));
|
|
751
|
-
await fs.appendFile(rewindLogPath(projectRoot), `${JSON.stringify(rewindLogEntry)}\n`, "utf8");
|
|
752
|
-
return {
|
|
753
|
-
rewindId,
|
|
754
|
-
from: state.currentStage,
|
|
755
|
-
to: options.to,
|
|
756
|
-
invalidatedStages,
|
|
757
|
-
staleArtifacts,
|
|
758
|
-
archivePath,
|
|
759
|
-
nextState
|
|
760
|
-
};
|
|
761
|
-
}
|
|
762
|
-
export async function acknowledgeStaleStage(projectRoot, stage) {
|
|
763
|
-
await ensureRunSystem(projectRoot);
|
|
764
|
-
const state = await readFlowState(projectRoot);
|
|
765
|
-
const marker = state.staleStages[stage];
|
|
766
|
-
if (!marker) {
|
|
767
|
-
return {
|
|
768
|
-
acknowledged: false,
|
|
769
|
-
remaining: Object.keys(state.staleStages).filter((value) => isFlowStage(value))
|
|
770
|
-
};
|
|
771
|
-
}
|
|
772
|
-
const nextStale = { ...state.staleStages };
|
|
773
|
-
delete nextStale[stage];
|
|
774
|
-
const nextState = {
|
|
775
|
-
...state,
|
|
776
|
-
staleStages: nextStale
|
|
777
|
-
};
|
|
778
|
-
await writeFlowState(projectRoot, nextState, { allowReset: true });
|
|
779
|
-
return {
|
|
780
|
-
acknowledged: true,
|
|
781
|
-
remaining: Object.keys(nextStale).filter((value) => isFlowStage(value))
|
|
782
|
-
};
|
|
783
|
-
}
|
|
784
597
|
const KNOWLEDGE_SOFT_THRESHOLD = 50;
|
|
785
598
|
async function readKnowledgeStats(projectRoot) {
|
|
786
599
|
const knowledgePath = path.join(projectRoot, RUNTIME_ROOT, "knowledge.jsonl");
|