@ornexus/neocortex 4.60.68 → 4.60.69
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/release-source.json +1 -1
- package/dist/sbom.cdx.json +1194 -1194
- package/install.ps1 +1 -1
- package/install.sh +1 -1
- package/package.json +2 -2
- package/packages/client/dist/adapters/adapter-registry.js +1 -108
- package/packages/client/dist/adapters/antigravity-adapter.js +2 -77
- package/packages/client/dist/adapters/claude-code-adapter.js +3 -79
- package/packages/client/dist/adapters/codex-adapter.js +2 -80
- package/packages/client/dist/adapters/cursor-adapter.js +4 -115
- package/packages/client/dist/adapters/gemini-adapter.js +2 -71
- package/packages/client/dist/adapters/grok-adapter.js +2 -42
- package/packages/client/dist/adapters/index.js +1 -22
- package/packages/client/dist/adapters/platform-detector.js +1 -212
- package/packages/client/dist/adapters/target-adapter.js +0 -12
- package/packages/client/dist/adapters/vscode-adapter.js +2 -72
- package/packages/client/dist/agent/refresh-stubs.js +2 -417
- package/packages/client/dist/agent/update-agent-yaml.js +1 -102
- package/packages/client/dist/agent/update-description.js +1 -251
- package/packages/client/dist/cache/crypto-utils.js +1 -78
- package/packages/client/dist/cache/encrypted-cache.js +1 -108
- package/packages/client/dist/cache/in-memory-asset-cache.js +1 -73
- package/packages/client/dist/cache/index.js +1 -13
- package/packages/client/dist/cache/protected-pi-boundary.js +1 -44
- package/packages/client/dist/checkpoint/checkpoint-client-reader.js +2 -127
- package/packages/client/dist/checkpoint/index.js +1 -11
- package/packages/client/dist/checkpoint/shared-checkpoint-types.js +1 -32
- package/packages/client/dist/cli.js +3 -290
- package/packages/client/dist/commands/activate.js +8 -394
- package/packages/client/dist/commands/architecture-answers.js +7 -274
- package/packages/client/dist/commands/architecture-memory-terminal.js +4 -253
- package/packages/client/dist/commands/architecture-non-ui-continuation.js +1 -100
- package/packages/client/dist/commands/architecture-root-binding.js +1 -51
- package/packages/client/dist/commands/cache-status.js +2 -112
- package/packages/client/dist/commands/config.js +1 -88
- package/packages/client/dist/commands/doctor.js +2 -635
- package/packages/client/dist/commands/invoke.d.ts +6 -0
- package/packages/client/dist/commands/invoke.js +142 -7140
- package/packages/client/dist/commands/legacy-epic-terminal.js +1 -71
- package/packages/client/dist/commands/legacy-story-branch.js +1 -96
- package/packages/client/dist/commands/legacy-story-pr.js +1 -276
- package/packages/client/dist/commands/mutation-qa-checkpoint.js +5 -285
- package/packages/client/dist/commands/mutation-qa-gate.js +1 -54
- package/packages/client/dist/commands/mutation-qa-git.js +1 -41
- package/packages/client/dist/commands/mutation-qa-policy.js +1 -102
- package/packages/client/dist/commands/mutation-qa-run.js +3 -219
- package/packages/client/dist/commands/mutation-qa-verdict.js +1 -149
- package/packages/client/dist/commands/mutation-qa.js +1 -220
- package/packages/client/dist/commands/native-agent.js +2 -136
- package/packages/client/dist/commands/planning-diagnosis.js +1 -43
- package/packages/client/dist/commands/planning-generation.js +1 -53
- package/packages/client/dist/commands/planning-memory.js +1 -40
- package/packages/client/dist/commands/planning-research.js +1 -42
- package/packages/client/dist/commands/private-session.js +1 -75
- package/packages/client/dist/commands/property-qa.js +1 -139
- package/packages/client/dist/commands/qa-control.js +1 -16
- package/packages/client/dist/commands/qa-scan-browser-launch.js +1 -55
- package/packages/client/dist/commands/qa-scan-browser-route.js +1 -28
- package/packages/client/dist/commands/qa-scan-capture.js +5 -663
- package/packages/client/dist/commands/qa-scan-discovery.js +3 -376
- package/packages/client/dist/commands/qa-scan-review.js +5 -826
- package/packages/client/dist/commands/qa-synthetic-backend-capture.js +1 -266
- package/packages/client/dist/commands/qa-synthetic-backend-review.js +1 -297
- package/packages/client/dist/commands/qa-synthetic-backend.js +2 -340
- package/packages/client/dist/commands/qa-synthetic-broker-port.js +1 -85
- package/packages/client/dist/commands/qa-synthetic-browser.js +1 -39
- package/packages/client/dist/commands/refactor-characterization.js +21 -671
- package/packages/client/dist/commands/refactor-commit.js +13 -399
- package/packages/client/dist/commands/refactor-control.js +3 -142
- package/packages/client/dist/commands/refactor-cpp-assertions.js +3 -93
- package/packages/client/dist/commands/refactor-review.js +5 -283
- package/packages/client/dist/commands/refactor-root.js +7 -588
- package/packages/client/dist/commands/refresh-memory.js +3 -156
- package/packages/client/dist/commands/shared-architecture-non-ui.js +1 -24
- package/packages/client/dist/commands/story-checkpoint.js +1 -171
- package/packages/client/dist/config/profile-directory.js +1 -13
- package/packages/client/dist/config/resolver-selection.js +1 -278
- package/packages/client/dist/config/secure-config.js +12 -281
- package/packages/client/dist/config.js +2 -223
- package/packages/client/dist/constants.js +1 -25
- package/packages/client/dist/context/context-collector.js +2 -222
- package/packages/client/dist/context/context-sanitizer.js +1 -145
- package/packages/client/dist/continuity/autonomous-governance.js +1 -285
- package/packages/client/dist/continuity/continuity-client-state-store.js +1 -1587
- package/packages/client/dist/continuity/invoke-hooks.js +1 -129
- package/packages/client/dist/continuity/migrations/001-initial-schema.js +2 -12
- package/packages/client/dist/continuity/migrations/002-processing-output-summary-indexes.js +2 -12
- package/packages/client/dist/continuity/migrations/003-headless-execution-attempts.js +2 -12
- package/packages/client/dist/continuity/migrations/004-outbox-delivery-state.js +2 -12
- package/packages/client/dist/continuity/migrations/005-bounded-headless-queue.js +2 -12
- package/packages/client/dist/continuity/migrations/006-native-agent-dispatches.js +2 -10
- package/packages/client/dist/continuity/native-agent-store.js +1 -69
- package/packages/client/dist/continuity/native-agent-types.js +1 -90
- package/packages/client/dist/continuity/outbox.js +1 -117
- package/packages/client/dist/continuity/runner-bootstrap-policy.js +1 -172
- package/packages/client/dist/continuity/sqlite-store.js +81 -2536
- package/packages/client/dist/effects/invoke-effect-gateways.js +1 -62
- package/packages/client/dist/errors/error-messages.js +2 -228
- package/packages/client/dist/errors/frontmatter-diagnostics.js +1 -125
- package/packages/client/dist/evidence/collector.js +1 -347
- package/packages/client/dist/evidence/index.js +1 -1
- package/packages/client/dist/evidence/types.js +0 -2
- package/packages/client/dist/fs/atomic-state-replace.js +1 -152
- package/packages/client/dist/generated/command-registry.generated.js +1 -4076
- package/packages/client/dist/generated/public-projections.generated.js +1 -149
- package/packages/client/dist/git/direct-yolo-c01-retry.js +4 -138
- package/packages/client/dist/git/direct-yolo-checkpoint.js +4 -1234
- package/packages/client/dist/git/git-lifecycle-step-ids.js +1 -14
- package/packages/client/dist/git/git-lifecycle-store.js +9 -1938
- package/packages/client/dist/git/legacy-story-branch-checkpoint.js +9 -259
- package/packages/client/dist/git/legacy-story-branch-setup.js +4 -348
- package/packages/client/dist/git/legacy-story-pr.js +70 -3538
- package/packages/client/dist/git/secure-filesystem-boundary.js +5 -182
- package/packages/client/dist/graph-retrieval/pre-command-hook.js +1 -284
- package/packages/client/dist/graph-retrieval/shared-graph-retrieval-contract.js +1 -237
- package/packages/client/dist/i18n/first-run.js +2 -60
- package/packages/client/dist/index.js +1 -47
- package/packages/client/dist/invocation/invoke-input.js +1 -24
- package/packages/client/dist/license/index.js +1 -5
- package/packages/client/dist/license/license-client.js +1 -304
- package/packages/client/dist/machine/fingerprint.js +2 -163
- package/packages/client/dist/machine/index.js +1 -5
- package/packages/client/dist/memory/project-memory-path-scope.js +1 -62
- package/packages/client/dist/memory/project-memory-writer.js +40 -1007
- package/packages/client/dist/memory/shared-project-memory-types.js +2 -253
- package/packages/client/dist/migrations/project-identity-capability.js +1 -50
- package/packages/client/dist/migrations/project-identity.js +7 -493
- package/packages/client/dist/migrations/state-json.js +4 -344
- package/packages/client/dist/policy/architecture-policy.js +2 -184
- package/packages/client/dist/policy/index.js +1 -7
- package/packages/client/dist/policy/shared-policy-types.js +0 -38
- package/packages/client/dist/policy/subagent-approval-policy.js +1 -55
- package/packages/client/dist/private-execution/clean-baseline.js +2 -279
- package/packages/client/dist/private-execution/codex-executor.js +4 -574
- package/packages/client/dist/private-execution/codex-host-runtime.js +1 -137
- package/packages/client/dist/private-execution/codex-lifecycle-guard.js +1 -97
- package/packages/client/dist/private-execution/codex-scoped-permissions.js +1 -128
- package/packages/client/dist/private-execution/delivery.js +13 -410
- package/packages/client/dist/private-execution/diagnosis-root-review.js +2 -294
- package/packages/client/dist/private-execution/frozen-handoff.js +6 -19
- package/packages/client/dist/private-execution/implementation-validation.js +2 -106
- package/packages/client/dist/private-execution/isolated-admission.js +23 -1432
- package/packages/client/dist/private-execution/isolated-session.js +5 -725
- package/packages/client/dist/private-execution/isolated-workspace.js +1 -122
- package/packages/client/dist/private-execution/pact-consumer-runtime.js +3 -409
- package/packages/client/dist/private-execution/pact-test-source.js +1 -258
- package/packages/client/dist/private-execution/pact-toolchain.js +3 -290
- package/packages/client/dist/private-execution/pact-write-set.js +1 -172
- package/packages/client/dist/private-execution/planning-diagnosis-checkpoint.js +5 -406
- package/packages/client/dist/private-execution/planning-generation-checkpoint.js +5 -462
- package/packages/client/dist/private-execution/planning-memory-checkpoint.js +3 -139
- package/packages/client/dist/private-execution/planning-research-checkpoint.js +4 -325
- package/packages/client/dist/private-execution/private-report-store.js +1 -42
- package/packages/client/dist/private-execution/property-test-candidate.js +1 -103
- package/packages/client/dist/private-execution/property-test-choices.js +3 -147
- package/packages/client/dist/private-execution/property-test-plan.js +7 -271
- package/packages/client/dist/private-execution/property-test-review.js +4 -258
- package/packages/client/dist/private-execution/property-test-runtime.js +9 -372
- package/packages/client/dist/private-execution/property-test-source.js +1 -139
- package/packages/client/dist/private-execution/root-checkpoint.js +1 -80
- package/packages/client/dist/private-execution/root-memory-git-evidence.js +3 -135
- package/packages/client/dist/private-execution/root-memory-recovery.js +1 -93
- package/packages/client/dist/private-execution/root-memory-request.js +1 -29
- package/packages/client/dist/private-execution/root-memory-scope.js +1 -178
- package/packages/client/dist/private-execution/root-memory-writer.js +1 -102
- package/packages/client/dist/private-execution/session-lease.js +1 -63
- package/packages/client/dist/private-execution/shared-private-admission-types.js +1 -50
- package/packages/client/dist/private-execution/shared-project-validation.js +1 -66
- package/packages/client/dist/private-execution/shared-root-memory-types.js +1 -61
- package/packages/client/dist/private-execution/ux-source-validation.js +1 -372
- package/packages/client/dist/private-execution/ux-write-set.js +2 -191
- package/packages/client/dist/resilience/circuit-breaker.js +1 -170
- package/packages/client/dist/resilience/degradation-manager.js +1 -164
- package/packages/client/dist/resilience/freshness-indicator.js +1 -100
- package/packages/client/dist/resilience/index.js +1 -8
- package/packages/client/dist/resilience/recovery-detector.js +1 -74
- package/packages/client/dist/resolvers/asset-resolver.js +0 -13
- package/packages/client/dist/resolvers/local-resolver.js +8 -255
- package/packages/client/dist/resolvers/remote-resolver.js +1 -233
- package/packages/client/dist/runner/binary.js +1 -404
- package/packages/client/dist/runner/cli.js +17 -1571
- package/packages/client/dist/runner/headless/adapters.js +1 -49
- package/packages/client/dist/runner/headless/claude-code-adapter.js +1 -79
- package/packages/client/dist/runner/headless/continuous-story-workspace.js +11 -1628
- package/packages/client/dist/runner/headless/epic-git-reconciliation-inventory.js +1 -69
- package/packages/client/dist/runner/headless/epic-merge-candidate-qa.js +2 -110
- package/packages/client/dist/runner/headless/epic-terminal-git-cli-adapter.js +8 -1236
- package/packages/client/dist/runner/headless/epic-terminal-git-runner.js +2 -318
- package/packages/client/dist/runner/headless/fake-adapter.js +1 -62
- package/packages/client/dist/runner/headless/managed-profile.js +1 -132
- package/packages/client/dist/runner/headless/opencode-adapter.js +2 -145
- package/packages/client/dist/runner/headless/prepared-story-bundle.js +1 -43
- package/packages/client/dist/runner/headless/resolver.js +1 -231
- package/packages/client/dist/runner/headless/run-yolo.js +1 -885
- package/packages/client/dist/runner/headless/run-yoloop.js +2 -2574
- package/packages/client/dist/runner/headless/types.js +2 -574
- package/packages/client/dist/runner/local-console.js +13 -396
- package/packages/client/dist/runner/portal-sync-worker.js +1 -1487
- package/packages/client/dist/runner/project-identity.js +1 -106
- package/packages/client/dist/runner/scheduler.js +7 -995
- package/packages/client/dist/runner-cli.js +2 -18
- package/packages/client/dist/state/invocation-shape.js +1 -13
- package/packages/client/dist/state/project-state-snapshot.js +1 -1677
- package/packages/client/dist/state/state-json-repair.js +3 -785
- package/packages/client/dist/state/structural-dag-projection.js +1 -118
- package/packages/client/dist/telemetry/index.js +1 -5
- package/packages/client/dist/telemetry/offline-queue.js +1 -131
- package/packages/client/dist/tier/index.js +1 -5
- package/packages/client/dist/tier/tier-aware-client.js +1 -405
- package/packages/client/dist/types/index.js +1 -38
- package/packages/client/dist/version.js +1 -27
- package/packages/client/dist/yolo/change-evidence-collector.js +1 -126
- package/packages/client/dist/yolo/invoke-envelope-capability.js +1 -62
- package/packages/client/dist/yolo/spec-envelope-collector.js +5 -185
- package/packages/client/dist/yoloop/discovery-hook.js +2 -300
- package/packages/client/dist/yoloop/index.js +1 -8
- package/packages/client/dist/yoloop/invoke-hooks.js +12 -669
- package/packages/client/dist/yoloop/shared-discover-epics.js +1 -1007
- package/packages/client/dist/yoloop/shared-yoloop-types.js +1 -33
- package/packages/client/dist/yoloop/target-normalization.js +1 -180
- package/packages/client/dist/yoloop/yoloop-client-state-store.js +1 -1723
- package/targets-stubs/antigravity/gemini.md +1 -1
- package/targets-stubs/antigravity/skill/SKILL.md +1 -1
- package/targets-stubs/claude-code/neocortex-root.agent.yaml +1 -1
- package/targets-stubs/claude-code/neocortex-root.md +2 -2
- package/targets-stubs/claude-code/neocortex.agent.yaml +1 -1
- package/targets-stubs/claude-code/neocortex.md +2 -2
- package/targets-stubs/codex/AGENTS.md +1 -1
- package/targets-stubs/cursor/agent.md +2 -2
- package/targets-stubs/gemini-cli/agent.md +2 -2
- package/targets-stubs/opencode/neocortex-root.md +1 -1
- package/targets-stubs/vscode/neocortex.agent.md +2 -2
|
@@ -1,462 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import { isDeepStrictEqual } from 'node:util';
|
|
7
|
-
import { planningPendingSidecarDigest, planningPhysicalPatchDigest } from './planning-diagnosis-checkpoint.js';
|
|
8
|
-
import { acceptedPlanningResearchPredecessor } from './planning-research-checkpoint.js';
|
|
9
|
-
const HEX = /^[a-f0-9]{64}$/;
|
|
10
|
-
const EPIC = /^P[1-9][0-9]{0,11}$/;
|
|
11
|
-
const CLAIM = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/;
|
|
12
|
-
const P01_STEP_DIGEST = 'acd455ed0e39d98b90fb57391db75e366e4c5604db043e03a89a20e33d643c3c';
|
|
13
|
-
const P02_STEP_DIGEST = 'fa2194f89c01f07e225fd6f39e98e679f628e072239b6cd0af3ee527f0722518';
|
|
14
|
-
const P03_STEP_DIGEST = '8636f22ec5950410902448ee1e9cb4947359ac98df518742bddbbb367468e3ae';
|
|
15
|
-
function record(value) {
|
|
16
|
-
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
17
|
-
}
|
|
18
|
-
function digest(value) {
|
|
19
|
-
return createHash('sha256').update(value).digest('hex');
|
|
20
|
-
}
|
|
21
|
-
function git(root, args, maxBuffer = 128 * 1024) {
|
|
22
|
-
return execFileSync('git', [...args], { cwd: root, encoding: 'utf8',
|
|
23
|
-
stdio: ['ignore', 'pipe', 'ignore'], maxBuffer }).trim();
|
|
24
|
-
}
|
|
25
|
-
function regularBytes(root, path, maxBytes) {
|
|
26
|
-
const entry = lstatSync(join(root, path));
|
|
27
|
-
if (!entry.isFile() || entry.isSymbolicLink() || entry.size < 1 || entry.size > maxBytes) {
|
|
28
|
-
throw new Error('planning-generation-physical-artifact-invalid');
|
|
29
|
-
}
|
|
30
|
-
return readFileSync(join(root, path));
|
|
31
|
-
}
|
|
32
|
-
function committedBytes(root, path, maxBytes, revision = 'HEAD') {
|
|
33
|
-
const result = spawnSync('git', ['show', `${revision}:${path}`], { cwd: root,
|
|
34
|
-
encoding: 'buffer', maxBuffer: maxBytes });
|
|
35
|
-
if (result.status !== 0 || !Buffer.isBuffer(result.stdout)
|
|
36
|
-
|| result.stdout.length < 1 || result.stdout.length > maxBytes) {
|
|
37
|
-
throw new Error('planning-generation-predecessor-uncommitted');
|
|
38
|
-
}
|
|
39
|
-
return result.stdout;
|
|
40
|
-
}
|
|
41
|
-
function safeParents(root, epicId) {
|
|
42
|
-
for (const relativePath of ['.neocortex', '.neocortex/planning',
|
|
43
|
-
`.neocortex/planning/epic-${epicId}`, 'docs', 'docs/epics', 'docs/stories']) {
|
|
44
|
-
const entry = lstatSync(join(root, relativePath));
|
|
45
|
-
if (!entry.isDirectory() || entry.isSymbolicLink()
|
|
46
|
-
|| entry.uid !== process.getuid?.()
|
|
47
|
-
|| relativePath === '.neocortex' && (entry.mode & 0o022) !== 0) {
|
|
48
|
-
throw new Error('planning-generation-directory-boundary-invalid');
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
function scalar(content, key) {
|
|
53
|
-
const header = /^---\n([\s\S]{1,8192}?)\n---\n/.exec(content.replace(/\r\n/g, '\n'))?.[1];
|
|
54
|
-
const fields = header?.split('\n').filter((line) => line.startsWith(`${key}:`)) ?? [];
|
|
55
|
-
return fields.length === 1 ? fields[0].slice(key.length + 1).trim()
|
|
56
|
-
.replace(/^(?:"(.*)"|'(.*)')$/, '$1$2') : undefined;
|
|
57
|
-
}
|
|
58
|
-
function stepMatches(value, expected) {
|
|
59
|
-
return typeof value === 'string' && value.length <= 128 && digest(value) === expected;
|
|
60
|
-
}
|
|
61
|
-
function storyPaths(epicId, count) {
|
|
62
|
-
return Array.from({ length: count }, (_, index) => `docs/stories/${epicId}.${String(index + 1).padStart(2, '0')}.story.md`);
|
|
63
|
-
}
|
|
64
|
-
function validateReview(value) {
|
|
65
|
-
if (!record(value) || Object.keys(value).sort().join('|') !== [
|
|
66
|
-
'schemaVersion', 'verdict', 'claimId', 'epicId', 'diagnosisReceiptId',
|
|
67
|
-
'researchReceiptId', 'intakeRevision', 'sourceHead', 'sourceBranch',
|
|
68
|
-
'sourceStateSha256', 'admission', 'artifactDigests', 'qaEvidenceId', 'artifactSummary',
|
|
69
|
-
].sort().join('|') || value.schemaVersion !== 'PlanningGenerationRootReview/1'
|
|
70
|
-
|| value.verdict !== 'accepted' || typeof value.claimId !== 'string' || !CLAIM.test(value.claimId)
|
|
71
|
-
|| typeof value.epicId !== 'string' || !EPIC.test(value.epicId)
|
|
72
|
-
|| typeof value.diagnosisReceiptId !== 'string' || !HEX.test(value.diagnosisReceiptId)
|
|
73
|
-
|| typeof value.researchReceiptId !== 'string' || !HEX.test(value.researchReceiptId)
|
|
74
|
-
|| typeof value.intakeRevision !== 'string' || !HEX.test(value.intakeRevision)
|
|
75
|
-
|| typeof value.sourceHead !== 'string' || !/^[a-f0-9]{40}$/.test(value.sourceHead)
|
|
76
|
-
|| typeof value.sourceBranch !== 'string'
|
|
77
|
-
|| !/^[a-zA-Z0-9][a-zA-Z0-9._/-]{0,127}$/.test(value.sourceBranch)
|
|
78
|
-
|| typeof value.sourceStateSha256 !== 'string' || !HEX.test(value.sourceStateSha256)
|
|
79
|
-
|| !record(value.admission) || Object.keys(value.admission).sort().join('|') !== [
|
|
80
|
-
'schemaVersion', 'outcome', 'baseSha', 'executionBranch', 'changedPaths', 'patchSha256',
|
|
81
|
-
].sort().join('|') || value.admission.schemaVersion !== 1
|
|
82
|
-
|| value.admission.outcome !== 'admitted-unverified'
|
|
83
|
-
|| value.admission.baseSha !== value.sourceHead
|
|
84
|
-
|| value.admission.executionBranch !== value.sourceBranch
|
|
85
|
-
|| typeof value.admission.patchSha256 !== 'string' || !HEX.test(value.admission.patchSha256)
|
|
86
|
-
|| !record(value.artifactDigests)
|
|
87
|
-
|| Object.keys(value.artifactDigests).sort().join('|') !== 'epic|index|stories'
|
|
88
|
-
|| typeof value.artifactDigests.epic !== 'string' || !HEX.test(value.artifactDigests.epic)
|
|
89
|
-
|| typeof value.artifactDigests.index !== 'string' || !HEX.test(value.artifactDigests.index)
|
|
90
|
-
|| !record(value.artifactDigests.stories)
|
|
91
|
-
|| Object.keys(value.artifactDigests.stories).length < 1
|
|
92
|
-
|| Object.keys(value.artifactDigests.stories).length > 24
|
|
93
|
-
|| Object.values(value.artifactDigests.stories).some((entry) => typeof entry !== 'string' || !HEX.test(entry))
|
|
94
|
-
|| typeof value.qaEvidenceId !== 'string' || !/^qa-[a-z0-9-]{8,80}$/.test(value.qaEvidenceId)
|
|
95
|
-
|| typeof value.artifactSummary !== 'string' || value.artifactSummary.length < 16
|
|
96
|
-
|| value.artifactSummary.length > 2048) {
|
|
97
|
-
throw new Error('planning-generation-review-invalid');
|
|
98
|
-
}
|
|
99
|
-
const stories = storyPaths(value.epicId, Object.keys(value.artifactDigests.stories).length);
|
|
100
|
-
if (!isDeepStrictEqual(Object.keys(value.artifactDigests.stories).sort(), [...stories].sort())) {
|
|
101
|
-
throw new Error('planning-generation-review-invalid');
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
function physicalAcceptance(review) {
|
|
105
|
-
const fields = { schemaVersion: 'PlanningGenerationPhysicalAcceptance/1',
|
|
106
|
-
claimId: review.claimId, epicId: review.epicId,
|
|
107
|
-
diagnosisReceiptId: review.diagnosisReceiptId, researchReceiptId: review.researchReceiptId,
|
|
108
|
-
intakeRevision: review.intakeRevision, qaEvidenceId: review.qaEvidenceId,
|
|
109
|
-
sourceHead: review.sourceHead, sourceStateSha256: review.sourceStateSha256,
|
|
110
|
-
admissionPatchSha256: review.admission.patchSha256,
|
|
111
|
-
artifactDigests: review.artifactDigests };
|
|
112
|
-
return { ...fields, receiptId: digest(JSON.stringify(fields)), publicSafe: true };
|
|
113
|
-
}
|
|
114
|
-
function evidence(root, review, checkPatch, ownTemporaryPath, sourceStateOverride) {
|
|
115
|
-
safeParents(root, review.epicId);
|
|
116
|
-
if (git(root, ['rev-parse', 'HEAD']) !== review.sourceHead
|
|
117
|
-
|| git(root, ['symbolic-ref', '--quiet', '--short', 'HEAD']) !== review.sourceBranch) {
|
|
118
|
-
throw new Error('planning-generation-head-conflict');
|
|
119
|
-
}
|
|
120
|
-
const prefix = `.neocortex/planning/epic-${review.epicId}`;
|
|
121
|
-
const epicPath = `docs/epics/epic-${review.epicId}.md`;
|
|
122
|
-
const indexPath = '.neocortex/planning/planning.json';
|
|
123
|
-
const stories = storyPaths(review.epicId, Object.keys(review.artifactDigests.stories).length);
|
|
124
|
-
const allowed = { index: indexPath, epic: epicPath,
|
|
125
|
-
...Object.fromEntries(stories.map((path, index) => [`story${index}`, path])) };
|
|
126
|
-
if (!Array.isArray(review.admission.changedPaths)
|
|
127
|
-
|| !isDeepStrictEqual([...review.admission.changedPaths].sort(), Object.values(allowed).sort())
|
|
128
|
-
|| checkPatch && planningPhysicalPatchDigest(root, allowed, ownTemporaryPath, true)
|
|
129
|
-
!== review.admission.patchSha256) {
|
|
130
|
-
throw new Error('planning-generation-admission-patch-conflict');
|
|
131
|
-
}
|
|
132
|
-
const indexBytes = regularBytes(root, indexPath, 128 * 1024);
|
|
133
|
-
const epicBytes = regularBytes(root, epicPath, 512 * 1024);
|
|
134
|
-
const storyBytes = stories.map((path) => regularBytes(root, path, 128 * 1024));
|
|
135
|
-
if (digest(indexBytes) !== review.artifactDigests.index
|
|
136
|
-
|| digest(epicBytes) !== review.artifactDigests.epic
|
|
137
|
-
|| stories.some((path, index) => digest(storyBytes[index]) !== review.artifactDigests.stories[path])) {
|
|
138
|
-
throw new Error('planning-generation-artifact-conflict');
|
|
139
|
-
}
|
|
140
|
-
const epicText = epicBytes.toString('utf8');
|
|
141
|
-
const epicTitle = scalar(epicText, 'title');
|
|
142
|
-
if (scalar(epicText, 'id') !== review.epicId
|
|
143
|
-
|| scalar(epicText, 'stories_count') !== String(stories.length)
|
|
144
|
-
|| !epicTitle || epicTitle.length > 200 || !epicText.includes('## Stories')) {
|
|
145
|
-
throw new Error('planning-generation-epic-invalid');
|
|
146
|
-
}
|
|
147
|
-
for (const [index, bytes] of storyBytes.entries()) {
|
|
148
|
-
const text = bytes.toString('utf8');
|
|
149
|
-
const header = /^---\n([\s\S]{1,8192}?)\n---\n/.exec(text.replace(/\r\n/g, '\n'))?.[1] ?? '';
|
|
150
|
-
const canonicalEpic = scalar(text, 'epic_id');
|
|
151
|
-
// Old accepted receipts keep their original bytes on replay. New seals
|
|
152
|
-
// require the canonical field consumed by story-context collection.
|
|
153
|
-
const epicMatches = canonicalEpic === review.epicId && !/^\s*epic\s*:/m.test(header)
|
|
154
|
-
|| !checkPatch && canonicalEpic === undefined && !/^\s*epic_id\s*:/m.test(header)
|
|
155
|
-
&& scalar(text, 'epic') === review.epicId;
|
|
156
|
-
if (scalar(text, 'id') !== `${review.epicId}.${String(index + 1).padStart(2, '0')}`
|
|
157
|
-
|| !epicMatches || scalar(text, 'status') !== 'backlog'
|
|
158
|
-
|| !scalar(text, 'title') || !text.includes('## Acceptance Criteria')
|
|
159
|
-
|| !/\bGiven\b[\s\S]*\bWhen\b[\s\S]*\bThen\b/.test(text)) {
|
|
160
|
-
throw new Error('planning-generation-story-invalid');
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
const stateRaw = sourceStateOverride ?? regularBytes(root, '.neocortex/state.json', 8 * 1024 * 1024);
|
|
164
|
-
const state = JSON.parse(stateRaw.toString('utf8'));
|
|
165
|
-
if (!record(state) || !record(state.epics) || !record(state.stories)) {
|
|
166
|
-
throw new Error('planning-generation-state-invalid');
|
|
167
|
-
}
|
|
168
|
-
const predecessor = acceptedPlanningResearchPredecessor(state, review.claimId, review.epicId);
|
|
169
|
-
if (predecessor.diagnosisReceiptId !== review.diagnosisReceiptId
|
|
170
|
-
|| predecessor.researchReceiptId !== review.researchReceiptId
|
|
171
|
-
|| predecessor.intakeRevision !== review.intakeRevision
|
|
172
|
-
|| checkPatch && digest(stateRaw) !== review.sourceStateSha256) {
|
|
173
|
-
throw new Error('planning-generation-state-conflict');
|
|
174
|
-
}
|
|
175
|
-
const priorEpic = state.epics[review.epicId];
|
|
176
|
-
if (!record(priorEpic) || stories.some((path, index) => Object.hasOwn(state.stories, `${review.epicId}.${String(index + 1).padStart(2, '0')}`))) {
|
|
177
|
-
throw new Error('planning-generation-story-collision');
|
|
178
|
-
}
|
|
179
|
-
const p01 = priorEpic.createEpicPlanningPhysical;
|
|
180
|
-
const p02 = priorEpic.createEpicPlanningResearchPhysical;
|
|
181
|
-
if (!record(p01) || !record(p02) || !record(p01.artifactDigests) || !record(p02.artifactDigests)) {
|
|
182
|
-
throw new Error('planning-generation-predecessor-invalid');
|
|
183
|
-
}
|
|
184
|
-
for (const [name, path] of Object.entries({ idea: `${prefix}/idea.json`,
|
|
185
|
-
diagnosis: `${prefix}/diagnosis.md`, enrichment: `${prefix}/enriched-prompt.md`,
|
|
186
|
-
research: `${prefix}/research.md` })) {
|
|
187
|
-
const expected = name === 'research' ? p02.artifactDigests.research
|
|
188
|
-
: p01.artifactDigests[name];
|
|
189
|
-
const current = regularBytes(root, path, name === 'idea' ? 32 * 1024 : 256 * 1024);
|
|
190
|
-
if (digest(current) !== expected || !current.equals(committedBytes(root, path, 512 * 1024))) {
|
|
191
|
-
throw new Error('planning-generation-predecessor-artifact-drift');
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
const baseline = committedBytes(root, indexPath, 128 * 1024);
|
|
195
|
-
if (digest(baseline) !== p02.artifactDigests.index) {
|
|
196
|
-
throw new Error('planning-generation-predecessor-index-drift');
|
|
197
|
-
}
|
|
198
|
-
const prior = JSON.parse(baseline.toString('utf8'));
|
|
199
|
-
const next = JSON.parse(indexBytes.toString('utf8'));
|
|
200
|
-
if (!record(prior) || !record(prior.epics) || !record(next) || !record(next.epics)) {
|
|
201
|
-
throw new Error('planning-generation-index-invalid');
|
|
202
|
-
}
|
|
203
|
-
const previous = prior.epics[review.epicId];
|
|
204
|
-
const generated = next.epics[review.epicId];
|
|
205
|
-
if (!record(previous) || previous.id !== review.epicId || previous.status !== 'researched'
|
|
206
|
-
|| !Array.isArray(previous.steps_completed) || previous.steps_completed.length !== 2
|
|
207
|
-
|| !stepMatches(previous.steps_completed[0], P01_STEP_DIGEST)
|
|
208
|
-
|| !stepMatches(previous.steps_completed[1], P02_STEP_DIGEST)
|
|
209
|
-
|| !isDeepStrictEqual(previous.output_files, { diagnosis: `${prefix}/diagnosis.md`,
|
|
210
|
-
research: `${prefix}/research.md` }) || !record(generated)
|
|
211
|
-
|| !Array.isArray(generated.steps_completed)
|
|
212
|
-
|| !stepMatches(generated.steps_completed[2], P03_STEP_DIGEST)) {
|
|
213
|
-
throw new Error('planning-generation-index-predecessor-invalid');
|
|
214
|
-
}
|
|
215
|
-
const expected = { ...prior, last_scan: next.last_scan, epics: { ...prior.epics,
|
|
216
|
-
[review.epicId]: { ...previous, status: 'generated',
|
|
217
|
-
steps_completed: [...previous.steps_completed, generated.steps_completed[2]],
|
|
218
|
-
completed_at: generated.completed_at,
|
|
219
|
-
output_files: { ...previous.output_files,
|
|
220
|
-
epic: epicPath, stories } } } };
|
|
221
|
-
if (typeof next.last_scan !== 'string' || !Number.isFinite(Date.parse(next.last_scan))
|
|
222
|
-
|| typeof generated.completed_at !== 'string'
|
|
223
|
-
|| !Number.isFinite(Date.parse(generated.completed_at))
|
|
224
|
-
|| !isDeepStrictEqual(next, expected)) {
|
|
225
|
-
throw new Error('planning-generation-index-transition-invalid');
|
|
226
|
-
}
|
|
227
|
-
return { stateRaw, state, title: epicTitle, stories, storyBytes,
|
|
228
|
-
completedAt: generated.completed_at };
|
|
229
|
-
}
|
|
230
|
-
function projectState(before, review, receipt) {
|
|
231
|
-
const state = before.state;
|
|
232
|
-
const storyIds = before.stories.map((path) => path.slice('docs/stories/'.length, -'.story.md'.length));
|
|
233
|
-
const epic = state.epics[review.epicId];
|
|
234
|
-
const stories = { ...state.stories };
|
|
235
|
-
for (const [index, id] of storyIds.entries()) {
|
|
236
|
-
stories[id] = { id, epic_id: review.epicId,
|
|
237
|
-
title: scalar(before.storyBytes[index].toString('utf8'), 'title'), status: 'backlog' };
|
|
238
|
-
}
|
|
239
|
-
return { ...state, epics: { ...state.epics, [review.epicId]: { ...epic,
|
|
240
|
-
title: before.title, status: 'generated', stories: storyIds,
|
|
241
|
-
total_stories: storyIds.length, completed_stories: 0,
|
|
242
|
-
createEpicPlanningGenerationPhysical: receipt } },
|
|
243
|
-
stories, last_updated: before.completedAt };
|
|
244
|
-
}
|
|
245
|
-
/**
|
|
246
|
-
* Reopen an already sealed and committed P03 before root memory work. This is
|
|
247
|
-
* physical consistency verification, not new QA, a claim or terminal authority.
|
|
248
|
-
* The sealer intentionally stays bound to its original pre-commit HEAD.
|
|
249
|
-
*/
|
|
250
|
-
export function verifyCommittedPlanningGeneration(projectRoot, claimId, epicId) {
|
|
251
|
-
const invalid = () => new Error('planning-generation-committed-evidence-invalid');
|
|
252
|
-
const root = resolve(projectRoot);
|
|
253
|
-
if (!CLAIM.test(claimId) || !EPIC.test(epicId) || realpathSync(root) !== root
|
|
254
|
-
|| git(root, ['rev-parse', '--show-toplevel']) !== root)
|
|
255
|
-
throw invalid();
|
|
256
|
-
safeParents(root, epicId);
|
|
257
|
-
const verifiedHead = git(root, ['rev-parse', 'HEAD']);
|
|
258
|
-
const stateRaw = regularBytes(root, '.neocortex/state.json', 8 * 1024 * 1024);
|
|
259
|
-
if (!stateRaw.equals(committedBytes(root, '.neocortex/state.json', 8 * 1024 * 1024)))
|
|
260
|
-
throw invalid();
|
|
261
|
-
const current = JSON.parse(stateRaw.toString('utf8'));
|
|
262
|
-
const epic = record(current) && record(current.epics) ? current.epics[epicId] : undefined;
|
|
263
|
-
const stored = record(epic) ? epic.createEpicPlanningGenerationPhysical : undefined;
|
|
264
|
-
if (!record(stored) || stored.schemaVersion !== 'PlanningGenerationPhysicalAcceptance/1'
|
|
265
|
-
|| stored.claimId !== claimId || stored.epicId !== epicId
|
|
266
|
-
|| typeof stored.sourceHead !== 'string' || !/^[a-f0-9]{40}$/.test(stored.sourceHead)
|
|
267
|
-
|| typeof stored.qaEvidenceId !== 'string' || !/^qa-[a-z0-9-]{8,80}$/.test(stored.qaEvidenceId)
|
|
268
|
-
|| ['diagnosisReceiptId', 'researchReceiptId', 'intakeRevision', 'sourceStateSha256',
|
|
269
|
-
'admissionPatchSha256'].some(key => typeof stored[key] !== 'string' || !HEX.test(stored[key]))
|
|
270
|
-
|| !record(stored.artifactDigests)
|
|
271
|
-
|| Object.keys(stored.artifactDigests).sort().join('|') !== 'epic|index|stories'
|
|
272
|
-
|| typeof stored.artifactDigests.epic !== 'string' || !HEX.test(stored.artifactDigests.epic)
|
|
273
|
-
|| typeof stored.artifactDigests.index !== 'string' || !HEX.test(stored.artifactDigests.index)
|
|
274
|
-
|| !record(stored.artifactDigests.stories))
|
|
275
|
-
throw invalid();
|
|
276
|
-
const count = Object.keys(stored.artifactDigests.stories).length;
|
|
277
|
-
const stories = storyPaths(epicId, count);
|
|
278
|
-
if (count < 1 || count > 24
|
|
279
|
-
|| !isDeepStrictEqual(Object.keys(stored.artifactDigests.stories).sort(), [...stories].sort())
|
|
280
|
-
|| Object.values(stored.artifactDigests.stories).some(value => typeof value !== 'string' || !HEX.test(value))) {
|
|
281
|
-
throw invalid();
|
|
282
|
-
}
|
|
283
|
-
const fields = { schemaVersion: 'PlanningGenerationPhysicalAcceptance/1',
|
|
284
|
-
claimId, epicId, diagnosisReceiptId: stored.diagnosisReceiptId,
|
|
285
|
-
researchReceiptId: stored.researchReceiptId, intakeRevision: stored.intakeRevision,
|
|
286
|
-
qaEvidenceId: stored.qaEvidenceId, sourceHead: stored.sourceHead,
|
|
287
|
-
sourceStateSha256: stored.sourceStateSha256,
|
|
288
|
-
admissionPatchSha256: stored.admissionPatchSha256,
|
|
289
|
-
// The original sealer hashes this closed object's reviewed insertion order.
|
|
290
|
-
artifactDigests: stored.artifactDigests };
|
|
291
|
-
const receipt = { ...fields, receiptId: digest(JSON.stringify(fields)), publicSafe: true };
|
|
292
|
-
if (!isDeepStrictEqual(stored, receipt))
|
|
293
|
-
throw invalid();
|
|
294
|
-
const ancestry = spawnSync('git', ['merge-base', '--is-ancestor', stored.sourceHead, 'HEAD'], { cwd: root, stdio: 'ignore' });
|
|
295
|
-
if (ancestry.status !== 0)
|
|
296
|
-
throw invalid();
|
|
297
|
-
const baselineRaw = committedBytes(root, '.neocortex/state.json', 8 * 1024 * 1024, stored.sourceHead);
|
|
298
|
-
if (digest(baselineRaw) !== stored.sourceStateSha256)
|
|
299
|
-
throw invalid();
|
|
300
|
-
const baseline = JSON.parse(baselineRaw.toString('utf8'));
|
|
301
|
-
const predecessor = acceptedPlanningResearchPredecessor(baseline, claimId, epicId);
|
|
302
|
-
if (predecessor.diagnosisReceiptId !== stored.diagnosisReceiptId
|
|
303
|
-
|| predecessor.researchReceiptId !== stored.researchReceiptId
|
|
304
|
-
|| predecessor.intakeRevision !== stored.intakeRevision || !record(baseline)
|
|
305
|
-
|| !record(baseline.epics) || !record(baseline.epics[epicId]))
|
|
306
|
-
throw invalid();
|
|
307
|
-
const baselineEpic = baseline.epics[epicId];
|
|
308
|
-
const p01 = baselineEpic.createEpicPlanningPhysical;
|
|
309
|
-
const p02 = baselineEpic.createEpicPlanningResearchPhysical;
|
|
310
|
-
if (!record(p01) || !record(p01.artifactDigests) || !record(p02) || !record(p02.artifactDigests))
|
|
311
|
-
throw invalid();
|
|
312
|
-
const prefix = `.neocortex/planning/epic-${epicId}`;
|
|
313
|
-
for (const [name, path, limit] of [['idea', `${prefix}/idea.json`, 32 * 1024],
|
|
314
|
-
['diagnosis', `${prefix}/diagnosis.md`, 256 * 1024],
|
|
315
|
-
['enrichment', `${prefix}/enriched-prompt.md`, 256 * 1024],
|
|
316
|
-
['research', `${prefix}/research.md`, 256 * 1024]]) {
|
|
317
|
-
const bytes = regularBytes(root, path, limit);
|
|
318
|
-
const expected = name === 'research' ? p02.artifactDigests.research : p01.artifactDigests[name];
|
|
319
|
-
if (digest(bytes) !== expected || !bytes.equals(committedBytes(root, path, limit))
|
|
320
|
-
|| !bytes.equals(committedBytes(root, path, limit, stored.sourceHead)))
|
|
321
|
-
throw invalid();
|
|
322
|
-
}
|
|
323
|
-
const readAccepted = (path, expected, limit) => {
|
|
324
|
-
const bytes = regularBytes(root, path, limit);
|
|
325
|
-
if (digest(bytes) !== expected || !bytes.equals(committedBytes(root, path, limit)))
|
|
326
|
-
throw invalid();
|
|
327
|
-
return bytes;
|
|
328
|
-
};
|
|
329
|
-
const epicBytes = readAccepted(`docs/epics/epic-${epicId}.md`, fields.artifactDigests.epic, 512 * 1024);
|
|
330
|
-
const storyBytes = stories.map(path => readAccepted(path, fields.artifactDigests.stories[path], 128 * 1024));
|
|
331
|
-
const indexPath = '.neocortex/planning/planning.json';
|
|
332
|
-
const index = JSON.parse(readAccepted(indexPath, fields.artifactDigests.index, 128 * 1024).toString('utf8'));
|
|
333
|
-
const priorIndexRaw = committedBytes(root, indexPath, 128 * 1024, stored.sourceHead);
|
|
334
|
-
if (digest(priorIndexRaw) !== p02.artifactDigests.index)
|
|
335
|
-
throw invalid();
|
|
336
|
-
const priorIndex = JSON.parse(priorIndexRaw.toString('utf8'));
|
|
337
|
-
if (!record(index) || !record(index.epics) || !record(index.epics[epicId])
|
|
338
|
-
|| !record(priorIndex) || !record(priorIndex.epics) || !record(priorIndex.epics[epicId]))
|
|
339
|
-
throw invalid();
|
|
340
|
-
const entry = index.epics[epicId];
|
|
341
|
-
const priorEntry = priorIndex.epics[epicId];
|
|
342
|
-
if (!Array.isArray(priorEntry.steps_completed) || priorEntry.steps_completed.length !== 2
|
|
343
|
-
|| !stepMatches(priorEntry.steps_completed[0], P01_STEP_DIGEST)
|
|
344
|
-
|| !stepMatches(priorEntry.steps_completed[1], P02_STEP_DIGEST)
|
|
345
|
-
|| !Array.isArray(entry.steps_completed) || !stepMatches(entry.steps_completed[2], P03_STEP_DIGEST)
|
|
346
|
-
|| typeof entry.completed_at !== 'string' || !Number.isFinite(Date.parse(entry.completed_at))
|
|
347
|
-
|| typeof index.last_scan !== 'string' || !Number.isFinite(Date.parse(index.last_scan))
|
|
348
|
-
|| !isDeepStrictEqual(index, { ...priorIndex, last_scan: index.last_scan, epics: { ...priorIndex.epics,
|
|
349
|
-
[epicId]: { ...priorEntry, status: 'generated',
|
|
350
|
-
steps_completed: [...priorEntry.steps_completed, entry.steps_completed[2]], completed_at: entry.completed_at,
|
|
351
|
-
output_files: { ...priorEntry.output_files,
|
|
352
|
-
epic: `docs/epics/epic-${epicId}.md`, stories } } } }))
|
|
353
|
-
throw invalid();
|
|
354
|
-
const title = scalar(epicBytes.toString('utf8'), 'title');
|
|
355
|
-
if (!title || !isDeepStrictEqual(current, projectState({ stateRaw: baselineRaw, state: baseline,
|
|
356
|
-
title, stories, storyBytes, completedAt: entry.completed_at }, { epicId }, receipt)))
|
|
357
|
-
throw invalid();
|
|
358
|
-
if (git(root, ['rev-parse', 'HEAD']) !== verifiedHead
|
|
359
|
-
|| !regularBytes(root, '.neocortex/state.json', 8 * 1024 * 1024).equals(stateRaw))
|
|
360
|
-
throw invalid();
|
|
361
|
-
return { epicId, physicalAcceptanceReceiptId: receipt.receiptId,
|
|
362
|
-
storyIds: stories.map(path => path.slice('docs/stories/'.length, -'.story.md'.length)),
|
|
363
|
-
verifiedHead, stateSha256: digest(stateRaw), publicSafe: true };
|
|
364
|
-
}
|
|
365
|
-
/** The root supplies independent QA; this CAS persists the accepted P03 checkpoint. */
|
|
366
|
-
export function sealPlanningGeneration(projectRoot, reviewInput) {
|
|
367
|
-
validateReview(reviewInput);
|
|
368
|
-
const review = reviewInput;
|
|
369
|
-
const root = resolve(projectRoot);
|
|
370
|
-
if (realpathSync(root) !== root || git(root, ['rev-parse', '--show-toplevel']) !== root) {
|
|
371
|
-
throw new Error('planning-generation-root-invalid');
|
|
372
|
-
}
|
|
373
|
-
safeParents(root, review.epicId);
|
|
374
|
-
const stateDir = join(root, '.neocortex');
|
|
375
|
-
const lockPath = join(stateDir, 'state.json.planning.lock');
|
|
376
|
-
const lock = openSync(lockPath, 'wx', 0o600);
|
|
377
|
-
let temporaryPath;
|
|
378
|
-
try {
|
|
379
|
-
const pendingDigest = planningPendingSidecarDigest(root);
|
|
380
|
-
const receipt = physicalAcceptance(review);
|
|
381
|
-
const storyIds = storyPaths(review.epicId, Object.keys(review.artifactDigests.stories).length)
|
|
382
|
-
.map((path) => path.slice('docs/stories/'.length, -'.story.md'.length));
|
|
383
|
-
const currentRaw = regularBytes(root, '.neocortex/state.json', 8 * 1024 * 1024);
|
|
384
|
-
if (digest(currentRaw) !== review.sourceStateSha256) {
|
|
385
|
-
const sourceState = committedBytes(root, '.neocortex/state.json', 8 * 1024 * 1024);
|
|
386
|
-
if (digest(sourceState) !== review.sourceStateSha256) {
|
|
387
|
-
throw new Error('planning-generation-replay-conflict');
|
|
388
|
-
}
|
|
389
|
-
const baseline = evidence(root, review, false, undefined, sourceState);
|
|
390
|
-
if (!isDeepStrictEqual(JSON.parse(currentRaw.toString('utf8')), projectState(baseline, review, receipt))) {
|
|
391
|
-
throw new Error('planning-generation-replay-conflict');
|
|
392
|
-
}
|
|
393
|
-
return { epicId: review.epicId, physicalAcceptanceReceiptId: receipt.receiptId,
|
|
394
|
-
qaEvidenceId: review.qaEvidenceId, storyIds };
|
|
395
|
-
}
|
|
396
|
-
const before = evidence(root, review, true);
|
|
397
|
-
const next = projectState(before, review, receipt);
|
|
398
|
-
const commonRaw = git(root, ['rev-parse', '--git-common-dir']);
|
|
399
|
-
const commonDir = isAbsolute(commonRaw) ? resolve(commonRaw) : resolve(root, commonRaw);
|
|
400
|
-
if (realpathSync(commonDir) !== commonDir || lstatSync(commonDir).isSymbolicLink()) {
|
|
401
|
-
throw new Error('planning-generation-backup-dir-invalid');
|
|
402
|
-
}
|
|
403
|
-
const backupDir = join(commonDir, 'neocortex-planning');
|
|
404
|
-
mkdirSync(backupDir, { recursive: true, mode: 0o700 });
|
|
405
|
-
const backupEntry = lstatSync(backupDir);
|
|
406
|
-
if (!backupEntry.isDirectory() || backupEntry.isSymbolicLink()
|
|
407
|
-
|| backupEntry.uid !== process.getuid?.() || (backupEntry.mode & 0o077) !== 0) {
|
|
408
|
-
throw new Error('planning-generation-backup-dir-invalid');
|
|
409
|
-
}
|
|
410
|
-
const suffix = randomUUID();
|
|
411
|
-
const backup = openSync(join(backupDir, `state-backup-${suffix}.json`), 'wx', 0o600);
|
|
412
|
-
try {
|
|
413
|
-
writeFileSync(backup, before.stateRaw);
|
|
414
|
-
fsyncSync(backup);
|
|
415
|
-
}
|
|
416
|
-
finally {
|
|
417
|
-
closeSync(backup);
|
|
418
|
-
}
|
|
419
|
-
const backupDirectory = openSync(backupDir, 'r');
|
|
420
|
-
try {
|
|
421
|
-
fsyncSync(backupDirectory);
|
|
422
|
-
}
|
|
423
|
-
finally {
|
|
424
|
-
closeSync(backupDirectory);
|
|
425
|
-
}
|
|
426
|
-
temporaryPath = join(stateDir, `state.json.planning-tmp-${suffix}`);
|
|
427
|
-
const temporary = openSync(temporaryPath, 'wx', 0o600);
|
|
428
|
-
try {
|
|
429
|
-
writeFileSync(temporary, `${JSON.stringify(next, null, 2)}\n`);
|
|
430
|
-
fsyncSync(temporary);
|
|
431
|
-
}
|
|
432
|
-
finally {
|
|
433
|
-
closeSync(temporary);
|
|
434
|
-
}
|
|
435
|
-
const recheck = evidence(root, review, true, `.neocortex/state.json.planning-tmp-${suffix}`);
|
|
436
|
-
if (!recheck.stateRaw.equals(before.stateRaw))
|
|
437
|
-
throw new Error('planning-generation-state-conflict');
|
|
438
|
-
if (planningPendingSidecarDigest(root) !== pendingDigest) {
|
|
439
|
-
throw new Error('planning-generation-pending-conflict');
|
|
440
|
-
}
|
|
441
|
-
renameSync(temporaryPath, join(stateDir, 'state.json'));
|
|
442
|
-
temporaryPath = undefined;
|
|
443
|
-
const stateDirectory = openSync(stateDir, 'r');
|
|
444
|
-
try {
|
|
445
|
-
fsyncSync(stateDirectory);
|
|
446
|
-
}
|
|
447
|
-
finally {
|
|
448
|
-
closeSync(stateDirectory);
|
|
449
|
-
}
|
|
450
|
-
return { epicId: review.epicId, physicalAcceptanceReceiptId: receipt.receiptId,
|
|
451
|
-
qaEvidenceId: review.qaEvidenceId, storyIds };
|
|
452
|
-
}
|
|
453
|
-
finally {
|
|
454
|
-
if (temporaryPath)
|
|
455
|
-
try {
|
|
456
|
-
unlinkSync(temporaryPath);
|
|
457
|
-
}
|
|
458
|
-
catch { /* preserve primary failure */ }
|
|
459
|
-
closeSync(lock);
|
|
460
|
-
unlinkSync(lockPath);
|
|
461
|
-
}
|
|
462
|
-
}
|
|
1
|
+
import{execFileSync as lt,spawnSync as st}from"node:child_process";import{createHash as ht,randomUUID as mt}from"node:crypto";import{closeSync as U,fsyncSync as Z,lstatSync as X,mkdirSync as yt,openSync as W,readFileSync as ut,realpathSync as Y,renameSync as St,unlinkSync as it,writeFileSync as nt}from"node:fs";import{isAbsolute as It,join as N,resolve as K}from"node:path";import{isDeepStrictEqual as G}from"node:util";import{planningPendingSidecarDigest as ct,planningPhysicalPatchDigest as Dt}from"./planning-diagnosis-checkpoint.js";import{acceptedPlanningResearchPredecessor as rt}from"./planning-research-checkpoint.js";const _=/^[a-f0-9]{64}$/,ot=/^P[1-9][0-9]{0,11}$/,at=/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/,pt="acd455ed0e39d98b90fb57391db75e366e4c5604db043e03a89a20e33d643c3c",dt="fa2194f89c01f07e225fd6f39e98e679f628e072239b6cd0af3ee527f0722518",gt="8636f22ec5950410902448ee1e9cb4947359ac98df518742bddbbb367468e3ae";function r(t){return t!==null&&typeof t=="object"&&!Array.isArray(t)}function l(t){return ht("sha256").update(t).digest("hex")}function V(t,e,s=128*1024){return lt("git",[...e],{cwd:t,encoding:"utf8",stdio:["ignore","pipe","ignore"],maxBuffer:s}).trim()}function q(t,e,s){const i=X(N(t,e));if(!i.isFile()||i.isSymbolicLink()||i.size<1||i.size>s)throw new Error("planning-generation-physical-artifact-invalid");return ut(N(t,e))}function J(t,e,s,i="HEAD"){const c=st("git",["show",`${i}:${e}`],{cwd:t,encoding:"buffer",maxBuffer:s});if(c.status!==0||!Buffer.isBuffer(c.stdout)||c.stdout.length<1||c.stdout.length>s)throw new Error("planning-generation-predecessor-uncommitted");return c.stdout}function v(t,e){for(const s of[".neocortex",".neocortex/planning",`.neocortex/planning/epic-${e}`,"docs","docs/epics","docs/stories"]){const i=X(N(t,s));if(!i.isDirectory()||i.isSymbolicLink()||i.uid!==process.getuid?.()||s===".neocortex"&&(i.mode&18)!==0)throw new Error("planning-generation-directory-boundary-invalid")}}function H(t,e){const i=/^---\n([\s\S]{1,8192}?)\n---\n/.exec(t.replace(/\r\n/g,`
|
|
2
|
+
`))?.[1]?.split(`
|
|
3
|
+
`).filter(c=>c.startsWith(`${e}:`))??[];return i.length===1?i[0].slice(e.length+1).trim().replace(/^(?:"(.*)"|'(.*)')$/,"$1$2"):void 0}function L(t,e){return typeof t=="string"&&t.length<=128&&l(t)===e}function Q(t,e){return Array.from({length:e},(s,i)=>`docs/stories/${t}.${String(i+1).padStart(2,"0")}.story.md`)}function bt(t){if(!r(t)||Object.keys(t).sort().join("|")!==["schemaVersion","verdict","claimId","epicId","diagnosisReceiptId","researchReceiptId","intakeRevision","sourceHead","sourceBranch","sourceStateSha256","admission","artifactDigests","qaEvidenceId","artifactSummary"].sort().join("|")||t.schemaVersion!=="PlanningGenerationRootReview/1"||t.verdict!=="accepted"||typeof t.claimId!="string"||!at.test(t.claimId)||typeof t.epicId!="string"||!ot.test(t.epicId)||typeof t.diagnosisReceiptId!="string"||!_.test(t.diagnosisReceiptId)||typeof t.researchReceiptId!="string"||!_.test(t.researchReceiptId)||typeof t.intakeRevision!="string"||!_.test(t.intakeRevision)||typeof t.sourceHead!="string"||!/^[a-f0-9]{40}$/.test(t.sourceHead)||typeof t.sourceBranch!="string"||!/^[a-zA-Z0-9][a-zA-Z0-9._/-]{0,127}$/.test(t.sourceBranch)||typeof t.sourceStateSha256!="string"||!_.test(t.sourceStateSha256)||!r(t.admission)||Object.keys(t.admission).sort().join("|")!==["schemaVersion","outcome","baseSha","executionBranch","changedPaths","patchSha256"].sort().join("|")||t.admission.schemaVersion!==1||t.admission.outcome!=="admitted-unverified"||t.admission.baseSha!==t.sourceHead||t.admission.executionBranch!==t.sourceBranch||typeof t.admission.patchSha256!="string"||!_.test(t.admission.patchSha256)||!r(t.artifactDigests)||Object.keys(t.artifactDigests).sort().join("|")!=="epic|index|stories"||typeof t.artifactDigests.epic!="string"||!_.test(t.artifactDigests.epic)||typeof t.artifactDigests.index!="string"||!_.test(t.artifactDigests.index)||!r(t.artifactDigests.stories)||Object.keys(t.artifactDigests.stories).length<1||Object.keys(t.artifactDigests.stories).length>24||Object.values(t.artifactDigests.stories).some(s=>typeof s!="string"||!_.test(s))||typeof t.qaEvidenceId!="string"||!/^qa-[a-z0-9-]{8,80}$/.test(t.qaEvidenceId)||typeof t.artifactSummary!="string"||t.artifactSummary.length<16||t.artifactSummary.length>2048)throw new Error("planning-generation-review-invalid");const e=Q(t.epicId,Object.keys(t.artifactDigests.stories).length);if(!G(Object.keys(t.artifactDigests.stories).sort(),[...e].sort()))throw new Error("planning-generation-review-invalid")}function Et(t){const e={schemaVersion:"PlanningGenerationPhysicalAcceptance/1",claimId:t.claimId,epicId:t.epicId,diagnosisReceiptId:t.diagnosisReceiptId,researchReceiptId:t.researchReceiptId,intakeRevision:t.intakeRevision,qaEvidenceId:t.qaEvidenceId,sourceHead:t.sourceHead,sourceStateSha256:t.sourceStateSha256,admissionPatchSha256:t.admission.patchSha256,artifactDigests:t.artifactDigests};return{...e,receiptId:l(JSON.stringify(e)),publicSafe:!0}}function tt(t,e,s,i,c){if(v(t,e.epicId),V(t,["rev-parse","HEAD"])!==e.sourceHead||V(t,["symbolic-ref","--quiet","--short","HEAD"])!==e.sourceBranch)throw new Error("planning-generation-head-conflict");const y=`.neocortex/planning/epic-${e.epicId}`,u=`docs/epics/epic-${e.epicId}.md`,o=".neocortex/planning/planning.json",h=Q(e.epicId,Object.keys(e.artifactDigests.stories).length),n={index:o,epic:u,...Object.fromEntries(h.map((m,f)=>[`story${f}`,m]))};if(!Array.isArray(e.admission.changedPaths)||!G([...e.admission.changedPaths].sort(),Object.values(n).sort())||s&&Dt(t,n,i,!0)!==e.admission.patchSha256)throw new Error("planning-generation-admission-patch-conflict");const B=q(t,o,128*1024),R=q(t,u,512*1024),S=h.map(m=>q(t,m,128*1024));if(l(B)!==e.artifactDigests.index||l(R)!==e.artifactDigests.epic||h.some((m,f)=>l(S[f])!==e.artifactDigests.stories[m]))throw new Error("planning-generation-artifact-conflict");const $=R.toString("utf8"),T=H($,"title");if(H($,"id")!==e.epicId||H($,"stories_count")!==String(h.length)||!T||T.length>200||!$.includes("## Stories"))throw new Error("planning-generation-epic-invalid");for(const[m,f]of S.entries()){const d=f.toString("utf8"),D=/^---\n([\s\S]{1,8192}?)\n---\n/.exec(d.replace(/\r\n/g,`
|
|
4
|
+
`))?.[1]??"",C=H(d,"epic_id"),g=C===e.epicId&&!/^\s*epic\s*:/m.test(D)||!s&&C===void 0&&!/^\s*epic_id\s*:/m.test(D)&&H(d,"epic")===e.epicId;if(H(d,"id")!==`${e.epicId}.${String(m+1).padStart(2,"0")}`||!g||H(d,"status")!=="backlog"||!H(d,"title")||!d.includes("## Acceptance Criteria")||!/\bGiven\b[\s\S]*\bWhen\b[\s\S]*\bThen\b/.test(d))throw new Error("planning-generation-story-invalid")}const b=c??q(t,".neocortex/state.json",8*1024*1024),a=JSON.parse(b.toString("utf8"));if(!r(a)||!r(a.epics)||!r(a.stories))throw new Error("planning-generation-state-invalid");const E=rt(a,e.claimId,e.epicId);if(E.diagnosisReceiptId!==e.diagnosisReceiptId||E.researchReceiptId!==e.researchReceiptId||E.intakeRevision!==e.intakeRevision||s&&l(b)!==e.sourceStateSha256)throw new Error("planning-generation-state-conflict");const k=a.epics[e.epicId];if(!r(k)||h.some((m,f)=>Object.hasOwn(a.stories,`${e.epicId}.${String(f+1).padStart(2,"0")}`)))throw new Error("planning-generation-story-collision");const P=k.createEpicPlanningPhysical,x=k.createEpicPlanningResearchPhysical;if(!r(P)||!r(x)||!r(P.artifactDigests)||!r(x.artifactDigests))throw new Error("planning-generation-predecessor-invalid");for(const[m,f]of Object.entries({idea:`${y}/idea.json`,diagnosis:`${y}/diagnosis.md`,enrichment:`${y}/enriched-prompt.md`,research:`${y}/research.md`})){const d=m==="research"?x.artifactDigests.research:P.artifactDigests[m],D=q(t,f,m==="idea"?32*1024:256*1024);if(l(D)!==d||!D.equals(J(t,f,512*1024)))throw new Error("planning-generation-predecessor-artifact-drift")}const j=J(t,o,128*1024);if(l(j)!==x.artifactDigests.index)throw new Error("planning-generation-predecessor-index-drift");const A=JSON.parse(j.toString("utf8")),w=JSON.parse(B.toString("utf8"));if(!r(A)||!r(A.epics)||!r(w)||!r(w.epics))throw new Error("planning-generation-index-invalid");const p=A.epics[e.epicId],I=w.epics[e.epicId];if(!r(p)||p.id!==e.epicId||p.status!=="researched"||!Array.isArray(p.steps_completed)||p.steps_completed.length!==2||!L(p.steps_completed[0],pt)||!L(p.steps_completed[1],dt)||!G(p.output_files,{diagnosis:`${y}/diagnosis.md`,research:`${y}/research.md`})||!r(I)||!Array.isArray(I.steps_completed)||!L(I.steps_completed[2],gt))throw new Error("planning-generation-index-predecessor-invalid");const O={...A,last_scan:w.last_scan,epics:{...A.epics,[e.epicId]:{...p,status:"generated",steps_completed:[...p.steps_completed,I.steps_completed[2]],completed_at:I.completed_at,output_files:{...p.output_files,epic:u,stories:h}}}};if(typeof w.last_scan!="string"||!Number.isFinite(Date.parse(w.last_scan))||typeof I.completed_at!="string"||!Number.isFinite(Date.parse(I.completed_at))||!G(w,O))throw new Error("planning-generation-index-transition-invalid");return{stateRaw:b,state:a,title:T,stories:h,storyBytes:S,completedAt:I.completed_at}}function et(t,e,s){const i=t.state,c=t.stories.map(o=>o.slice(13,-9)),y=i.epics[e.epicId],u={...i.stories};for(const[o,h]of c.entries())u[h]={id:h,epic_id:e.epicId,title:H(t.storyBytes[o].toString("utf8"),"title"),status:"backlog"};return{...i,epics:{...i.epics,[e.epicId]:{...y,title:t.title,status:"generated",stories:c,total_stories:c.length,completed_stories:0,createEpicPlanningGenerationPhysical:s}},stories:u,last_updated:t.completedAt}}function kt(t,e,s){const i=()=>new Error("planning-generation-committed-evidence-invalid"),c=K(t);if(!at.test(e)||!ot.test(s)||Y(c)!==c||V(c,["rev-parse","--show-toplevel"])!==c)throw i();v(c,s);const y=V(c,["rev-parse","HEAD"]),u=q(c,".neocortex/state.json",8*1024*1024);if(!u.equals(J(c,".neocortex/state.json",8*1024*1024)))throw i();const o=JSON.parse(u.toString("utf8")),h=r(o)&&r(o.epics)?o.epics[s]:void 0,n=r(h)?h.createEpicPlanningGenerationPhysical:void 0;if(!r(n)||n.schemaVersion!=="PlanningGenerationPhysicalAcceptance/1"||n.claimId!==e||n.epicId!==s||typeof n.sourceHead!="string"||!/^[a-f0-9]{40}$/.test(n.sourceHead)||typeof n.qaEvidenceId!="string"||!/^qa-[a-z0-9-]{8,80}$/.test(n.qaEvidenceId)||["diagnosisReceiptId","researchReceiptId","intakeRevision","sourceStateSha256","admissionPatchSha256"].some(g=>typeof n[g]!="string"||!_.test(n[g]))||!r(n.artifactDigests)||Object.keys(n.artifactDigests).sort().join("|")!=="epic|index|stories"||typeof n.artifactDigests.epic!="string"||!_.test(n.artifactDigests.epic)||typeof n.artifactDigests.index!="string"||!_.test(n.artifactDigests.index)||!r(n.artifactDigests.stories))throw i();const B=Object.keys(n.artifactDigests.stories).length,R=Q(s,B);if(B<1||B>24||!G(Object.keys(n.artifactDigests.stories).sort(),[...R].sort())||Object.values(n.artifactDigests.stories).some(g=>typeof g!="string"||!_.test(g)))throw i();const S={schemaVersion:"PlanningGenerationPhysicalAcceptance/1",claimId:e,epicId:s,diagnosisReceiptId:n.diagnosisReceiptId,researchReceiptId:n.researchReceiptId,intakeRevision:n.intakeRevision,qaEvidenceId:n.qaEvidenceId,sourceHead:n.sourceHead,sourceStateSha256:n.sourceStateSha256,admissionPatchSha256:n.admissionPatchSha256,artifactDigests:n.artifactDigests},$={...S,receiptId:l(JSON.stringify(S)),publicSafe:!0};if(!G(n,$)||st("git",["merge-base","--is-ancestor",n.sourceHead,"HEAD"],{cwd:c,stdio:"ignore"}).status!==0)throw i();const b=J(c,".neocortex/state.json",8*1024*1024,n.sourceHead);if(l(b)!==n.sourceStateSha256)throw i();const a=JSON.parse(b.toString("utf8")),E=rt(a,e,s);if(E.diagnosisReceiptId!==n.diagnosisReceiptId||E.researchReceiptId!==n.researchReceiptId||E.intakeRevision!==n.intakeRevision||!r(a)||!r(a.epics)||!r(a.epics[s]))throw i();const k=a.epics[s],P=k.createEpicPlanningPhysical,x=k.createEpicPlanningResearchPhysical;if(!r(P)||!r(P.artifactDigests)||!r(x)||!r(x.artifactDigests))throw i();const j=`.neocortex/planning/epic-${s}`;for(const[g,M,z]of[["idea",`${j}/idea.json`,32*1024],["diagnosis",`${j}/diagnosis.md`,256*1024],["enrichment",`${j}/enriched-prompt.md`,256*1024],["research",`${j}/research.md`,256*1024]]){const F=q(c,M,z),ft=g==="research"?x.artifactDigests.research:P.artifactDigests[g];if(l(F)!==ft||!F.equals(J(c,M,z))||!F.equals(J(c,M,z,n.sourceHead)))throw i()}const A=(g,M,z)=>{const F=q(c,g,z);if(l(F)!==M||!F.equals(J(c,g,z)))throw i();return F},w=A(`docs/epics/epic-${s}.md`,S.artifactDigests.epic,512*1024),p=R.map(g=>A(g,S.artifactDigests.stories[g],128*1024)),I=".neocortex/planning/planning.json",O=JSON.parse(A(I,S.artifactDigests.index,128*1024).toString("utf8")),m=J(c,I,128*1024,n.sourceHead);if(l(m)!==x.artifactDigests.index)throw i();const f=JSON.parse(m.toString("utf8"));if(!r(O)||!r(O.epics)||!r(O.epics[s])||!r(f)||!r(f.epics)||!r(f.epics[s]))throw i();const d=O.epics[s],D=f.epics[s];if(!Array.isArray(D.steps_completed)||D.steps_completed.length!==2||!L(D.steps_completed[0],pt)||!L(D.steps_completed[1],dt)||!Array.isArray(d.steps_completed)||!L(d.steps_completed[2],gt)||typeof d.completed_at!="string"||!Number.isFinite(Date.parse(d.completed_at))||typeof O.last_scan!="string"||!Number.isFinite(Date.parse(O.last_scan))||!G(O,{...f,last_scan:O.last_scan,epics:{...f.epics,[s]:{...D,status:"generated",steps_completed:[...D.steps_completed,d.steps_completed[2]],completed_at:d.completed_at,output_files:{...D.output_files,epic:`docs/epics/epic-${s}.md`,stories:R}}}}))throw i();const C=H(w.toString("utf8"),"title");if(!C||!G(o,et({stateRaw:b,state:a,title:C,stories:R,storyBytes:p,completedAt:d.completed_at},{epicId:s},$))||V(c,["rev-parse","HEAD"])!==y||!q(c,".neocortex/state.json",8*1024*1024).equals(u))throw i();return{epicId:s,physicalAcceptanceReceiptId:$.receiptId,storyIds:R.map(g=>g.slice(13,-9)),verifiedHead:y,stateSha256:l(u),publicSafe:!0}}function At(t,e){bt(e);const s=e,i=K(t);if(Y(i)!==i||V(i,["rev-parse","--show-toplevel"])!==i)throw new Error("planning-generation-root-invalid");v(i,s.epicId);const c=N(i,".neocortex"),y=N(c,"state.json.planning.lock"),u=W(y,"wx",384);let o;try{const h=ct(i),n=Et(s),B=Q(s.epicId,Object.keys(s.artifactDigests.stories).length).map(p=>p.slice(13,-9)),R=q(i,".neocortex/state.json",8*1024*1024);if(l(R)!==s.sourceStateSha256){const p=J(i,".neocortex/state.json",8388608);if(l(p)!==s.sourceStateSha256)throw new Error("planning-generation-replay-conflict");const I=tt(i,s,!1,void 0,p);if(!G(JSON.parse(R.toString("utf8")),et(I,s,n)))throw new Error("planning-generation-replay-conflict");return{epicId:s.epicId,physicalAcceptanceReceiptId:n.receiptId,qaEvidenceId:s.qaEvidenceId,storyIds:B}}const S=tt(i,s,!0),$=et(S,s,n),T=V(i,["rev-parse","--git-common-dir"]),b=It(T)?K(T):K(i,T);if(Y(b)!==b||X(b).isSymbolicLink())throw new Error("planning-generation-backup-dir-invalid");const a=N(b,"neocortex-planning");yt(a,{recursive:!0,mode:448});const E=X(a);if(!E.isDirectory()||E.isSymbolicLink()||E.uid!==process.getuid?.()||(E.mode&63)!==0)throw new Error("planning-generation-backup-dir-invalid");const k=mt(),P=W(N(a,`state-backup-${k}.json`),"wx",384);try{nt(P,S.stateRaw),Z(P)}finally{U(P)}const x=W(a,"r");try{Z(x)}finally{U(x)}o=N(c,`state.json.planning-tmp-${k}`);const j=W(o,"wx",384);try{nt(j,`${JSON.stringify($,null,2)}
|
|
5
|
+
`),Z(j)}finally{U(j)}if(!tt(i,s,!0,`.neocortex/state.json.planning-tmp-${k}`).stateRaw.equals(S.stateRaw))throw new Error("planning-generation-state-conflict");if(ct(i)!==h)throw new Error("planning-generation-pending-conflict");St(o,N(c,"state.json")),o=void 0;const w=W(c,"r");try{Z(w)}finally{U(w)}return{epicId:s.epicId,physicalAcceptanceReceiptId:n.receiptId,qaEvidenceId:s.qaEvidenceId,storyIds:B}}finally{if(o)try{it(o)}catch{}U(u),it(y)}}export{At as sealPlanningGeneration,kt as verifyCommittedPlanningGeneration};
|