@zigrivers/scaffold 3.18.1 → 3.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commands/adopt.d.ts.map +1 -1
- package/dist/cli/commands/adopt.js +6 -2
- package/dist/cli/commands/adopt.js.map +1 -1
- package/dist/cli/commands/complete.d.ts.map +1 -1
- package/dist/cli/commands/complete.js +1 -1
- package/dist/cli/commands/complete.js.map +1 -1
- package/dist/cli/commands/dashboard.d.ts.map +1 -1
- package/dist/cli/commands/dashboard.js +6 -1
- package/dist/cli/commands/dashboard.js.map +1 -1
- package/dist/cli/commands/info.d.ts.map +1 -1
- package/dist/cli/commands/info.js +2 -2
- package/dist/cli/commands/info.js.map +1 -1
- package/dist/cli/commands/next.d.ts.map +1 -1
- package/dist/cli/commands/next.js +7 -2
- package/dist/cli/commands/next.js.map +1 -1
- package/dist/cli/commands/next.test.js +95 -0
- package/dist/cli/commands/next.test.js.map +1 -1
- package/dist/cli/commands/reset.d.ts.map +1 -1
- package/dist/cli/commands/reset.js +1 -1
- package/dist/cli/commands/reset.js.map +1 -1
- package/dist/cli/commands/rework.d.ts.map +1 -1
- package/dist/cli/commands/rework.js +6 -1
- package/dist/cli/commands/rework.js.map +1 -1
- package/dist/cli/commands/run.d.ts.map +1 -1
- package/dist/cli/commands/run.js +1 -1
- package/dist/cli/commands/run.js.map +1 -1
- package/dist/cli/commands/skip.d.ts.map +1 -1
- package/dist/cli/commands/skip.js +1 -1
- package/dist/cli/commands/skip.js.map +1 -1
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +14 -5
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/commands/status.test.js +60 -0
- package/dist/cli/commands/status.test.js.map +1 -1
- package/dist/core/pipeline/graph-hash.d.ts +18 -0
- package/dist/core/pipeline/graph-hash.d.ts.map +1 -0
- package/dist/core/pipeline/graph-hash.js +35 -0
- package/dist/core/pipeline/graph-hash.js.map +1 -0
- package/dist/core/pipeline/graph-hash.test.d.ts +2 -0
- package/dist/core/pipeline/graph-hash.test.d.ts.map +1 -0
- package/dist/core/pipeline/graph-hash.test.js +107 -0
- package/dist/core/pipeline/graph-hash.test.js.map +1 -0
- package/dist/core/pipeline/read-eligible.d.ts +20 -0
- package/dist/core/pipeline/read-eligible.d.ts.map +1 -0
- package/dist/core/pipeline/read-eligible.js +28 -0
- package/dist/core/pipeline/read-eligible.js.map +1 -0
- package/dist/core/pipeline/read-eligible.test.d.ts +2 -0
- package/dist/core/pipeline/read-eligible.test.d.ts.map +1 -0
- package/dist/core/pipeline/read-eligible.test.js +99 -0
- package/dist/core/pipeline/read-eligible.test.js.map +1 -0
- package/dist/core/pipeline/resolver.d.ts.map +1 -1
- package/dist/core/pipeline/resolver.js +24 -1
- package/dist/core/pipeline/resolver.js.map +1 -1
- package/dist/core/pipeline/resolver.test.js +86 -0
- package/dist/core/pipeline/resolver.test.js.map +1 -1
- package/dist/core/pipeline/types.d.ts +5 -0
- package/dist/core/pipeline/types.d.ts.map +1 -1
- package/dist/e2e/eligible-cache.test.d.ts +2 -0
- package/dist/e2e/eligible-cache.test.d.ts.map +1 -0
- package/dist/e2e/eligible-cache.test.js +104 -0
- package/dist/e2e/eligible-cache.test.js.map +1 -0
- package/dist/state/root-counter-reader.d.ts +11 -0
- package/dist/state/root-counter-reader.d.ts.map +1 -0
- package/dist/state/root-counter-reader.js +25 -0
- package/dist/state/root-counter-reader.js.map +1 -0
- package/dist/state/root-counter-reader.test.d.ts +2 -0
- package/dist/state/root-counter-reader.test.d.ts.map +1 -0
- package/dist/state/root-counter-reader.test.js +43 -0
- package/dist/state/root-counter-reader.test.js.map +1 -0
- package/dist/state/state-manager.d.ts +23 -2
- package/dist/state/state-manager.d.ts.map +1 -1
- package/dist/state/state-manager.js +57 -5
- package/dist/state/state-manager.js.map +1 -1
- package/dist/state/state-manager.test.js +246 -1
- package/dist/state/state-manager.test.js.map +1 -1
- package/dist/types/state.d.ts +19 -0
- package/dist/types/state.d.ts.map +1 -1
- package/dist/types/state.test.d.ts +2 -0
- package/dist/types/state.test.d.ts.map +1 -0
- package/dist/types/state.test.js +40 -0
- package/dist/types/state.test.js.map +1 -0
- package/dist/wizard/wizard.d.ts.map +1 -1
- package/dist/wizard/wizard.js +3 -1
- package/dist/wizard/wizard.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import { loadPipelineContext } from '../core/pipeline/context.js';
|
|
6
|
+
import { resolvePipeline } from '../core/pipeline/resolver.js';
|
|
7
|
+
import { StateManager } from '../state/state-manager.js';
|
|
8
|
+
import { StatePathResolver } from '../state/state-path-resolver.js';
|
|
9
|
+
import { readEligible } from '../core/pipeline/read-eligible.js';
|
|
10
|
+
import { readRootSaveCounter } from '../state/root-counter-reader.js';
|
|
11
|
+
import { createOutputContext } from '../cli/output/context.js';
|
|
12
|
+
describe('Eligible-Step Cache v2 — E2E', () => {
|
|
13
|
+
let tmpRoot;
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'ec2-e2e-'));
|
|
16
|
+
fs.mkdirSync(path.join(tmpRoot, '.scaffold', 'services', 'api'), { recursive: true });
|
|
17
|
+
fs.writeFileSync(path.join(tmpRoot, '.scaffold', 'config.yml'), `version: 2
|
|
18
|
+
methodology: deep
|
|
19
|
+
platforms: [claude-code]
|
|
20
|
+
project:
|
|
21
|
+
services:
|
|
22
|
+
- name: api
|
|
23
|
+
projectType: backend
|
|
24
|
+
backendConfig:
|
|
25
|
+
apiStyle: rest
|
|
26
|
+
`);
|
|
27
|
+
const baseState = {
|
|
28
|
+
'schema-version': 3,
|
|
29
|
+
'scaffold-version': '1.0.0',
|
|
30
|
+
init_methodology: 'deep',
|
|
31
|
+
config_methodology: 'deep',
|
|
32
|
+
'init-mode': 'greenfield',
|
|
33
|
+
created: '2026-04-20T00:00:00.000Z',
|
|
34
|
+
in_progress: null,
|
|
35
|
+
steps: {},
|
|
36
|
+
next_eligible: [],
|
|
37
|
+
'extra-steps': [],
|
|
38
|
+
};
|
|
39
|
+
fs.writeFileSync(path.join(tmpRoot, '.scaffold', 'state.json'), JSON.stringify(baseState));
|
|
40
|
+
fs.writeFileSync(path.join(tmpRoot, '.scaffold', 'services', 'api', 'state.json'), JSON.stringify(baseState));
|
|
41
|
+
});
|
|
42
|
+
afterEach(() => {
|
|
43
|
+
fs.rmSync(tmpRoot, { recursive: true, force: true });
|
|
44
|
+
});
|
|
45
|
+
it('AC2: service cache is invalidated by root state mutation (cross-file)', () => {
|
|
46
|
+
const context = loadPipelineContext(tmpRoot);
|
|
47
|
+
const output = createOutputContext('auto');
|
|
48
|
+
const rootPipeline = resolvePipeline(context, { output });
|
|
49
|
+
const rootPathResolver = new StatePathResolver(tmpRoot);
|
|
50
|
+
const rootSm = new StateManager(tmpRoot, rootPipeline.computeEligible, () => context.config ?? undefined, rootPathResolver, rootPipeline.globalSteps, rootPipeline.getPipelineHash('global'));
|
|
51
|
+
// Seed root counter by doing an initial root save
|
|
52
|
+
rootSm.saveState(rootSm.loadState());
|
|
53
|
+
const svcPipeline = resolvePipeline(context, { output, serviceId: 'api' });
|
|
54
|
+
const pathResolver = new StatePathResolver(tmpRoot, 'api');
|
|
55
|
+
const sm = new StateManager(tmpRoot, svcPipeline.computeEligible, () => context.config ?? undefined, pathResolver, svcPipeline.globalSteps, svcPipeline.getPipelineHash('service'));
|
|
56
|
+
const state = sm.loadState();
|
|
57
|
+
state.steps['some-step'] = { status: 'pending', source: 'pipeline', produces: [] };
|
|
58
|
+
sm.saveState(state);
|
|
59
|
+
const svcDisk = JSON.parse(fs.readFileSync(path.join(tmpRoot, '.scaffold', 'services', 'api', 'state.json'), 'utf8'));
|
|
60
|
+
expect(svcDisk.next_eligible_root_counter).toBe(1);
|
|
61
|
+
expect(typeof svcDisk.next_eligible_hash).toBe('string');
|
|
62
|
+
// Mutate root — bumps save_counter from 1 to 2
|
|
63
|
+
rootSm.saveState(rootSm.loadState());
|
|
64
|
+
expect(readRootSaveCounter(tmpRoot)).toBe(2);
|
|
65
|
+
// readEligible must fall back because next_eligible_root_counter (1) !== current root counter (2)
|
|
66
|
+
const liveCalls = [];
|
|
67
|
+
const sentinelPipeline = {
|
|
68
|
+
...svcPipeline,
|
|
69
|
+
computeEligible: ((steps, opts) => {
|
|
70
|
+
liveCalls.push('live-recompute-fired');
|
|
71
|
+
return svcPipeline.computeEligible(steps, opts);
|
|
72
|
+
}),
|
|
73
|
+
};
|
|
74
|
+
readEligible(sm.loadState(), sentinelPipeline, { scope: 'service', globalSteps: svcPipeline.globalSteps }, () => readRootSaveCounter(tmpRoot));
|
|
75
|
+
expect(liveCalls).toContain('live-recompute-fired');
|
|
76
|
+
});
|
|
77
|
+
it('AC3: pipeline-graph change (different hash on re-resolution) invalidates cache on read', () => {
|
|
78
|
+
const context = loadPipelineContext(tmpRoot);
|
|
79
|
+
const output = createOutputContext('auto');
|
|
80
|
+
const pipelineA = resolvePipeline(context, { output });
|
|
81
|
+
const pathResolver = new StatePathResolver(tmpRoot);
|
|
82
|
+
const sm = new StateManager(tmpRoot, pipelineA.computeEligible, () => context.config ?? undefined, pathResolver, pipelineA.globalSteps, pipelineA.getPipelineHash('global'));
|
|
83
|
+
sm.saveState(sm.loadState());
|
|
84
|
+
// Build a new context whose metaPrompts differs — delete first slug
|
|
85
|
+
const firstSlug = [...context.metaPrompts.keys()][0];
|
|
86
|
+
expect(firstSlug).toBeDefined();
|
|
87
|
+
const mutatedMetaPrompts = new Map(context.metaPrompts);
|
|
88
|
+
mutatedMetaPrompts.delete(firstSlug);
|
|
89
|
+
const mutatedContext = { ...context, metaPrompts: mutatedMetaPrompts };
|
|
90
|
+
const pipelineB = resolvePipeline(mutatedContext, { output });
|
|
91
|
+
expect(pipelineB.getPipelineHash('global')).not.toBe(pipelineA.getPipelineHash('global'));
|
|
92
|
+
const liveCalls = [];
|
|
93
|
+
const sentinelPipeline = {
|
|
94
|
+
...pipelineB,
|
|
95
|
+
computeEligible: ((steps, opts) => {
|
|
96
|
+
liveCalls.push('live-fired');
|
|
97
|
+
return pipelineB.computeEligible(steps, opts);
|
|
98
|
+
}),
|
|
99
|
+
};
|
|
100
|
+
readEligible(sm.loadState(), sentinelPipeline, undefined, undefined);
|
|
101
|
+
expect(liveCalls).toContain('live-fired');
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
//# sourceMappingURL=eligible-cache.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eligible-cache.test.js","sourceRoot":"","sources":["../../src/e2e/eligible-cache.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AACpE,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAA;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAA;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAA;AACrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAE9D,QAAQ,CAAC,8BAA8B,EAAE,GAAG,EAAE;IAC5C,IAAI,OAAe,CAAA;IAEnB,UAAU,CAAC,GAAG,EAAE;QACd,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,UAAU,CAAC,CAAC,CAAA;QAC5D,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,KAAK,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;QACrF,EAAE,CAAC,aAAa,CACd,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,EAAE,YAAY,CAAC,EAC7C;;;;;;;;;CASL,CACI,CAAA;QACD,MAAM,SAAS,GAAG;YAChB,gBAAgB,EAAE,CAAC;YACnB,kBAAkB,EAAE,OAAO;YAC3B,gBAAgB,EAAE,MAAM;YACxB,kBAAkB,EAAE,MAAM;YAC1B,WAAW,EAAE,YAAY;YACzB,OAAO,EAAE,0BAA0B;YACnC,WAAW,EAAE,IAAI;YACjB,KAAK,EAAE,EAAE;YACT,aAAa,EAAE,EAAE;YACjB,aAAa,EAAE,EAAE;SAClB,CAAA;QACD,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,EAAE,YAAY,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAA;QAC1F,EAAE,CAAC,aAAa,CACd,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,KAAK,EAAE,YAAY,CAAC,EAChE,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAC1B,CAAA;IACH,CAAC,CAAC,CAAA;IAEF,SAAS,CAAC,GAAG,EAAE;QACb,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;IACtD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,uEAAuE,EAAE,GAAG,EAAE;QAC/E,MAAM,OAAO,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAA;QAC5C,MAAM,MAAM,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAA;QAC1C,MAAM,YAAY,GAAG,eAAe,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,CAAC,CAAA;QACzD,MAAM,gBAAgB,GAAG,IAAI,iBAAiB,CAAC,OAAO,CAAC,CAAA;QACvD,MAAM,MAAM,GAAG,IAAI,YAAY,CAC7B,OAAO,EACP,YAAY,CAAC,eAAe,EAC5B,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,IAAI,SAAS,EACjC,gBAAgB,EAChB,YAAY,CAAC,WAAW,EACxB,YAAY,CAAC,eAAe,CAAC,QAAQ,CAAC,CACvC,CAAA;QACD,kDAAkD;QAClD,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,CAAA;QAEpC,MAAM,WAAW,GAAG,eAAe,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAA;QAC1E,MAAM,YAAY,GAAG,IAAI,iBAAiB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;QAC1D,MAAM,EAAE,GAAG,IAAI,YAAY,CACzB,OAAO,EACP,WAAW,CAAC,eAAe,EAC3B,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,IAAI,SAAS,EACjC,YAAY,EACZ,WAAW,CAAC,WAAW,EACvB,WAAW,CAAC,eAAe,CAAC,SAAS,CAAC,CACvC,CAAA;QACD,MAAM,KAAK,GAAG,EAAE,CAAC,SAAS,EAAE,CAAA;QAC5B,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAA;QAClF,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;QAEnB,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CACxC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,KAAK,EAAE,YAAY,CAAC,EAAE,MAAM,CACzE,CAAC,CAAA;QACF,MAAM,CAAC,OAAO,CAAC,0BAA0B,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QAClD,MAAM,CAAC,OAAO,OAAO,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QAExD,+CAA+C;QAC/C,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,CAAA;QACpC,MAAM,CAAC,mBAAmB,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QAE5C,kGAAkG;QAClG,MAAM,SAAS,GAAa,EAAE,CAAA;QAC9B,MAAM,gBAAgB,GAAG;YACvB,GAAG,WAAW;YACd,eAAe,EAAE,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;gBAChC,SAAS,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAA;gBACtC,OAAO,WAAW,CAAC,eAAe,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;YACjD,CAAC,CAAuC;SACzC,CAAA;QACD,YAAY,CACV,EAAE,CAAC,SAAS,EAAE,EACd,gBAAgB,EAChB,EAAE,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,WAAW,CAAC,WAAW,EAAE,EAC1D,GAAG,EAAE,CAAC,mBAAmB,CAAC,OAAO,CAAC,CACnC,CAAA;QACD,MAAM,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,sBAAsB,CAAC,CAAA;IACrD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,wFAAwF,EAAE,GAAG,EAAE;QAChG,MAAM,OAAO,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAA;QAC5C,MAAM,MAAM,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAA;QAC1C,MAAM,SAAS,GAAG,eAAe,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,CAAC,CAAA;QACtD,MAAM,YAAY,GAAG,IAAI,iBAAiB,CAAC,OAAO,CAAC,CAAA;QACnD,MAAM,EAAE,GAAG,IAAI,YAAY,CACzB,OAAO,EACP,SAAS,CAAC,eAAe,EACzB,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,IAAI,SAAS,EACjC,YAAY,EACZ,SAAS,CAAC,WAAW,EACrB,SAAS,CAAC,eAAe,CAAC,QAAQ,CAAC,CACpC,CAAA;QACD,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,CAAA;QAE5B,oEAAoE;QACpE,MAAM,SAAS,GAAG,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;QACpD,MAAM,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE,CAAA;QAC/B,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,CAAA;QACvD,kBAAkB,CAAC,MAAM,CAAC,SAAU,CAAC,CAAA;QACrC,MAAM,cAAc,GAAG,EAAE,GAAG,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,CAAA;QACtE,MAAM,SAAS,GAAG,eAAe,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,CAAC,CAAA;QAC7D,MAAM,CAAC,SAAS,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAA;QAEzF,MAAM,SAAS,GAAa,EAAE,CAAA;QAC9B,MAAM,gBAAgB,GAAG;YACvB,GAAG,SAAS;YACZ,eAAe,EAAE,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;gBAChC,SAAS,CAAC,IAAI,CAAC,YAAY,CAAC,CAAA;gBAC5B,OAAO,SAAS,CAAC,eAAe,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;YAC/C,CAAC,CAAqC;SACvC,CAAA;QACD,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,EAAE,gBAAgB,EAAE,SAAS,EAAE,SAAS,CAAC,CAAA;QACpE,MAAM,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAA;IAC3C,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reads `.scaffold/state.json` and returns the `save_counter` field.
|
|
3
|
+
* Returns null on any failure (missing file, invalid JSON, missing field).
|
|
4
|
+
*
|
|
5
|
+
* Used by service-scope cache readers (readEligible) to verify that the
|
|
6
|
+
* service's cached next_eligible was written against the current root state
|
|
7
|
+
* (spec §6). Not used at cache WRITE time — StateManager captures the counter
|
|
8
|
+
* internally during loadState to avoid TOCTOU.
|
|
9
|
+
*/
|
|
10
|
+
export declare function readRootSaveCounter(projectRoot: string): number | null;
|
|
11
|
+
//# sourceMappingURL=root-counter-reader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"root-counter-reader.d.ts","sourceRoot":"","sources":["../../src/state/root-counter-reader.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUtE"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
/**
|
|
4
|
+
* Reads `.scaffold/state.json` and returns the `save_counter` field.
|
|
5
|
+
* Returns null on any failure (missing file, invalid JSON, missing field).
|
|
6
|
+
*
|
|
7
|
+
* Used by service-scope cache readers (readEligible) to verify that the
|
|
8
|
+
* service's cached next_eligible was written against the current root state
|
|
9
|
+
* (spec §6). Not used at cache WRITE time — StateManager captures the counter
|
|
10
|
+
* internally during loadState to avoid TOCTOU.
|
|
11
|
+
*/
|
|
12
|
+
export function readRootSaveCounter(projectRoot) {
|
|
13
|
+
const rootStatePath = path.join(projectRoot, '.scaffold', 'state.json');
|
|
14
|
+
try {
|
|
15
|
+
if (!fs.existsSync(rootStatePath))
|
|
16
|
+
return null;
|
|
17
|
+
const raw = JSON.parse(fs.readFileSync(rootStatePath, 'utf8'));
|
|
18
|
+
const counter = raw['save_counter'];
|
|
19
|
+
return typeof counter === 'number' ? counter : null;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=root-counter-reader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"root-counter-reader.js","sourceRoot":"","sources":["../../src/state/root-counter-reader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,IAAI,MAAM,WAAW,CAAA;AAE5B;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB,CAAC,WAAmB;IACrD,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,WAAW,EAAE,YAAY,CAAC,CAAA;IACvE,IAAI,CAAC;QACH,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC;YAAE,OAAO,IAAI,CAAA;QAC9C,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,aAAa,EAAE,MAAM,CAAC,CAA4B,CAAA;QACzF,MAAM,OAAO,GAAG,GAAG,CAAC,cAAc,CAAC,CAAA;QACnC,OAAO,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAA;IACrD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"root-counter-reader.test.d.ts","sourceRoot":"","sources":["../../src/state/root-counter-reader.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import { readRootSaveCounter } from './root-counter-reader.js';
|
|
6
|
+
describe('readRootSaveCounter', () => {
|
|
7
|
+
let tmpRoot;
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'rcr-'));
|
|
10
|
+
fs.mkdirSync(path.join(tmpRoot, '.scaffold'), { recursive: true });
|
|
11
|
+
});
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
fs.rmSync(tmpRoot, { recursive: true, force: true });
|
|
14
|
+
});
|
|
15
|
+
it('returns null when root state file is missing', () => {
|
|
16
|
+
expect(readRootSaveCounter(tmpRoot)).toBeNull();
|
|
17
|
+
});
|
|
18
|
+
it('returns the counter when state file has a valid save_counter', () => {
|
|
19
|
+
fs.writeFileSync(path.join(tmpRoot, '.scaffold', 'state.json'), JSON.stringify({ save_counter: 42, 'schema-version': 3 }));
|
|
20
|
+
expect(readRootSaveCounter(tmpRoot)).toBe(42);
|
|
21
|
+
});
|
|
22
|
+
it('returns null when state file has invalid JSON', () => {
|
|
23
|
+
fs.writeFileSync(path.join(tmpRoot, '.scaffold', 'state.json'), '{ not valid json');
|
|
24
|
+
expect(readRootSaveCounter(tmpRoot)).toBeNull();
|
|
25
|
+
});
|
|
26
|
+
it('returns null when state file lacks save_counter (legacy file)', () => {
|
|
27
|
+
fs.writeFileSync(path.join(tmpRoot, '.scaffold', 'state.json'), JSON.stringify({ 'schema-version': 3 }));
|
|
28
|
+
expect(readRootSaveCounter(tmpRoot)).toBeNull();
|
|
29
|
+
});
|
|
30
|
+
it('returns null when save_counter is non-number (Codex + Gemini MMR coverage lock)', () => {
|
|
31
|
+
// Coverage lock: regression guard against a future change that forgets
|
|
32
|
+
// the typeof check and coerces with Number() or String().
|
|
33
|
+
fs.writeFileSync(path.join(tmpRoot, '.scaffold', 'state.json'), JSON.stringify({ save_counter: '42', 'schema-version': 3 }));
|
|
34
|
+
expect(readRootSaveCounter(tmpRoot)).toBeNull();
|
|
35
|
+
});
|
|
36
|
+
it('returns 0 (not null) when save_counter is the number zero', () => {
|
|
37
|
+
// Coverage lock: 0 is a valid counter value (truthy-confusing). A future
|
|
38
|
+
// refactor from `typeof === "number"` to `if (counter)` would regress.
|
|
39
|
+
fs.writeFileSync(path.join(tmpRoot, '.scaffold', 'state.json'), JSON.stringify({ save_counter: 0, 'schema-version': 3 }));
|
|
40
|
+
expect(readRootSaveCounter(tmpRoot)).toBe(0);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
//# sourceMappingURL=root-counter-reader.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"root-counter-reader.test.js","sourceRoot":"","sources":["../../src/state/root-counter-reader.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,13 +6,34 @@ export declare class StateManager {
|
|
|
6
6
|
private computeEligible;
|
|
7
7
|
private configProvider?;
|
|
8
8
|
private globalSteps?;
|
|
9
|
+
/**
|
|
10
|
+
* Pipeline-graph hash for the manager's scope. If omitted, saveState writes
|
|
11
|
+
* `next_eligible_hash: undefined`, which consumers treat as invalid cache
|
|
12
|
+
* (live recompute). Legacy-safe default.
|
|
13
|
+
*/
|
|
14
|
+
private pipelineHash?;
|
|
9
15
|
private statePath;
|
|
10
16
|
private pathResolver;
|
|
11
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Captured during service-mode loadState. Used by saveState to stamp
|
|
19
|
+
* next_eligible_root_counter TOCTOU-safely (spec §3). `undefined` = never
|
|
20
|
+
* loaded; `null` = loaded but root file had no save_counter (legacy).
|
|
21
|
+
*/
|
|
22
|
+
private loadedRootCounter;
|
|
23
|
+
constructor(projectRoot: string, computeEligible: (steps: Record<string, StepStateEntry>, options?: {
|
|
24
|
+
scope?: 'global' | 'service';
|
|
25
|
+
globalSteps?: Set<string>;
|
|
26
|
+
}) => string[], configProvider?: (() => {
|
|
12
27
|
project?: {
|
|
13
28
|
services?: unknown[];
|
|
14
29
|
};
|
|
15
|
-
} | undefined) | undefined, pathResolver?: StatePathResolver, globalSteps?: Set<string> | undefined
|
|
30
|
+
} | undefined) | undefined, pathResolver?: StatePathResolver, globalSteps?: Set<string> | undefined,
|
|
31
|
+
/**
|
|
32
|
+
* Pipeline-graph hash for the manager's scope. If omitted, saveState writes
|
|
33
|
+
* `next_eligible_hash: undefined`, which consumers treat as invalid cache
|
|
34
|
+
* (live recompute). Legacy-safe default.
|
|
35
|
+
*/
|
|
36
|
+
pipelineHash?: string | undefined);
|
|
16
37
|
/** Load and validate state.json from disk. Throws ScaffoldError on schema mismatch. */
|
|
17
38
|
loadState(): PipelineState;
|
|
18
39
|
/** Atomically persist state to disk (write tmp + rename). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state-manager.d.ts","sourceRoot":"","sources":["../../src/state/state-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,gBAAgB,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAChH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAKtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAK5D,qBAAa,YAAY;
|
|
1
|
+
{"version":3,"file":"state-manager.d.ts","sourceRoot":"","sources":["../../src/state/state-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,13 +10,27 @@ export class StateManager {
|
|
|
10
10
|
computeEligible;
|
|
11
11
|
configProvider;
|
|
12
12
|
globalSteps;
|
|
13
|
+
pipelineHash;
|
|
13
14
|
statePath;
|
|
14
15
|
pathResolver;
|
|
15
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Captured during service-mode loadState. Used by saveState to stamp
|
|
18
|
+
* next_eligible_root_counter TOCTOU-safely (spec §3). `undefined` = never
|
|
19
|
+
* loaded; `null` = loaded but root file had no save_counter (legacy).
|
|
20
|
+
*/
|
|
21
|
+
loadedRootCounter = undefined;
|
|
22
|
+
constructor(projectRoot, computeEligible, configProvider, pathResolver, globalSteps,
|
|
23
|
+
/**
|
|
24
|
+
* Pipeline-graph hash for the manager's scope. If omitted, saveState writes
|
|
25
|
+
* `next_eligible_hash: undefined`, which consumers treat as invalid cache
|
|
26
|
+
* (live recompute). Legacy-safe default.
|
|
27
|
+
*/
|
|
28
|
+
pipelineHash) {
|
|
16
29
|
this.projectRoot = projectRoot;
|
|
17
30
|
this.computeEligible = computeEligible;
|
|
18
31
|
this.configProvider = configProvider;
|
|
19
32
|
this.globalSteps = globalSteps;
|
|
33
|
+
this.pipelineHash = pipelineHash;
|
|
20
34
|
this.pathResolver = pathResolver ?? new StatePathResolver(projectRoot);
|
|
21
35
|
this.statePath = this.pathResolver.statePath;
|
|
22
36
|
}
|
|
@@ -62,20 +76,58 @@ export class StateManager {
|
|
|
62
76
|
const globalState = globalParsed;
|
|
63
77
|
// Merge: global steps as base, service steps override
|
|
64
78
|
state.steps = { ...globalState.steps, ...state.steps };
|
|
79
|
+
// Capture root's save_counter at the SAME read moment (TOCTOU-safe, spec §3)
|
|
80
|
+
this.loadedRootCounter =
|
|
81
|
+
typeof globalParsed['save_counter'] === 'number'
|
|
82
|
+
? globalParsed['save_counter']
|
|
83
|
+
: null;
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
// Root state file missing — treat as legacy (null counter)
|
|
87
|
+
this.loadedRootCounter = null;
|
|
65
88
|
}
|
|
66
89
|
}
|
|
67
90
|
return state;
|
|
68
91
|
}
|
|
69
92
|
/** Atomically persist state to disk (write tmp + rename). */
|
|
70
93
|
saveState(state) {
|
|
71
|
-
|
|
94
|
+
const isService = this.pathResolver.isServiceScoped && this.globalSteps;
|
|
95
|
+
const scopeOptions = isService
|
|
96
|
+
? { scope: 'service', globalSteps: this.globalSteps }
|
|
97
|
+
: undefined;
|
|
98
|
+
// FIXES ORIGINAL P0: compute with proper scope so service state only caches
|
|
99
|
+
// service-eligible steps.
|
|
100
|
+
state.next_eligible = this.computeEligible(state.steps, scopeOptions);
|
|
101
|
+
state.next_eligible_hash = this.pipelineHash;
|
|
102
|
+
if (isService) {
|
|
103
|
+
// Cross-file invalidation stamp: capture root counter at load time,
|
|
104
|
+
// reuse at save time (TOCTOU-safe per spec §3). Only stamp when a concrete
|
|
105
|
+
// counter value was captured; under strict `exactOptionalPropertyTypes`,
|
|
106
|
+
// omitting the key (via delete) is safer than assigning `undefined`.
|
|
107
|
+
if (typeof this.loadedRootCounter === 'number') {
|
|
108
|
+
state.next_eligible_root_counter = this.loadedRootCounter;
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
delete state.next_eligible_root_counter;
|
|
112
|
+
}
|
|
113
|
+
// Spec §1 exclusivity: service saves must NEVER write save_counter on
|
|
114
|
+
// their own state. Strip any leftover (e.g. from a previously-polluted
|
|
115
|
+
// file) before persisting.
|
|
116
|
+
delete state.save_counter;
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
// Root state: bump the monotonic counter.
|
|
120
|
+
state.save_counter = (state.save_counter ?? 0) + 1;
|
|
121
|
+
// Spec §1 exclusivity: root state doesn't carry next_eligible_root_counter.
|
|
122
|
+
delete state.next_eligible_root_counter;
|
|
123
|
+
}
|
|
124
|
+
// Existing global-step stripping for service-mode persisted steps
|
|
72
125
|
let stateToWrite = state;
|
|
73
|
-
if (
|
|
126
|
+
if (isService) {
|
|
74
127
|
const filteredSteps = {};
|
|
75
128
|
for (const [name, entry] of Object.entries(state.steps)) {
|
|
76
|
-
if (!this.globalSteps.has(name))
|
|
129
|
+
if (!this.globalSteps.has(name))
|
|
77
130
|
filteredSteps[name] = entry;
|
|
78
|
-
}
|
|
79
131
|
}
|
|
80
132
|
stateToWrite = { ...state, steps: filteredSteps };
|
|
81
133
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state-manager.js","sourceRoot":"","sources":["../../src/state/state-manager.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAC5D,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AACxF,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AACnD,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAA;AACpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,MAAM,SAAS,CAAA;AAGxB,MAAM,OAAO,YAAY;
|
|
1
|
+
{"version":3,"file":"state-manager.js","sourceRoot":"","sources":["../../src/state/state-manager.ts"],"names":[],"mappings":"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"}
|