@ornexus/neocortex 4.60.71 → 4.60.73

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.
Files changed (243) hide show
  1. package/dist/release-source.json +1 -1
  2. package/dist/sbom.cdx.json +1211 -1217
  3. package/install.ps1 +1 -1
  4. package/install.sh +1 -1
  5. package/package.json +2 -2
  6. package/packages/client/dist/adapters/adapter-registry.js +108 -1
  7. package/packages/client/dist/adapters/antigravity-adapter.js +77 -2
  8. package/packages/client/dist/adapters/claude-code-adapter.js +79 -3
  9. package/packages/client/dist/adapters/codex-adapter.js +80 -2
  10. package/packages/client/dist/adapters/cursor-adapter.js +115 -4
  11. package/packages/client/dist/adapters/gemini-adapter.js +71 -2
  12. package/packages/client/dist/adapters/grok-adapter.js +42 -2
  13. package/packages/client/dist/adapters/index.js +22 -1
  14. package/packages/client/dist/adapters/platform-detector.js +212 -1
  15. package/packages/client/dist/adapters/target-adapter.js +12 -0
  16. package/packages/client/dist/adapters/vscode-adapter.js +72 -2
  17. package/packages/client/dist/agent/refresh-stubs.js +417 -2
  18. package/packages/client/dist/agent/update-agent-yaml.js +102 -1
  19. package/packages/client/dist/agent/update-description.js +251 -1
  20. package/packages/client/dist/cache/crypto-utils.js +78 -1
  21. package/packages/client/dist/cache/encrypted-cache.js +108 -1
  22. package/packages/client/dist/cache/in-memory-asset-cache.js +73 -1
  23. package/packages/client/dist/cache/index.js +13 -1
  24. package/packages/client/dist/cache/protected-pi-boundary.js +44 -1
  25. package/packages/client/dist/checkpoint/checkpoint-client-reader.js +127 -2
  26. package/packages/client/dist/checkpoint/index.js +11 -1
  27. package/packages/client/dist/checkpoint/shared-checkpoint-types.js +32 -1
  28. package/packages/client/dist/cli.js +358 -5
  29. package/packages/client/dist/commands/activate.js +394 -8
  30. package/packages/client/dist/commands/architecture-answers.js +274 -7
  31. package/packages/client/dist/commands/architecture-memory-terminal.js +253 -4
  32. package/packages/client/dist/commands/architecture-non-ui-continuation.js +100 -1
  33. package/packages/client/dist/commands/architecture-root-binding.js +51 -1
  34. package/packages/client/dist/commands/cache-status.js +112 -2
  35. package/packages/client/dist/commands/config.js +88 -1
  36. package/packages/client/dist/commands/doctor.js +635 -2
  37. package/packages/client/dist/commands/invoke.js +7200 -143
  38. package/packages/client/dist/commands/legacy-epic-terminal.js +71 -1
  39. package/packages/client/dist/commands/legacy-story-branch.js +96 -1
  40. package/packages/client/dist/commands/legacy-story-pr.js +276 -1
  41. package/packages/client/dist/commands/mutation-qa-checkpoint.js +285 -5
  42. package/packages/client/dist/commands/mutation-qa-gate.js +54 -1
  43. package/packages/client/dist/commands/mutation-qa-git.js +41 -1
  44. package/packages/client/dist/commands/mutation-qa-policy.js +102 -1
  45. package/packages/client/dist/commands/mutation-qa-run.js +219 -3
  46. package/packages/client/dist/commands/mutation-qa-verdict.js +149 -1
  47. package/packages/client/dist/commands/mutation-qa.js +220 -1
  48. package/packages/client/dist/commands/native-agent.js +136 -2
  49. package/packages/client/dist/commands/planning-diagnosis.js +43 -1
  50. package/packages/client/dist/commands/planning-generation.js +53 -1
  51. package/packages/client/dist/commands/planning-memory.js +40 -1
  52. package/packages/client/dist/commands/planning-research.js +42 -1
  53. package/packages/client/dist/commands/private-session.js +75 -1
  54. package/packages/client/dist/commands/property-qa.js +139 -1
  55. package/packages/client/dist/commands/qa-control.js +16 -1
  56. package/packages/client/dist/commands/qa-scan-browser-launch.js +55 -1
  57. package/packages/client/dist/commands/qa-scan-browser-route.js +28 -1
  58. package/packages/client/dist/commands/qa-scan-capture.js +663 -5
  59. package/packages/client/dist/commands/qa-scan-discovery.js +376 -3
  60. package/packages/client/dist/commands/qa-scan-review.js +826 -5
  61. package/packages/client/dist/commands/qa-synthetic-backend-capture.js +266 -1
  62. package/packages/client/dist/commands/qa-synthetic-backend-review.js +297 -1
  63. package/packages/client/dist/commands/qa-synthetic-backend.js +340 -2
  64. package/packages/client/dist/commands/qa-synthetic-broker-port.js +85 -1
  65. package/packages/client/dist/commands/qa-synthetic-browser.js +39 -1
  66. package/packages/client/dist/commands/refactor-characterization.js +671 -21
  67. package/packages/client/dist/commands/refactor-commit.js +399 -13
  68. package/packages/client/dist/commands/refactor-control.js +142 -3
  69. package/packages/client/dist/commands/refactor-cpp-assertions.js +93 -3
  70. package/packages/client/dist/commands/refactor-review.js +283 -5
  71. package/packages/client/dist/commands/refactor-root.js +588 -7
  72. package/packages/client/dist/commands/refresh-memory.js +156 -3
  73. package/packages/client/dist/commands/shared-architecture-non-ui.js +24 -1
  74. package/packages/client/dist/commands/story-checkpoint.js +171 -1
  75. package/packages/client/dist/config/profile-directory.js +13 -1
  76. package/packages/client/dist/config/resolver-selection.js +278 -1
  77. package/packages/client/dist/config/secure-config.js +281 -12
  78. package/packages/client/dist/config.js +223 -2
  79. package/packages/client/dist/constants.js +25 -1
  80. package/packages/client/dist/context/context-collector.js +222 -2
  81. package/packages/client/dist/context/context-sanitizer.js +145 -1
  82. package/packages/client/dist/continuity/autonomous-governance.js +285 -1
  83. package/packages/client/dist/continuity/continuity-client-state-store.js +1587 -1
  84. package/packages/client/dist/continuity/invoke-hooks.js +129 -1
  85. package/packages/client/dist/continuity/migrations/001-initial-schema.js +12 -2
  86. package/packages/client/dist/continuity/migrations/002-processing-output-summary-indexes.js +12 -2
  87. package/packages/client/dist/continuity/migrations/003-headless-execution-attempts.js +12 -2
  88. package/packages/client/dist/continuity/migrations/004-outbox-delivery-state.js +12 -2
  89. package/packages/client/dist/continuity/migrations/005-bounded-headless-queue.js +12 -2
  90. package/packages/client/dist/continuity/migrations/006-native-agent-dispatches.js +10 -2
  91. package/packages/client/dist/continuity/native-agent-store.js +69 -1
  92. package/packages/client/dist/continuity/native-agent-types.js +90 -1
  93. package/packages/client/dist/continuity/outbox.js +117 -1
  94. package/packages/client/dist/continuity/runner-bootstrap-policy.js +172 -1
  95. package/packages/client/dist/continuity/sqlite-store.d.ts +22 -1
  96. package/packages/client/dist/continuity/sqlite-store.js +2612 -81
  97. package/packages/client/dist/effects/invoke-effect-gateways.js +62 -1
  98. package/packages/client/dist/errors/error-messages.js +228 -2
  99. package/packages/client/dist/errors/frontmatter-diagnostics.js +125 -1
  100. package/packages/client/dist/evidence/collector.js +347 -1
  101. package/packages/client/dist/evidence/index.js +1 -1
  102. package/packages/client/dist/evidence/types.js +2 -0
  103. package/packages/client/dist/fs/atomic-state-replace.js +152 -1
  104. package/packages/client/dist/generated/command-registry.generated.js +4076 -1
  105. package/packages/client/dist/generated/public-projections.generated.js +149 -1
  106. package/packages/client/dist/git/direct-yolo-c01-retry.js +138 -4
  107. package/packages/client/dist/git/direct-yolo-checkpoint.js +1234 -4
  108. package/packages/client/dist/git/epic-operational-state.d.ts +198 -0
  109. package/packages/client/dist/git/epic-operational-state.js +672 -0
  110. package/packages/client/dist/git/git-lifecycle-step-ids.js +14 -1
  111. package/packages/client/dist/git/git-lifecycle-store.d.ts +9 -1
  112. package/packages/client/dist/git/git-lifecycle-store.js +1981 -9
  113. package/packages/client/dist/git/legacy-story-branch-checkpoint.js +259 -9
  114. package/packages/client/dist/git/legacy-story-branch-setup.js +348 -4
  115. package/packages/client/dist/git/legacy-story-pr.js +3538 -70
  116. package/packages/client/dist/git/secure-filesystem-boundary.js +182 -5
  117. package/packages/client/dist/graph-retrieval/pre-command-hook.js +284 -1
  118. package/packages/client/dist/graph-retrieval/shared-graph-retrieval-contract.js +237 -1
  119. package/packages/client/dist/i18n/first-run.js +60 -2
  120. package/packages/client/dist/index.js +47 -1
  121. package/packages/client/dist/invocation/invoke-input.js +24 -1
  122. package/packages/client/dist/license/index.js +5 -1
  123. package/packages/client/dist/license/license-client.js +304 -1
  124. package/packages/client/dist/machine/fingerprint.js +163 -2
  125. package/packages/client/dist/machine/index.js +5 -1
  126. package/packages/client/dist/memory/project-memory-path-scope.js +62 -1
  127. package/packages/client/dist/memory/project-memory-writer.js +1007 -40
  128. package/packages/client/dist/memory/shared-project-memory-types.js +253 -2
  129. package/packages/client/dist/migrations/epic-lifecycle-backfill.d.ts +97 -0
  130. package/packages/client/dist/migrations/epic-lifecycle-backfill.js +1060 -0
  131. package/packages/client/dist/migrations/project-identity-capability.js +50 -1
  132. package/packages/client/dist/migrations/project-identity.js +493 -7
  133. package/packages/client/dist/migrations/state-json.d.ts +22 -0
  134. package/packages/client/dist/migrations/state-json.js +393 -4
  135. package/packages/client/dist/policy/architecture-policy.js +184 -2
  136. package/packages/client/dist/policy/index.js +7 -1
  137. package/packages/client/dist/policy/shared-policy-types.js +38 -0
  138. package/packages/client/dist/policy/subagent-approval-policy.js +55 -1
  139. package/packages/client/dist/private-execution/clean-baseline.js +279 -2
  140. package/packages/client/dist/private-execution/codex-executor.js +574 -4
  141. package/packages/client/dist/private-execution/codex-host-runtime.js +137 -1
  142. package/packages/client/dist/private-execution/codex-lifecycle-guard.js +97 -1
  143. package/packages/client/dist/private-execution/codex-scoped-permissions.js +128 -1
  144. package/packages/client/dist/private-execution/delivery.js +410 -13
  145. package/packages/client/dist/private-execution/diagnosis-root-review.js +294 -2
  146. package/packages/client/dist/private-execution/frozen-handoff.js +19 -6
  147. package/packages/client/dist/private-execution/implementation-validation.js +106 -2
  148. package/packages/client/dist/private-execution/isolated-admission.js +1432 -23
  149. package/packages/client/dist/private-execution/isolated-session.js +725 -5
  150. package/packages/client/dist/private-execution/isolated-workspace.js +122 -1
  151. package/packages/client/dist/private-execution/pact-consumer-runtime.js +409 -3
  152. package/packages/client/dist/private-execution/pact-test-source.js +258 -1
  153. package/packages/client/dist/private-execution/pact-toolchain.js +290 -3
  154. package/packages/client/dist/private-execution/pact-write-set.js +172 -1
  155. package/packages/client/dist/private-execution/planning-diagnosis-checkpoint.js +406 -5
  156. package/packages/client/dist/private-execution/planning-generation-checkpoint.js +462 -5
  157. package/packages/client/dist/private-execution/planning-memory-checkpoint.js +139 -3
  158. package/packages/client/dist/private-execution/planning-research-checkpoint.js +325 -4
  159. package/packages/client/dist/private-execution/private-report-store.js +42 -1
  160. package/packages/client/dist/private-execution/property-test-candidate.js +103 -1
  161. package/packages/client/dist/private-execution/property-test-choices.js +147 -3
  162. package/packages/client/dist/private-execution/property-test-plan.js +271 -7
  163. package/packages/client/dist/private-execution/property-test-review.js +258 -4
  164. package/packages/client/dist/private-execution/property-test-runtime.js +372 -9
  165. package/packages/client/dist/private-execution/property-test-source.js +139 -1
  166. package/packages/client/dist/private-execution/root-checkpoint.js +80 -1
  167. package/packages/client/dist/private-execution/root-memory-git-evidence.js +135 -3
  168. package/packages/client/dist/private-execution/root-memory-recovery.js +93 -1
  169. package/packages/client/dist/private-execution/root-memory-request.js +29 -1
  170. package/packages/client/dist/private-execution/root-memory-scope.js +178 -1
  171. package/packages/client/dist/private-execution/root-memory-writer.js +102 -1
  172. package/packages/client/dist/private-execution/session-lease.js +63 -1
  173. package/packages/client/dist/private-execution/shared-private-admission-types.js +50 -1
  174. package/packages/client/dist/private-execution/shared-project-validation.js +66 -1
  175. package/packages/client/dist/private-execution/shared-root-memory-types.js +61 -1
  176. package/packages/client/dist/private-execution/ux-source-validation.js +372 -1
  177. package/packages/client/dist/private-execution/ux-write-set.js +191 -2
  178. package/packages/client/dist/registry/registry-reconciliation.js +234 -1
  179. package/packages/client/dist/resilience/circuit-breaker.js +170 -1
  180. package/packages/client/dist/resilience/degradation-manager.js +164 -1
  181. package/packages/client/dist/resilience/freshness-indicator.js +100 -1
  182. package/packages/client/dist/resilience/index.js +8 -1
  183. package/packages/client/dist/resilience/recovery-detector.js +74 -1
  184. package/packages/client/dist/resolvers/asset-resolver.js +13 -0
  185. package/packages/client/dist/resolvers/local-resolver.js +255 -8
  186. package/packages/client/dist/resolvers/remote-resolver.js +233 -1
  187. package/packages/client/dist/runner/binary.js +404 -1
  188. package/packages/client/dist/runner/cli.js +1571 -17
  189. package/packages/client/dist/runner/headless/adapters.js +49 -1
  190. package/packages/client/dist/runner/headless/claude-code-adapter.js +79 -1
  191. package/packages/client/dist/runner/headless/continuous-story-workspace.js +1628 -11
  192. package/packages/client/dist/runner/headless/epic-git-reconciliation-inventory.js +69 -1
  193. package/packages/client/dist/runner/headless/epic-merge-candidate-qa.js +110 -2
  194. package/packages/client/dist/runner/headless/epic-terminal-git-cli-adapter.js +1236 -8
  195. package/packages/client/dist/runner/headless/epic-terminal-git-runner.js +318 -2
  196. package/packages/client/dist/runner/headless/fake-adapter.js +62 -1
  197. package/packages/client/dist/runner/headless/managed-profile.js +132 -1
  198. package/packages/client/dist/runner/headless/opencode-adapter.js +145 -2
  199. package/packages/client/dist/runner/headless/prepared-story-bundle.js +43 -1
  200. package/packages/client/dist/runner/headless/resolver.js +231 -1
  201. package/packages/client/dist/runner/headless/run-yolo.js +885 -1
  202. package/packages/client/dist/runner/headless/run-yoloop.js +2574 -2
  203. package/packages/client/dist/runner/headless/types.js +574 -2
  204. package/packages/client/dist/runner/local-console.js +396 -13
  205. package/packages/client/dist/runner/portal-sync-worker.js +1487 -1
  206. package/packages/client/dist/runner/project-identity.js +106 -1
  207. package/packages/client/dist/runner/scheduler.js +995 -7
  208. package/packages/client/dist/runner-cli.js +18 -2
  209. package/packages/client/dist/state/invocation-shape.js +13 -1
  210. package/packages/client/dist/state/project-state-snapshot.d.ts +8 -0
  211. package/packages/client/dist/state/project-state-snapshot.js +1731 -1
  212. package/packages/client/dist/state/state-json-repair.js +785 -3
  213. package/packages/client/dist/state/structural-dag-projection.js +118 -1
  214. package/packages/client/dist/telemetry/index.js +5 -1
  215. package/packages/client/dist/telemetry/offline-queue.js +131 -1
  216. package/packages/client/dist/tier/index.js +5 -1
  217. package/packages/client/dist/tier/tier-aware-client.js +405 -1
  218. package/packages/client/dist/types/index.js +38 -1
  219. package/packages/client/dist/version.js +27 -1
  220. package/packages/client/dist/yolo/change-evidence-collector.js +126 -1
  221. package/packages/client/dist/yolo/invoke-envelope-capability.js +62 -1
  222. package/packages/client/dist/yolo/spec-envelope-collector.js +184 -5
  223. package/packages/client/dist/yoloop/discovery-hook.js +300 -2
  224. package/packages/client/dist/yoloop/index.js +8 -1
  225. package/packages/client/dist/yoloop/invoke-hooks.js +669 -12
  226. package/packages/client/dist/yoloop/shared-discover-epics.js +1007 -1
  227. package/packages/client/dist/yoloop/shared-yoloop-types.d.ts +65 -1
  228. package/packages/client/dist/yoloop/shared-yoloop-types.js +33 -1
  229. package/packages/client/dist/yoloop/target-normalization.js +180 -1
  230. package/packages/client/dist/yoloop/yoloop-client-state-store.d.ts +8 -0
  231. package/packages/client/dist/yoloop/yoloop-client-state-store.js +1920 -1
  232. package/packages/shared/dist/yolo/spec-envelope.js +3 -6
  233. package/targets-stubs/antigravity/gemini.md +1 -1
  234. package/targets-stubs/antigravity/skill/SKILL.md +1 -1
  235. package/targets-stubs/claude-code/neocortex-root.agent.yaml +1 -1
  236. package/targets-stubs/claude-code/neocortex-root.md +2 -2
  237. package/targets-stubs/claude-code/neocortex.agent.yaml +1 -1
  238. package/targets-stubs/claude-code/neocortex.md +2 -2
  239. package/targets-stubs/codex/AGENTS.md +1 -1
  240. package/targets-stubs/cursor/agent.md +2 -2
  241. package/targets-stubs/gemini-cli/agent.md +2 -2
  242. package/targets-stubs/opencode/neocortex-root.md +1 -1
  243. package/targets-stubs/vscode/neocortex.agent.md +2 -2
@@ -1,7 +1,274 @@
1
- import{createHash as A,randomBytes as T,randomUUID as U}from"node:crypto";import{execFileSync as R}from"node:child_process";import{isDeepStrictEqual as j}from"node:util";import{closeSync as I,existsSync as w,fsyncSync as P,linkSync as L,lstatSync as l,mkdirSync as B,openSync as k,readFileSync as O,realpathSync as b,unlinkSync as q,writeFileSync as D}from"node:fs";import{isAbsolute as F,join as u,resolve as S}from"node:path";const z=4096,m=Object.freeze(["PACT-Q1-CONSUMER","PACT-Q2-PROVIDER","PACT-Q3-BROKER","PACT-Q4-MATCHERS","PACT-Q5-VERIFY"]),N=Object.freeze(["arch-prd","arch-architecture","arch-fitness-functions","arch-api-contracts"]);function d(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function y(e,n){return Object.keys(e).length===n.length&&n.every(r=>Object.hasOwn(e,r))}function x(e){return typeof e=="string"&&/^[A-Za-z0-9][A-Za-z0-9._-]{0,79}$/.test(e)}function Q(e){if(e===null)return!0;if(typeof e!="string"||e.length>512||!e.startsWith("https://")||/[\s\\]/.test(e))return!1;try{const n=new URL(e);return n.protocol==="https:"&&!!n.hostname&&!n.username&&!n.password&&!n.search&&!n.hash}catch{return!1}}function $(e){if(!d(e)||!y(e,["consumer","provider","brokerUrl","matchersAggressive","generateProvider"])||!x(e.consumer)||!x(e.provider)||!Q(e.brokerUrl)||typeof e.matchersAggressive!="boolean"||typeof e.generateProvider!="boolean")throw new Error("architecture-answers-invalid");return{consumer:e.consumer,provider:e.provider,brokerUrl:e.brokerUrl,matchersAggressive:e.matchersAggressive,generateProvider:e.generateProvider}}function C(e,n){const r=S(e),c=R("git",["rev-parse","--show-toplevel"],{cwd:r,encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim();if(b(r)!==r||S(c)!==r)throw new Error("architecture-answers-private-store-invalid");const o=R("git",["rev-parse","--git-common-dir"],{cwd:r,encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim(),s=F(o)?S(o):S(r,o);if(b(s)!==s||l(s).isSymbolicLink())throw new Error("architecture-answers-private-store-invalid");const a=u(s,"neocortex-architecture");if(!w(a)&&!n)return;n&&B(a,{recursive:!0,mode:448});const t=l(a);if(!t.isDirectory()||t.isSymbolicLink()||t.uid!==process.getuid?.()||(t.mode&63)!==0)throw new Error("architecture-answers-private-store-invalid");return a}function J(e,n){const r=u(e,".neocortex","state.json"),c=l(r);if(!c.isFile()||c.isSymbolicLink())throw new Error("architecture-answers-state-invalid");const o=O(r,"utf8"),s=JSON.parse(o),a=d(s)&&d(s.archPlan)?s.archPlan:void 0,t=a&&d(a.execution)?a.execution:void 0,h=t?.acceptedStages;if(!t||t.schemaVersion!==1||t.owner!=="root"||t.evidencePolicy!=="v2-required"||t.currentStageCommand!=="generate-pacts"||!(t.continuationState==="ask-user"&&JSON.stringify(t.questionIds)===JSON.stringify(m)||n&&t.continuationState==="delegate"&&JSON.stringify(t.questionIds)==="[]")||typeof t.targetEpicId!="string"||!/^P[1-9][0-9]{0,11}$/.test(t.targetEpicId)||!d(s)||!d(s.epics)||!Object.hasOwn(s.epics,t.targetEpicId)||typeof t.graphRevision!="string"||!/^[a-f0-9]{64}$/.test(t.graphRevision)||!Array.isArray(h)||h.length!==N.length||!h.every((f,p)=>d(f)&&f.stageCommand===N[p]&&f.targetEpicId===t.targetEpicId&&d(f.completionEvidence)&&d(f.validationEvidence)))throw new Error("architecture-answers-checkpoint-invalid");const i=t.continuationState==="delegate"?t.pactAnswerReceipt:void 0;if(t.continuationState==="delegate"&&(!d(i)||!y(i,["schemaVersion","targetEpicId","graphRevision","stateSha256","answersSha256","questionIds","publicSafe"])||i.schemaVersion!=="PactAnswerReceipt/1"||i.targetEpicId!==t.targetEpicId||i.graphRevision!==t.graphRevision||typeof i.stateSha256!="string"||!/^[a-f0-9]{64}$/.test(i.stateSha256)||typeof i.answersSha256!="string"||!/^[a-f0-9]{64}$/.test(i.answersSha256)||JSON.stringify(i.questionIds)!==JSON.stringify(m)||i.publicSafe!==!0))throw new Error("architecture-answers-checkpoint-invalid");return{targetEpicId:t.targetEpicId,graphRevision:t.graphRevision,stateSha256:t.continuationState==="ask-user"?A("sha256").update(o).digest("hex"):i.stateSha256,...i?{delegatedReceipt:i}:{}}}function g(e){return{schemaVersion:"PactAnswerReceipt/1",targetEpicId:e.targetEpicId,graphRevision:e.graphRevision,stateSha256:e.stateSha256,answersSha256:e.checksumSha256,questionIds:m,publicSafe:!0}}function V(e,n,r){const c={schemaVersion:r?"PactLocalAnswers/2":"PactLocalAnswers/1",...e,...r?{nonce:r}:{},answers:n};return{...c,checksumSha256:A("sha256").update(`${JSON.stringify(c)}
2
- `).digest("hex")}}function _(e,n){const r=$(n),c=J(e,!1),o=E(e);if(o){if(!j(o.answers,r))throw new Error("architecture-answers-conflict");return g(o)}const s=V(c,r,T(32).toString("hex")),a=`${JSON.stringify(s)}
3
- `,t=C(e,!0),h=u(t,`pact-answers-${s.targetEpicId}-${s.graphRevision}.json`);if(w(h))throw new Error("architecture-answers-conflict");const i=u(t,`pact-answers-tmp-${U()}.json`);let f=!1;const p=k(i,"wx",384);try{D(p,a),P(p)}finally{I(p)}try{L(i,h),f=!0;const v=k(t,"r");try{P(v)}finally{I(v)}}finally{if(q(i),!f&&w(h))throw new Error("architecture-answers-conflict")}return g(s)}function E(e,n=!1){const r=J(e,!0);if(n&&!r.delegatedReceipt)throw new Error("architecture-answers-checkpoint-invalid");const c=C(e,!1);if(!c)return;const o=u(c,`pact-answers-${r.targetEpicId}-${r.graphRevision}.json`);if(!w(o))return;const s=l(o);if(!s.isFile()||s.isSymbolicLink()||s.size>8192||s.uid!==process.getuid?.()||(s.mode&63)!==0)throw new Error("architecture-answers-conflict");const a=O(o,"utf8"),t=JSON.parse(a);if(!d(t)||!(t.schemaVersion==="PactLocalAnswers/1"&&y(t,["schemaVersion","targetEpicId","graphRevision","stateSha256","answers","checksumSha256"])||t.schemaVersion==="PactLocalAnswers/2"&&y(t,["schemaVersion","targetEpicId","graphRevision","stateSha256","nonce","answers","checksumSha256"])&&typeof t.nonce=="string"&&/^[a-f0-9]{64}$/.test(t.nonce))||t.targetEpicId!==r.targetEpicId||t.graphRevision!==r.graphRevision||t.stateSha256!==r.stateSha256)throw new Error("architecture-answers-conflict");const h=V({targetEpicId:r.targetEpicId,graphRevision:r.graphRevision,stateSha256:r.stateSha256},$(t.answers),t.schemaVersion==="PactLocalAnswers/2"?t.nonce:void 0),i=`${JSON.stringify(h)}
4
- `;if(a!==i||r.delegatedReceipt&&JSON.stringify(g(h))!==JSON.stringify(r.delegatedReceipt))throw new Error("architecture-answers-conflict");return h}function H(e){const n=E(e);return n?g(n):void 0}function X(e){const n=E(e,!0);return n?{receipt:g(n),answers:n.answers}:void 0}async function M(){const e=[];let n=0;for await(const r of process.stdin){const c=Buffer.isBuffer(r)?r:Buffer.from(String(r));if(n+=c.length,n>z)throw new Error("architecture-answers-input-too-large");e.push(c)}return JSON.parse(Buffer.concat(e).toString("utf8"))}async function ee(e){if(e[0]!=="save"&&e[0]!=="status"||e.length>1&&(e.length!==3||e[1]!=="--project-root"||!e[2]))return process.stdout.write(`{"ok":false,"reasonCode":"architecture-answers-usage-invalid","publicSafe":true}
5
- `),1;try{const n=e[2]??process.cwd(),r=e[0]==="save"?_(n,await M()):H(n);return process.stdout.write(`${JSON.stringify({ok:!0,...r,...r?{}:{recorded:!1,publicSafe:!0}})}
6
- `),0}catch(n){const r=n instanceof Error&&/^architecture-answers-[a-z-]+$/.test(n.message)?n.message:"architecture-answers-failed";return process.stdout.write(`${JSON.stringify({ok:!1,reasonCode:r,publicSafe:!0})}
7
- `),1}}export{ee as architectureAnswersCliHandler,H as loadPactAnswerReceipt,X as loadPactPrivateAnswers,_ as savePactAnswers};
1
+ /**
2
+ * P222.26: root-owned, local-only collection of the five Pact decisions.
3
+ * The answers never enter invoke args, tracked state, public metadata or logs.
4
+ * Saving this record does not authorize a private architecture child.
5
+ */
6
+ import { createHash, randomBytes, randomUUID } from 'node:crypto';
7
+ import { execFileSync } from 'node:child_process';
8
+ import { isDeepStrictEqual } from 'node:util';
9
+ import { closeSync, existsSync, fsyncSync, linkSync, lstatSync, mkdirSync, openSync, readFileSync, realpathSync, unlinkSync, writeFileSync, } from 'node:fs';
10
+ import { isAbsolute, join, resolve } from 'node:path';
11
+ const MAX_INPUT_BYTES = 4096;
12
+ const PACT_QUESTION_IDS = Object.freeze([
13
+ 'PACT-Q1-CONSUMER', 'PACT-Q2-PROVIDER', 'PACT-Q3-BROKER',
14
+ 'PACT-Q4-MATCHERS', 'PACT-Q5-VERIFY',
15
+ ]);
16
+ const STAGE_CHAIN = Object.freeze([
17
+ 'arch-prd', 'arch-architecture', 'arch-fitness-functions', 'arch-api-contracts',
18
+ ]);
19
+ function object(value) {
20
+ return !!value && typeof value === 'object' && !Array.isArray(value);
21
+ }
22
+ function exactKeys(value, keys) {
23
+ return Object.keys(value).length === keys.length
24
+ && keys.every((key) => Object.hasOwn(value, key));
25
+ }
26
+ function safeName(value) {
27
+ return typeof value === 'string' && /^[A-Za-z0-9][A-Za-z0-9._-]{0,79}$/.test(value);
28
+ }
29
+ function safeBrokerUrl(value) {
30
+ if (value === null)
31
+ return true;
32
+ if (typeof value !== 'string' || value.length > 512
33
+ || !value.startsWith('https://') || /[\s\\]/.test(value))
34
+ return false;
35
+ try {
36
+ const url = new URL(value);
37
+ return url.protocol === 'https:' && !!url.hostname && !url.username && !url.password
38
+ && !url.search && !url.hash;
39
+ }
40
+ catch {
41
+ return false;
42
+ }
43
+ }
44
+ function parseAnswers(value) {
45
+ if (!object(value) || !exactKeys(value, [
46
+ 'consumer', 'provider', 'brokerUrl', 'matchersAggressive', 'generateProvider',
47
+ ]) || !safeName(value.consumer) || !safeName(value.provider)
48
+ || !safeBrokerUrl(value.brokerUrl)
49
+ || typeof value.matchersAggressive !== 'boolean'
50
+ || typeof value.generateProvider !== 'boolean') {
51
+ throw new Error('architecture-answers-invalid');
52
+ }
53
+ return {
54
+ consumer: value.consumer, provider: value.provider, brokerUrl: value.brokerUrl,
55
+ matchersAggressive: value.matchersAggressive, generateProvider: value.generateProvider,
56
+ };
57
+ }
58
+ function checkedPrivateDirectory(projectRoot, create) {
59
+ const root = resolve(projectRoot);
60
+ const top = execFileSync('git', ['rev-parse', '--show-toplevel'], {
61
+ cwd: root, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'],
62
+ }).trim();
63
+ if (realpathSync(root) !== root || resolve(top) !== root) {
64
+ throw new Error('architecture-answers-private-store-invalid');
65
+ }
66
+ const commonRaw = execFileSync('git', ['rev-parse', '--git-common-dir'], {
67
+ cwd: root, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'],
68
+ }).trim();
69
+ const commonDir = isAbsolute(commonRaw) ? resolve(commonRaw) : resolve(root, commonRaw);
70
+ if (realpathSync(commonDir) !== commonDir || lstatSync(commonDir).isSymbolicLink()) {
71
+ throw new Error('architecture-answers-private-store-invalid');
72
+ }
73
+ const directory = join(commonDir, 'neocortex-architecture');
74
+ if (!existsSync(directory) && !create)
75
+ return undefined;
76
+ if (create)
77
+ mkdirSync(directory, { recursive: true, mode: 0o700 });
78
+ const entry = lstatSync(directory);
79
+ if (!entry.isDirectory() || entry.isSymbolicLink()
80
+ || entry.uid !== process.getuid?.() || (entry.mode & 0o077) !== 0) {
81
+ throw new Error('architecture-answers-private-store-invalid');
82
+ }
83
+ return directory;
84
+ }
85
+ function answerContext(projectRoot, allowDelegated) {
86
+ const statePath = join(projectRoot, '.neocortex', 'state.json');
87
+ const stateEntry = lstatSync(statePath);
88
+ if (!stateEntry.isFile() || stateEntry.isSymbolicLink()) {
89
+ throw new Error('architecture-answers-state-invalid');
90
+ }
91
+ const rawState = readFileSync(statePath, 'utf8');
92
+ const state = JSON.parse(rawState);
93
+ const archPlan = object(state) && object(state.archPlan) ? state.archPlan : undefined;
94
+ const checkpoint = archPlan && object(archPlan.execution) ? archPlan.execution : undefined;
95
+ const rows = checkpoint?.acceptedStages;
96
+ if (!checkpoint || checkpoint.schemaVersion !== 1 || checkpoint.owner !== 'root'
97
+ || checkpoint.evidencePolicy !== 'v2-required'
98
+ || checkpoint.currentStageCommand !== 'generate-pacts'
99
+ || !(checkpoint.continuationState === 'ask-user'
100
+ && JSON.stringify(checkpoint.questionIds) === JSON.stringify(PACT_QUESTION_IDS)
101
+ || allowDelegated && checkpoint.continuationState === 'delegate'
102
+ && JSON.stringify(checkpoint.questionIds) === '[]')
103
+ || typeof checkpoint.targetEpicId !== 'string'
104
+ || !/^P[1-9][0-9]{0,11}$/.test(checkpoint.targetEpicId)
105
+ || !object(state) || !object(state.epics) || !Object.hasOwn(state.epics, checkpoint.targetEpicId)
106
+ || typeof checkpoint.graphRevision !== 'string'
107
+ || !/^[a-f0-9]{64}$/.test(checkpoint.graphRevision)
108
+ || !Array.isArray(rows) || rows.length !== STAGE_CHAIN.length
109
+ || !rows.every((row, index) => object(row) && row.stageCommand === STAGE_CHAIN[index]
110
+ && row.targetEpicId === checkpoint.targetEpicId
111
+ && object(row.completionEvidence) && object(row.validationEvidence))) {
112
+ throw new Error('architecture-answers-checkpoint-invalid');
113
+ }
114
+ const delegatedReceipt = checkpoint.continuationState === 'delegate'
115
+ ? checkpoint.pactAnswerReceipt : undefined;
116
+ if (checkpoint.continuationState === 'delegate'
117
+ && (!object(delegatedReceipt) || !exactKeys(delegatedReceipt, [
118
+ 'schemaVersion', 'targetEpicId', 'graphRevision', 'stateSha256',
119
+ 'answersSha256', 'questionIds', 'publicSafe',
120
+ ]) || delegatedReceipt.schemaVersion !== 'PactAnswerReceipt/1'
121
+ || delegatedReceipt.targetEpicId !== checkpoint.targetEpicId
122
+ || delegatedReceipt.graphRevision !== checkpoint.graphRevision
123
+ || typeof delegatedReceipt.stateSha256 !== 'string'
124
+ || !/^[a-f0-9]{64}$/.test(delegatedReceipt.stateSha256)
125
+ || typeof delegatedReceipt.answersSha256 !== 'string'
126
+ || !/^[a-f0-9]{64}$/.test(delegatedReceipt.answersSha256)
127
+ || JSON.stringify(delegatedReceipt.questionIds) !== JSON.stringify(PACT_QUESTION_IDS)
128
+ || delegatedReceipt.publicSafe !== true)) {
129
+ throw new Error('architecture-answers-checkpoint-invalid');
130
+ }
131
+ return { targetEpicId: checkpoint.targetEpicId, graphRevision: checkpoint.graphRevision,
132
+ stateSha256: checkpoint.continuationState === 'ask-user'
133
+ ? createHash('sha256').update(rawState).digest('hex')
134
+ : delegatedReceipt.stateSha256,
135
+ ...(delegatedReceipt ? { delegatedReceipt: delegatedReceipt } : {}) };
136
+ }
137
+ function receiptFor(record) {
138
+ return {
139
+ schemaVersion: 'PactAnswerReceipt/1', targetEpicId: record.targetEpicId,
140
+ graphRevision: record.graphRevision, stateSha256: record.stateSha256,
141
+ answersSha256: record.checksumSha256,
142
+ questionIds: PACT_QUESTION_IDS, publicSafe: true,
143
+ };
144
+ }
145
+ function checkedRecord(context, answers, nonce) {
146
+ const payload = { schemaVersion: nonce ? 'PactLocalAnswers/2' : 'PactLocalAnswers/1',
147
+ ...context, ...(nonce ? { nonce } : {}), answers };
148
+ return { ...payload,
149
+ checksumSha256: createHash('sha256').update(`${JSON.stringify(payload)}\n`).digest('hex') };
150
+ }
151
+ /** Store explicit answers without mutating the tracked architecture checkpoint. */
152
+ export function savePactAnswers(projectRoot, value) {
153
+ const answers = parseAnswers(value);
154
+ const context = answerContext(projectRoot, false);
155
+ const existing = loadPrivatePactRecord(projectRoot);
156
+ if (existing) {
157
+ if (!isDeepStrictEqual(existing.answers, answers))
158
+ throw new Error('architecture-answers-conflict');
159
+ return receiptFor(existing);
160
+ }
161
+ const record = checkedRecord(context, answers, randomBytes(32).toString('hex'));
162
+ const canonical = `${JSON.stringify(record)}\n`;
163
+ const directory = checkedPrivateDirectory(projectRoot, true);
164
+ const target = join(directory, `pact-answers-${record.targetEpicId}-${record.graphRevision}.json`);
165
+ if (existsSync(target))
166
+ throw new Error('architecture-answers-conflict');
167
+ const temporary = join(directory, `pact-answers-tmp-${randomUUID()}.json`);
168
+ let linked = false;
169
+ const file = openSync(temporary, 'wx', 0o600);
170
+ try {
171
+ writeFileSync(file, canonical);
172
+ fsyncSync(file);
173
+ }
174
+ finally {
175
+ closeSync(file);
176
+ }
177
+ try {
178
+ linkSync(temporary, target);
179
+ linked = true;
180
+ const handle = openSync(directory, 'r');
181
+ try {
182
+ fsyncSync(handle);
183
+ }
184
+ finally {
185
+ closeSync(handle);
186
+ }
187
+ }
188
+ finally {
189
+ unlinkSync(temporary);
190
+ if (!linked && existsSync(target))
191
+ throw new Error('architecture-answers-conflict');
192
+ }
193
+ return receiptFor(record);
194
+ }
195
+ function loadPrivatePactRecord(projectRoot, requireDelegated = false) {
196
+ const context = answerContext(projectRoot, true);
197
+ if (requireDelegated && !context.delegatedReceipt) {
198
+ throw new Error('architecture-answers-checkpoint-invalid');
199
+ }
200
+ const directory = checkedPrivateDirectory(projectRoot, false);
201
+ if (!directory)
202
+ return undefined;
203
+ const target = join(directory, `pact-answers-${context.targetEpicId}-${context.graphRevision}.json`);
204
+ if (!existsSync(target))
205
+ return undefined;
206
+ const entry = lstatSync(target);
207
+ if (!entry.isFile() || entry.isSymbolicLink() || entry.size > 8192
208
+ || entry.uid !== process.getuid?.() || (entry.mode & 0o077) !== 0) {
209
+ throw new Error('architecture-answers-conflict');
210
+ }
211
+ const raw = readFileSync(target, 'utf8');
212
+ const parsed = JSON.parse(raw);
213
+ if (!object(parsed) || !(parsed.schemaVersion === 'PactLocalAnswers/1' && exactKeys(parsed, [
214
+ 'schemaVersion', 'targetEpicId', 'graphRevision', 'stateSha256', 'answers', 'checksumSha256',
215
+ ]) || parsed.schemaVersion === 'PactLocalAnswers/2' && exactKeys(parsed, [
216
+ 'schemaVersion', 'targetEpicId', 'graphRevision', 'stateSha256', 'nonce', 'answers', 'checksumSha256',
217
+ ]) && typeof parsed.nonce === 'string' && /^[a-f0-9]{64}$/.test(parsed.nonce))
218
+ || parsed.targetEpicId !== context.targetEpicId
219
+ || parsed.graphRevision !== context.graphRevision
220
+ || parsed.stateSha256 !== context.stateSha256) {
221
+ throw new Error('architecture-answers-conflict');
222
+ }
223
+ const record = checkedRecord({ targetEpicId: context.targetEpicId,
224
+ graphRevision: context.graphRevision, stateSha256: context.stateSha256 }, parseAnswers(parsed.answers), parsed.schemaVersion === 'PactLocalAnswers/2' ? parsed.nonce : undefined);
225
+ const canonical = `${JSON.stringify(record)}\n`;
226
+ if (raw !== canonical || context.delegatedReceipt
227
+ && JSON.stringify(receiptFor(record)) !== JSON.stringify(context.delegatedReceipt)) {
228
+ throw new Error('architecture-answers-conflict');
229
+ }
230
+ return record;
231
+ }
232
+ /** Revalidate the private record against the exact current root checkpoint. Never print answers. */
233
+ export function loadPactAnswerReceipt(projectRoot) {
234
+ const record = loadPrivatePactRecord(projectRoot);
235
+ return record ? receiptFor(record) : undefined;
236
+ }
237
+ /** Internal private handoff only. Callers must never log or return `answers`. */
238
+ export function loadPactPrivateAnswers(projectRoot) {
239
+ const record = loadPrivatePactRecord(projectRoot, true);
240
+ return record ? { receipt: receiptFor(record), answers: record.answers } : undefined;
241
+ }
242
+ async function readBoundedStdin() {
243
+ const chunks = [];
244
+ let total = 0;
245
+ for await (const chunk of process.stdin) {
246
+ const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk));
247
+ total += bytes.length;
248
+ if (total > MAX_INPUT_BYTES)
249
+ throw new Error('architecture-answers-input-too-large');
250
+ chunks.push(bytes);
251
+ }
252
+ return JSON.parse(Buffer.concat(chunks).toString('utf8'));
253
+ }
254
+ export async function architectureAnswersCliHandler(args) {
255
+ if ((args[0] !== 'save' && args[0] !== 'status')
256
+ || (args.length > 1 && (args.length !== 3 || args[1] !== '--project-root' || !args[2]))) {
257
+ process.stdout.write('{"ok":false,"reasonCode":"architecture-answers-usage-invalid","publicSafe":true}\n');
258
+ return 1;
259
+ }
260
+ try {
261
+ const root = args[2] ?? process.cwd();
262
+ const receipt = args[0] === 'save'
263
+ ? savePactAnswers(root, await readBoundedStdin()) : loadPactAnswerReceipt(root);
264
+ process.stdout.write(`${JSON.stringify({ ok: true, ...receipt,
265
+ ...(receipt ? {} : { recorded: false, publicSafe: true }) })}\n`);
266
+ return 0;
267
+ }
268
+ catch (error) {
269
+ const code = error instanceof Error && /^architecture-answers-[a-z-]+$/.test(error.message)
270
+ ? error.message : 'architecture-answers-failed';
271
+ process.stdout.write(`${JSON.stringify({ ok: false, reasonCode: code, publicSafe: true })}\n`);
272
+ return 1;
273
+ }
274
+ }
@@ -1,4 +1,253 @@
1
- import{architectureMemoryPredecessorCount as F}from"./architecture-non-ui-continuation.js";import{execFileSync as g}from"node:child_process";import{createHash as V,randomUUID as B}from"node:crypto";import{closeSync as w,fsyncSync as I,lstatSync as j,mkdirSync as J,openSync as E,readFileSync as S,realpathSync as M,renameSync as L,unlinkSync as H,writeFileSync as O}from"node:fs";import{isAbsolute as X,join as f,resolve as v}from"node:path";import{listProjectMemoryTargets as q}from"../memory/shared-project-memory-types.js";import{parseRefreshMemoryArgs as U,verifyProjectMemoryTargets as _}from"./refresh-memory.js";function z(r,e){if(!c(r)||!c(e)||Object.keys(r).sort().join(",")!==["schemaVersion","targetEpicId","graphRevision","sourceHead","canonicalSha256","projectionSha256","fileCount","completionReceiptId","validationReceiptId","receiptId","status","evidenceIds","publicSafe"].sort().join(",")||r.schemaVersion!=="ArchitectureTerminalMemoryReceipt/1"||r.targetEpicId!==e.targetEpicId||r.graphRevision!==e.graphRevision||r.completionReceiptId!==e.completionEvidence?.receiptId||r.validationReceiptId!==e.validationEvidence?.receiptId||typeof r.sourceHead!="string"||!/^[a-f0-9]{40}$/.test(r.sourceHead)||![r.graphRevision,r.canonicalSha256,r.projectionSha256,r.completionReceiptId,r.validationReceiptId,r.receiptId].every(n=>typeof n=="string"&&/^[a-f0-9]{64}$/.test(n))||!Number.isInteger(r.fileCount)||r.fileCount<1||r.fileCount>64||r.status!=="passed"||r.publicSafe!==!0||!Array.isArray(r.evidenceIds)||r.evidenceIds.length!==1)return!1;const s={schemaVersion:r.schemaVersion,targetEpicId:r.targetEpicId,graphRevision:r.graphRevision,sourceHead:r.sourceHead,canonicalSha256:r.canonicalSha256,projectionSha256:r.projectionSha256,fileCount:r.fileCount,completionReceiptId:r.completionReceiptId,validationReceiptId:r.validationReceiptId},i=b(JSON.stringify(s));return r.receiptId===i&&r.evidenceIds[0]===`memory-physical-${i.slice(0,24)}`}function c(r){return typeof r=="object"&&r!==null&&!Array.isArray(r)}function b(r){return V("sha256").update(r).digest("hex")}function T(r,e){if(!c(e)||e.owner!=="root"||e.currentStageCommand!=="update-memory"||e.continuationState!=="delegate"||e.childStatus!=="succeeded"||e.evidencePolicy!=="v2-required"||typeof e.targetEpicId!="string"||!/^P[1-9][0-9]{0,11}$/.test(e.targetEpicId)||typeof e.graphRevision!="string"||!/^[a-f0-9]{64}$/.test(e.graphRevision)||!Array.isArray(e.acceptedStages)||e.acceptedStages.length!==F(e)||!c(e.reviewDecision)||e.reviewDecision.decision!=="approved"||!c(e.completionEvidence)||e.completionEvidence.status!=="succeeded"||e.completionEvidence.stageCommand!=="update-memory"||!c(e.validationEvidence)||e.validationEvidence.status!=="passed"||e.validationEvidence.stageCommand!=="update-memory"||typeof e.completionEvidence.receiptId!="string"||!/^[a-f0-9]{64}$/.test(e.completionEvidence.receiptId)||typeof e.validationEvidence.receiptId!="string"||!/^[a-f0-9]{64}$/.test(e.validationEvidence.receiptId))throw new Error("architecture-terminal-memory-evidence-incomplete");const s=_(U(["--verify","--project-root",r])),i=g("git",["rev-parse","HEAD"],{cwd:r,encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim();if(!/^[a-f0-9]{40}$/.test(i))throw new Error("architecture-terminal-memory-head-invalid");const n=S(f(r,"NEOCORTEX.md")),u=g("git",["show","HEAD:NEOCORTEX.md"],{cwd:r,stdio:["ignore","pipe","ignore"],maxBuffer:65536});if(!n.equals(u)||b(n)!==s.canonicalSha256)throw new Error("architecture-terminal-memory-uncommitted");const k=[...new Set(["NEOCORTEX.md",...q().flatMap(a=>[a.primaryFile,...a.secondaryFiles.filter(o=>o.scope==="project").map(o=>o.path)])])];if(g("git",["diff","--name-only","HEAD","--",...k],{cwd:r,encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim())throw new Error("architecture-terminal-memory-uncommitted");let p=i;if(e.terminalMemoryCheckpoint!==void 0){if(!z(e.terminalMemoryCheckpoint,e))throw new Error("architecture-terminal-memory-checkpoint-invalid");p=e.terminalMemoryCheckpoint.sourceHead;const a=o=>g("git",o,{cwd:r,encoding:"utf8",stdio:["ignore","pipe","ignore"],maxBuffer:1024*1024}).trim();try{a(["merge-base","--is-ancestor",p,i]);const o=a(["rev-list","--max-count=257","--reverse","--parents",`${p}..${i}`]).split(`
2
- `).filter(Boolean);if(o.length>256)throw new Error("checkpoint-range-too-large");for(const y of o){const l=y.split(" ");if(l.length!==2)throw new Error("checkpoint-merge-unverified");const h=a(["diff-tree","--no-commit-id","--name-only","-r",l[0]]).split(`
3
- `).filter(Boolean);if(h.length!==1||h[0]!==".neocortex/state.json")throw new Error("checkpoint-range-not-state-only")}}catch{throw new Error("architecture-terminal-memory-checkpoint-range-invalid")}}const m={schemaVersion:"ArchitectureTerminalMemoryReceipt/1",targetEpicId:e.targetEpicId,graphRevision:e.graphRevision,sourceHead:p,canonicalSha256:s.canonicalSha256,projectionSha256:s.projectionSha256,fileCount:s.fileCount,completionReceiptId:e.completionEvidence.receiptId,validationReceiptId:e.validationEvidence.receiptId},t=b(JSON.stringify(m));return{...m,receiptId:t,status:"passed",evidenceIds:[`memory-physical-${t.slice(0,24)}`],publicSafe:!0}}function G(r){const e=v(r),s=g("git",["rev-parse","--show-toplevel"],{cwd:e,encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim();if(M(e)!==e||v(s)!==e)throw new Error("architecture-terminal-memory-project-root-invalid");const i=f(e,".neocortex"),n=f(i,"state.json"),u=f(i,"state.json.architecture.lock"),k=E(u,"wx",384);let d;try{const p=j(n);if(!p.isFile()||p.isSymbolicLink())throw new Error("architecture-terminal-memory-state-invalid");const m=S(n,"utf8"),t=JSON.parse(m);if(!c(t)||!c(t.archPlan)||!c(t.archPlan.execution)||!c(t.epics)||t.scope&&c(t.scope)&&t.scope.kind==="projected"||!Object.hasOwn(t.epics,String(t.archPlan.execution.targetEpicId))||t.archPlan.execution.terminalMemoryCheckpoint!==void 0)throw new Error("architecture-terminal-memory-state-invalid");const a=T(e,t.archPlan.execution),o=g("git",["rev-parse","--git-common-dir"],{cwd:e,encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim(),y=X(o)?v(o):v(e,o);if(M(y)!==y||j(y).isSymbolicLink())throw new Error("architecture-terminal-memory-backup-dir-invalid");const l=f(y,"neocortex-architecture");J(l,{recursive:!0,mode:448});const h=j(l);if(!h.isDirectory()||h.isSymbolicLink()||h.uid!==process.getuid?.()||(h.mode&63)!==0)throw new Error("architecture-terminal-memory-backup-dir-invalid");if(S(n,"utf8")!==m)throw new Error("architecture-terminal-memory-state-conflict");const P=B(),D=f(l,`state-backup-${P}.json`),R=E(D,"wx",384);try{O(R,m),I(R)}finally{w(R)}const A=E(l,"r");try{I(A)}finally{w(A)}const N={...t,archPlan:{...t.archPlan,execution:{...t.archPlan.execution,terminalMemoryCheckpoint:a}}};d=f(i,`state.json.architecture-tmp-${P}`);const C=E(d,"wx",384);try{O(C,`${JSON.stringify(N,null,2)}
4
- `),I(C)}finally{w(C)}if(S(n,"utf8")!==m)throw new Error("architecture-terminal-memory-state-conflict");if(T(e,t.archPlan.execution).receiptId!==a.receiptId)throw new Error("architecture-terminal-memory-physical-conflict");L(d,n),d=void 0;const $=E(i,"r");try{I($)}finally{w($)}return a}finally{if(d)try{H(d)}catch{}w(k),H(u)}}async function re(r){try{if(r[0]!=="seal"||r.length!==1&&r.length!==3||r.length===3&&r[1]!=="--project-root")throw new Error("usage: architecture-memory seal [--project-root <path>]");const e=G(r.length===3?r[2]:process.cwd());return console.log(`Architecture memory sealed: receipt=${e.receiptId} head=${e.sourceHead} files=${e.fileCount}`),0}catch(e){return console.error(e instanceof Error?e.message:String(e)),1}}export{z as acceptedArchitectureTerminalMemoryReceipt,re as architectureMemoryTerminalCliHandler,T as deriveArchitectureTerminalMemoryReceipt,G as sealArchitectureTerminalMemory};
1
+ import { architectureMemoryPredecessorCount } from './architecture-non-ui-continuation.js';
2
+ import { execFileSync } from 'node:child_process';
3
+ import { createHash, randomUUID } from 'node:crypto';
4
+ import { closeSync, fsyncSync, lstatSync, mkdirSync, openSync, readFileSync, realpathSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';
5
+ import { isAbsolute, join, resolve } from 'node:path';
6
+ import { listProjectMemoryTargets } from '../memory/shared-project-memory-types.js';
7
+ import { parseRefreshMemoryArgs, verifyProjectMemoryTargets } from './refresh-memory.js';
8
+ export function acceptedArchitectureTerminalMemoryReceipt(value, execution) {
9
+ if (!isRecord(value) || !isRecord(execution)
10
+ || Object.keys(value).sort().join(',') !== [
11
+ 'schemaVersion', 'targetEpicId', 'graphRevision', 'sourceHead', 'canonicalSha256',
12
+ 'projectionSha256', 'fileCount', 'completionReceiptId', 'validationReceiptId',
13
+ 'receiptId', 'status', 'evidenceIds', 'publicSafe',
14
+ ].sort().join(',')
15
+ || value.schemaVersion !== 'ArchitectureTerminalMemoryReceipt/1'
16
+ || value.targetEpicId !== execution.targetEpicId
17
+ || value.graphRevision !== execution.graphRevision
18
+ || value.completionReceiptId !== execution.completionEvidence?.receiptId
19
+ || value.validationReceiptId !== execution.validationEvidence?.receiptId
20
+ || typeof value.sourceHead !== 'string' || !/^[a-f0-9]{40}$/.test(value.sourceHead)
21
+ || ![value.graphRevision, value.canonicalSha256, value.projectionSha256,
22
+ value.completionReceiptId, value.validationReceiptId, value.receiptId]
23
+ .every((field) => typeof field === 'string' && /^[a-f0-9]{64}$/.test(field))
24
+ || !Number.isInteger(value.fileCount) || value.fileCount < 1
25
+ || value.fileCount > 64 || value.status !== 'passed'
26
+ || value.publicSafe !== true || !Array.isArray(value.evidenceIds)
27
+ || value.evidenceIds.length !== 1)
28
+ return false;
29
+ const fields = {
30
+ schemaVersion: value.schemaVersion, targetEpicId: value.targetEpicId,
31
+ graphRevision: value.graphRevision, sourceHead: value.sourceHead,
32
+ canonicalSha256: value.canonicalSha256, projectionSha256: value.projectionSha256,
33
+ fileCount: value.fileCount, completionReceiptId: value.completionReceiptId,
34
+ validationReceiptId: value.validationReceiptId,
35
+ };
36
+ const receiptId = hash(JSON.stringify(fields));
37
+ return value.receiptId === receiptId
38
+ && value.evidenceIds[0] === `memory-physical-${receiptId.slice(0, 24)}`;
39
+ }
40
+ function isRecord(value) {
41
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
42
+ }
43
+ function hash(value) {
44
+ return createHash('sha256').update(value).digest('hex');
45
+ }
46
+ /** A root-only physical seal; child output and a copied JSON assertion are insufficient. */
47
+ export function deriveArchitectureTerminalMemoryReceipt(projectRoot, execution) {
48
+ if (!isRecord(execution) || execution.owner !== 'root'
49
+ || execution.currentStageCommand !== 'update-memory'
50
+ || execution.continuationState !== 'delegate'
51
+ || execution.childStatus !== 'succeeded'
52
+ || execution.evidencePolicy !== 'v2-required'
53
+ || typeof execution.targetEpicId !== 'string'
54
+ || !/^P[1-9][0-9]{0,11}$/.test(execution.targetEpicId)
55
+ || typeof execution.graphRevision !== 'string'
56
+ || !/^[a-f0-9]{64}$/.test(execution.graphRevision)
57
+ || !Array.isArray(execution.acceptedStages) || execution.acceptedStages.length !== architectureMemoryPredecessorCount(execution)
58
+ || !isRecord(execution.reviewDecision) || execution.reviewDecision.decision !== 'approved'
59
+ || !isRecord(execution.completionEvidence) || execution.completionEvidence.status !== 'succeeded'
60
+ || execution.completionEvidence.stageCommand !== 'update-memory'
61
+ || !isRecord(execution.validationEvidence) || execution.validationEvidence.status !== 'passed'
62
+ || execution.validationEvidence.stageCommand !== 'update-memory'
63
+ || typeof execution.completionEvidence.receiptId !== 'string'
64
+ || !/^[a-f0-9]{64}$/.test(execution.completionEvidence.receiptId)
65
+ || typeof execution.validationEvidence.receiptId !== 'string'
66
+ || !/^[a-f0-9]{64}$/.test(execution.validationEvidence.receiptId)) {
67
+ throw new Error('architecture-terminal-memory-evidence-incomplete');
68
+ }
69
+ const verification = verifyProjectMemoryTargets(parseRefreshMemoryArgs([
70
+ '--verify', '--project-root', projectRoot,
71
+ ]));
72
+ const head = execFileSync('git', ['rev-parse', 'HEAD'], {
73
+ cwd: projectRoot, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'],
74
+ }).trim();
75
+ if (!/^[a-f0-9]{40}$/.test(head))
76
+ throw new Error('architecture-terminal-memory-head-invalid');
77
+ const canonical = readFileSync(join(projectRoot, 'NEOCORTEX.md'));
78
+ const committedCanonical = execFileSync('git', ['show', 'HEAD:NEOCORTEX.md'], {
79
+ cwd: projectRoot, stdio: ['ignore', 'pipe', 'ignore'], maxBuffer: 65_536,
80
+ });
81
+ if (!canonical.equals(committedCanonical) || hash(canonical) !== verification.canonicalSha256) {
82
+ throw new Error('architecture-terminal-memory-uncommitted');
83
+ }
84
+ const projectPaths = [...new Set(['NEOCORTEX.md', ...listProjectMemoryTargets().flatMap((target) => [
85
+ target.primaryFile, ...target.secondaryFiles.filter((file) => file.scope === 'project').map((file) => file.path),
86
+ ])])];
87
+ const changedTracked = execFileSync('git', ['diff', '--name-only', 'HEAD', '--', ...projectPaths], {
88
+ cwd: projectRoot, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'],
89
+ }).trim();
90
+ if (changedTracked)
91
+ throw new Error('architecture-terminal-memory-uncommitted');
92
+ // Persisting the accepted public checkpoint advances HEAD after the memory
93
+ // commit. Keep its immutable seal only across independently inspected,
94
+ // single-parent state-only commits, including every intermediate commit.
95
+ let sourceHead = head;
96
+ if (execution.terminalMemoryCheckpoint !== undefined) {
97
+ if (!acceptedArchitectureTerminalMemoryReceipt(execution.terminalMemoryCheckpoint, execution)) {
98
+ throw new Error('architecture-terminal-memory-checkpoint-invalid');
99
+ }
100
+ sourceHead = execution.terminalMemoryCheckpoint.sourceHead;
101
+ const git = (args) => execFileSync('git', args, {
102
+ cwd: projectRoot, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], maxBuffer: 1024 * 1024,
103
+ }).trim();
104
+ try {
105
+ git(['merge-base', '--is-ancestor', sourceHead, head]);
106
+ const commits = git(['rev-list', '--max-count=257', '--reverse', '--parents', `${sourceHead}..${head}`])
107
+ .split('\n').filter(Boolean);
108
+ if (commits.length > 256)
109
+ throw new Error('checkpoint-range-too-large');
110
+ for (const row of commits) {
111
+ const ids = row.split(' ');
112
+ if (ids.length !== 2)
113
+ throw new Error('checkpoint-merge-unverified');
114
+ const paths = git(['diff-tree', '--no-commit-id', '--name-only', '-r', ids[0]]).split('\n').filter(Boolean);
115
+ if (paths.length !== 1 || paths[0] !== '.neocortex/state.json')
116
+ throw new Error('checkpoint-range-not-state-only');
117
+ }
118
+ }
119
+ catch {
120
+ throw new Error('architecture-terminal-memory-checkpoint-range-invalid');
121
+ }
122
+ }
123
+ const fields = {
124
+ schemaVersion: 'ArchitectureTerminalMemoryReceipt/1',
125
+ targetEpicId: execution.targetEpicId,
126
+ graphRevision: execution.graphRevision,
127
+ sourceHead,
128
+ canonicalSha256: verification.canonicalSha256,
129
+ projectionSha256: verification.projectionSha256,
130
+ fileCount: verification.fileCount,
131
+ completionReceiptId: execution.completionEvidence.receiptId,
132
+ validationReceiptId: execution.validationEvidence.receiptId,
133
+ };
134
+ const receiptId = hash(JSON.stringify(fields));
135
+ return {
136
+ ...fields, receiptId, status: 'passed',
137
+ evidenceIds: [`memory-physical-${receiptId.slice(0, 24)}`],
138
+ publicSafe: true,
139
+ };
140
+ }
141
+ /** Root-only CAS: seal a committed, verified memory projection into local state. */
142
+ export function sealArchitectureTerminalMemory(projectRoot) {
143
+ const root = resolve(projectRoot);
144
+ const top = execFileSync('git', ['rev-parse', '--show-toplevel'], {
145
+ cwd: root, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'],
146
+ }).trim();
147
+ if (realpathSync(root) !== root || resolve(top) !== root) {
148
+ throw new Error('architecture-terminal-memory-project-root-invalid');
149
+ }
150
+ const stateDir = join(root, '.neocortex');
151
+ const statePath = join(stateDir, 'state.json');
152
+ const lockPath = join(stateDir, 'state.json.architecture.lock');
153
+ const lock = openSync(lockPath, 'wx', 0o600);
154
+ let temporaryPath;
155
+ try {
156
+ const entry = lstatSync(statePath);
157
+ if (!entry.isFile() || entry.isSymbolicLink()) {
158
+ throw new Error('architecture-terminal-memory-state-invalid');
159
+ }
160
+ const raw = readFileSync(statePath, 'utf8');
161
+ const state = JSON.parse(raw);
162
+ if (!isRecord(state) || !isRecord(state.archPlan) || !isRecord(state.archPlan.execution)
163
+ || !isRecord(state.epics) || state.scope && isRecord(state.scope) && state.scope.kind === 'projected'
164
+ || !Object.hasOwn(state.epics, String(state.archPlan.execution.targetEpicId))
165
+ || state.archPlan.execution.terminalMemoryCheckpoint !== undefined) {
166
+ throw new Error('architecture-terminal-memory-state-invalid');
167
+ }
168
+ const receipt = deriveArchitectureTerminalMemoryReceipt(root, state.archPlan.execution);
169
+ const commonRaw = execFileSync('git', ['rev-parse', '--git-common-dir'], {
170
+ cwd: root, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'],
171
+ }).trim();
172
+ const commonDir = isAbsolute(commonRaw) ? resolve(commonRaw) : resolve(root, commonRaw);
173
+ if (realpathSync(commonDir) !== commonDir || lstatSync(commonDir).isSymbolicLink()) {
174
+ throw new Error('architecture-terminal-memory-backup-dir-invalid');
175
+ }
176
+ const backupDir = join(commonDir, 'neocortex-architecture');
177
+ mkdirSync(backupDir, { recursive: true, mode: 0o700 });
178
+ const backupEntry = lstatSync(backupDir);
179
+ if (!backupEntry.isDirectory() || backupEntry.isSymbolicLink()
180
+ || backupEntry.uid !== process.getuid?.() || (backupEntry.mode & 0o077) !== 0) {
181
+ throw new Error('architecture-terminal-memory-backup-dir-invalid');
182
+ }
183
+ if (readFileSync(statePath, 'utf8') !== raw)
184
+ throw new Error('architecture-terminal-memory-state-conflict');
185
+ const suffix = randomUUID();
186
+ const backupPath = join(backupDir, `state-backup-${suffix}.json`);
187
+ const backup = openSync(backupPath, 'wx', 0o600);
188
+ try {
189
+ writeFileSync(backup, raw);
190
+ fsyncSync(backup);
191
+ }
192
+ finally {
193
+ closeSync(backup);
194
+ }
195
+ const backupDirectory = openSync(backupDir, 'r');
196
+ try {
197
+ fsyncSync(backupDirectory);
198
+ }
199
+ finally {
200
+ closeSync(backupDirectory);
201
+ }
202
+ const next = { ...state, archPlan: { ...state.archPlan,
203
+ execution: { ...state.archPlan.execution, terminalMemoryCheckpoint: receipt } } };
204
+ temporaryPath = join(stateDir, `state.json.architecture-tmp-${suffix}`);
205
+ const temporary = openSync(temporaryPath, 'wx', 0o600);
206
+ try {
207
+ writeFileSync(temporary, `${JSON.stringify(next, null, 2)}\n`);
208
+ fsyncSync(temporary);
209
+ }
210
+ finally {
211
+ closeSync(temporary);
212
+ }
213
+ if (readFileSync(statePath, 'utf8') !== raw)
214
+ throw new Error('architecture-terminal-memory-state-conflict');
215
+ if (deriveArchitectureTerminalMemoryReceipt(root, state.archPlan.execution).receiptId !== receipt.receiptId) {
216
+ throw new Error('architecture-terminal-memory-physical-conflict');
217
+ }
218
+ renameSync(temporaryPath, statePath);
219
+ temporaryPath = undefined;
220
+ const directory = openSync(stateDir, 'r');
221
+ try {
222
+ fsyncSync(directory);
223
+ }
224
+ finally {
225
+ closeSync(directory);
226
+ }
227
+ return receipt;
228
+ }
229
+ finally {
230
+ if (temporaryPath)
231
+ try {
232
+ unlinkSync(temporaryPath);
233
+ }
234
+ catch { /* preserve primary failure */ }
235
+ closeSync(lock);
236
+ unlinkSync(lockPath);
237
+ }
238
+ }
239
+ export async function architectureMemoryTerminalCliHandler(args) {
240
+ try {
241
+ if (args[0] !== 'seal' || args.length !== 1 && args.length !== 3
242
+ || args.length === 3 && args[1] !== '--project-root') {
243
+ throw new Error('usage: architecture-memory seal [--project-root <path>]');
244
+ }
245
+ const receipt = sealArchitectureTerminalMemory(args.length === 3 ? args[2] : process.cwd());
246
+ console.log(`Architecture memory sealed: receipt=${receipt.receiptId} head=${receipt.sourceHead} files=${receipt.fileCount}`);
247
+ return 0;
248
+ }
249
+ catch (error) {
250
+ console.error(error instanceof Error ? error.message : String(error));
251
+ return 1;
252
+ }
253
+ }