@ornexus/neocortex 4.60.68 → 4.60.70
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 +3 -3
- 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,135 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import { isDeepStrictEqual } from 'node:util';
|
|
5
|
-
import { canonicalRootMemoryEffectJson, getProjectMemoryTarget, listProjectMemoryTargets, isRootProjectMemoryEffect, rootProjectMemoryPaths } from './shared-root-memory-types.js';
|
|
6
|
-
import { shouldUseManagedWrapper } from '../memory/project-memory-writer.js';
|
|
7
|
-
const hash = (bytes) => createHash('sha256').update(bytes).digest('hex');
|
|
8
|
-
const fail = () => { throw new Error('private-root-memory-git-evidence-unverified'); };
|
|
9
|
-
function git(root, args) {
|
|
10
|
-
return execFileSync('git', [...args], { cwd: root, stdio: ['ignore', 'pipe', 'ignore'],
|
|
11
|
-
env: { ...Object.fromEntries(Object.entries(process.env).filter(([key]) => !key.startsWith('GIT_'))), GIT_NO_REPLACE_OBJECTS: '1' },
|
|
12
|
-
timeout: 30_000, maxBuffer: 2 * 1024 * 1024 });
|
|
13
|
-
}
|
|
14
|
-
/** Hash Git blobs without following links, using the same link-byte hash as the writer. */
|
|
15
|
-
export function rootMemoryGitFileState(root, sha, path) {
|
|
16
|
-
if (sha !== ':' && !/^[a-f0-9]{40,64}$/.test(sha) || !rootProjectMemoryPaths().includes(path))
|
|
17
|
-
return fail();
|
|
18
|
-
const tree = git(root, sha === ':' ? ['ls-files', '--stage', '-z', '--', path] : ['ls-tree', '-z', sha, '--', path]).toString('utf8');
|
|
19
|
-
if (!tree)
|
|
20
|
-
return { kind: 'absent', sha256: null };
|
|
21
|
-
const match = (sha === ':' ? /^(100644|120000) ([a-f0-9]{40,64}) 0\t([^\0]+)\0$/
|
|
22
|
-
: /^(100644|120000) blob ([a-f0-9]{40,64})\t([^\0]+)\0$/).exec(tree);
|
|
23
|
-
if (!match || match[3] !== path)
|
|
24
|
-
return fail();
|
|
25
|
-
const bytes = git(root, ['cat-file', 'blob', match[2]]);
|
|
26
|
-
if (bytes.byteLength > 512 * 1024)
|
|
27
|
-
return fail();
|
|
28
|
-
return match[1] === '120000'
|
|
29
|
-
? { kind: 'symlink', sha256: hash(bytes), linkTarget: bytes.toString('utf8') }
|
|
30
|
-
: { kind: 'regular', sha256: hash(bytes) };
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Prove that the recorded root range contains exactly the registered memory delta.
|
|
34
|
-
* The caller must separately verify the private producer receipt and live session.
|
|
35
|
-
* A JSON effect plus this inspection alone is never an execution authority.
|
|
36
|
-
*/
|
|
37
|
-
export function verifyRootMemoryEffectGitEvidence(root, effect, binding) {
|
|
38
|
-
try {
|
|
39
|
-
if (!isRootProjectMemoryEffect(effect)
|
|
40
|
-
|| !binding || ['storyId', 'graphRevision', 'childClaimRevision', 'setupSessionId', 'sourceHeadSha', 'executionBranch']
|
|
41
|
-
.some(key => effect[key] !== binding[key]))
|
|
42
|
-
return fail();
|
|
43
|
-
git(root, ['merge-base', '--is-ancestor', effect.sourceHeadSha, effect.sourceSnapshotSha]);
|
|
44
|
-
git(root, ['merge-base', '--is-ancestor', effect.sourceSnapshotSha, effect.memorySnapshotSha]);
|
|
45
|
-
const changed = git(root, ['diff', '--name-only', '--no-renames', '--no-ext-diff', '--no-textconv', '-z',
|
|
46
|
-
effect.sourceSnapshotSha, effect.memorySnapshotSha]).toString('utf8').split('\0').filter(Boolean).sort();
|
|
47
|
-
if (!isDeepStrictEqual(changed, effect.files.map(file => file.path).sort()))
|
|
48
|
-
return fail();
|
|
49
|
-
for (const file of effect.files) {
|
|
50
|
-
const before = rootMemoryGitFileState(root, effect.sourceSnapshotSha, file.path);
|
|
51
|
-
const after = rootMemoryGitFileState(root, effect.memorySnapshotSha, file.path);
|
|
52
|
-
if (!isDeepStrictEqual(before, file.before) || !isDeepStrictEqual(after, file.after)
|
|
53
|
-
|| isDeepStrictEqual(before, after))
|
|
54
|
-
return fail();
|
|
55
|
-
}
|
|
56
|
-
verifyRootMemoryGitProjection(root, effect.memorySnapshotSha, effect);
|
|
57
|
-
return effect;
|
|
58
|
-
}
|
|
59
|
-
catch {
|
|
60
|
-
return fail();
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
function verifyRootMemoryGitProjection(root, ref, effect) {
|
|
64
|
-
const canonical = rootMemoryGitFileState(root, ref, 'NEOCORTEX.md');
|
|
65
|
-
if (canonical.kind !== 'regular' || canonical.sha256 !== effect.canonicalSha256)
|
|
66
|
-
return fail();
|
|
67
|
-
// Recompute the all-target projection digest from immutable Git blobs, never
|
|
68
|
-
// from a caller's list, current working files, or a followed external link.
|
|
69
|
-
const modes = new Map();
|
|
70
|
-
for (const target of [getProjectMemoryTarget('canonical'), ...listProjectMemoryTargets()]) {
|
|
71
|
-
for (const path of [target.primaryFile, ...(target.targetId === 'canonical' ? []
|
|
72
|
-
: target.secondaryFiles.filter(file => file.scope === 'project').map(file => file.path))]) {
|
|
73
|
-
if (!modes.has(path))
|
|
74
|
-
modes.set(path, target.targetId === 'canonical' ? 'canonical'
|
|
75
|
-
: shouldUseManagedWrapper(target, path) ? 'managed-wrapper' : 'semantic-symlink');
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
const projection = [...modes].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([path, mode]) => {
|
|
79
|
-
const state = rootMemoryGitFileState(root, ref, path);
|
|
80
|
-
if (state.kind === 'absent' || mode !== 'semantic-symlink' && state.kind !== 'regular'
|
|
81
|
-
|| mode === 'semantic-symlink' && (state.kind !== 'symlink'
|
|
82
|
-
|| state.linkTarget !== '../'.repeat(path.split('/').length - 1) + 'NEOCORTEX.md'))
|
|
83
|
-
return fail();
|
|
84
|
-
return `${path}:${mode}:${state.kind === 'symlink' ? canonical.sha256 : state.sha256}`;
|
|
85
|
-
});
|
|
86
|
-
if (effect.projectionFileCount !== projection.length
|
|
87
|
-
|| effect.projectionSha256 !== hash(Buffer.from(projection.join('\n'))))
|
|
88
|
-
return fail();
|
|
89
|
-
}
|
|
90
|
-
export function rootMemoryEffectDigest(effect) {
|
|
91
|
-
return hash(Buffer.from(canonicalRootMemoryEffectJson(effect)));
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* Verify only physical ownership and bytes in the staged/committed C08 bundle.
|
|
95
|
-
* Selection must come from private producer custody or an already accepted typed
|
|
96
|
-
* checkpoint. This inspector alone does not grant QA, review or commit authority.
|
|
97
|
-
*/
|
|
98
|
-
export function verifyRootMemoryPhysicalWriteSet(root, candidate, binding) {
|
|
99
|
-
try {
|
|
100
|
-
if (!isRootProjectMemoryEffect(candidate) || !binding || candidate.storyId !== binding.storyId
|
|
101
|
-
|| candidate.executionBranch !== binding.executionBranch || candidate.sourceHeadSha !== binding.baseSha
|
|
102
|
-
|| !/^[a-f0-9]{40,64}$/.test(binding.baseSha) || binding.headRef !== ':' && !/^[a-f0-9]{40,64}$/.test(binding.headRef)
|
|
103
|
-
|| !Array.isArray(binding.childPaths) || binding.childPaths.length < 1 || binding.childPaths.length > 1024
|
|
104
|
-
|| binding.childPaths.some(path => typeof path !== 'string' || !path || path.startsWith('/') || /[\\\x00-\x1f\x7f]/.test(path)
|
|
105
|
-
|| path.split('/').includes('..') || path.split('/').includes('.git'))
|
|
106
|
-
|| new Set(binding.childPaths).size !== binding.childPaths.length
|
|
107
|
-
|| candidate.childClaimRevision !== hash(Buffer.from([...binding.childPaths].sort().join('\0'))))
|
|
108
|
-
return fail();
|
|
109
|
-
const childCovers = (path) => binding.childPaths.some(scope => scope === path || scope.endsWith('/') && path.startsWith(scope));
|
|
110
|
-
const paths = git(root, ['diff', ...(binding.headRef === ':' ? ['--cached'] : []), '--name-only', '--no-renames', '--no-ext-diff', '--no-textconv', '-z',
|
|
111
|
-
binding.baseSha, ...(binding.headRef === ':' ? [] : [binding.headRef])]).toString('utf8').split('\0').filter(Boolean);
|
|
112
|
-
const rootPaths = candidate.files.map(file => file.path).sort();
|
|
113
|
-
if (!isDeepStrictEqual(paths.filter(path => !childCovers(path)).sort(), rootPaths)
|
|
114
|
-
|| candidate.files.some(file => childCovers(file.path)))
|
|
115
|
-
return fail();
|
|
116
|
-
for (const file of candidate.files) {
|
|
117
|
-
const before = rootMemoryGitFileState(root, binding.baseSha, file.path);
|
|
118
|
-
const after = rootMemoryGitFileState(root, binding.headRef, file.path);
|
|
119
|
-
if (!isDeepStrictEqual(before, file.before) || !isDeepStrictEqual(after, file.after)
|
|
120
|
-
|| isDeepStrictEqual(before, after))
|
|
121
|
-
return fail();
|
|
122
|
-
}
|
|
123
|
-
verifyRootMemoryGitProjection(root, binding.headRef, candidate);
|
|
124
|
-
if (binding.headRef !== ':') {
|
|
125
|
-
const body = git(root, ['show', '--no-patch', '--format=%B', binding.headRef]).toString('utf8');
|
|
126
|
-
const trailers = body.split('\n').filter(line => line.startsWith('Neocortex-Root-Memory-Effect:'));
|
|
127
|
-
if (trailers.length !== 1 || trailers[0] !== `Neocortex-Root-Memory-Effect: ${rootMemoryEffectDigest(candidate)}`)
|
|
128
|
-
return fail();
|
|
129
|
-
}
|
|
130
|
-
return candidate;
|
|
131
|
-
}
|
|
132
|
-
catch {
|
|
133
|
-
return fail();
|
|
134
|
-
}
|
|
135
|
-
}
|
|
1
|
+
import{execFileSync as S}from"node:child_process";import{createHash as R}from"node:crypto";import{isDeepStrictEqual as f}from"node:util";import{canonicalRootMemoryEffectJson as x,getProjectMemoryTarget as d,listProjectMemoryTargets as E,isRootProjectMemoryEffect as p,rootProjectMemoryPaths as P}from"./shared-root-memory-types.js";import{shouldUseManagedWrapper as g}from"../memory/project-memory-writer.js";const u=o=>R("sha256").update(o).digest("hex"),i=()=>{throw new Error("private-root-memory-git-evidence-unverified")};function l(o,t){return S("git",[...t],{cwd:o,stdio:["ignore","pipe","ignore"],env:{...Object.fromEntries(Object.entries(process.env).filter(([e])=>!e.startsWith("GIT_"))),GIT_NO_REPLACE_OBJECTS:"1"},timeout:3e4,maxBuffer:2*1024*1024})}function m(o,t,e){if(t!==":"&&!/^[a-f0-9]{40,64}$/.test(t)||!P().includes(e))return i();const h=l(o,t===":"?["ls-files","--stage","-z","--",e]:["ls-tree","-z",t,"--",e]).toString("utf8");if(!h)return{kind:"absent",sha256:null};const s=(t===":"?/^(100644|120000) ([a-f0-9]{40,64}) 0\t([^\0]+)\0$/:/^(100644|120000) blob ([a-f0-9]{40,64})\t([^\0]+)\0$/).exec(h);if(!s||s[3]!==e)return i();const c=l(o,["cat-file","blob",s[2]]);return c.byteLength>512*1024?i():s[1]==="120000"?{kind:"symlink",sha256:u(c),linkTarget:c.toString("utf8")}:{kind:"regular",sha256:u(c)}}function $(o,t,e){try{if(!p(t)||!e||["storyId","graphRevision","childClaimRevision","setupSessionId","sourceHeadSha","executionBranch"].some(s=>t[s]!==e[s]))return i();l(o,["merge-base","--is-ancestor",t.sourceHeadSha,t.sourceSnapshotSha]),l(o,["merge-base","--is-ancestor",t.sourceSnapshotSha,t.memorySnapshotSha]);const h=l(o,["diff","--name-only","--no-renames","--no-ext-diff","--no-textconv","-z",t.sourceSnapshotSha,t.memorySnapshotSha]).toString("utf8").split("\0").filter(Boolean).sort();if(!f(h,t.files.map(s=>s.path).sort()))return i();for(const s of t.files){const c=m(o,t.sourceSnapshotSha,s.path),r=m(o,t.memorySnapshotSha,s.path);if(!f(c,s.before)||!f(r,s.after)||f(c,r))return i()}return y(o,t.memorySnapshotSha,t),t}catch{return i()}}function y(o,t,e){const h=m(o,t,"NEOCORTEX.md");if(h.kind!=="regular"||h.sha256!==e.canonicalSha256)return i();const s=new Map;for(const r of[d("canonical"),...E()])for(const a of[r.primaryFile,...r.targetId==="canonical"?[]:r.secondaryFiles.filter(n=>n.scope==="project").map(n=>n.path)])s.has(a)||s.set(a,r.targetId==="canonical"?"canonical":g(r,a)?"managed-wrapper":"semantic-symlink");const c=[...s].sort(([r],[a])=>r<a?-1:r>a?1:0).map(([r,a])=>{const n=m(o,t,r);return n.kind==="absent"||a!=="semantic-symlink"&&n.kind!=="regular"||a==="semantic-symlink"&&(n.kind!=="symlink"||n.linkTarget!=="../".repeat(r.split("/").length-1)+"NEOCORTEX.md")?i():`${r}:${a}:${n.kind==="symlink"?h.sha256:n.sha256}`});if(e.projectionFileCount!==c.length||e.projectionSha256!==u(Buffer.from(c.join(`
|
|
2
|
+
`))))return i()}function k(o){return u(Buffer.from(x(o)))}function C(o,t,e){try{if(!p(t)||!e||t.storyId!==e.storyId||t.executionBranch!==e.executionBranch||t.sourceHeadSha!==e.baseSha||!/^[a-f0-9]{40,64}$/.test(e.baseSha)||e.headRef!==":"&&!/^[a-f0-9]{40,64}$/.test(e.headRef)||!Array.isArray(e.childPaths)||e.childPaths.length<1||e.childPaths.length>1024||e.childPaths.some(r=>typeof r!="string"||!r||r.startsWith("/")||/[\\\x00-\x1f\x7f]/.test(r)||r.split("/").includes("..")||r.split("/").includes(".git"))||new Set(e.childPaths).size!==e.childPaths.length||t.childClaimRevision!==u(Buffer.from([...e.childPaths].sort().join("\0"))))return i();const h=r=>e.childPaths.some(a=>a===r||a.endsWith("/")&&r.startsWith(a)),s=l(o,["diff",...e.headRef===":"?["--cached"]:[],"--name-only","--no-renames","--no-ext-diff","--no-textconv","-z",e.baseSha,...e.headRef===":"?[]:[e.headRef]]).toString("utf8").split("\0").filter(Boolean),c=t.files.map(r=>r.path).sort();if(!f(s.filter(r=>!h(r)).sort(),c)||t.files.some(r=>h(r.path)))return i();for(const r of t.files){const a=m(o,e.baseSha,r.path),n=m(o,e.headRef,r.path);if(!f(a,r.before)||!f(n,r.after)||f(a,n))return i()}if(y(o,e.headRef,t),e.headRef!==":"){const a=l(o,["show","--no-patch","--format=%B",e.headRef]).toString("utf8").split(`
|
|
3
|
+
`).filter(n=>n.startsWith("Neocortex-Root-Memory-Effect:"));if(a.length!==1||a[0]!==`Neocortex-Root-Memory-Effect: ${k(t)}`)return i()}return t}catch{return i()}}export{k as rootMemoryEffectDigest,m as rootMemoryGitFileState,$ as verifyRootMemoryEffectGitEvidence,C as verifyRootMemoryPhysicalWriteSet};
|
|
@@ -1,93 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { createHash } from 'node:crypto';
|
|
3
|
-
import { execFileSync } from 'node:child_process';
|
|
4
|
-
import { existsSync, lstatSync, readFileSync, realpathSync } from 'node:fs';
|
|
5
|
-
import { join } from 'node:path';
|
|
6
|
-
import { isDeepStrictEqual } from 'node:util';
|
|
7
|
-
import { isRootProjectMemoryEffect, rootProjectMemoryPaths } from './shared-root-memory-types.js';
|
|
8
|
-
import { assertGitLifecyclePrivateFile } from '../git/git-lifecycle-store.js';
|
|
9
|
-
import { privateYoloSourceFileMatchesSnapshot } from './isolated-admission.js';
|
|
10
|
-
import { rootMemoryGitFileState, verifyRootMemoryEffectGitEvidence } from './root-memory-git-evidence.js';
|
|
11
|
-
import { rootMemoryFileState } from './root-memory-writer.js';
|
|
12
|
-
const hash = (value) => createHash('sha256').update(value).digest('hex');
|
|
13
|
-
const fail = () => { throw new Error('private-root-memory-prepared-recovery-unverified'); };
|
|
14
|
-
const exact = (value, keys) => Object.keys(value).sort().join('|') === [...keys].sort().join('|');
|
|
15
|
-
function git(root, args) {
|
|
16
|
-
return execFileSync('git', [...args], { cwd: root, stdio: ['ignore', 'pipe', 'ignore'],
|
|
17
|
-
env: { ...Object.fromEntries(Object.entries(process.env).filter(([key]) => !key.startsWith('GIT_'))), GIT_NO_REPLACE_OBJECTS: '1' },
|
|
18
|
-
timeout: 30_000, maxBuffer: 16 * 1024 * 1024 });
|
|
19
|
-
}
|
|
20
|
-
export function rootMemoryOperationJournalName(current, request) {
|
|
21
|
-
return `memory-operation-${hash(JSON.stringify([current.storyId, current.setupSessionId, current.graphRevision,
|
|
22
|
-
request.review.snapshotSha, request.review.proposalSha256, request.review.sourceStateSha256,
|
|
23
|
-
request.review.rootQaReceiptId, hash(JSON.stringify(request.review.notes))]))}.json`;
|
|
24
|
-
}
|
|
25
|
-
export async function readVerifiedPreparedRootMemory(input) {
|
|
26
|
-
try {
|
|
27
|
-
const { projectRoot: root, current, request, childPaths } = input, { review, directive } = request;
|
|
28
|
-
const path = join(input.parent, rootMemoryOperationJournalName(current, request));
|
|
29
|
-
await assertGitLifecyclePrivateFile(path, input.parent);
|
|
30
|
-
const bytes = readFileSync(path);
|
|
31
|
-
if (bytes.length > 256 * 1024)
|
|
32
|
-
return fail();
|
|
33
|
-
const journal = JSON.parse(bytes.toString('utf8'));
|
|
34
|
-
if (!journal || !exact(journal, ['schemaVersion', 'kind', 'previousSession', 'receipt', 'patchSha256'])
|
|
35
|
-
|| journal.schemaVersion !== 1 || journal.kind !== 'prepared-root-memory-effect'
|
|
36
|
-
|| !isDeepStrictEqual(journal.previousSession, current) || current.rootMemoryEffect !== undefined)
|
|
37
|
-
return fail();
|
|
38
|
-
const receipt = journal.receipt, effect = receipt?.rootMemoryEffect;
|
|
39
|
-
if (!receipt || !exact(receipt, ['schemaVersion', 'kind', 'storyId', 'graphRevision', 'sourceHeadSha', 'snapshotSha',
|
|
40
|
-
'proposalSha256', 'notesSha256', 'sourceStateSha256', 'checks', 'reviewedAt', 'rootMemoryEffect', 'independentStoryQaAccepted', 'terminalAccepted'])
|
|
41
|
-
|| !isRootProjectMemoryEffect(effect) || receipt.schemaVersion !== 1 || receipt.kind !== 'private-project-memory-root-review'
|
|
42
|
-
|| receipt.storyId !== current.storyId || receipt.graphRevision !== current.graphRevision
|
|
43
|
-
|| receipt.sourceHeadSha !== current.workspace.baseSha || receipt.snapshotSha !== effect.memorySnapshotSha
|
|
44
|
-
|| receipt.proposalSha256 !== review.proposalSha256 || receipt.sourceStateSha256 !== review.sourceStateSha256
|
|
45
|
-
|| receipt.notesSha256 !== hash(JSON.stringify(review.notes)) || receipt.notesSha256 !== effect.proposalSha256
|
|
46
|
-
|| !isDeepStrictEqual(receipt.checks, review.checks) || receipt.reviewedAt !== effect.recordedAt
|
|
47
|
-
|| effect.sourceSnapshotSha !== current.snapshotSha || effect.rootQaReceiptId !== review.rootQaReceiptId
|
|
48
|
-
|| receipt.independentStoryQaAccepted !== false || receipt.terminalAccepted !== false)
|
|
49
|
-
return fail();
|
|
50
|
-
const clone = current.workspace.workspacePath;
|
|
51
|
-
if (realpathSync(root) !== root || realpathSync(clone) !== clone || !lstatSync(clone).isDirectory()
|
|
52
|
-
|| lstatSync(clone).uid !== process.getuid?.() || (lstatSync(clone).mode & 0o077) !== 0
|
|
53
|
-
|| !lstatSync(join(clone, '.git')).isDirectory() || lstatSync(join(clone, '.git')).isSymbolicLink()
|
|
54
|
-
|| existsSync(join(clone, '.git/objects/info/alternates'))
|
|
55
|
-
|| git(clone, ['rev-parse', '--show-toplevel']).toString().trim() !== clone
|
|
56
|
-
|| git(root, ['rev-parse', '--show-toplevel']).toString().trim() !== root
|
|
57
|
-
|| git(root, ['rev-parse', 'HEAD']).toString().trim() !== current.workspace.baseSha
|
|
58
|
-
|| git(root, ['symbolic-ref', '--short', 'HEAD']).toString().trim() !== directive.executionBranch
|
|
59
|
-
|| git(clone, ['rev-parse', 'HEAD']).toString().trim() !== effect.memorySnapshotSha
|
|
60
|
-
|| git(clone, ['symbolic-ref', '--short', 'HEAD']).toString().trim() !== directive.executionBranch
|
|
61
|
-
|| git(clone, ['status', '--porcelain=v1', '-z', '--untracked-files=all', '--ignored=matching']).length !== 0
|
|
62
|
-
|| git(root, ['diff', '--cached', '--name-only', '-z']).length !== 0)
|
|
63
|
-
return fail();
|
|
64
|
-
verifyRootMemoryEffectGitEvidence(clone, effect, { storyId: current.storyId, graphRevision: current.graphRevision,
|
|
65
|
-
childClaimRevision: hash([...childPaths].sort().join('\0')), setupSessionId: current.setupSessionId,
|
|
66
|
-
sourceHeadSha: current.workspace.baseSha, executionBranch: directive.executionBranch });
|
|
67
|
-
if (childPaths.some(path => !privateYoloSourceFileMatchesSnapshot(root, clone, current.snapshotSha, path)))
|
|
68
|
-
return fail();
|
|
69
|
-
const rootPaths = effect.files.map(file => file.path);
|
|
70
|
-
const rows = git(root, ['status', '--porcelain=v1', '-z', '--untracked-files=all']).toString('utf8').split('\0').filter(Boolean);
|
|
71
|
-
if (rows.some(row => ![' M', ' D', '??', ' T'].includes(row.slice(0, 2))
|
|
72
|
-
|| !childPaths.includes(row.slice(3)) && !rootPaths.includes(row.slice(3))
|
|
73
|
-
|| row.slice(0, 2) === ' T' && !rootPaths.includes(row.slice(3))))
|
|
74
|
-
return fail();
|
|
75
|
-
let before = true, after = true;
|
|
76
|
-
for (const path of rootProjectMemoryPaths()) {
|
|
77
|
-
if (childPaths.includes(path))
|
|
78
|
-
return fail();
|
|
79
|
-
const actual = rootMemoryFileState(root, path);
|
|
80
|
-
before &&= isDeepStrictEqual(actual, rootMemoryGitFileState(clone, current.snapshotSha, path));
|
|
81
|
-
after &&= isDeepStrictEqual(actual, rootMemoryGitFileState(clone, effect.memorySnapshotSha, path));
|
|
82
|
-
}
|
|
83
|
-
if (!before && !after)
|
|
84
|
-
return fail();
|
|
85
|
-
const patch = git(clone, ['diff', '--binary', '--full-index', current.snapshotSha, effect.memorySnapshotSha]);
|
|
86
|
-
if (hash(patch) !== journal.patchSha256)
|
|
87
|
-
return fail();
|
|
88
|
-
return { receipt, effect, patch, sourceAlreadyApplied: after, journalPath: path };
|
|
89
|
-
}
|
|
90
|
-
catch {
|
|
91
|
-
return fail();
|
|
92
|
-
}
|
|
93
|
-
}
|
|
1
|
+
import{createHash as x}from"node:crypto";import{execFileSync as M}from"node:child_process";import{existsSync as O,lstatSync as m,readFileSync as P,realpathSync as A}from"node:fs";import{join as l}from"node:path";import{isDeepStrictEqual as d}from"node:util";import{isRootProjectMemoryEffect as D,rootProjectMemoryPaths as H}from"./shared-root-memory-types.js";import{assertGitLifecyclePrivateFile as B}from"../git/git-lifecycle-store.js";import{privateYoloSourceFileMatchesSnapshot as F}from"./isolated-admission.js";import{rootMemoryGitFileState as I,verifyRootMemoryEffectGitEvidence as J}from"./root-memory-git-evidence.js";import{rootMemoryFileState as N}from"./root-memory-writer.js";const S=s=>x("sha256").update(s).digest("hex"),n=()=>{throw new Error("private-root-memory-prepared-recovery-unverified")},b=(s,e)=>Object.keys(s).sort().join("|")===[...e].sort().join("|");function c(s,e){return M("git",[...e],{cwd:s,stdio:["ignore","pipe","ignore"],env:{...Object.fromEntries(Object.entries(process.env).filter(([t])=>!t.startsWith("GIT_"))),GIT_NO_REPLACE_OBJECTS:"1"},timeout:3e4,maxBuffer:16*1024*1024})}function G(s,e){return`memory-operation-${S(JSON.stringify([s.storyId,s.setupSessionId,s.graphRevision,e.review.snapshotSha,e.review.proposalSha256,e.review.sourceStateSha256,e.review.rootQaReceiptId,S(JSON.stringify(e.review.notes))]))}.json`}async function U(s){try{const{projectRoot:e,current:t,request:g,childPaths:f}=s,{review:h,directive:u}=g,y=l(s.parent,G(t,g));await B(y,s.parent);const w=P(y);if(w.length>256*1024)return n();const p=JSON.parse(w.toString("utf8"));if(!p||!b(p,["schemaVersion","kind","previousSession","receipt","patchSha256"])||p.schemaVersion!==1||p.kind!=="prepared-root-memory-effect"||!d(p.previousSession,t)||t.rootMemoryEffect!==void 0)return n();const r=p.receipt,a=r?.rootMemoryEffect;if(!r||!b(r,["schemaVersion","kind","storyId","graphRevision","sourceHeadSha","snapshotSha","proposalSha256","notesSha256","sourceStateSha256","checks","reviewedAt","rootMemoryEffect","independentStoryQaAccepted","terminalAccepted"])||!D(a)||r.schemaVersion!==1||r.kind!=="private-project-memory-root-review"||r.storyId!==t.storyId||r.graphRevision!==t.graphRevision||r.sourceHeadSha!==t.workspace.baseSha||r.snapshotSha!==a.memorySnapshotSha||r.proposalSha256!==h.proposalSha256||r.sourceStateSha256!==h.sourceStateSha256||r.notesSha256!==S(JSON.stringify(h.notes))||r.notesSha256!==a.proposalSha256||!d(r.checks,h.checks)||r.reviewedAt!==a.recordedAt||a.sourceSnapshotSha!==t.snapshotSha||a.rootQaReceiptId!==h.rootQaReceiptId||r.independentStoryQaAccepted!==!1||r.terminalAccepted!==!1)return n();const o=t.workspace.workspacePath;if(A(e)!==e||A(o)!==o||!m(o).isDirectory()||m(o).uid!==process.getuid?.()||(m(o).mode&63)!==0||!m(l(o,".git")).isDirectory()||m(l(o,".git")).isSymbolicLink()||O(l(o,".git/objects/info/alternates"))||c(o,["rev-parse","--show-toplevel"]).toString().trim()!==o||c(e,["rev-parse","--show-toplevel"]).toString().trim()!==e||c(e,["rev-parse","HEAD"]).toString().trim()!==t.workspace.baseSha||c(e,["symbolic-ref","--short","HEAD"]).toString().trim()!==u.executionBranch||c(o,["rev-parse","HEAD"]).toString().trim()!==a.memorySnapshotSha||c(o,["symbolic-ref","--short","HEAD"]).toString().trim()!==u.executionBranch||c(o,["status","--porcelain=v1","-z","--untracked-files=all","--ignored=matching"]).length!==0||c(e,["diff","--cached","--name-only","-z"]).length!==0||(J(o,a,{storyId:t.storyId,graphRevision:t.graphRevision,childClaimRevision:S([...f].sort().join("\0")),setupSessionId:t.setupSessionId,sourceHeadSha:t.workspace.baseSha,executionBranch:u.executionBranch}),f.some(i=>!F(e,o,t.snapshotSha,i))))return n();const k=a.files.map(i=>i.path);if(c(e,["status","--porcelain=v1","-z","--untracked-files=all"]).toString("utf8").split("\0").filter(Boolean).some(i=>![" M"," D","??"," T"].includes(i.slice(0,2))||!f.includes(i.slice(3))&&!k.includes(i.slice(3))||i.slice(0,2)===" T"&&!k.includes(i.slice(3))))return n();let E=!0,v=!0;for(const i of H()){if(f.includes(i))return n();const R=N(e,i);E&&=d(R,I(o,t.snapshotSha,i)),v&&=d(R,I(o,a.memorySnapshotSha,i))}if(!E&&!v)return n();const j=c(o,["diff","--binary","--full-index",t.snapshotSha,a.memorySnapshotSha]);return S(j)!==p.patchSha256?n():{receipt:r,effect:a,patch:j,sourceAlreadyApplied:v,journalPath:y}}catch{return n()}}export{U as readVerifiedPreparedRootMemory,G as rootMemoryOperationJournalName};
|
|
@@ -1,29 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { createHash } from 'node:crypto';
|
|
3
|
-
export const ROOT_MEMORY_STEP_DIGEST = '638cc25234b6a507841c889ad7f5c8c1e33bd38aa20ca7818b0f21c4a3e9030e';
|
|
4
|
-
export const ROOT_MEMORY_REVIEW_CHECKS = ['canonicalUpdateReviewed', 'acceptedQaReviewed', 'scopeReviewed', 'unresolvedGatesPreserved',
|
|
5
|
-
'platformTargetsReviewed', 'wrapperPreservationReviewed', 'mandatoryRequirementsResolved', 'noImplementationChange'];
|
|
6
|
-
export function parsePrivateRootMemoryRequest(value) {
|
|
7
|
-
const fail = () => { throw new Error('private-root-memory-review-unverified'); };
|
|
8
|
-
const object = (v) => v !== null && typeof v === 'object' && !Array.isArray(v) ? v : undefined;
|
|
9
|
-
const exact = (v, keys) => Object.keys(v).sort().join('|') === keys.sort().join('|');
|
|
10
|
-
const r = object(value), d = object(r?.directive), review = object(r?.review), checks = object(review?.checks);
|
|
11
|
-
if (!r || !d || !review || !checks || !exact(r, ['schemaVersion', 'executorStopped', 'directive', 'review'])
|
|
12
|
-
|| r.schemaVersion !== 'PrivateYoloProjectMemoryAcceptance/1' || r.executorStopped !== true
|
|
13
|
-
|| !exact(d, ['schemaVersion', 'action', 'storyId', 'executionBranch', 'graphRevision', 'stepId', 'publicSafe'])
|
|
14
|
-
|| d.schemaVersion !== 1 || d.action !== 'review-project-memory-candidate' || d.stepId !== 'step-638cc252' || d.publicSafe !== true
|
|
15
|
-
|| typeof d.storyId !== 'string' || !/^P[1-9][0-9]*\.[0-9]+$/.test(d.storyId)
|
|
16
|
-
|| d.executionBranch !== `epic/${d.storyId.split('.')[0]}` || typeof d.graphRevision !== 'string' || !/^[a-f0-9]{64}$/.test(d.graphRevision)
|
|
17
|
-
|| !exact(review, ['reviewer', 'decision', 'snapshotSha', 'proposalSha256', 'sourceStateSha256', 'rootQaReceiptId', 'rawStepId', 'reviewedAt', 'notes', 'checks'])
|
|
18
|
-
|| review.reviewer !== 'root' || review.decision !== 'approve-project-memory'
|
|
19
|
-
|| typeof review.snapshotSha !== 'string' || !/^[a-f0-9]{40,64}$/.test(review.snapshotSha)
|
|
20
|
-
|| ['proposalSha256', 'sourceStateSha256', 'rootQaReceiptId'].some(k => typeof review[k] !== 'string' || !/^[a-f0-9]{64}$/.test(review[k]))
|
|
21
|
-
|| typeof review.rawStepId !== 'string' || createHash('sha256').update(review.rawStepId).digest('hex') !== ROOT_MEMORY_STEP_DIGEST
|
|
22
|
-
|| typeof review.reviewedAt !== 'string' || !Number.isFinite(Date.parse(review.reviewedAt))
|
|
23
|
-
|| Date.parse(review.reviewedAt) > Date.now() || Date.now() - Date.parse(review.reviewedAt) > 10 * 60_000
|
|
24
|
-
|| !Array.isArray(review.notes) || review.notes.length < 1 || review.notes.length > 8
|
|
25
|
-
|| review.notes.some(note => typeof note !== 'string' || Buffer.byteLength(note) < 1 || Buffer.byteLength(note) > 512)
|
|
26
|
-
|| !exact(checks, [...ROOT_MEMORY_REVIEW_CHECKS]) || ROOT_MEMORY_REVIEW_CHECKS.some(key => checks[key] !== true))
|
|
27
|
-
return fail();
|
|
28
|
-
return structuredClone(value);
|
|
29
|
-
}
|
|
1
|
+
import{createHash as h}from"node:crypto";const v="638cc25234b6a507841c889ad7f5c8c1e33bd38aa20ca7818b0f21c4a3e9030e",c=["canonicalUpdateReviewed","acceptedQaReviewed","scopeReviewed","unresolvedGatesPreserved","platformTargetsReviewed","wrapperPreservationReviewed","mandatoryRequirementsResolved","noImplementationChange"];function u(n){const p=()=>{throw new Error("private-root-memory-review-unverified")},s=t=>t!==null&&typeof t=="object"&&!Array.isArray(t)?t:void 0,a=(t,d)=>Object.keys(t).sort().join("|")===d.sort().join("|"),o=s(n),r=s(o?.directive),e=s(o?.review),i=s(e?.checks);return!o||!r||!e||!i||!a(o,["schemaVersion","executorStopped","directive","review"])||o.schemaVersion!=="PrivateYoloProjectMemoryAcceptance/1"||o.executorStopped!==!0||!a(r,["schemaVersion","action","storyId","executionBranch","graphRevision","stepId","publicSafe"])||r.schemaVersion!==1||r.action!=="review-project-memory-candidate"||r.stepId!=="step-638cc252"||r.publicSafe!==!0||typeof r.storyId!="string"||!/^P[1-9][0-9]*\.[0-9]+$/.test(r.storyId)||r.executionBranch!==`epic/${r.storyId.split(".")[0]}`||typeof r.graphRevision!="string"||!/^[a-f0-9]{64}$/.test(r.graphRevision)||!a(e,["reviewer","decision","snapshotSha","proposalSha256","sourceStateSha256","rootQaReceiptId","rawStepId","reviewedAt","notes","checks"])||e.reviewer!=="root"||e.decision!=="approve-project-memory"||typeof e.snapshotSha!="string"||!/^[a-f0-9]{40,64}$/.test(e.snapshotSha)||["proposalSha256","sourceStateSha256","rootQaReceiptId"].some(t=>typeof e[t]!="string"||!/^[a-f0-9]{64}$/.test(e[t]))||typeof e.rawStepId!="string"||h("sha256").update(e.rawStepId).digest("hex")!==v||typeof e.reviewedAt!="string"||!Number.isFinite(Date.parse(e.reviewedAt))||Date.parse(e.reviewedAt)>Date.now()||Date.now()-Date.parse(e.reviewedAt)>10*6e4||!Array.isArray(e.notes)||e.notes.length<1||e.notes.length>8||e.notes.some(t=>typeof t!="string"||Buffer.byteLength(t)<1||Buffer.byteLength(t)>512)||!a(i,[...c])||c.some(t=>i[t]!==!0)?p():structuredClone(n)}export{c as ROOT_MEMORY_REVIEW_CHECKS,v as ROOT_MEMORY_STEP_DIGEST,u as parsePrivateRootMemoryRequest};
|
|
@@ -1,178 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { createHash } from 'node:crypto';
|
|
3
|
-
import { execFileSync } from 'node:child_process';
|
|
4
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
5
|
-
import { join, resolve } from 'node:path';
|
|
6
|
-
import { isDeepStrictEqual } from 'node:util';
|
|
7
|
-
import { isRootProjectMemoryEffect } from './shared-root-memory-types.js';
|
|
8
|
-
import { assertGitLifecyclePrivateFile, ensureGitLifecyclePrivateChildDirectory, gitLifecycleRuntimeDirectory, readGitLifecycleRuntime } from '../git/git-lifecycle-store.js';
|
|
9
|
-
import { rootMemoryGitFileState, verifyRootMemoryEffectGitEvidence, verifyRootMemoryPhysicalWriteSet } from './root-memory-git-evidence.js';
|
|
10
|
-
import { rootMemoryFileState } from './root-memory-writer.js';
|
|
11
|
-
import { ROOT_MEMORY_REVIEW_CHECKS, ROOT_MEMORY_STEP_DIGEST } from './root-memory-request.js';
|
|
12
|
-
import { parseRefreshMemoryArgs, verifyProjectMemoryTargets } from '../commands/refresh-memory.js';
|
|
13
|
-
const scopes = new WeakMap();
|
|
14
|
-
const fail = () => { throw new Error('private-root-memory-scope-unverified'); };
|
|
15
|
-
function git(root, args) {
|
|
16
|
-
return execFileSync('git', [...args], { cwd: root, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'],
|
|
17
|
-
env: { ...Object.fromEntries(Object.entries(process.env).filter(([key]) => !key.startsWith('GIT_'))), GIT_NO_REPLACE_OBJECTS: '1' },
|
|
18
|
-
timeout: 30_000, maxBuffer: 2 * 1024 * 1024 });
|
|
19
|
-
}
|
|
20
|
-
/** Absence means no extra scope; malformed or mismatched existing evidence fails closed. */
|
|
21
|
-
export async function readPrivateRootMemoryScope(input) {
|
|
22
|
-
let stage = 'pin';
|
|
23
|
-
try {
|
|
24
|
-
const root = resolve(input.projectRoot);
|
|
25
|
-
if (!/^P[1-9][0-9]*\.[0-9]+$/.test(input.storyId))
|
|
26
|
-
return fail();
|
|
27
|
-
const parent = await ensureGitLifecyclePrivateChildDirectory(await gitLifecycleRuntimeDirectory(root), 'private-execution');
|
|
28
|
-
const path = join(parent, `session-${createHash('sha256').update(input.storyId).digest('hex')}.json`);
|
|
29
|
-
await assertGitLifecyclePrivateFile(path, parent);
|
|
30
|
-
const bytes = readFileSync(path);
|
|
31
|
-
if (bytes.length > 128 * 1024)
|
|
32
|
-
return fail();
|
|
33
|
-
const pin = JSON.parse(bytes.toString('utf8'));
|
|
34
|
-
stage = 'binding';
|
|
35
|
-
if (pin.storyId !== input.storyId || pin.graphRevision !== input.graphRevision
|
|
36
|
-
|| pin.setupSessionId !== input.setupSessionId || pin.snapshotSha !== input.snapshotSha
|
|
37
|
-
|| pin.workspace?.sourceRoot !== root || pin.workspace?.workspacePath !== input.snapshotRoot
|
|
38
|
-
|| pin.workspace?.baseSha !== input.sourceHeadSha || pin.workspace?.executionBranch !== input.executionBranch)
|
|
39
|
-
return fail();
|
|
40
|
-
if (pin.rootMemoryEffect === undefined)
|
|
41
|
-
return undefined;
|
|
42
|
-
const effect = pin.rootMemoryEffect;
|
|
43
|
-
if (!isRootProjectMemoryEffect(effect) || !Array.isArray(pin.graphHistory)
|
|
44
|
-
|| !pin.graphHistory.includes(effect.graphRevision))
|
|
45
|
-
return fail();
|
|
46
|
-
stage = 'producer-receipt';
|
|
47
|
-
const digest = (value) => createHash('sha256').update(value).digest('hex');
|
|
48
|
-
const receiptPath = join(parent, `memory-review-${digest(JSON.stringify(effect))}.json`);
|
|
49
|
-
await assertGitLifecyclePrivateFile(receiptPath, parent);
|
|
50
|
-
const receiptBytes = readFileSync(receiptPath);
|
|
51
|
-
if (receiptBytes.length > 128 * 1024)
|
|
52
|
-
return fail();
|
|
53
|
-
const receipt = JSON.parse(receiptBytes.toString('utf8'));
|
|
54
|
-
if (Object.keys(receipt).sort().join('|') !== ['schemaVersion', 'kind', 'storyId', 'graphRevision', 'sourceHeadSha', 'snapshotSha',
|
|
55
|
-
'proposalSha256', 'notesSha256', 'sourceStateSha256', 'checks', 'reviewedAt', 'rootMemoryEffect', 'independentStoryQaAccepted', 'terminalAccepted'].sort().join('|')
|
|
56
|
-
|| receipt.schemaVersion !== 1 || receipt.kind !== 'private-project-memory-root-review'
|
|
57
|
-
|| receipt.storyId !== input.storyId || receipt.graphRevision !== effect.graphRevision || receipt.sourceHeadSha !== input.sourceHeadSha
|
|
58
|
-
|| receipt.snapshotSha !== effect.memorySnapshotSha || receipt.notesSha256 !== effect.proposalSha256 || receipt.reviewedAt !== effect.recordedAt
|
|
59
|
-
|| receipt.independentStoryQaAccepted !== false || receipt.terminalAccepted !== false || !isDeepStrictEqual(receipt.rootMemoryEffect, effect)
|
|
60
|
-
|| !receipt.checks || Object.keys(receipt.checks).sort().join('|') !== [...ROOT_MEMORY_REVIEW_CHECKS].sort().join('|')
|
|
61
|
-
|| ROOT_MEMORY_REVIEW_CHECKS.some(key => receipt.checks[key] !== true)
|
|
62
|
-
|| receipt.proposalSha256 !== digest(Buffer.from(git(input.snapshotRoot, ['show', `${effect.sourceSnapshotSha}:.neocortex/stories/${input.storyId}/validation-evidence.md`])))
|
|
63
|
-
|| receipt.sourceStateSha256 !== digest(Buffer.from(git(input.snapshotRoot, ['show', `${effect.sourceSnapshotSha}:.neocortex/state.json`]))))
|
|
64
|
-
return fail();
|
|
65
|
-
const qaPath = join(parent, `qa-review-${effect.rootQaReceiptId}.json`);
|
|
66
|
-
await assertGitLifecyclePrivateFile(qaPath, parent);
|
|
67
|
-
const qaBytes = readFileSync(qaPath);
|
|
68
|
-
if (qaBytes.length > 16384 || digest(qaBytes) !== effect.rootQaReceiptId)
|
|
69
|
-
return fail();
|
|
70
|
-
const qa = JSON.parse(qaBytes.toString('utf8'));
|
|
71
|
-
if (qa.kind !== 'private-qa-root-review' || qa.storyId !== input.storyId || qa.sourceHeadSha !== input.sourceHeadSha
|
|
72
|
-
|| qa.precommitQaAccepted !== true || qa.independentStoryQaAccepted !== false || qa.terminalAccepted !== false)
|
|
73
|
-
return fail();
|
|
74
|
-
git(input.snapshotRoot, ['merge-base', '--is-ancestor', qa.snapshotSha, effect.sourceSnapshotSha]);
|
|
75
|
-
stage = 'git-evidence';
|
|
76
|
-
verifyRootMemoryEffectGitEvidence(input.snapshotRoot, effect, {
|
|
77
|
-
storyId: input.storyId, executionBranch: input.executionBranch, sourceHeadSha: input.sourceHeadSha,
|
|
78
|
-
graphRevision: effect.graphRevision, setupSessionId: input.setupSessionId,
|
|
79
|
-
childClaimRevision: createHash('sha256').update([...input.childPaths].sort().join('\0')).digest('hex'),
|
|
80
|
-
});
|
|
81
|
-
stage = 'range';
|
|
82
|
-
git(input.snapshotRoot, ['merge-base', '--is-ancestor', effect.memorySnapshotSha, input.snapshotSha]);
|
|
83
|
-
const changed = git(input.snapshotRoot, ['diff', '--name-only', '--no-renames', '--no-ext-diff', '--no-textconv', '-z',
|
|
84
|
-
input.sourceHeadSha, input.snapshotSha]).split('\0').filter(Boolean);
|
|
85
|
-
if (changed.some(path => !input.childPaths.includes(path) && !effect.files.some(file => file.path === path)))
|
|
86
|
-
return fail();
|
|
87
|
-
stage = 'files';
|
|
88
|
-
for (const file of effect.files) {
|
|
89
|
-
if (input.childPaths.includes(file.path)
|
|
90
|
-
|| !isDeepStrictEqual(rootMemoryGitFileState(input.snapshotRoot, input.sourceHeadSha, file.path), file.before)
|
|
91
|
-
|| !isDeepStrictEqual(rootMemoryGitFileState(input.snapshotRoot, input.snapshotSha, file.path), file.after))
|
|
92
|
-
return fail();
|
|
93
|
-
}
|
|
94
|
-
const scope = Object.freeze(Object.create(null));
|
|
95
|
-
scopes.set(scope, Object.freeze({ effect: structuredClone(effect), root, snapshotRoot: input.snapshotRoot }));
|
|
96
|
-
return scope;
|
|
97
|
-
}
|
|
98
|
-
catch {
|
|
99
|
-
throw new Error(`private-root-memory-scope-${stage}-unverified`);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
/** A caller-provided object never supplies extra source or agent permissions. */
|
|
103
|
-
export function privateRootMemoryEffect(scope) {
|
|
104
|
-
const value = scopes.get(scope);
|
|
105
|
-
if (!value)
|
|
106
|
-
return fail();
|
|
107
|
-
return structuredClone(value.effect);
|
|
108
|
-
}
|
|
109
|
-
export function privateRootMemorySourceMatches(scope, root, snapshotRoot, snapshotSha) {
|
|
110
|
-
return privateRootMemorySourceMatchReason(scope, root, snapshotRoot, snapshotSha) === 'matched';
|
|
111
|
-
}
|
|
112
|
-
export function privateRootMemorySourceMatchReason(scope, root, snapshotRoot, snapshotSha) {
|
|
113
|
-
const value = scopes.get(scope);
|
|
114
|
-
if (!value || value.root !== resolve(root) || snapshotRoot !== undefined && snapshotRoot !== value.snapshotRoot)
|
|
115
|
-
return 'scope-binding';
|
|
116
|
-
let stage = 'snapshot-ancestry';
|
|
117
|
-
try {
|
|
118
|
-
if (snapshotSha) {
|
|
119
|
-
git(value.snapshotRoot, ['merge-base', '--is-ancestor', value.effect.memorySnapshotSha, snapshotSha]);
|
|
120
|
-
stage = 'snapshot-content';
|
|
121
|
-
if (value.effect.files.some(file => !isDeepStrictEqual(rootMemoryGitFileState(value.snapshotRoot, snapshotSha, file.path), file.after)))
|
|
122
|
-
return stage;
|
|
123
|
-
}
|
|
124
|
-
stage = 'source-content';
|
|
125
|
-
if (!value.effect.files.every(file => isDeepStrictEqual(rootMemoryFileState(root, file.path), file.after)))
|
|
126
|
-
return stage;
|
|
127
|
-
stage = 'source-projection';
|
|
128
|
-
const verified = verifyProjectMemoryTargets(parseRefreshMemoryArgs(['--project-root', root, '--verify']));
|
|
129
|
-
return verified.canonicalSha256 === value.effect.canonicalSha256 && verified.projectionSha256 === value.effect.projectionSha256
|
|
130
|
-
&& verified.fileCount === value.effect.projectionFileCount ? 'matched' : stage;
|
|
131
|
-
}
|
|
132
|
-
catch {
|
|
133
|
-
return stage + '-unsafe';
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
/** C08 selection comes from actual private custody, never caller-supplied JSON. */
|
|
137
|
-
export async function readRootMemoryEffectForCheckpoint(input) {
|
|
138
|
-
try {
|
|
139
|
-
const root = resolve(input.projectRoot), runtime = await readGitLifecycleRuntime(root, { required: true });
|
|
140
|
-
const checkpoint = runtime.stories[input.storyId]?.checkpoint;
|
|
141
|
-
if (checkpoint?.rootMemoryEffect !== undefined) {
|
|
142
|
-
if (checkpoint.storyId !== input.storyId || checkpoint.branch !== input.executionBranch
|
|
143
|
-
|| typeof checkpoint.baseSha !== 'string' || typeof checkpoint.headSha !== 'string')
|
|
144
|
-
return fail();
|
|
145
|
-
return structuredClone(verifyRootMemoryPhysicalWriteSet(root, checkpoint.rootMemoryEffect, {
|
|
146
|
-
storyId: input.storyId, executionBranch: input.executionBranch, baseSha: checkpoint.baseSha,
|
|
147
|
-
headRef: checkpoint.headSha, childPaths: input.childPaths,
|
|
148
|
-
}));
|
|
149
|
-
}
|
|
150
|
-
if (!/^P[1-9][0-9]*\.[0-9]+$/.test(input.storyId))
|
|
151
|
-
return undefined;
|
|
152
|
-
const parent = await ensureGitLifecyclePrivateChildDirectory(await gitLifecycleRuntimeDirectory(root), 'private-execution');
|
|
153
|
-
const path = join(parent, `session-${createHash('sha256').update(input.storyId).digest('hex')}.json`);
|
|
154
|
-
if (!existsSync(path))
|
|
155
|
-
return undefined;
|
|
156
|
-
await assertGitLifecyclePrivateFile(path, parent);
|
|
157
|
-
const bytes = readFileSync(path);
|
|
158
|
-
if (bytes.length > 128 * 1024)
|
|
159
|
-
return fail();
|
|
160
|
-
const pin = JSON.parse(bytes.toString('utf8'));
|
|
161
|
-
if (pin.rootMemoryEffect === undefined)
|
|
162
|
-
return undefined;
|
|
163
|
-
const story = runtime.stories[input.storyId];
|
|
164
|
-
if (story?.status !== 'memory-updated' || typeof story.last_step !== 'string'
|
|
165
|
-
|| createHash('sha256').update(story.last_step).digest('hex') !== ROOT_MEMORY_STEP_DIGEST
|
|
166
|
-
|| !Array.isArray(story.steps_completed) || story.steps_completed.at(-1) !== story.last_step)
|
|
167
|
-
return fail();
|
|
168
|
-
const scope = await readPrivateRootMemoryScope({ projectRoot: root, storyId: input.storyId,
|
|
169
|
-
snapshotRoot: pin.workspace?.workspacePath, snapshotSha: pin.snapshotSha, sourceHeadSha: pin.workspace?.baseSha,
|
|
170
|
-
executionBranch: input.executionBranch, graphRevision: input.graphRevision, setupSessionId: pin.setupSessionId, childPaths: input.childPaths });
|
|
171
|
-
if (!scope || !privateRootMemorySourceMatches(scope, root, pin.workspace?.workspacePath, pin.snapshotSha))
|
|
172
|
-
return fail();
|
|
173
|
-
return privateRootMemoryEffect(scope);
|
|
174
|
-
}
|
|
175
|
-
catch {
|
|
176
|
-
return fail();
|
|
177
|
-
}
|
|
178
|
-
}
|
|
1
|
+
import{createHash as S}from"node:crypto";import{execFileSync as A}from"node:child_process";import{existsSync as B,readFileSync as l}from"node:fs";import{join as g,resolve as w}from"node:path";import{isDeepStrictEqual as u}from"node:util";import{isRootProjectMemoryEffect as _}from"./shared-root-memory-types.js";import{assertGitLifecyclePrivateFile as R,ensureGitLifecyclePrivateChildDirectory as k,gitLifecycleRuntimeDirectory as P,readGitLifecycleRuntime as H}from"../git/git-lifecycle-store.js";import{rootMemoryGitFileState as I,verifyRootMemoryEffectGitEvidence as C,verifyRootMemoryPhysicalWriteSet as $}from"./root-memory-git-evidence.js";import{rootMemoryFileState as G}from"./root-memory-writer.js";import{ROOT_MEMORY_REVIEW_CHECKS as b,ROOT_MEMORY_STEP_DIGEST as T}from"./root-memory-request.js";import{parseRefreshMemoryArgs as q,verifyProjectMemoryTargets as F}from"../commands/refresh-memory.js";const j=new WeakMap,n=()=>{throw new Error("private-root-memory-scope-unverified")};function m(e,o){return A("git",[...o],{cwd:e,encoding:"utf8",stdio:["ignore","pipe","ignore"],env:{...Object.fromEntries(Object.entries(process.env).filter(([h])=>!h.startsWith("GIT_"))),GIT_NO_REPLACE_OBJECTS:"1"},timeout:3e4,maxBuffer:2*1024*1024})}async function J(e){let o="pin";try{const h=w(e.projectRoot);if(!/^P[1-9][0-9]*\.[0-9]+$/.test(e.storyId))return n();const r=await k(await P(h),"private-execution"),a=g(r,`session-${S("sha256").update(e.storyId).digest("hex")}.json`);await R(a,r);const i=l(a);if(i.length>128*1024)return n();const s=JSON.parse(i.toString("utf8"));if(o="binding",s.storyId!==e.storyId||s.graphRevision!==e.graphRevision||s.setupSessionId!==e.setupSessionId||s.snapshotSha!==e.snapshotSha||s.workspace?.sourceRoot!==h||s.workspace?.workspacePath!==e.snapshotRoot||s.workspace?.baseSha!==e.sourceHeadSha||s.workspace?.executionBranch!==e.executionBranch)return n();if(s.rootMemoryEffect===void 0)return;const t=s.rootMemoryEffect;if(!_(t)||!Array.isArray(s.graphHistory)||!s.graphHistory.includes(t.graphRevision))return n();o="producer-receipt";const d=f=>S("sha256").update(f).digest("hex"),y=g(r,`memory-review-${d(JSON.stringify(t))}.json`);await R(y,r);const x=l(y);if(x.length>128*1024)return n();const c=JSON.parse(x.toString("utf8"));if(Object.keys(c).sort().join("|")!==["schemaVersion","kind","storyId","graphRevision","sourceHeadSha","snapshotSha","proposalSha256","notesSha256","sourceStateSha256","checks","reviewedAt","rootMemoryEffect","independentStoryQaAccepted","terminalAccepted"].sort().join("|")||c.schemaVersion!==1||c.kind!=="private-project-memory-root-review"||c.storyId!==e.storyId||c.graphRevision!==t.graphRevision||c.sourceHeadSha!==e.sourceHeadSha||c.snapshotSha!==t.memorySnapshotSha||c.notesSha256!==t.proposalSha256||c.reviewedAt!==t.recordedAt||c.independentStoryQaAccepted!==!1||c.terminalAccepted!==!1||!u(c.rootMemoryEffect,t)||!c.checks||Object.keys(c.checks).sort().join("|")!==[...b].sort().join("|")||b.some(f=>c.checks[f]!==!0)||c.proposalSha256!==d(Buffer.from(m(e.snapshotRoot,["show",`${t.sourceSnapshotSha}:.neocortex/stories/${e.storyId}/validation-evidence.md`])))||c.sourceStateSha256!==d(Buffer.from(m(e.snapshotRoot,["show",`${t.sourceSnapshotSha}:.neocortex/state.json`]))))return n();const E=g(r,`qa-review-${t.rootQaReceiptId}.json`);await R(E,r);const v=l(E);if(v.length>16384||d(v)!==t.rootQaReceiptId)return n();const p=JSON.parse(v.toString("utf8"));if(p.kind!=="private-qa-root-review"||p.storyId!==e.storyId||p.sourceHeadSha!==e.sourceHeadSha||p.precommitQaAccepted!==!0||p.independentStoryQaAccepted!==!1||p.terminalAccepted!==!1||(m(e.snapshotRoot,["merge-base","--is-ancestor",p.snapshotSha,t.sourceSnapshotSha]),o="git-evidence",C(e.snapshotRoot,t,{storyId:e.storyId,executionBranch:e.executionBranch,sourceHeadSha:e.sourceHeadSha,graphRevision:t.graphRevision,setupSessionId:e.setupSessionId,childClaimRevision:S("sha256").update([...e.childPaths].sort().join("\0")).digest("hex")}),o="range",m(e.snapshotRoot,["merge-base","--is-ancestor",t.memorySnapshotSha,e.snapshotSha]),m(e.snapshotRoot,["diff","--name-only","--no-renames","--no-ext-diff","--no-textconv","-z",e.sourceHeadSha,e.snapshotSha]).split("\0").filter(Boolean).some(f=>!e.childPaths.includes(f)&&!t.files.some(O=>O.path===f))))return n();o="files";for(const f of t.files)if(e.childPaths.includes(f.path)||!u(I(e.snapshotRoot,e.sourceHeadSha,f.path),f.before)||!u(I(e.snapshotRoot,e.snapshotSha,f.path),f.after))return n();const M=Object.freeze(Object.create(null));return j.set(M,Object.freeze({effect:structuredClone(t),root:h,snapshotRoot:e.snapshotRoot})),M}catch{throw new Error(`private-root-memory-scope-${o}-unverified`)}}function N(e){const o=j.get(e);return o?structuredClone(o.effect):n()}function Q(e,o,h,r){return L(e,o,h,r)==="matched"}function L(e,o,h,r){const a=j.get(e);if(!a||a.root!==w(o)||h!==void 0&&h!==a.snapshotRoot)return"scope-binding";let i="snapshot-ancestry";try{if(r&&(m(a.snapshotRoot,["merge-base","--is-ancestor",a.effect.memorySnapshotSha,r]),i="snapshot-content",a.effect.files.some(t=>!u(I(a.snapshotRoot,r,t.path),t.after)))||(i="source-content",!a.effect.files.every(t=>u(G(o,t.path),t.after))))return i;i="source-projection";const s=F(q(["--project-root",o,"--verify"]));return s.canonicalSha256===a.effect.canonicalSha256&&s.projectionSha256===a.effect.projectionSha256&&s.fileCount===a.effect.projectionFileCount?"matched":i}catch{return i+"-unsafe"}}async function re(e){try{const o=w(e.projectRoot),h=await H(o,{required:!0}),r=h.stories[e.storyId]?.checkpoint;if(r?.rootMemoryEffect!==void 0)return r.storyId!==e.storyId||r.branch!==e.executionBranch||typeof r.baseSha!="string"||typeof r.headSha!="string"?n():structuredClone($(o,r.rootMemoryEffect,{storyId:e.storyId,executionBranch:e.executionBranch,baseSha:r.baseSha,headRef:r.headSha,childPaths:e.childPaths}));if(!/^P[1-9][0-9]*\.[0-9]+$/.test(e.storyId))return;const a=await k(await P(o),"private-execution"),i=g(a,`session-${S("sha256").update(e.storyId).digest("hex")}.json`);if(!B(i))return;await R(i,a);const s=l(i);if(s.length>128*1024)return n();const t=JSON.parse(s.toString("utf8"));if(t.rootMemoryEffect===void 0)return;const d=h.stories[e.storyId];if(d?.status!=="memory-updated"||typeof d.last_step!="string"||S("sha256").update(d.last_step).digest("hex")!==T||!Array.isArray(d.steps_completed)||d.steps_completed.at(-1)!==d.last_step)return n();const y=await J({projectRoot:o,storyId:e.storyId,snapshotRoot:t.workspace?.workspacePath,snapshotSha:t.snapshotSha,sourceHeadSha:t.workspace?.baseSha,executionBranch:e.executionBranch,graphRevision:e.graphRevision,setupSessionId:t.setupSessionId,childPaths:e.childPaths});return!y||!Q(y,o,t.workspace?.workspacePath,t.snapshotSha)?n():N(y)}catch{return n()}}export{N as privateRootMemoryEffect,L as privateRootMemorySourceMatchReason,Q as privateRootMemorySourceMatches,J as readPrivateRootMemoryScope,re as readRootMemoryEffectForCheckpoint};
|
|
@@ -1,102 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* This produces an opaque execution authority, never source or checkpoint permission.
|
|
3
|
-
*/
|
|
4
|
-
import { execFileSync } from 'node:child_process';
|
|
5
|
-
import { createHash } from 'node:crypto';
|
|
6
|
-
import { lstatSync, readFileSync, readlinkSync, realpathSync } from 'node:fs';
|
|
7
|
-
import { join, resolve } from 'node:path';
|
|
8
|
-
import { rootProjectMemoryPaths } from './shared-root-memory-types.js';
|
|
9
|
-
import { parseRefreshMemoryArgs, refreshProjectMemoryTargets, verifyProjectMemoryTargets } from '../commands/refresh-memory.js';
|
|
10
|
-
import { assertProjectMemoryPaths } from '../memory/project-memory-path-scope.js';
|
|
11
|
-
import { classifyProjectMemoryUpdate } from '../memory/project-memory-writer.js';
|
|
12
|
-
const hash = (bytes) => createHash('sha256').update(bytes).digest('hex');
|
|
13
|
-
const authorities = new WeakMap();
|
|
14
|
-
const fail = () => { throw new Error('private-root-memory-writer-unverified'); };
|
|
15
|
-
function git(root, ...args) {
|
|
16
|
-
return execFileSync('git', args, { cwd: root, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'],
|
|
17
|
-
env: Object.fromEntries(Object.entries(process.env).filter(([key]) => !key.startsWith('GIT_'))),
|
|
18
|
-
timeout: 30_000, maxBuffer: 2 * 1024 * 1024 });
|
|
19
|
-
}
|
|
20
|
-
export function rootMemoryFileState(root, path) {
|
|
21
|
-
if (!rootProjectMemoryPaths().includes(path))
|
|
22
|
-
return fail();
|
|
23
|
-
assertProjectMemoryPaths(root, [path]);
|
|
24
|
-
const full = join(root, path);
|
|
25
|
-
let info;
|
|
26
|
-
try {
|
|
27
|
-
info = lstatSync(full);
|
|
28
|
-
}
|
|
29
|
-
catch (error) {
|
|
30
|
-
if (error.code === 'ENOENT')
|
|
31
|
-
return { kind: 'absent', sha256: null };
|
|
32
|
-
return fail();
|
|
33
|
-
}
|
|
34
|
-
if (info.uid !== process.getuid?.() || info.nlink !== 1 || (info.mode & 0o022) !== 0 && !info.isSymbolicLink())
|
|
35
|
-
return fail();
|
|
36
|
-
if (info.isSymbolicLink()) {
|
|
37
|
-
const linkTarget = readlinkSync(full);
|
|
38
|
-
// Never follow an arbitrary source link or copy external data into memory.
|
|
39
|
-
if (resolve(root, path, '..', linkTarget) !== join(resolve(root), 'NEOCORTEX.md'))
|
|
40
|
-
return fail();
|
|
41
|
-
return { kind: 'symlink', sha256: hash(linkTarget), linkTarget };
|
|
42
|
-
}
|
|
43
|
-
if (!info.isFile() || info.size > 512 * 1024)
|
|
44
|
-
return fail();
|
|
45
|
-
return { kind: 'regular', sha256: hash(readFileSync(full)) };
|
|
46
|
-
}
|
|
47
|
-
export function executeRootMemoryWriter(input) {
|
|
48
|
-
const root = resolve(input.snapshotRoot), backup = resolve(input.backupDirectory);
|
|
49
|
-
const info = lstatSync(root), backupInfo = lstatSync(backup);
|
|
50
|
-
if (!info.isDirectory() || info.uid !== process.getuid?.() || (info.mode & 0o077) !== 0 || realpathSync(root) !== root || !backupInfo.isDirectory() || backupInfo.isSymbolicLink()
|
|
51
|
-
|| backupInfo.uid !== process.getuid?.() || (backupInfo.mode & 0o077) !== 0 || realpathSync(backup) !== backup
|
|
52
|
-
|| backup === root || backup.startsWith(root + '/') || !/^[a-f0-9]{40,64}$/.test(input.expectedSnapshotSha)
|
|
53
|
-
|| !Array.isArray(input.notes) || input.notes.length < 1 || input.notes.length > 8
|
|
54
|
-
|| input.notes.some(note => typeof note !== 'string' || Buffer.byteLength(note) < 1 || Buffer.byteLength(note) > 512)
|
|
55
|
-
|| git(root, 'rev-parse', 'HEAD').trim() !== input.expectedSnapshotSha
|
|
56
|
-
|| git(root, 'status', '--porcelain=v1', '--untracked-files=all').length !== 0)
|
|
57
|
-
return fail();
|
|
58
|
-
for (const note of input.notes)
|
|
59
|
-
classifyProjectMemoryUpdate(note);
|
|
60
|
-
const paths = rootProjectMemoryPaths(), before = new Map(paths.map(path => [path, rootMemoryFileState(root, path)]));
|
|
61
|
-
const options = parseRefreshMemoryArgs(['--project-root', root, ...input.notes.flatMap(note => ['--note', note])]);
|
|
62
|
-
const preview = refreshProjectMemoryTargets({ ...options, dryRun: true });
|
|
63
|
-
if (preview.some(result => result.files.some(file => file.fallbackReason || file.remediationDebt
|
|
64
|
-
|| file.mode === 'compatibility-materialized-fallback' || !paths.includes(file.relativeFile))))
|
|
65
|
-
return fail();
|
|
66
|
-
for (const [path, state] of before)
|
|
67
|
-
if (JSON.stringify(rootMemoryFileState(root, path)) !== JSON.stringify(state))
|
|
68
|
-
return fail();
|
|
69
|
-
const results = refreshProjectMemoryTargets({ ...options, backupDirectory: backup });
|
|
70
|
-
if (results.some(result => result.files.some(file => file.fallbackReason || file.remediationDebt
|
|
71
|
-
|| file.mode === 'compatibility-materialized-fallback' || !paths.includes(file.relativeFile))))
|
|
72
|
-
return fail();
|
|
73
|
-
const verification = verifyProjectMemoryTargets(parseRefreshMemoryArgs(['--project-root', root, '--verify']));
|
|
74
|
-
const changed = git(root, 'status', '--porcelain=v1', '-z', '--untracked-files=all').split('\0').filter(Boolean)
|
|
75
|
-
.map(row => row.slice(3));
|
|
76
|
-
if (changed.some(path => !paths.includes(path)) || git(root, 'rev-parse', 'HEAD').trim() !== input.expectedSnapshotSha)
|
|
77
|
-
return fail();
|
|
78
|
-
const files = paths.flatMap(path => {
|
|
79
|
-
const previous = before.get(path), after = rootMemoryFileState(root, path);
|
|
80
|
-
return JSON.stringify(previous) === JSON.stringify(after) ? [] : [Object.freeze({ path, before: Object.freeze(previous), after: Object.freeze(after) })];
|
|
81
|
-
});
|
|
82
|
-
if (files.some(file => !changed.includes(file.path)) || changed.some(path => !files.some(file => file.path === path)))
|
|
83
|
-
return fail();
|
|
84
|
-
const authority = Object.freeze(Object.create(null));
|
|
85
|
-
authorities.set(authority, Object.freeze({ snapshotRoot: root, sourceSnapshotSha: input.expectedSnapshotSha,
|
|
86
|
-
notesSha256: hash(JSON.stringify(input.notes)), files: Object.freeze(files), verification: Object.freeze(verification) }));
|
|
87
|
-
return authority;
|
|
88
|
-
}
|
|
89
|
-
export function projectRootMemoryWriterExecution(authority) {
|
|
90
|
-
return authorities.get(authority);
|
|
91
|
-
}
|
|
92
|
-
/** Recheck the actual writer output immediately before source admission. */
|
|
93
|
-
export function verifyRootMemoryWriterExecution(authority) {
|
|
94
|
-
const value = authorities.get(authority);
|
|
95
|
-
if (!value || git(value.snapshotRoot, 'rev-parse', 'HEAD').trim() !== value.sourceSnapshotSha)
|
|
96
|
-
return fail();
|
|
97
|
-
const changed = git(value.snapshotRoot, 'status', '--porcelain=v1', '-z', '--untracked-files=all').split('\0').filter(Boolean).map(row => row.slice(3)).sort();
|
|
98
|
-
if (JSON.stringify(changed) !== JSON.stringify(value.files.map(file => file.path).sort())
|
|
99
|
-
|| value.files.some(file => JSON.stringify(rootMemoryFileState(value.snapshotRoot, file.path)) !== JSON.stringify(file.after)))
|
|
100
|
-
return fail();
|
|
101
|
-
return value;
|
|
102
|
-
}
|
|
1
|
+
import{execFileSync as z}from"node:child_process";import{createHash as N}from"node:crypto";import{lstatSync as y,readFileSync as E,readlinkSync as D,realpathSync as v}from"node:fs";import{join as O,resolve as l}from"node:path";import{rootProjectMemoryPaths as j}from"./shared-root-memory-types.js";import{parseRefreshMemoryArgs as x,refreshProjectMemoryTargets as M,verifyProjectMemoryTargets as J}from"../commands/refresh-memory.js";import{assertProjectMemoryPaths as w}from"../memory/project-memory-path-scope.js";import{classifyProjectMemoryUpdate as P}from"../memory/project-memory-writer.js";const d=r=>N("sha256").update(r).digest("hex"),h=new WeakMap,n=()=>{throw new Error("private-root-memory-writer-unverified")};function c(r,...e){return z("git",e,{cwd:r,encoding:"utf8",stdio:["ignore","pipe","ignore"],env:Object.fromEntries(Object.entries(process.env).filter(([i])=>!i.startsWith("GIT_"))),timeout:3e4,maxBuffer:2*1024*1024})}function m(r,e){if(!j().includes(e))return n();w(r,[e]);const i=O(r,e);let o;try{o=y(i)}catch(a){return a.code==="ENOENT"?{kind:"absent",sha256:null}:n()}if(o.uid!==process.getuid?.()||o.nlink!==1||(o.mode&18)!==0&&!o.isSymbolicLink())return n();if(o.isSymbolicLink()){const a=D(i);return l(r,e,"..",a)!==O(l(r),"NEOCORTEX.md")?n():{kind:"symlink",sha256:d(a),linkTarget:a}}return!o.isFile()||o.size>512*1024?n():{kind:"regular",sha256:d(E(i))}}function G(r){const e=l(r.snapshotRoot),i=l(r.backupDirectory),o=y(e),a=y(i);if(!o.isDirectory()||o.uid!==process.getuid?.()||(o.mode&63)!==0||v(e)!==e||!a.isDirectory()||a.isSymbolicLink()||a.uid!==process.getuid?.()||(a.mode&63)!==0||v(i)!==i||i===e||i.startsWith(e+"/")||!/^[a-f0-9]{40,64}$/.test(r.expectedSnapshotSha)||!Array.isArray(r.notes)||r.notes.length<1||r.notes.length>8||r.notes.some(t=>typeof t!="string"||Buffer.byteLength(t)<1||Buffer.byteLength(t)>512)||c(e,"rev-parse","HEAD").trim()!==r.expectedSnapshotSha||c(e,"status","--porcelain=v1","--untracked-files=all").length!==0)return n();for(const t of r.notes)P(t);const f=j(),g=new Map(f.map(t=>[t,m(e,t)])),S=x(["--project-root",e,...r.notes.flatMap(t=>["--note",t])]);if(M({...S,dryRun:!0}).some(t=>t.files.some(s=>s.fallbackReason||s.remediationDebt||s.mode==="compatibility-materialized-fallback"||!f.includes(s.relativeFile))))return n();for(const[t,s]of g)if(JSON.stringify(m(e,t))!==JSON.stringify(s))return n();if(M({...S,backupDirectory:i}).some(t=>t.files.some(s=>s.fallbackReason||s.remediationDebt||s.mode==="compatibility-materialized-fallback"||!f.includes(s.relativeFile))))return n();const R=J(x(["--project-root",e,"--verify"])),u=c(e,"status","--porcelain=v1","-z","--untracked-files=all").split("\0").filter(Boolean).map(t=>t.slice(3));if(u.some(t=>!f.includes(t))||c(e,"rev-parse","HEAD").trim()!==r.expectedSnapshotSha)return n();const p=f.flatMap(t=>{const s=g.get(t),k=m(e,t);return JSON.stringify(s)===JSON.stringify(k)?[]:[Object.freeze({path:t,before:Object.freeze(s),after:Object.freeze(k)})]});if(p.some(t=>!u.includes(t.path))||u.some(t=>!p.some(s=>s.path===t)))return n();const b=Object.freeze(Object.create(null));return h.set(b,Object.freeze({snapshotRoot:e,sourceSnapshotSha:r.expectedSnapshotSha,notesSha256:d(JSON.stringify(r.notes)),files:Object.freeze(p),verification:Object.freeze(R)})),b}function U(r){return h.get(r)}function X(r){const e=h.get(r);if(!e||c(e.snapshotRoot,"rev-parse","HEAD").trim()!==e.sourceSnapshotSha)return n();const i=c(e.snapshotRoot,"status","--porcelain=v1","-z","--untracked-files=all").split("\0").filter(Boolean).map(o=>o.slice(3)).sort();return JSON.stringify(i)!==JSON.stringify(e.files.map(o=>o.path).sort())||e.files.some(o=>JSON.stringify(m(e.snapshotRoot,o.path))!==JSON.stringify(o.after))?n():e}export{G as executeRootMemoryWriter,U as projectRootMemoryWriterExecution,m as rootMemoryFileState,X as verifyRootMemoryWriterExecution};
|