@ornexus/neocortex 4.60.73 → 4.60.75

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 (269) hide show
  1. package/dist/release-source.json +1 -1
  2. package/dist/sbom.cdx.json +1216 -1210
  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 +1 -108
  7. package/packages/client/dist/adapters/antigravity-adapter.js +2 -77
  8. package/packages/client/dist/adapters/claude-code-adapter.js +3 -79
  9. package/packages/client/dist/adapters/codex-adapter.js +2 -80
  10. package/packages/client/dist/adapters/cursor-adapter.js +4 -115
  11. package/packages/client/dist/adapters/gemini-adapter.js +2 -71
  12. package/packages/client/dist/adapters/grok-adapter.js +2 -42
  13. package/packages/client/dist/adapters/index.js +1 -22
  14. package/packages/client/dist/adapters/platform-detector.js +1 -212
  15. package/packages/client/dist/adapters/target-adapter.js +0 -12
  16. package/packages/client/dist/adapters/vscode-adapter.js +2 -72
  17. package/packages/client/dist/agent/refresh-stubs.js +2 -417
  18. package/packages/client/dist/agent/update-agent-yaml.js +1 -102
  19. package/packages/client/dist/agent/update-description.js +1 -251
  20. package/packages/client/dist/cache/crypto-utils.js +1 -78
  21. package/packages/client/dist/cache/encrypted-cache.d.ts +27 -0
  22. package/packages/client/dist/cache/encrypted-cache.js +1 -108
  23. package/packages/client/dist/cache/in-memory-asset-cache.js +1 -73
  24. package/packages/client/dist/cache/index.js +1 -13
  25. package/packages/client/dist/cache/protected-pi-boundary.js +1 -44
  26. package/packages/client/dist/checkpoint/checkpoint-client-reader.js +2 -127
  27. package/packages/client/dist/checkpoint/index.js +1 -11
  28. package/packages/client/dist/checkpoint/shared-checkpoint-types.js +1 -32
  29. package/packages/client/dist/cli.js +5 -358
  30. package/packages/client/dist/commands/activate.js +8 -394
  31. package/packages/client/dist/commands/architecture-answers.js +7 -274
  32. package/packages/client/dist/commands/architecture-memory-terminal.js +4 -253
  33. package/packages/client/dist/commands/architecture-non-ui-continuation.js +1 -100
  34. package/packages/client/dist/commands/architecture-root-binding.js +1 -51
  35. package/packages/client/dist/commands/cache-status.js +2 -112
  36. package/packages/client/dist/commands/config.js +1 -88
  37. package/packages/client/dist/commands/doctor.js +2 -635
  38. package/packages/client/dist/commands/invoke.d.ts +38 -1
  39. package/packages/client/dist/commands/invoke.js +143 -7200
  40. package/packages/client/dist/commands/legacy-epic-terminal.js +1 -71
  41. package/packages/client/dist/commands/legacy-story-branch.js +1 -96
  42. package/packages/client/dist/commands/legacy-story-pr.js +1 -276
  43. package/packages/client/dist/commands/mutation-qa-checkpoint.js +5 -285
  44. package/packages/client/dist/commands/mutation-qa-gate.js +1 -54
  45. package/packages/client/dist/commands/mutation-qa-git.js +1 -41
  46. package/packages/client/dist/commands/mutation-qa-policy.js +1 -102
  47. package/packages/client/dist/commands/mutation-qa-run.js +3 -219
  48. package/packages/client/dist/commands/mutation-qa-verdict.js +1 -149
  49. package/packages/client/dist/commands/mutation-qa.js +1 -220
  50. package/packages/client/dist/commands/native-agent.js +2 -136
  51. package/packages/client/dist/commands/planning-diagnosis.js +1 -43
  52. package/packages/client/dist/commands/planning-generation.js +1 -53
  53. package/packages/client/dist/commands/planning-memory.js +1 -40
  54. package/packages/client/dist/commands/planning-research.js +1 -42
  55. package/packages/client/dist/commands/private-session.js +1 -75
  56. package/packages/client/dist/commands/property-qa.js +1 -139
  57. package/packages/client/dist/commands/qa-control.js +1 -16
  58. package/packages/client/dist/commands/qa-scan-browser-launch.js +1 -55
  59. package/packages/client/dist/commands/qa-scan-browser-route.js +1 -28
  60. package/packages/client/dist/commands/qa-scan-capture.js +5 -663
  61. package/packages/client/dist/commands/qa-scan-discovery.js +3 -376
  62. package/packages/client/dist/commands/qa-scan-review.js +5 -826
  63. package/packages/client/dist/commands/qa-synthetic-backend-capture.js +1 -266
  64. package/packages/client/dist/commands/qa-synthetic-backend-review.js +1 -297
  65. package/packages/client/dist/commands/qa-synthetic-backend.js +2 -340
  66. package/packages/client/dist/commands/qa-synthetic-broker-port.js +1 -85
  67. package/packages/client/dist/commands/qa-synthetic-browser.js +1 -39
  68. package/packages/client/dist/commands/refactor-characterization.js +21 -671
  69. package/packages/client/dist/commands/refactor-commit.js +13 -399
  70. package/packages/client/dist/commands/refactor-control.js +3 -142
  71. package/packages/client/dist/commands/refactor-cpp-assertions.js +3 -93
  72. package/packages/client/dist/commands/refactor-review.js +5 -283
  73. package/packages/client/dist/commands/refactor-root.js +7 -588
  74. package/packages/client/dist/commands/refresh-memory.js +3 -156
  75. package/packages/client/dist/commands/shared-architecture-non-ui.js +1 -24
  76. package/packages/client/dist/commands/story-checkpoint.js +1 -171
  77. package/packages/client/dist/config/host-auth-broker.d.ts +31 -0
  78. package/packages/client/dist/config/host-auth-broker.js +1 -0
  79. package/packages/client/dist/config/host-auth-reference.d.ts +32 -0
  80. package/packages/client/dist/config/host-auth-reference.js +1 -0
  81. package/packages/client/dist/config/profile-directory.js +1 -13
  82. package/packages/client/dist/config/resolver-selection.js +1 -278
  83. package/packages/client/dist/config/secure-config.js +12 -281
  84. package/packages/client/dist/config.js +2 -223
  85. package/packages/client/dist/constants.js +1 -25
  86. package/packages/client/dist/context/context-collector.js +2 -222
  87. package/packages/client/dist/context/context-sanitizer.js +1 -145
  88. package/packages/client/dist/continuity/autonomous-governance.js +1 -285
  89. package/packages/client/dist/continuity/continuity-client-state-store.js +1 -1587
  90. package/packages/client/dist/continuity/invoke-hooks.js +1 -129
  91. package/packages/client/dist/continuity/migrations/001-initial-schema.js +2 -12
  92. package/packages/client/dist/continuity/migrations/002-processing-output-summary-indexes.js +2 -12
  93. package/packages/client/dist/continuity/migrations/003-headless-execution-attempts.js +2 -12
  94. package/packages/client/dist/continuity/migrations/004-outbox-delivery-state.js +2 -12
  95. package/packages/client/dist/continuity/migrations/005-bounded-headless-queue.js +2 -12
  96. package/packages/client/dist/continuity/migrations/006-native-agent-dispatches.js +2 -10
  97. package/packages/client/dist/continuity/native-agent-store.js +1 -69
  98. package/packages/client/dist/continuity/native-agent-types.d.ts +11 -0
  99. package/packages/client/dist/continuity/native-agent-types.js +1 -90
  100. package/packages/client/dist/continuity/outbox.js +1 -117
  101. package/packages/client/dist/continuity/runner-bootstrap-policy.js +1 -172
  102. package/packages/client/dist/continuity/sqlite-store.js +81 -2612
  103. package/packages/client/dist/effects/invoke-effect-gateways.js +1 -62
  104. package/packages/client/dist/errors/error-messages.d.ts +3 -0
  105. package/packages/client/dist/errors/error-messages.js +2 -228
  106. package/packages/client/dist/errors/frontmatter-diagnostics.js +1 -125
  107. package/packages/client/dist/evidence/collector.js +1 -347
  108. package/packages/client/dist/evidence/index.js +1 -1
  109. package/packages/client/dist/evidence/types.js +0 -2
  110. package/packages/client/dist/fs/atomic-state-replace.js +1 -152
  111. package/packages/client/dist/generated/command-registry.generated.js +1 -4076
  112. package/packages/client/dist/generated/public-projections.generated.js +1 -149
  113. package/packages/client/dist/git/direct-yolo-c01-retry.js +4 -138
  114. package/packages/client/dist/git/direct-yolo-checkpoint.d.ts +11 -1
  115. package/packages/client/dist/git/direct-yolo-checkpoint.js +4 -1234
  116. package/packages/client/dist/git/epic-operational-state.js +2 -672
  117. package/packages/client/dist/git/git-lifecycle-step-ids.js +1 -14
  118. package/packages/client/dist/git/git-lifecycle-store.d.ts +23 -0
  119. package/packages/client/dist/git/git-lifecycle-store.js +10 -1981
  120. package/packages/client/dist/git/legacy-story-branch-checkpoint.js +9 -259
  121. package/packages/client/dist/git/legacy-story-branch-setup.js +4 -348
  122. package/packages/client/dist/git/legacy-story-pr.js +70 -3538
  123. package/packages/client/dist/git/secure-filesystem-boundary.js +5 -182
  124. package/packages/client/dist/graph-retrieval/pre-command-hook.js +1 -284
  125. package/packages/client/dist/graph-retrieval/shared-graph-retrieval-contract.js +1 -237
  126. package/packages/client/dist/i18n/first-run.js +2 -60
  127. package/packages/client/dist/index.d.ts +2 -0
  128. package/packages/client/dist/index.js +1 -47
  129. package/packages/client/dist/invocation/invoke-input.js +1 -24
  130. package/packages/client/dist/license/host-auth-recovery.d.ts +158 -0
  131. package/packages/client/dist/license/host-auth-recovery.js +1 -0
  132. package/packages/client/dist/license/index.js +1 -5
  133. package/packages/client/dist/license/license-client.d.ts +10 -1
  134. package/packages/client/dist/license/license-client.js +1 -304
  135. package/packages/client/dist/machine/fingerprint.js +2 -163
  136. package/packages/client/dist/machine/index.js +1 -5
  137. package/packages/client/dist/memory/project-memory-path-scope.js +1 -62
  138. package/packages/client/dist/memory/project-memory-writer.js +40 -1007
  139. package/packages/client/dist/memory/shared-project-memory-types.js +2 -253
  140. package/packages/client/dist/migrations/epic-lifecycle-backfill.js +3 -1060
  141. package/packages/client/dist/migrations/project-identity-capability.js +1 -50
  142. package/packages/client/dist/migrations/project-identity.js +7 -493
  143. package/packages/client/dist/migrations/state-json.js +4 -393
  144. package/packages/client/dist/policy/architecture-policy.js +2 -184
  145. package/packages/client/dist/policy/index.js +1 -7
  146. package/packages/client/dist/policy/shared-policy-types.js +0 -38
  147. package/packages/client/dist/policy/subagent-approval-policy.js +1 -55
  148. package/packages/client/dist/private-execution/clean-baseline.js +2 -279
  149. package/packages/client/dist/private-execution/codex-executor.js +4 -574
  150. package/packages/client/dist/private-execution/codex-host-runtime.js +1 -137
  151. package/packages/client/dist/private-execution/codex-lifecycle-guard.js +1 -97
  152. package/packages/client/dist/private-execution/codex-scoped-permissions.js +1 -128
  153. package/packages/client/dist/private-execution/delivery.js +13 -410
  154. package/packages/client/dist/private-execution/diagnosis-root-review.js +2 -294
  155. package/packages/client/dist/private-execution/frozen-handoff.js +6 -19
  156. package/packages/client/dist/private-execution/implementation-validation.js +2 -106
  157. package/packages/client/dist/private-execution/isolated-admission.js +23 -1432
  158. package/packages/client/dist/private-execution/isolated-session.js +5 -725
  159. package/packages/client/dist/private-execution/isolated-workspace.js +1 -122
  160. package/packages/client/dist/private-execution/pact-consumer-runtime.js +3 -409
  161. package/packages/client/dist/private-execution/pact-test-source.js +1 -258
  162. package/packages/client/dist/private-execution/pact-toolchain.js +3 -290
  163. package/packages/client/dist/private-execution/pact-write-set.js +1 -172
  164. package/packages/client/dist/private-execution/planning-diagnosis-checkpoint.js +5 -406
  165. package/packages/client/dist/private-execution/planning-generation-checkpoint.js +5 -462
  166. package/packages/client/dist/private-execution/planning-memory-checkpoint.js +3 -139
  167. package/packages/client/dist/private-execution/planning-research-checkpoint.js +4 -325
  168. package/packages/client/dist/private-execution/private-report-store.js +1 -42
  169. package/packages/client/dist/private-execution/property-test-candidate.js +1 -103
  170. package/packages/client/dist/private-execution/property-test-choices.js +3 -147
  171. package/packages/client/dist/private-execution/property-test-plan.js +7 -271
  172. package/packages/client/dist/private-execution/property-test-review.js +4 -258
  173. package/packages/client/dist/private-execution/property-test-runtime.js +9 -372
  174. package/packages/client/dist/private-execution/property-test-source.js +1 -139
  175. package/packages/client/dist/private-execution/root-checkpoint.js +1 -80
  176. package/packages/client/dist/private-execution/root-memory-git-evidence.js +3 -135
  177. package/packages/client/dist/private-execution/root-memory-recovery.js +1 -93
  178. package/packages/client/dist/private-execution/root-memory-request.js +1 -29
  179. package/packages/client/dist/private-execution/root-memory-scope.js +1 -178
  180. package/packages/client/dist/private-execution/root-memory-writer.js +1 -102
  181. package/packages/client/dist/private-execution/session-lease.js +1 -63
  182. package/packages/client/dist/private-execution/shared-private-admission-types.js +1 -50
  183. package/packages/client/dist/private-execution/shared-project-validation.js +1 -66
  184. package/packages/client/dist/private-execution/shared-root-memory-types.js +1 -61
  185. package/packages/client/dist/private-execution/ux-source-validation.js +1 -372
  186. package/packages/client/dist/private-execution/ux-write-set.js +2 -191
  187. package/packages/client/dist/registry/registry-reconciliation.js +1 -234
  188. package/packages/client/dist/resilience/circuit-breaker.js +1 -170
  189. package/packages/client/dist/resilience/degradation-manager.js +1 -164
  190. package/packages/client/dist/resilience/freshness-indicator.js +1 -100
  191. package/packages/client/dist/resilience/index.js +1 -8
  192. package/packages/client/dist/resilience/recovery-detector.js +1 -74
  193. package/packages/client/dist/resolvers/asset-resolver.js +0 -13
  194. package/packages/client/dist/resolvers/local-resolver.js +8 -255
  195. package/packages/client/dist/resolvers/remote-resolver.js +1 -233
  196. package/packages/client/dist/runner/binary.js +1 -404
  197. package/packages/client/dist/runner/cli.js +17 -1571
  198. package/packages/client/dist/runner/epic-background-dispatcher.d.ts +61 -0
  199. package/packages/client/dist/runner/epic-background-dispatcher.js +1 -0
  200. package/packages/client/dist/runner/headless/adapters.js +1 -49
  201. package/packages/client/dist/runner/headless/background-root.d.ts +65 -0
  202. package/packages/client/dist/runner/headless/background-root.js +1 -0
  203. package/packages/client/dist/runner/headless/claude-code-adapter.js +1 -79
  204. package/packages/client/dist/runner/headless/continuous-story-workspace.js +11 -1628
  205. package/packages/client/dist/runner/headless/epic-git-reconciliation-inventory.js +1 -69
  206. package/packages/client/dist/runner/headless/epic-merge-candidate-qa.d.ts +1 -1
  207. package/packages/client/dist/runner/headless/epic-merge-candidate-qa.js +2 -110
  208. package/packages/client/dist/runner/headless/epic-terminal-git-cli-adapter.d.ts +2 -0
  209. package/packages/client/dist/runner/headless/epic-terminal-git-cli-adapter.js +10 -1236
  210. package/packages/client/dist/runner/headless/epic-terminal-git-runner.d.ts +27 -2
  211. package/packages/client/dist/runner/headless/epic-terminal-git-runner.js +2 -318
  212. package/packages/client/dist/runner/headless/fake-adapter.js +1 -62
  213. package/packages/client/dist/runner/headless/managed-environment-cleanup.d.ts +95 -0
  214. package/packages/client/dist/runner/headless/managed-environment-cleanup.js +1 -0
  215. package/packages/client/dist/runner/headless/managed-profile.js +1 -132
  216. package/packages/client/dist/runner/headless/opencode-adapter.js +2 -145
  217. package/packages/client/dist/runner/headless/prepared-story-bundle.js +1 -43
  218. package/packages/client/dist/runner/headless/resolver.js +1 -231
  219. package/packages/client/dist/runner/headless/run-yolo.js +1 -885
  220. package/packages/client/dist/runner/headless/run-yoloop.d.ts +1 -0
  221. package/packages/client/dist/runner/headless/run-yoloop.js +2 -2574
  222. package/packages/client/dist/runner/headless/types.js +2 -574
  223. package/packages/client/dist/runner/local-console.js +13 -396
  224. package/packages/client/dist/runner/portal-sync-worker.js +1 -1487
  225. package/packages/client/dist/runner/project-identity.js +1 -106
  226. package/packages/client/dist/runner/scheduler.js +7 -995
  227. package/packages/client/dist/runner-cli.js +2 -18
  228. package/packages/client/dist/state/invocation-shape.js +1 -13
  229. package/packages/client/dist/state/project-state-snapshot.d.ts +7 -0
  230. package/packages/client/dist/state/project-state-snapshot.js +1 -1731
  231. package/packages/client/dist/state/state-json-repair.js +3 -785
  232. package/packages/client/dist/state/structural-dag-projection.js +1 -118
  233. package/packages/client/dist/telemetry/index.js +1 -5
  234. package/packages/client/dist/telemetry/offline-queue.js +1 -131
  235. package/packages/client/dist/tier/index.js +1 -5
  236. package/packages/client/dist/tier/tier-aware-client.js +1 -405
  237. package/packages/client/dist/types/index.js +1 -38
  238. package/packages/client/dist/version.js +1 -27
  239. package/packages/client/dist/yolo/change-evidence-collector.d.ts +21 -1
  240. package/packages/client/dist/yolo/change-evidence-collector.js +1 -126
  241. package/packages/client/dist/yolo/invoke-envelope-capability.js +1 -62
  242. package/packages/client/dist/yolo/spec-envelope-collector.js +5 -184
  243. package/packages/client/dist/yoloop/discovery-hook.js +2 -300
  244. package/packages/client/dist/yoloop/index.js +1 -8
  245. package/packages/client/dist/yoloop/invoke-hooks.js +12 -669
  246. package/packages/client/dist/yoloop/shared-discover-epics.js +1 -1007
  247. package/packages/client/dist/yoloop/shared-yoloop-types.js +1 -33
  248. package/packages/client/dist/yoloop/target-normalization.js +1 -180
  249. package/packages/client/dist/yoloop/yoloop-client-state-store.js +1 -1920
  250. package/packages/shared/dist/invocation/explicit-intent-recovery.d.ts +83 -0
  251. package/packages/shared/dist/invocation/explicit-intent-recovery.js +187 -0
  252. package/packages/shared/dist/invocation/index.d.ts +7 -0
  253. package/packages/shared/dist/invocation/index.js +22 -0
  254. package/packages/shared/dist/yolo/change-evidence.js +1 -1
  255. package/packages/shared/dist/yoloop/epic-terminal-git.js +144 -2
  256. package/targets-stubs/antigravity/gemini.md +1 -1
  257. package/targets-stubs/antigravity/skill/SKILL.md +1 -1
  258. package/targets-stubs/claude-code/neocortex-root.agent.yaml +1 -1
  259. package/targets-stubs/claude-code/neocortex-root.md +2 -2
  260. package/targets-stubs/claude-code/neocortex.agent.yaml +1 -1
  261. package/targets-stubs/claude-code/neocortex.md +2 -2
  262. package/targets-stubs/codex/AGENTS.md +24 -21
  263. package/targets-stubs/codex/README.md +6 -1
  264. package/targets-stubs/codex/neocortex.toml +7 -2
  265. package/targets-stubs/cursor/agent.md +2 -2
  266. package/targets-stubs/gemini-cli/agent.md +2 -2
  267. package/targets-stubs/opencode/neocortex-root.md +16 -1
  268. package/targets-stubs/opencode/neocortex.md +6 -0
  269. package/targets-stubs/vscode/neocortex.agent.md +2 -2
@@ -1,1234 +1,4 @@
1
- /** Root-owned two-phase commit/checkpoint boundary for direct `*yolo` runs. */
2
- import { execFile } from 'node:child_process';
3
- import { createHash, randomBytes, timingSafeEqual } from 'node:crypto';
4
- import { readFile } from 'node:fs/promises';
5
- import { join, resolve } from 'node:path';
6
- import { promisify } from 'node:util';
7
- import { readRootMemoryEffectForCheckpoint } from '../private-execution/root-memory-scope.js';
8
- import { rootMemoryEffectDigest, verifyRootMemoryPhysicalWriteSet } from '../private-execution/root-memory-git-evidence.js';
9
- import { acquireGitLifecyclePhysicalIntegrationLock, applyGitLifecycleRuntimeOverlay, computeImmutableStoryQaEvidenceDigest, computeGitLifecyclePhysicalIntegrationLockOwnerDigest, computeP205StoryQaEvidenceDigest, computeLegacyDirectYoloRecoveryReceiptRef, ensureRuntimeEpic, ensureRuntimeStory, ensureGitLifecyclePrivateChildDirectory, executeP205TransientAuthority, projectP205TransientExecutionEvidence, publicStoryReviewArtifactFromGit, LEGACY_DIRECT_YOLO_RECOVERY_COMMITS, mutateGitLifecycleRuntime, P205_COMPLETE_COMMAND, readGitLifecycleRuntime, gitLifecycleRuntimeDirectory, assertGitLifecyclePrivateFile, releaseGitLifecyclePhysicalIntegrationLock, releaseGitLifecyclePhysicalIntegrationLockForCheckpoint, verifyGitLifecyclePhysicalIntegrationLock, verifyAcceptedGitLifecycleStoryCheckpoint, } from './git-lifecycle-store.js';
10
- import { anchoredAtomicWriteFile } from './secure-filesystem-boundary.js';
11
- import { GIT_LIFECYCLE_COMMIT_PUSH_STEP_ID, GIT_LIFECYCLE_REVIEW_PR_STEP_ID, GIT_LIFECYCLE_SETUP_BRANCH_STEP_ID, } from './git-lifecycle-step-ids.js';
12
- const execFileAsync = promisify(execFile);
13
- /**
14
- * Productive P207 setup boundary. It owns only the current checkout and the
15
- * epic branch; it never creates, removes or repositions a worktree. Existing
16
- * staged and unstaged bytes must remain identical across the branch switch.
17
- */
18
- export async function prepareDirectYoloSetup(options) {
19
- try {
20
- const authority = await resolveLogicalAuthority(options.projectRoot, options.storyId);
21
- const nowIso = normalizedTime(options.nowIso);
22
- const nowMs = Date.parse(nowIso);
23
- const currentBranch = await git(authority.projectRoot, ['symbolic-ref', '--quiet', '--short', 'HEAD']);
24
- const currentHead = await git(authority.projectRoot, ['rev-parse', 'HEAD']);
25
- const runtimeBefore = await readGitLifecycleRuntime(authority.projectRoot, { required: true });
26
- const runtimeEpic = asRecord(runtimeBefore.epics[authority.epicId]);
27
- const currentLease = asRecord(runtimeEpic?.branch_lease);
28
- const liveLease = currentLease && Number.isFinite(Date.parse(String(currentLease.expiresAt)))
29
- && Date.parse(String(currentLease.expiresAt)) > nowMs;
30
- if (liveLease && currentLease?.ownerStoryId !== options.storyId) {
31
- return failure('direct-yolo-writer-active');
32
- }
33
- let originBinding = validOriginBinding(runtimeEpic?.origin_binding, authority.executionBranch, authority.originBranch);
34
- if (currentBranch === authority.executionBranch) {
35
- if (!originBinding)
36
- return failure('direct-yolo-origin-binding-invalid');
37
- }
38
- else {
39
- if (currentBranch !== authority.originBranch)
40
- return failure('direct-yolo-current-branch-mismatch');
41
- if (originBinding && originBinding.originHeadSha !== currentHead) {
42
- return failure('direct-yolo-origin-binding-conflict');
43
- }
44
- originBinding ??= {
45
- schemaVersion: 1,
46
- originBranch: authority.originBranch,
47
- originHeadSha: currentHead,
48
- executionBranch: authority.executionBranch,
49
- capturedFromCurrentCheckout: true,
50
- capturedAt: nowIso,
51
- };
52
- const occupiedElsewhere = (await worktreeBranches(authority.projectRoot)).some((item) => item.branch === authority.executionBranch && resolve(item.path) !== authority.projectRoot);
53
- if (occupiedElsewhere)
54
- return failure('direct-yolo-execution-branch-checked-out-elsewhere');
55
- const checkoutBefore = await checkoutPayload(authority.projectRoot);
56
- const executionRef = `refs/heads/${authority.executionBranch}`;
57
- if (await gitExitOk(authority.projectRoot, ['show-ref', '--verify', '--quiet', executionRef])) {
58
- const executionHead = await git(authority.projectRoot, ['rev-parse', executionRef]);
59
- if (executionHead !== currentHead) {
60
- if (!(await gitExitOk(authority.projectRoot, ['merge-base', '--is-ancestor', executionHead, currentHead]))) {
61
- return failure('direct-yolo-execution-branch-diverged');
62
- }
63
- await git(authority.projectRoot, ['update-ref', executionRef, currentHead, executionHead]);
64
- }
65
- await git(authority.projectRoot, ['switch', authority.executionBranch]);
66
- }
67
- else {
68
- await git(authority.projectRoot, ['switch', '-c', authority.executionBranch]);
69
- }
70
- if (await checkoutPayload(authority.projectRoot) !== checkoutBefore) {
71
- return failure('direct-yolo-checkout-payload-raced');
72
- }
73
- }
74
- const branchAfter = await git(authority.projectRoot, ['symbolic-ref', '--quiet', '--short', 'HEAD']);
75
- const headAfter = await git(authority.projectRoot, ['rev-parse', 'HEAD']);
76
- if (branchAfter !== authority.executionBranch)
77
- return failure('direct-yolo-current-branch-mismatch');
78
- if (liveLease && currentLease?.ownerStoryId === options.storyId) {
79
- if (currentLease.setupHeadSha !== headAfter)
80
- return failure('direct-yolo-setup-lease-head-unbound');
81
- return { ok: true, value: setupReceipt(authority, originBinding, String(currentLease.expiresAt), true) };
82
- }
83
- const acquiredAt = nowIso;
84
- const expiresAt = new Date(nowMs + 60 * 60 * 1000).toISOString();
85
- const sessionId = `setup-${randomBytes(32).toString('hex')}`;
86
- const runtimeAtCas = await readGitLifecycleRuntime(authority.projectRoot, { required: true });
87
- await mutateGitLifecycleRuntime(authority.projectRoot, (document) => {
88
- const epic = ensureRuntimeEpic(document, authority.epicId);
89
- const lease = asRecord(epic.branch_lease);
90
- if (lease && Date.parse(String(lease.expiresAt)) > nowMs && lease.ownerStoryId !== options.storyId) {
91
- throw new Error('direct-yolo-writer-active');
92
- }
93
- const persistedBinding = validOriginBinding(epic.origin_binding, authority.executionBranch, authority.originBranch);
94
- if (epic.origin_binding && !persistedBinding)
95
- throw new Error('direct-yolo-origin-binding-conflict');
96
- if (persistedBinding && JSON.stringify(persistedBinding) !== JSON.stringify(originBinding)) {
97
- throw new Error('direct-yolo-origin-binding-conflict');
98
- }
99
- epic.branch_mode = 'epic';
100
- epic.execution_branch = authority.executionBranch;
101
- epic.origin_branch = authority.originBranch;
102
- epic.graph_revision = authority.graphRevision;
103
- epic.origin_binding = originBinding;
104
- epic.branch_lease = {
105
- schemaVersion: 1, ownerStoryId: options.storyId, sessionId, idempotencyKey: sessionId,
106
- acquiredAt, expiresAt, setupHeadSha: headAfter,
107
- };
108
- }, {
109
- expectedRevision: runtimeAtCas.revision,
110
- preCommit: async () => {
111
- if (await git(authority.projectRoot, ['symbolic-ref', '--quiet', '--short', 'HEAD']) !== authority.executionBranch
112
- || await git(authority.projectRoot, ['rev-parse', 'HEAD']) !== headAfter) {
113
- throw new Error('direct-yolo-setup-physical-state-raced');
114
- }
115
- },
116
- });
117
- return { ok: true, value: setupReceipt(authority, originBinding, expiresAt, false) };
118
- }
119
- catch (error) {
120
- return failure(publicReason(error, 'direct-yolo-setup-failed'));
121
- }
122
- }
123
- /** Accept C01 only from the root after its own physical setup and live lease. */
124
- export async function acceptDirectYoloSetupCheckpoint(options) {
125
- let physicalLock;
126
- try {
127
- const authority = await resolveAuthority(options.projectRoot, options.storyId);
128
- const nowIso = normalizedTime(options.nowIso);
129
- const statePath = join(authority.projectRoot, '.neocortex/state.json');
130
- const trackedBefore = await readFile(statePath, 'utf8');
131
- const payloadBefore = await checkoutPayload(authority.projectRoot);
132
- const headSha = await git(authority.projectRoot, ['rev-parse', 'HEAD']);
133
- const executionRef = `refs/heads/${authority.executionBranch}`;
134
- const runtime = await readGitLifecycleRuntime(authority.projectRoot, { required: true });
135
- const epic = asRecord(runtime.epics[authority.epicId]);
136
- const binding = validOriginBinding(epic?.origin_binding, authority.executionBranch, authority.originBranch);
137
- const lease = asRecord(epic?.branch_lease);
138
- if (!binding)
139
- return failure('direct-yolo-origin-binding-invalid');
140
- if (!lease || !isLiveSameStorySetupLease(lease, options.storyId, nowIso)
141
- || lease?.sessionId !== lease?.idempotencyKey)
142
- return failure('direct-yolo-setup-lease-invalid');
143
- const acquired = await acquireGitLifecyclePhysicalIntegrationLock(authority.projectRoot, {
144
- kind: 'direct', storyId: options.storyId,
145
- sessionId: String(lease.sessionId), idempotencyKey: String(lease.idempotencyKey),
146
- expiresAt: new Date(Date.now() + 5 * 60 * 1000).toISOString(), persistent: false,
147
- });
148
- if (!acquired.ok)
149
- return failure(acquired.reasonCode);
150
- physicalLock = acquired;
151
- if (headSha !== lease.setupHeadSha
152
- || !(await gitExitOk(authority.projectRoot, ['merge-base', '--is-ancestor', binding.originHeadSha, headSha]))
153
- || await git(authority.projectRoot, ['rev-parse', executionRef]) !== headSha) {
154
- return failure('direct-yolo-setup-head-mismatch');
155
- }
156
- const occupiedElsewhere = (await worktreeBranches(authority.projectRoot)).some((item) => item.branch === authority.executionBranch && resolve(item.path) !== authority.projectRoot);
157
- if (occupiedElsewhere)
158
- return failure('direct-yolo-execution-branch-checked-out-elsewhere');
159
- const tracked = asRecord(JSON.parse(trackedBefore));
160
- const trackedStory = asRecord(asRecord(tracked?.stories)?.[options.storyId]);
161
- const runtimeStory = asRecord(runtime.stories[options.storyId]);
162
- if (!trackedStory || !['backlog', 'ready_to_start'].includes(String(trackedStory.status))
163
- || (trackedStory.steps_completed !== undefined
164
- && (!Array.isArray(trackedStory.steps_completed) || trackedStory.steps_completed.length > 0))
165
- || trackedStory.branch_name || trackedStory.checkpoint) {
166
- return failure('direct-yolo-setup-story-progress-conflict');
167
- }
168
- const accepted = runtimeStory?.status === 'branch-ready'
169
- && runtimeStory.last_step === GIT_LIFECYCLE_SETUP_BRANCH_STEP_ID
170
- && Array.isArray(runtimeStory.steps_completed)
171
- && runtimeStory.steps_completed.length === 1
172
- && runtimeStory.steps_completed[0] === GIT_LIFECYCLE_SETUP_BRANCH_STEP_ID
173
- && Object.keys(runtimeStory).sort().join(',') === 'last_step,status,steps_completed';
174
- if (runtimeStory && Object.keys(runtimeStory).length > 0 && !accepted) {
175
- return failure('direct-yolo-setup-story-progress-conflict');
176
- }
177
- const physicalGuard = async () => {
178
- if (Date.parse(String(lease.expiresAt)) <= (options.nowIso ? Date.parse(nowIso) : Date.now())
179
- || !physicalLock || !(await verifyGitLifecyclePhysicalIntegrationLock(physicalLock.path, physicalLock.owner))
180
- || await readFile(statePath, 'utf8') !== trackedBefore
181
- || await checkoutPayload(authority.projectRoot) !== payloadBefore
182
- || await git(authority.projectRoot, ['symbolic-ref', '--quiet', '--short', 'HEAD']) !== authority.executionBranch
183
- || await git(authority.projectRoot, ['rev-parse', 'HEAD']) !== headSha
184
- || !(await gitExitOk(authority.projectRoot, ['merge-base', '--is-ancestor', binding.originHeadSha, headSha]))
185
- || await git(authority.projectRoot, ['rev-parse', executionRef]) !== headSha) {
186
- throw new Error('direct-yolo-setup-physical-state-raced');
187
- }
188
- };
189
- if (accepted) {
190
- await physicalGuard();
191
- }
192
- else {
193
- await mutateGitLifecycleRuntime(authority.projectRoot, (document) => {
194
- const currentEpic = asRecord(document.epics[authority.epicId]);
195
- const currentStory = ensureRuntimeStory(document, options.storyId);
196
- if (JSON.stringify(currentEpic?.branch_lease) !== JSON.stringify(lease)
197
- || JSON.stringify(currentEpic?.origin_binding) !== JSON.stringify(binding)
198
- || currentEpic?.graph_revision !== authority.graphRevision
199
- || Object.keys(currentStory).length > 0) {
200
- throw new Error('direct-yolo-setup-runtime-raced');
201
- }
202
- currentStory.status = 'branch-ready';
203
- currentStory.last_step = GIT_LIFECYCLE_SETUP_BRANCH_STEP_ID;
204
- currentStory.steps_completed = [GIT_LIFECYCLE_SETUP_BRANCH_STEP_ID];
205
- }, { expectedRevision: runtime.revision, preCommit: physicalGuard });
206
- }
207
- return { ok: true, value: Object.freeze({
208
- ...setupReceipt(authority, binding, String(lease.expiresAt), accepted),
209
- checkpointAccepted: true, graphRevision: authority.graphRevision, headSha,
210
- }) };
211
- }
212
- catch (error) {
213
- return failure(publicReason(error, 'direct-yolo-setup-acceptance-failed'));
214
- }
215
- finally {
216
- if (physicalLock) {
217
- await releaseGitLifecyclePhysicalIntegrationLock(physicalLock.path, physicalLock.owner).catch(() => undefined);
218
- }
219
- }
220
- }
221
- /**
222
- * Productive setup-owner handoff emitter used immediately before C08. The
223
- * one-time token never crosses stdout; only a public receipt is returned.
224
- */
225
- export async function issueDirectYoloSetupLeaseHandoff(options) {
226
- try {
227
- const authority = await resolveAuthority(options.projectRoot, options.storyId);
228
- const changedPaths = splitNull(await gitRaw(authority.projectRoot, ['diff', '--cached', '--name-only', '-z']));
229
- const writeSet = validateDirectWriteSet(changedPaths, authority.authorizedWriteSet, options.storyId, authority.rootMemoryEffect);
230
- if (!writeSet.ok)
231
- return failure(writeSet.reasonCode);
232
- if (authority.rootMemoryEffect)
233
- verifyRootMemoryPhysicalWriteSet(authority.projectRoot, authority.rootMemoryEffect, { storyId: options.storyId, executionBranch: authority.executionBranch,
234
- baseSha: await git(authority.projectRoot, ['rev-parse', 'HEAD']), headRef: ':', childPaths: authority.authorizedWriteSet });
235
- const stagedReview = publicStoryReviewArtifactFromGit(authority.projectRoot, options.storyId, ':');
236
- if (stagedReview?.verdict !== 'approved')
237
- return failure('direct-yolo-review-artifact-not-approved');
238
- const unstagedPaths = splitNull(await gitRaw(authority.projectRoot, ['diff', '--name-only', '-z']));
239
- if (unstagedPaths.some((path) => changedPaths.includes(path)))
240
- return failure('direct-yolo-user-overlap');
241
- const payload = await gitRaw(authority.projectRoot, ['diff', '--cached', '--binary', '--full-index']);
242
- if (!payload)
243
- return failure('direct-yolo-staged-diff-empty');
244
- const bundleDigest = digest(payload);
245
- const claimRevision = digest([...authority.authorizedWriteSet].sort().join('\0'));
246
- const nowIso = normalizedTime(options.nowIso);
247
- const runtime = await readGitLifecycleRuntime(authority.projectRoot, { required: true });
248
- const setupLease = asRecord(runtime.epics[authority.epicId]?.branch_lease);
249
- if (!isLiveSameStorySetupLease(setupLease, options.storyId, nowIso)) {
250
- return failure('direct-yolo-setup-handoff-owner-invalid');
251
- }
252
- const sessionId = String(setupLease.sessionId);
253
- const leaseExpiresAt = String(setupLease.expiresAt);
254
- const candidateExpiresAt = new Date(Math.min(Date.parse(leaseExpiresAt), Date.parse(nowIso) + 5 * 60 * 1000)).toISOString();
255
- const prior = await readSetupHandoffTokenRecord(authority.projectRoot, options.storyId);
256
- const reused = Boolean(prior?.state === 'issued'
257
- && prior.fromSessionId === sessionId && prior.bundleDigest === bundleDigest
258
- && prior.claimRevision === claimRevision
259
- && Date.parse(prior.expiresAt) > Date.parse(nowIso));
260
- const expiresAt = reused ? prior.expiresAt : candidateExpiresAt;
261
- const token = reused ? prior.token : randomBytes(32).toString('base64url');
262
- const tokenDigest = digest(token);
263
- const grant = Object.freeze({
264
- schemaVersion: 1,
265
- kind: 'direct-yolo-setup-handoff',
266
- storyId: options.storyId,
267
- fromSessionId: sessionId,
268
- tokenDigest,
269
- bundleDigest,
270
- claimRevision,
271
- expiresAt,
272
- });
273
- const grantDigest = digest(JSON.stringify(grant));
274
- const physicalLock = await acquireGitLifecyclePhysicalIntegrationLock(authority.projectRoot, {
275
- kind: 'direct', storyId: options.storyId, sessionId, idempotencyKey: sessionId,
276
- expiresAt, persistent: false, createdAt: nowIso,
277
- });
278
- if (!physicalLock.ok)
279
- return failure(physicalLock.reasonCode === 'git-lifecycle-integration-backpressure'
280
- ? 'direct-yolo-integration-backpressure' : 'direct-yolo-integration-lock-failed');
281
- try {
282
- await writeSetupHandoffTokenRecord(authority.projectRoot, {
283
- schemaVersion: 1, kind: 'direct-yolo-setup-handoff-token', state: 'issued',
284
- storyId: options.storyId, fromSessionId: sessionId, token, tokenDigest,
285
- bundleDigest, claimRevision, grantDigest, expiresAt,
286
- });
287
- await mutateGitLifecycleRuntime(authority.projectRoot, (document) => {
288
- const current = asRecord(ensureRuntimeEpic(document, authority.epicId).branch_lease);
289
- if (!isLiveSameStorySetupLease(current, options.storyId, nowIso)
290
- || current?.sessionId !== sessionId || current.idempotencyKey !== sessionId) {
291
- throw new Error('direct-yolo-setup-handoff-owner-raced');
292
- }
293
- current.handoffGrant = grant;
294
- }, {
295
- expectedRevision: runtime.revision,
296
- preCommit: async () => {
297
- if (!(await verifyGitLifecyclePhysicalIntegrationLock(physicalLock.path, physicalLock.owner))) {
298
- throw new Error('direct-yolo-integration-lock-raced');
299
- }
300
- const persisted = await readSetupHandoffTokenRecord(authority.projectRoot, options.storyId);
301
- if (!persisted || persisted.state !== 'issued' || persisted.grantDigest !== grantDigest
302
- || persisted.tokenDigest !== tokenDigest)
303
- throw new Error('direct-yolo-setup-handoff-token-raced');
304
- },
305
- });
306
- }
307
- finally {
308
- await releaseGitLifecyclePhysicalIntegrationLock(physicalLock.path, physicalLock.owner).catch(() => undefined);
309
- }
310
- return { ok: true, value: Object.freeze({
311
- schemaVersion: 1, kind: 'direct-yolo-setup-handoff-receipt', storyId: options.storyId,
312
- sessionId, bundleDigest, claimRevision, grantDigest, expiresAt, reused, publicSafe: true,
313
- }) };
314
- }
315
- catch (error) {
316
- return failure(error instanceof Error && /^direct-yolo-/.test(error.message)
317
- ? error.message : 'direct-yolo-setup-handoff-failed');
318
- }
319
- }
320
- export async function beginDirectYoloStoryCommit(options) {
321
- try {
322
- const authority = await resolveAuthority(options.projectRoot, options.storyId);
323
- const baseSha = await git(authority.projectRoot, ['rev-parse', 'HEAD']);
324
- const changedPaths = splitNull(await gitRaw(authority.projectRoot, ['diff', '--cached', '--name-only', '-z']));
325
- const writeSet = validateDirectWriteSet(changedPaths, authority.authorizedWriteSet, options.storyId, authority.rootMemoryEffect);
326
- if (!writeSet.ok)
327
- return failure(writeSet.reasonCode);
328
- if (authority.rootMemoryEffect)
329
- verifyRootMemoryPhysicalWriteSet(authority.projectRoot, authority.rootMemoryEffect, { storyId: options.storyId, executionBranch: authority.executionBranch, baseSha, headRef: ':', childPaths: authority.authorizedWriteSet });
330
- const stagedReview = publicStoryReviewArtifactFromGit(authority.projectRoot, options.storyId, ':');
331
- if (stagedReview?.verdict !== 'approved')
332
- return failure('direct-yolo-review-artifact-not-approved');
333
- const unstagedPaths = splitNull(await gitRaw(authority.projectRoot, ['diff', '--name-only', '-z']));
334
- if (unstagedPaths.some((path) => changedPaths.includes(path)))
335
- return failure('direct-yolo-user-overlap');
336
- const payload = await gitRaw(authority.projectRoot, ['diff', '--cached', '--binary', '--full-index']);
337
- if (!payload)
338
- return failure('direct-yolo-staged-diff-empty');
339
- const bundleDigest = digest(payload);
340
- const claimRevision = digest([...authority.authorizedWriteSet].sort().join('\0'));
341
- const idempotencyKey = digest([
342
- 'direct-yolo-commit-v1', options.storyId, authority.epicId, authority.executionBranch,
343
- baseSha, bundleDigest, authority.graphRevision, claimRevision,
344
- ].join('\0'));
345
- const nowIso = normalizedTime(options.nowIso);
346
- let issuedHandoff = options.setupLeaseHandoff
347
- ?? await resolveIssuedSetupLeaseHandoff(authority.projectRoot, options.storyId, bundleDigest, claimRevision, nowIso);
348
- const runtimeBeforeLock = await readGitLifecycleRuntime(authority.projectRoot, { required: true });
349
- const priorDirectLease = asRecord(runtimeBeforeLock.epics[authority.epicId]?.branch_lease);
350
- const reusableDirectLease = priorDirectLease?.ownerStoryId === options.storyId
351
- && priorDirectLease.sessionId === idempotencyKey && priorDirectLease.idempotencyKey === idempotencyKey
352
- && typeof priorDirectLease.expiresAt === 'string'
353
- && Date.parse(priorDirectLease.expiresAt) > Date.parse(nowIso)
354
- ? priorDirectLease : undefined;
355
- if (reusableDirectLease && options.setupLeaseHandoff) {
356
- return failure('direct-yolo-setup-handoff-replayed');
357
- }
358
- const priorConsumption = asRecord(reusableDirectLease?.handoffConsumption);
359
- if (priorConsumption) {
360
- const privateRecord = await readSetupHandoffTokenRecord(authority.projectRoot, options.storyId);
361
- if (!privateRecord || !['consuming', 'consumed'].includes(privateRecord.state)
362
- || privateRecord.fromSessionId !== priorConsumption.fromSessionId
363
- || privateRecord.tokenDigest !== priorConsumption.tokenDigest
364
- || privateRecord.bundleDigest !== bundleDigest || privateRecord.claimRevision !== claimRevision
365
- || priorConsumption.bundleDigest !== bundleDigest || priorConsumption.claimRevision !== claimRevision
366
- || privateRecord.consumedBySessionId !== idempotencyKey
367
- || priorConsumption.consumedBySessionId !== idempotencyKey) {
368
- return failure('direct-yolo-setup-handoff-consume-invalid');
369
- }
370
- // The runtime CAS already consumed this exact grant. Recovery may finish
371
- // its private receipt after expiry; it never promotes a second writer.
372
- if (privateRecord.state === 'consuming') {
373
- if (!privateRecord.token || digest(privateRecord.token) !== privateRecord.tokenDigest) {
374
- return failure('direct-yolo-setup-handoff-consume-invalid');
375
- }
376
- issuedHandoff = { sessionId: privateRecord.fromSessionId, token: privateRecord.token };
377
- }
378
- }
379
- const expiresAt = reusableDirectLease
380
- ? String(reusableDirectLease.expiresAt)
381
- : new Date(Date.parse(nowIso) + 30 * 60 * 1000).toISOString();
382
- const physicalLock = await acquireGitLifecyclePhysicalIntegrationLock(authority.projectRoot, {
383
- kind: 'direct', storyId: options.storyId, sessionId: idempotencyKey, idempotencyKey,
384
- expiresAt, persistent: true,
385
- createdAt: reusableDirectLease && typeof reusableDirectLease.acquiredAt === 'string'
386
- ? reusableDirectLease.acquiredAt : nowIso,
387
- });
388
- if (!physicalLock.ok)
389
- return failure(physicalLock.reasonCode === 'git-lifecycle-integration-backpressure'
390
- ? 'direct-yolo-integration-backpressure' : 'direct-yolo-integration-lock-failed');
391
- try {
392
- if (issuedHandoff && options.setupLeaseHandoff === undefined) {
393
- await markSetupHandoffTokenConsuming(authority.projectRoot, options.storyId, issuedHandoff.sessionId, issuedHandoff.token, idempotencyKey, nowIso);
394
- await options.setupHandoffFault?.('after-consuming-persisted');
395
- }
396
- await mutateGitLifecycleRuntime(authority.projectRoot, (document) => {
397
- const epic = ensureRuntimeEpic(document, authority.epicId);
398
- const current = asRecord(epic.branch_lease);
399
- const live = current && Date.parse(String(current.expiresAt)) > Date.parse(nowIso);
400
- const alreadyPromoted = current?.ownerStoryId === options.storyId
401
- && current.sessionId === idempotencyKey && current.idempotencyKey === idempotencyKey;
402
- const sameStorySetup = current?.ownerStoryId === options.storyId
403
- && current.sessionId === current.idempotencyKey
404
- && typeof current.idempotencyKey === 'string'
405
- && /^setup-[a-f0-9]{64}$/.test(current.idempotencyKey);
406
- const setupHandoffAuthorized = live && sameStorySetup && validSetupLeaseHandoff({
407
- current,
408
- handoff: issuedHandoff,
409
- storyId: options.storyId,
410
- bundleDigest,
411
- claimRevision,
412
- nowIso,
413
- });
414
- if (issuedHandoff && live && !alreadyPromoted && !setupHandoffAuthorized) {
415
- throw new Error('direct-yolo-setup-handoff-invalid');
416
- }
417
- if (live && !alreadyPromoted && !setupHandoffAuthorized) {
418
- throw new Error('direct-yolo-writer-active');
419
- }
420
- const setupGrant = setupHandoffAuthorized ? asRecord(current?.handoffGrant) : undefined;
421
- const handoffConsumption = setupGrant ? {
422
- schemaVersion: 1,
423
- kind: 'direct-yolo-setup-handoff-consumption',
424
- state: 'consumed',
425
- storyId: options.storyId,
426
- fromSessionId: String(current?.sessionId),
427
- tokenDigest: String(setupGrant.tokenDigest),
428
- bundleDigest,
429
- claimRevision,
430
- consumedBySessionId: idempotencyKey,
431
- consumedAt: nowIso,
432
- } : undefined;
433
- epic.branch_mode = 'epic';
434
- epic.execution_branch = authority.executionBranch;
435
- epic.origin_branch = authority.originBranch;
436
- epic.graph_revision = authority.graphRevision;
437
- epic.branch_lease = live && alreadyPromoted ? current : {
438
- schemaVersion: 1, ownerStoryId: options.storyId, sessionId: idempotencyKey,
439
- idempotencyKey, acquiredAt: nowIso, expiresAt,
440
- ...(handoffConsumption ? { handoffConsumption } : {}),
441
- };
442
- }, {
443
- expectedRevision: runtimeBeforeLock.revision,
444
- preCommit: async () => {
445
- if (!(await verifyGitLifecyclePhysicalIntegrationLock(physicalLock.path, physicalLock.owner))) {
446
- throw new Error('direct-yolo-integration-lock-raced');
447
- }
448
- },
449
- });
450
- }
451
- catch (error) {
452
- await releaseGitLifecyclePhysicalIntegrationLock(physicalLock.path, physicalLock.owner).catch(() => undefined);
453
- throw error;
454
- }
455
- if (issuedHandoff && options.setupLeaseHandoff === undefined) {
456
- await options.setupHandoffFault?.('before-consumed-persist');
457
- await markSetupHandoffTokenConsumed(authority.projectRoot, authority.epicId, options.storyId, issuedHandoff.sessionId, issuedHandoff.token, bundleDigest, claimRevision, idempotencyKey, nowIso);
458
- }
459
- const trailers = Object.freeze([
460
- `Neocortex-Story: ${options.storyId}`,
461
- `Neocortex-Epic: ${authority.epicId}`,
462
- `Neocortex-Execution-Branch: ${authority.executionBranch}`,
463
- `Neocortex-Prepared-Base: ${baseSha}`,
464
- `Neocortex-Bundle-Digest: ${bundleDigest}`,
465
- `Neocortex-Review-Artifact-Digest: ${stagedReview.digest}`,
466
- `Neocortex-Graph-Revision: ${authority.graphRevision}`,
467
- `Neocortex-Claim-Revision: ${claimRevision}`,
468
- ...(authority.rootMemoryEffect ? [`Neocortex-Root-Memory-Effect: ${rootMemoryEffectDigest(authority.rootMemoryEffect)}`] : []),
469
- ]);
470
- return { ok: true, value: Object.freeze({
471
- schemaVersion: 1, storyId: options.storyId, epicId: authority.epicId,
472
- executionBranch: authority.executionBranch, baseSha, bundleDigest,
473
- reviewArtifactDigest: stagedReview.digest,
474
- graphRevision: authority.graphRevision, claimRevision, idempotencyKey,
475
- changedPaths: Object.freeze(changedPaths), trailers,
476
- }) };
477
- }
478
- catch (error) {
479
- return failure(publicReason(error, 'direct-yolo-begin-failed'));
480
- }
481
- }
482
- export async function finalizeDirectYoloStoryCheckpoint(options) {
483
- let cleanupAuthority;
484
- let cleanupLease;
485
- let cleanupPhysical;
486
- try {
487
- const authority = await resolveAuthority(options.projectRoot, options.storyId);
488
- cleanupAuthority = authority;
489
- const aggregateHeadSha = await git(authority.projectRoot, ['rev-parse', 'HEAD']);
490
- let runtime = await readGitLifecycleRuntime(authority.projectRoot, { required: true });
491
- const existing = asRecord(runtime.stories[options.storyId]?.checkpoint);
492
- if (existing) {
493
- const checkpointHeadSha = typeof existing.headSha === 'string' ? existing.headSha : '';
494
- const checkpointGraphRevision = typeof existing.graphRevision === 'string' ? existing.graphRevision : '';
495
- const checkpointPaths = /^[a-f0-9]{40,64}$/.test(checkpointHeadSha)
496
- ? splitNull(await gitRaw(authority.projectRoot, ['diff-tree', '--no-commit-id', '--name-only', '-r', '-z', checkpointHeadSha]))
497
- : [];
498
- const replayWriteSet = validateDirectWriteSet(checkpointPaths, authority.authorizedWriteSet, options.storyId, authority.rootMemoryEffect);
499
- const ancestral = /^[a-f0-9]{40,64}$/.test(checkpointHeadSha)
500
- && await gitExitOk(authority.projectRoot, ['merge-base', '--is-ancestor', checkpointHeadSha, aggregateHeadSha]);
501
- if (!ancestral || !/^[a-f0-9]{64}$/.test(checkpointGraphRevision) || !replayWriteSet.ok) {
502
- return failure('direct-yolo-replay-invalid');
503
- }
504
- const replayQa = await runQa(options, authority.projectRoot, aggregateHeadSha, checkpointGraphRevision);
505
- if (!(await verifyAcceptedGitLifecycleStoryCheckpoint(authority.projectRoot, existing, options.storyId, authority.epicId, authority.executionBranch, replayQa.transientAuthority, authority.authorizedWriteSet)))
506
- return failure('direct-yolo-replay-invalid');
507
- if (!(await physicalCheckoutUnchanged(authority.projectRoot, authority.executionBranch, aggregateHeadSha, checkpointPaths)))
508
- return failure('direct-yolo-replay-head-raced');
509
- if (options.push && !(await remoteContainsHead(authority.projectRoot, options.remote ?? 'origin', authority.executionBranch, checkpointHeadSha)))
510
- return failure('direct-yolo-remote-head-mismatch');
511
- const physicalOwnerRef = Array.isArray(existing.receiptRefs)
512
- ? existing.receiptRefs.find((item) => typeof item === 'string' && /^physical-lock-owner:[a-f0-9]{64}$/.test(item))
513
- : undefined;
514
- if (physicalOwnerRef && typeof existing.idempotencyKey === 'string') {
515
- await releaseGitLifecyclePhysicalIntegrationLockForCheckpoint(authority.projectRoot, {
516
- storyId: options.storyId,
517
- idempotencyKey: existing.idempotencyKey,
518
- ownerDigest: physicalOwnerRef.slice('physical-lock-owner:'.length),
519
- });
520
- }
521
- return { ok: true, value: existing };
522
- }
523
- const headSha = aggregateHeadSha;
524
- const baseSha = await git(authority.projectRoot, ['rev-parse', `${headSha}^`]);
525
- const commitBody = await gitRaw(authority.projectRoot, ['show', '--no-patch', '--format=%B', headSha]);
526
- const changedPaths = splitNull(await gitRaw(authority.projectRoot, ['diff-tree', '--no-commit-id', '--name-only', '-r', '-z', headSha]));
527
- const writeSet = validateDirectWriteSet(changedPaths, authority.authorizedWriteSet, options.storyId, authority.rootMemoryEffect);
528
- if (!writeSet.ok)
529
- return failure(writeSet.reasonCode);
530
- if (authority.rootMemoryEffect)
531
- verifyRootMemoryPhysicalWriteSet(authority.projectRoot, authority.rootMemoryEffect, { storyId: options.storyId, executionBranch: authority.executionBranch, baseSha, headRef: headSha, childPaths: authority.authorizedWriteSet });
532
- const bundleDigest = trailer(commitBody, 'Neocortex-Bundle-Digest');
533
- const graphRevision = trailer(commitBody, 'Neocortex-Graph-Revision');
534
- const claimRevision = trailer(commitBody, 'Neocortex-Claim-Revision');
535
- const required = new Map([
536
- ['Neocortex-Story', options.storyId], ['Neocortex-Epic', authority.epicId],
537
- ['Neocortex-Execution-Branch', authority.executionBranch], ['Neocortex-Prepared-Base', baseSha],
538
- ['Neocortex-Graph-Revision', authority.graphRevision],
539
- ['Neocortex-Claim-Revision', digest([...authority.authorizedWriteSet].sort().join('\0'))],
540
- ]);
541
- if (!bundleDigest || !/^[a-f0-9]{64}$/.test(bundleDigest) || !graphRevision || !claimRevision
542
- || [...required].some(([key, value]) => trailer(commitBody, key) !== value)) {
543
- return failure('direct-yolo-trailers-invalid');
544
- }
545
- const publicReview = publicStoryReviewArtifactFromGit(authority.projectRoot, options.storyId, headSha);
546
- if (publicReview?.verdict !== 'approved')
547
- return failure('direct-yolo-review-artifact-not-approved');
548
- const reviewTrailer = trailer(commitBody, 'Neocortex-Review-Artifact-Digest');
549
- if (reviewTrailer !== undefined && reviewTrailer !== publicReview.digest) {
550
- return failure('direct-yolo-review-artifact-digest-mismatch');
551
- }
552
- const expectedPayloadDigest = digest(await gitRaw(authority.projectRoot, ['diff', '--binary', '--full-index', `${baseSha}..${headSha}`]));
553
- if (bundleDigest !== expectedPayloadDigest)
554
- return failure('direct-yolo-bundle-digest-mismatch');
555
- const idempotencyKey = digest([
556
- 'direct-yolo-commit-v1', options.storyId, authority.epicId, authority.executionBranch,
557
- baseSha, bundleDigest, graphRevision, claimRevision,
558
- ].join('\0'));
559
- const currentLease = asRecord(runtime.epics[authority.epicId]?.branch_lease);
560
- const nowIso = normalizedTime(options.nowIso);
561
- const matchingLease = currentLease?.ownerStoryId === options.storyId
562
- && currentLease.idempotencyKey === idempotencyKey
563
- && currentLease.sessionId === idempotencyKey
564
- && Date.parse(String(currentLease.expiresAt)) > Date.now()
565
- ? currentLease : undefined;
566
- if (currentLease && Date.parse(String(currentLease.expiresAt)) > Date.now() && !matchingLease) {
567
- return failure('direct-yolo-writer-active');
568
- }
569
- const requestedLease = matchingLease ?? {
570
- schemaVersion: 1, ownerStoryId: options.storyId, sessionId: idempotencyKey,
571
- idempotencyKey, acquiredAt: nowIso,
572
- expiresAt: new Date(Date.parse(nowIso) + 30 * 60 * 1000).toISOString(),
573
- };
574
- const physicalLock = await acquireGitLifecyclePhysicalIntegrationLock(authority.projectRoot, {
575
- kind: 'direct', storyId: options.storyId, sessionId: idempotencyKey, idempotencyKey,
576
- expiresAt: String(requestedLease.expiresAt), persistent: true,
577
- createdAt: String(requestedLease.acquiredAt),
578
- });
579
- if (!physicalLock.ok)
580
- return failure(physicalLock.reasonCode === 'git-lifecycle-integration-backpressure'
581
- ? 'direct-yolo-integration-backpressure' : 'direct-yolo-integration-lock-failed');
582
- cleanupPhysical = physicalLock;
583
- if (!matchingLease) {
584
- const leased = await mutateGitLifecycleRuntime(authority.projectRoot, (document) => {
585
- const epic = ensureRuntimeEpic(document, authority.epicId);
586
- const current = asRecord(epic.branch_lease);
587
- if (current && Date.parse(String(current.expiresAt)) > Date.parse(nowIso)) {
588
- throw new Error('direct-yolo-writer-active');
589
- }
590
- if (epic.graph_revision !== authority.graphRevision)
591
- throw new Error('direct-yolo-graph-revision-raced');
592
- epic.branch_lease = requestedLease;
593
- }, { expectedRevision: runtime.revision });
594
- runtime = leased.document;
595
- }
596
- cleanupLease = requestedLease;
597
- {
598
- const qaExecution = await runQa(options, authority.projectRoot, headSha, authority.graphRevision);
599
- if (!(await physicalCheckoutUnchanged(authority.projectRoot, authority.executionBranch, headSha, changedPaths))) {
600
- return failure('direct-yolo-head-raced');
601
- }
602
- if (options.push) {
603
- const remote = safeRemote(options.remote ?? 'origin');
604
- await git(authority.projectRoot, ['push', remote, authority.executionBranch]);
605
- if (!(await remoteHeadIsExact(authority.projectRoot, remote, authority.executionBranch, headSha))) {
606
- return failure('direct-yolo-remote-head-mismatch');
607
- }
608
- }
609
- const checkpoint = await buildCheckpoint({
610
- projectRoot: authority.projectRoot, storyId: options.storyId, epicId: authority.epicId,
611
- executionBranch: authority.executionBranch, baseSha, headSha, bundleDigest,
612
- graphRevision, claimRevision, idempotencyKey, qaExecution,
613
- physicalLockOwnerDigest: computeGitLifecyclePhysicalIntegrationLockOwnerDigest(physicalLock.owner),
614
- nowIso: normalizedTime(options.nowIso), legacyReceiptRef: undefined, rootMemoryEffect: authority.rootMemoryEffect,
615
- });
616
- await options.beforeCheckpointPersist?.();
617
- const persisted = await persistCheckpoint({
618
- projectRoot: authority.projectRoot, checkpoint,
619
- remote: options.push ? options.remote ?? 'origin' : undefined,
620
- expectedRevision: runtime.revision, expectedLease: requestedLease,
621
- expectedGraphRevision: authority.graphRevision,
622
- physicalLockPath: physicalLock.path, physicalLockOwner: physicalLock.owner,
623
- transientAuthority: qaExecution.transientAuthority,
624
- authorizedWriteSet: authority.authorizedWriteSet,
625
- });
626
- if (!persisted)
627
- return failure('direct-yolo-checkpoint-persist-failed');
628
- await options.afterCheckpointPersist?.();
629
- return { ok: true, value: checkpoint };
630
- }
631
- }
632
- catch (error) {
633
- return failure(publicReason(error, 'direct-yolo-finalize-failed'));
634
- }
635
- finally {
636
- if (cleanupPhysical) {
637
- await releaseGitLifecyclePhysicalIntegrationLock(cleanupPhysical.path, cleanupPhysical.owner).catch(() => undefined);
638
- }
639
- if (cleanupAuthority && cleanupLease) {
640
- await releaseOwnedDirectLease(cleanupAuthority.projectRoot, cleanupAuthority.epicId, cleanupLease).catch(() => undefined);
641
- }
642
- else if (cleanupAuthority) {
643
- await cleanupOwnedDirectAuthority(cleanupAuthority, options.storyId).catch(() => undefined);
644
- }
645
- }
646
- }
647
- export async function recoverLegacyDirectYoloStoryCheckpoint(options) {
648
- try {
649
- const authority = await resolveAuthority(options.projectRoot, options.storyId);
650
- if (!/^[a-f0-9]{40,64}$/.test(options.commitSha))
651
- return failure('direct-yolo-recovery-commit-invalid');
652
- const headSha = await git(authority.projectRoot, ['rev-parse', options.commitSha]);
653
- if (LEGACY_DIRECT_YOLO_RECOVERY_COMMITS[options.storyId] !== headSha) {
654
- return failure('direct-yolo-recovery-not-allowlisted');
655
- }
656
- if (await git(authority.projectRoot, ['rev-parse', 'HEAD']) !== headSha) {
657
- return failure('direct-yolo-recovery-head-mismatch');
658
- }
659
- await git(authority.projectRoot, ['merge-base', '--is-ancestor', headSha, `refs/heads/${authority.executionBranch}`]);
660
- const baseSha = await git(authority.projectRoot, ['rev-parse', `${headSha}^`]);
661
- const payload = await gitRaw(authority.projectRoot, ['diff', '--binary', '--full-index', `${baseSha}..${headSha}`]);
662
- const changedPaths = splitNull(await gitRaw(authority.projectRoot, ['diff-tree', '--no-commit-id', '--name-only', '-r', '-z', headSha]));
663
- const recoveryWriteSet = validateDirectWriteSet(changedPaths, authority.authorizedWriteSet.length > 0 ? authority.authorizedWriteSet : changedPaths, options.storyId);
664
- if (!recoveryWriteSet.ok)
665
- return failure('direct-yolo-recovery-write-set-invalid');
666
- const bundleDigest = digest(payload);
667
- const claimRevision = digest([...changedPaths].sort().join('\0'));
668
- const idempotencyKey = digest(['legacy-direct-yolo-recovery-v1', options.storyId, headSha].join('\0'));
669
- const nowIso = normalizedTime(options.nowIso);
670
- const expiresAt = new Date(Date.parse(nowIso) + 30 * 60 * 1000).toISOString();
671
- const initialRuntime = await readGitLifecycleRuntime(authority.projectRoot, { required: true });
672
- const physicalLock = await acquireGitLifecyclePhysicalIntegrationLock(authority.projectRoot, {
673
- kind: 'direct', storyId: options.storyId, sessionId: idempotencyKey, idempotencyKey,
674
- expiresAt, persistent: true, createdAt: nowIso,
675
- });
676
- if (!physicalLock.ok)
677
- return failure(physicalLock.reasonCode === 'git-lifecycle-integration-backpressure'
678
- ? 'direct-yolo-recovery-integration-backpressure' : 'direct-yolo-recovery-integration-lock-failed');
679
- const requestedLease = {
680
- schemaVersion: 1, ownerStoryId: options.storyId, sessionId: idempotencyKey,
681
- idempotencyKey, acquiredAt: nowIso, expiresAt,
682
- };
683
- let leaseAcquired = false;
684
- try {
685
- const leased = await mutateGitLifecycleRuntime(authority.projectRoot, (document) => {
686
- const epic = ensureRuntimeEpic(document, authority.epicId);
687
- const current = asRecord(epic.branch_lease);
688
- if (current && Date.parse(String(current.expiresAt)) > Date.parse(nowIso)
689
- && !sameLease(current, requestedLease))
690
- throw new Error('direct-yolo-recovery-writer-active');
691
- if (epic.graph_revision !== authority.graphRevision)
692
- throw new Error('direct-yolo-recovery-graph-raced');
693
- epic.branch_mode = 'epic';
694
- epic.execution_branch = authority.executionBranch;
695
- epic.origin_branch = authority.originBranch;
696
- epic.branch_lease = requestedLease;
697
- }, { expectedRevision: initialRuntime.revision });
698
- leaseAcquired = true;
699
- const qaExecution = await runQa(options, authority.projectRoot, headSha, authority.graphRevision);
700
- if (!(await physicalCheckoutUnchanged(authority.projectRoot, authority.executionBranch, headSha, changedPaths))) {
701
- return failure('direct-yolo-recovery-head-raced');
702
- }
703
- if (options.push) {
704
- const remote = safeRemote(options.remote ?? 'origin');
705
- const remoteHead = (await git(authority.projectRoot, ['ls-remote', '--heads', remote, `refs/heads/${authority.executionBranch}`])).split(/\s+/)[0];
706
- if (!remoteHead || !(await gitExitOk(authority.projectRoot, ['merge-base', '--is-ancestor', headSha, remoteHead]))) {
707
- return failure('direct-yolo-recovery-remote-proof-missing');
708
- }
709
- }
710
- const provisional = await buildCheckpoint({
711
- projectRoot: authority.projectRoot, storyId: options.storyId, epicId: authority.epicId,
712
- executionBranch: authority.executionBranch, baseSha, headSha, bundleDigest,
713
- graphRevision: authority.graphRevision, claimRevision, idempotencyKey,
714
- qaExecution,
715
- physicalLockOwnerDigest: computeGitLifecyclePhysicalIntegrationLockOwnerDigest(physicalLock.owner),
716
- nowIso, legacyReceiptRef: undefined,
717
- });
718
- const legacyReceiptRef = await computeLegacyDirectYoloRecoveryReceiptRef(authority.projectRoot, {
719
- storyId: options.storyId,
720
- epicId: authority.epicId,
721
- executionBranch: authority.executionBranch,
722
- baseSha,
723
- headSha,
724
- bundleDigest,
725
- graphRevision: authority.graphRevision,
726
- });
727
- if (!legacyReceiptRef)
728
- return failure('direct-yolo-recovery-evidence-invalid');
729
- const checkpoint = Object.freeze({
730
- ...provisional,
731
- receiptRefs: Object.freeze([...provisional.receiptRefs, legacyReceiptRef]),
732
- });
733
- const persisted = await persistCheckpoint({
734
- projectRoot: authority.projectRoot, checkpoint,
735
- remote: options.push ? options.remote ?? 'origin' : undefined,
736
- remotePolicy: 'ancestor-recovery',
737
- expectedRevision: leased.document.revision,
738
- expectedLease: requestedLease,
739
- expectedGraphRevision: authority.graphRevision,
740
- physicalLockPath: physicalLock.path,
741
- physicalLockOwner: physicalLock.owner,
742
- transientAuthority: qaExecution.transientAuthority,
743
- authorizedWriteSet: authority.authorizedWriteSet,
744
- });
745
- return persisted ? { ok: true, value: checkpoint } : failure('direct-yolo-recovery-persist-failed');
746
- }
747
- finally {
748
- await releaseGitLifecyclePhysicalIntegrationLock(physicalLock.path, physicalLock.owner).catch(() => undefined);
749
- if (leaseAcquired)
750
- await releaseOwnedDirectLease(authority.projectRoot, authority.epicId, requestedLease).catch(() => undefined);
751
- }
752
- }
753
- catch (error) {
754
- return failure(publicReason(error, 'direct-yolo-recovery-failed'));
755
- }
756
- }
757
- async function resolveLogicalAuthority(projectRootInput, storyId) {
758
- if (!/^[A-Za-z][A-Za-z0-9_-]*\.[A-Za-z0-9._-]+$/.test(storyId))
759
- throw new Error('direct-yolo-story-id-invalid');
760
- const projectRoot = resolve(projectRootInput);
761
- const tracked = asRecord(JSON.parse(await readFile(join(projectRoot, '.neocortex/state.json'), 'utf8')));
762
- const runtime = await readGitLifecycleRuntime(projectRoot, { required: true });
763
- const effective = applyGitLifecycleRuntimeOverlay(tracked, runtime);
764
- const stories = asRecord(effective.stories);
765
- const epics = asRecord(effective.epics);
766
- const story = asRecord(stories?.[storyId]);
767
- const epicId = String(story?.epic_id ?? storyId.split('.')[0]);
768
- const epic = asRecord(epics?.[epicId]);
769
- const executionBranch = String(epic?.execution_branch ?? '');
770
- const originBranch = String(epic?.origin_branch ?? '');
771
- const graphRevision = String(epic?.graph_revision ?? '');
772
- const authorizedWriteSet = Array.isArray(story?.files_to_modify)
773
- ? [...new Set(story.files_to_modify.filter((item) => typeof item === 'string' && safePath(item)))].sort()
774
- : [];
775
- if (!story)
776
- throw new Error('direct-yolo-story-state-missing');
777
- if (epic?.branch_mode !== 'epic')
778
- throw new Error('direct-yolo-branch-mode-invalid');
779
- if (!safeRef(executionBranch)
780
- || !(await gitExitOk(projectRoot, ['check-ref-format', '--branch', executionBranch]))) {
781
- throw new Error('direct-yolo-execution-branch-invalid');
782
- }
783
- if (!safeRef(originBranch)
784
- || !(await gitExitOk(projectRoot, ['check-ref-format', '--branch', originBranch]))) {
785
- throw new Error('direct-yolo-origin-branch-invalid');
786
- }
787
- if (!/^[a-f0-9]{64}$/.test(graphRevision))
788
- throw new Error('direct-yolo-graph-revision-invalid');
789
- const rootMemoryEffect = await readRootMemoryEffectForCheckpoint({ projectRoot, storyId, graphRevision,
790
- executionBranch, childPaths: authorizedWriteSet });
791
- return { projectRoot, storyId, story, epicId, executionBranch, originBranch, graphRevision, authorizedWriteSet, rootMemoryEffect };
792
- }
793
- async function resolveAuthority(projectRootInput, storyId) {
794
- const authority = await resolveLogicalAuthority(projectRootInput, storyId);
795
- const currentBranch = await git(authority.projectRoot, ['symbolic-ref', '--quiet', '--short', 'HEAD']);
796
- if (currentBranch !== authority.executionBranch)
797
- throw new Error('direct-yolo-current-branch-mismatch');
798
- return authority;
799
- }
800
- async function buildCheckpoint(input) {
801
- const checkedAt = input.nowIso;
802
- const commitBody = await gitRaw(input.projectRoot, ['show', '--no-patch', '--format=%H%n%P%n%B', input.headSha]);
803
- const changedPaths = splitNull(await gitRaw(input.projectRoot, ['diff-tree', '--no-commit-id', '--name-only', '-r', '-z', input.headSha]));
804
- const gitEvidenceDigest = computeImmutableStoryQaEvidenceDigest({
805
- storyId: input.storyId, baseSha: input.baseSha, headSha: input.headSha,
806
- bundleDigest: input.bundleDigest, graphRevision: input.graphRevision,
807
- commitBody, changedPaths,
808
- });
809
- const qaDigest = computeP205StoryQaEvidenceDigest({
810
- gitEvidenceDigest, headSha: input.headSha, graphRevision: input.graphRevision,
811
- manifestDigest: input.qaExecution.manifestDigest, gateIds: input.qaExecution.gateIds,
812
- resultDigest: input.qaExecution.resultDigest,
813
- });
814
- const qaReceiptRef = `story-qa:${qaDigest}`;
815
- const qaReceipt = Object.freeze({
816
- schemaVersion: 1, kind: 'story_qa_evidence', producer: 'local-yolo-tdd', evidenceVersion: 'p205-execution-v2',
817
- storyId: input.storyId, epicId: input.epicId, baseSha: input.baseSha, headSha: input.headSha,
818
- bundleDigest: input.bundleDigest, graphRevision: input.graphRevision,
819
- contract: input.qaExecution.contract, command: input.qaExecution.command,
820
- manifestDigest: input.qaExecution.manifestDigest, gateIds: input.qaExecution.gateIds,
821
- resultDigest: input.qaExecution.resultDigest,
822
- sourceEvidenceDigest: qaDigest, verdict: 'passed', checkedAt, receiptRef: qaReceiptRef,
823
- });
824
- const publicReview = publicStoryReviewArtifactFromGit(input.projectRoot, input.storyId, input.headSha);
825
- if (publicReview?.verdict !== 'approved')
826
- throw new Error('direct-yolo-review-artifact-not-approved');
827
- const reviewDigest = digest([
828
- 'root-commit-range-review-v2', input.storyId, input.baseSha, input.headSha,
829
- input.bundleDigest, input.graphRevision, qaReceiptRef, digest(commitBody), [...changedPaths].sort().join(','),
830
- publicReview.digest,
831
- ].join('\0'));
832
- const reviewReceiptRef = `story-review:${reviewDigest}`;
833
- const reviewReceipt = Object.freeze({
834
- schemaVersion: 1, kind: 'story_review_evidence', producer: 'root-commit-range-review',
835
- storyId: input.storyId, epicId: input.epicId, baseSha: input.baseSha, headSha: input.headSha,
836
- bundleDigest: input.bundleDigest, graphRevision: input.graphRevision,
837
- sourceEvidenceDigest: reviewDigest, reviewArtifactDigest: publicReview.digest,
838
- qaReceiptRef, verdict: 'approved', reviewedAt: checkedAt,
839
- receiptRef: reviewReceiptRef,
840
- });
841
- const evidenceRefs = [
842
- `.neocortex/stories/${input.storyId}/qa-gate-result.json`,
843
- `.neocortex/stories/${input.storyId}/validation-evidence.md`, qaReceiptRef, reviewReceiptRef,
844
- ...(input.physicalLockOwnerDigest ? [`physical-lock-owner:${input.physicalLockOwnerDigest}`] : []),
845
- ...(input.legacyReceiptRef ? [input.legacyReceiptRef] : []),
846
- ];
847
- return Object.freeze({
848
- schemaVersion: 1, storyId: input.storyId, epicId: input.epicId, branch: input.executionBranch,
849
- baseSha: input.baseSha, headSha: input.headSha, commits: Object.freeze([input.headSha]),
850
- idempotencyKey: input.idempotencyKey, bundleDigest: input.bundleDigest,
851
- graphRevision: input.graphRevision, claimRevision: input.claimRevision,
852
- receiptRefs: Object.freeze(evidenceRefs), qaVerdict: 'passed', reviewVerdict: 'approved',
853
- qaReceipt, reviewReceipt, recordedAt: checkedAt,
854
- ...(input.rootMemoryEffect ? { rootMemoryEffect: input.rootMemoryEffect } : {}),
855
- });
856
- }
857
- async function persistCheckpoint(input) {
858
- const { projectRoot, checkpoint, remote } = input;
859
- let conflict = false;
860
- await mutateGitLifecycleRuntime(projectRoot, (document) => {
861
- const epic = ensureRuntimeEpic(document, checkpoint.epicId);
862
- if (input.expectedLease) {
863
- const currentLease = asRecord(epic.branch_lease);
864
- if (!sameLease(currentLease, input.expectedLease))
865
- throw new Error('direct-yolo-runtime-revision-conflict');
866
- if (Date.parse(String(currentLease?.expiresAt)) <= Date.now())
867
- throw new Error('direct-yolo-writer-lease-expired');
868
- if (epic.graph_revision !== input.expectedGraphRevision || checkpoint.graphRevision !== input.expectedGraphRevision) {
869
- throw new Error('direct-yolo-graph-revision-raced');
870
- }
871
- }
872
- const story = ensureRuntimeStory(document, checkpoint.storyId);
873
- const existing = asRecord(story.checkpoint);
874
- if (existing && existing.headSha !== checkpoint.headSha) {
875
- conflict = true;
876
- return;
877
- }
878
- story.checkpoint = checkpoint;
879
- story.status = 'done';
880
- story.last_step = GIT_LIFECYCLE_REVIEW_PR_STEP_ID;
881
- const steps = Array.isArray(story.steps_completed) ? story.steps_completed.filter((item) => typeof item === 'string') : [];
882
- story.steps_completed = [...new Set([...steps, GIT_LIFECYCLE_COMMIT_PUSH_STEP_ID, GIT_LIFECYCLE_REVIEW_PR_STEP_ID])];
883
- epic.branch_lease = null;
884
- }, { expectedRevision: input.expectedRevision, preCommit: async () => {
885
- if (conflict)
886
- throw new Error('direct-yolo-checkpoint-conflict');
887
- const currentBranch = await git(projectRoot, ['symbolic-ref', '--quiet', '--short', 'HEAD']);
888
- if (currentBranch !== checkpoint.branch || await git(projectRoot, ['rev-parse', 'HEAD']) !== checkpoint.headSha) {
889
- throw new Error('direct-yolo-checkpoint-head-raced');
890
- }
891
- if (!(await verifyAcceptedGitLifecycleStoryCheckpoint(projectRoot, checkpoint, checkpoint.storyId, checkpoint.epicId, checkpoint.branch, input.transientAuthority, input.authorizedWriteSet)))
892
- throw new Error('direct-yolo-checkpoint-physical-invalid');
893
- if (input.physicalLockPath && input.physicalLockOwner
894
- && !(await verifyGitLifecyclePhysicalIntegrationLock(input.physicalLockPath, input.physicalLockOwner))) {
895
- throw new Error('direct-yolo-integration-lock-raced');
896
- }
897
- if (remote) {
898
- const accepted = input.remotePolicy === 'ancestor-recovery'
899
- ? await remoteContainsHead(projectRoot, remote, checkpoint.branch, checkpoint.headSha)
900
- : await remoteHeadIsExact(projectRoot, remote, checkpoint.branch, checkpoint.headSha);
901
- if (!accepted) {
902
- throw new Error('direct-yolo-checkpoint-remote-raced');
903
- }
904
- }
905
- } });
906
- return !conflict;
907
- }
908
- async function remoteContainsHead(projectRoot, remoteInput, branch, headSha) {
909
- const remote = safeRemote(remoteInput);
910
- const remoteHead = (await git(projectRoot, ['ls-remote', '--heads', remote, `refs/heads/${branch}`])).split(/\s+/)[0];
911
- return Boolean(remoteHead && await gitExitOk(projectRoot, ['merge-base', '--is-ancestor', headSha, remoteHead]));
912
- }
913
- function isLiveSameStorySetupLease(lease, storyId, nowIso) {
914
- return Boolean(lease?.ownerStoryId === storyId
915
- && lease.sessionId === lease.idempotencyKey
916
- && typeof lease.idempotencyKey === 'string'
917
- && /^setup-[a-f0-9]{64}$/.test(lease.idempotencyKey)
918
- && typeof lease.expiresAt === 'string'
919
- && Date.parse(lease.expiresAt) > Date.parse(nowIso));
920
- }
921
- async function setupHandoffTokenLocation(projectRoot, storyId) {
922
- const runtimeDirectory = await gitLifecycleRuntimeDirectory(projectRoot);
923
- const parent = await ensureGitLifecyclePrivateChildDirectory(runtimeDirectory, 'handoffs');
924
- const name = `direct-${digest(storyId)}.json`;
925
- return { parent, name, path: join(parent, name) };
926
- }
927
- async function writeSetupHandoffTokenRecord(projectRoot, record) {
928
- const location = await setupHandoffTokenLocation(projectRoot, record.storyId);
929
- await anchoredAtomicWriteFile(location.parent, location.name, `${JSON.stringify(record, null, 2)}\n`);
930
- await assertGitLifecyclePrivateFile(location.path, location.parent);
931
- }
932
- async function readSetupHandoffTokenRecord(projectRoot, storyId) {
933
- try {
934
- const location = await setupHandoffTokenLocation(projectRoot, storyId);
935
- await assertGitLifecyclePrivateFile(location.path, location.parent);
936
- const item = JSON.parse(await readFile(location.path, 'utf8'));
937
- const exactKeys = item.state === 'issued'
938
- ? ['schemaVersion', 'kind', 'state', 'storyId', 'fromSessionId', 'token', 'tokenDigest', 'bundleDigest', 'claimRevision', 'grantDigest', 'expiresAt']
939
- : item.state === 'consuming'
940
- ? ['schemaVersion', 'kind', 'state', 'storyId', 'fromSessionId', 'token', 'tokenDigest', 'bundleDigest', 'claimRevision', 'grantDigest', 'expiresAt', 'consumedBySessionId', 'consumeStartedAt']
941
- : ['schemaVersion', 'kind', 'state', 'storyId', 'fromSessionId', 'tokenDigest', 'bundleDigest', 'claimRevision', 'grantDigest', 'expiresAt', 'consumedBySessionId', 'consumedAt'];
942
- if (Object.keys(item).sort().join('\0') !== [...exactKeys].sort().join('\0')
943
- || item.schemaVersion !== 1 || item.kind !== 'direct-yolo-setup-handoff-token'
944
- || (item.state !== 'issued' && item.state !== 'consuming' && item.state !== 'consumed') || item.storyId !== storyId
945
- || typeof item.fromSessionId !== 'string' || !/^setup-[a-f0-9]{64}$/.test(item.fromSessionId)
946
- || ![item.tokenDigest, item.bundleDigest, item.claimRevision, item.grantDigest].every((value) => typeof value === 'string' && /^[a-f0-9]{64}$/.test(value))
947
- || typeof item.expiresAt !== 'string' || !Number.isFinite(Date.parse(item.expiresAt))
948
- || (item.state === 'issued' || item.state === 'consuming')
949
- && (typeof item.token !== 'string' || !/^[A-Za-z0-9_-]{32,256}$/.test(item.token))
950
- || item.state === 'consuming' && (typeof item.consumedBySessionId !== 'string'
951
- || !/^[a-f0-9]{64}$/.test(item.consumedBySessionId)
952
- || typeof item.consumeStartedAt !== 'string' || !Number.isFinite(Date.parse(item.consumeStartedAt)))
953
- || item.state === 'consumed' && (typeof item.consumedBySessionId !== 'string'
954
- || !/^[a-f0-9]{64}$/.test(item.consumedBySessionId)
955
- || typeof item.consumedAt !== 'string' || !Number.isFinite(Date.parse(item.consumedAt))))
956
- return undefined;
957
- return item;
958
- }
959
- catch {
960
- return undefined;
961
- }
962
- }
963
- async function resolveIssuedSetupLeaseHandoff(projectRoot, storyId, bundleDigest, claimRevision, nowIso) {
964
- const record = await readSetupHandoffTokenRecord(projectRoot, storyId);
965
- if (!record || record.state === 'consumed' || !record.token
966
- || record.bundleDigest !== bundleDigest || record.claimRevision !== claimRevision
967
- || Date.parse(record.expiresAt) <= Date.parse(nowIso)
968
- || digest(record.token) !== record.tokenDigest)
969
- return undefined;
970
- return { sessionId: record.fromSessionId, token: record.token };
971
- }
972
- async function markSetupHandoffTokenConsuming(projectRoot, storyId, fromSessionId, token, consumedBySessionId, consumeStartedAt) {
973
- const record = await readSetupHandoffTokenRecord(projectRoot, storyId);
974
- if (!record || record.state === 'consumed' || record.fromSessionId !== fromSessionId
975
- || record.token !== token || record.tokenDigest !== digest(token)) {
976
- throw new Error('direct-yolo-setup-handoff-consume-invalid');
977
- }
978
- if (record.state === 'consuming') {
979
- if (record.consumedBySessionId !== consumedBySessionId) {
980
- throw new Error('direct-yolo-setup-handoff-consume-raced');
981
- }
982
- return;
983
- }
984
- await writeSetupHandoffTokenRecord(projectRoot, {
985
- ...record,
986
- state: 'consuming',
987
- consumedBySessionId,
988
- consumeStartedAt,
989
- });
990
- }
991
- async function markSetupHandoffTokenConsumed(projectRoot, epicId, storyId, fromSessionId, token, bundleDigest, claimRevision, consumedBySessionId, consumedAt) {
992
- const record = await readSetupHandoffTokenRecord(projectRoot, storyId);
993
- if (record?.state === 'consumed' && record.fromSessionId === fromSessionId
994
- && record.consumedBySessionId === consumedBySessionId)
995
- return;
996
- const runtime = await readGitLifecycleRuntime(projectRoot, { required: true });
997
- const lease = asRecord(runtime.epics[epicId]?.branch_lease);
998
- const consumption = asRecord(lease?.handoffConsumption);
999
- if (!record || record.state !== 'consuming' || record.fromSessionId !== fromSessionId
1000
- || record.token !== token || record.tokenDigest !== digest(token)
1001
- || record.bundleDigest !== bundleDigest || record.claimRevision !== claimRevision
1002
- || record.consumedBySessionId !== consumedBySessionId
1003
- || lease?.ownerStoryId !== storyId || lease.sessionId !== consumedBySessionId
1004
- || consumption?.kind !== 'direct-yolo-setup-handoff-consumption'
1005
- || consumption.state !== 'consumed' || consumption.fromSessionId !== fromSessionId
1006
- || consumption.tokenDigest !== record.tokenDigest || consumption.bundleDigest !== bundleDigest
1007
- || consumption.claimRevision !== claimRevision || consumption.consumedBySessionId !== consumedBySessionId) {
1008
- throw new Error('direct-yolo-setup-handoff-consume-invalid');
1009
- }
1010
- try {
1011
- await writeSetupHandoffTokenRecord(projectRoot, {
1012
- schemaVersion: 1, kind: record.kind, state: 'consumed', storyId,
1013
- fromSessionId, tokenDigest: record.tokenDigest, bundleDigest: record.bundleDigest,
1014
- claimRevision: record.claimRevision, grantDigest: record.grantDigest, expiresAt: record.expiresAt,
1015
- consumedBySessionId, consumedAt,
1016
- });
1017
- }
1018
- catch {
1019
- throw new Error('direct-yolo-setup-handoff-consume-persist-failed');
1020
- }
1021
- }
1022
- function validSetupLeaseHandoff(input) {
1023
- const grant = asRecord(input.current?.handoffGrant);
1024
- const token = input.handoff?.token;
1025
- const tokenDigest = typeof token === 'string' && /^[A-Za-z0-9_-]{32,256}$/.test(token)
1026
- ? digest(token)
1027
- : undefined;
1028
- return Boolean(grant && tokenDigest && input.handoff
1029
- && input.handoff.sessionId === input.current?.sessionId
1030
- && grant.schemaVersion === 1
1031
- && grant.kind === 'direct-yolo-setup-handoff'
1032
- && grant.storyId === input.storyId
1033
- && grant.fromSessionId === input.current?.sessionId
1034
- && grant.bundleDigest === input.bundleDigest
1035
- && grant.claimRevision === input.claimRevision
1036
- && typeof grant.expiresAt === 'string'
1037
- && Date.parse(grant.expiresAt) > Date.parse(input.nowIso)
1038
- && typeof grant.tokenDigest === 'string'
1039
- && /^[a-f0-9]{64}$/.test(grant.tokenDigest)
1040
- && safeDigestEqual(grant.tokenDigest, tokenDigest));
1041
- }
1042
- function safeDigestEqual(left, right) {
1043
- const leftBytes = Buffer.from(left, 'hex');
1044
- const rightBytes = Buffer.from(right, 'hex');
1045
- return leftBytes.length === 32 && rightBytes.length === 32 && timingSafeEqual(leftBytes, rightBytes);
1046
- }
1047
- function sameLease(left, right) {
1048
- return Boolean(left && ['schemaVersion', 'ownerStoryId', 'sessionId', 'idempotencyKey', 'acquiredAt', 'expiresAt']
1049
- .every((key) => left[key] === right[key]));
1050
- }
1051
- async function releaseOwnedDirectLease(projectRoot, epicId, expected) {
1052
- await mutateGitLifecycleRuntime(projectRoot, (document) => {
1053
- const epic = ensureRuntimeEpic(document, epicId);
1054
- const current = asRecord(epic.branch_lease);
1055
- if (sameLease(current, expected))
1056
- epic.branch_lease = null;
1057
- });
1058
- }
1059
- async function cleanupOwnedDirectAuthority(authority, storyId) {
1060
- const runtime = await readGitLifecycleRuntime(authority.projectRoot, { required: true });
1061
- const lease = asRecord(runtime.epics[authority.epicId]?.branch_lease);
1062
- if (!lease || lease.ownerStoryId !== storyId || !safeTokenValue(lease.sessionId)
1063
- || !safeTokenValue(lease.idempotencyKey) || !Number.isFinite(Date.parse(String(lease.acquiredAt)))
1064
- || !Number.isFinite(Date.parse(String(lease.expiresAt))))
1065
- return;
1066
- const physical = await acquireGitLifecyclePhysicalIntegrationLock(authority.projectRoot, {
1067
- kind: 'direct', storyId, sessionId: String(lease.sessionId), idempotencyKey: String(lease.idempotencyKey),
1068
- createdAt: String(lease.acquiredAt), expiresAt: String(lease.expiresAt), persistent: true,
1069
- });
1070
- if (!physical.ok)
1071
- return;
1072
- const released = await releaseGitLifecyclePhysicalIntegrationLock(physical.path, physical.owner);
1073
- if (released)
1074
- await releaseOwnedDirectLease(authority.projectRoot, authority.epicId, lease);
1075
- }
1076
- async function runQa(options, projectRoot, headSha, graphRevision) {
1077
- const transientAuthority = await executeP205TransientAuthority(projectRoot, {
1078
- headSha,
1079
- graphRevision,
1080
- nowIso: options.nowIso,
1081
- });
1082
- await options.qaRunner?.({ projectRoot, storyId: options.storyId, headSha });
1083
- const evidence = projectP205TransientExecutionEvidence(transientAuthority);
1084
- if (!evidence)
1085
- throw new Error('p205-transient-authority-invalid');
1086
- return {
1087
- contract: 'P205-CI-02', command: P205_COMPLETE_COMMAND,
1088
- manifestDigest: evidence.manifestDigest, gateIds: evidence.gateIds,
1089
- resultDigest: evidence.resultDigest, transientAuthority,
1090
- };
1091
- }
1092
- async function physicalCheckoutUnchanged(projectRoot, executionBranch, headSha, committedPaths) {
1093
- if (await git(projectRoot, ['rev-parse', 'HEAD']) !== headSha
1094
- || await git(projectRoot, ['symbolic-ref', '--quiet', '--short', 'HEAD']) !== executionBranch
1095
- || !(await gitExitOk(projectRoot, ['diff', '--cached', '--quiet'])))
1096
- return false;
1097
- return gitExitOk(projectRoot, ['diff', '--quiet', 'HEAD', '--', ...committedPaths]);
1098
- }
1099
- async function remoteHeadIsExact(projectRoot, remoteInput, branch, headSha) {
1100
- const remote = safeRemote(remoteInput);
1101
- const remoteHead = (await git(projectRoot, ['ls-remote', '--heads', remote, `refs/heads/${branch}`])).split(/\s+/)[0];
1102
- return remoteHead === headSha;
1103
- }
1104
- function validateDirectWriteSet(paths, authorizedWriteSet, storyId, rootMemoryEffect) {
1105
- if (paths.length === 0 || paths.some((path) => !safePath(path))) {
1106
- return { ok: false, reasonCode: 'direct-yolo-write-set-invalid' };
1107
- }
1108
- const required = [
1109
- '.neocortex/state.json', `docs/stories/${storyId}.story.md`,
1110
- `.neocortex/stories/${storyId}/qa-gate-result.json`,
1111
- `.neocortex/stories/${storyId}/validation-evidence.md`,
1112
- `.neocortex/stories/${storyId}/review.md`,
1113
- ];
1114
- if (!required.every((path) => paths.includes(path)) || !required.every((path) => authorizedWriteSet.includes(path))) {
1115
- return { ok: false, reasonCode: 'direct-yolo-write-set-invalid' };
1116
- }
1117
- if (rootMemoryEffect && (rootMemoryEffect.storyId !== storyId
1118
- || rootMemoryEffect.childClaimRevision !== digest([...authorizedWriteSet].sort().join('\0')))) {
1119
- return { ok: false, reasonCode: 'direct-yolo-root-memory-claim-mismatch' };
1120
- }
1121
- if (paths.some((path) => !authorizedWriteSet.includes(path) && !rootMemoryEffect?.files.some(file => file.path === path))) {
1122
- return { ok: false, reasonCode: 'direct-yolo-write-set-unauthorized' };
1123
- }
1124
- return { ok: true };
1125
- }
1126
- function safePath(value) {
1127
- return value.length > 0 && value.length <= 1024 && !value.startsWith('/') && !value.includes('\\')
1128
- && !value.split('/').includes('..') && !value.startsWith('.git/') && !value.includes('\0');
1129
- }
1130
- function safeTokenValue(value) {
1131
- return typeof value === 'string' && value.length > 0 && value.length <= 512
1132
- && /^[A-Za-z0-9._:@/+\-=]+$/.test(value);
1133
- }
1134
- function safeRef(value) {
1135
- return value.length > 0 && value.length <= 240 && !value.startsWith('-') && !/[\s~^:?*[\\]/.test(value) && !value.includes('..');
1136
- }
1137
- function safeRemote(value) {
1138
- if (!/^[A-Za-z0-9._-]{1,128}$/.test(value))
1139
- throw new Error('direct-yolo-remote-invalid');
1140
- return value;
1141
- }
1142
- function trailer(body, key) {
1143
- const prefix = `${key}: `;
1144
- const values = body.split('\n').map((line) => line.trim()).filter((line) => line.startsWith(prefix)).map((line) => line.slice(prefix.length));
1145
- return values.length === 1 ? values[0] : undefined;
1146
- }
1147
- function digest(value) { return createHash('sha256').update(value).digest('hex'); }
1148
- function splitNull(value) { return value.split('\0').filter(Boolean); }
1149
- function normalizedTime(value) {
1150
- return value && Number.isFinite(Date.parse(value)) ? new Date(Date.parse(value)).toISOString() : new Date().toISOString();
1151
- }
1152
- function asRecord(value) {
1153
- return value && typeof value === 'object' && !Array.isArray(value) ? value : undefined;
1154
- }
1155
- function validOriginBinding(value, executionBranch, originBranch) {
1156
- const item = asRecord(value);
1157
- if (!item || item.schemaVersion !== 1 || item.originBranch !== originBranch
1158
- || item.executionBranch !== executionBranch || item.capturedFromCurrentCheckout !== true
1159
- || typeof item.originHeadSha !== 'string' || !/^[a-f0-9]{40,64}$/.test(item.originHeadSha)
1160
- || typeof item.capturedAt !== 'string' || !Number.isFinite(Date.parse(item.capturedAt)))
1161
- return undefined;
1162
- return item;
1163
- }
1164
- function setupReceipt(authority, binding, leaseExpiresAt, reused) {
1165
- return Object.freeze({
1166
- schemaVersion: 1, kind: 'direct-yolo-setup-receipt', storyId: authority.storyId,
1167
- epicId: authority.epicId, executionBranch: authority.executionBranch, originBranch: authority.originBranch,
1168
- originHeadSha: binding.originHeadSha, leaseExpiresAt, reused, publicSafe: true,
1169
- });
1170
- }
1171
- async function checkoutPayload(projectRoot) {
1172
- const untrackedPaths = splitNull(await gitRaw(projectRoot, [
1173
- 'ls-files', '--others', '--exclude-standard', '-z',
1174
- ])).sort();
1175
- const untrackedBlobs = await Promise.all(untrackedPaths.map(async (path) => {
1176
- if (!safePath(path))
1177
- throw new Error('direct-yolo-checkout-payload-invalid');
1178
- return `${path}\0${await git(projectRoot, ['hash-object', '--no-filters', '--', path])}`;
1179
- }));
1180
- return digest([
1181
- await gitRaw(projectRoot, ['diff', '--cached', '--binary', '--full-index']),
1182
- await gitRaw(projectRoot, ['diff', '--binary', '--full-index']),
1183
- await gitRaw(projectRoot, ['status', '--porcelain=v1', '-z', '--untracked-files=all']),
1184
- ...untrackedBlobs,
1185
- ].join('\0'));
1186
- }
1187
- /** Physical observation for a private YOLO agent step; this is not actor attribution. */
1188
- export async function captureDirectYoloCheckoutEvidence(projectRootInput) {
1189
- const projectRoot = resolve(projectRootInput);
1190
- const headSha = await git(projectRoot, ['rev-parse', 'HEAD']);
1191
- const branch = await git(projectRoot, ['symbolic-ref', '--quiet', '--short', 'HEAD']);
1192
- const payloadDigest = await checkoutPayload(projectRoot);
1193
- if (await git(projectRoot, ['rev-parse', 'HEAD']) !== headSha
1194
- || await git(projectRoot, ['symbolic-ref', '--quiet', '--short', 'HEAD']) !== branch) {
1195
- throw new Error('direct-yolo-checkout-observation-raced');
1196
- }
1197
- return Object.freeze({ headSha, branch, payloadDigest });
1198
- }
1199
- async function worktreeBranches(projectRoot) {
1200
- const records = [];
1201
- let current;
1202
- for (const line of (await gitRaw(projectRoot, ['worktree', 'list', '--porcelain'])).split('\n')) {
1203
- if (line.startsWith('worktree ')) {
1204
- current = { path: line.slice('worktree '.length) };
1205
- records.push(current);
1206
- }
1207
- else if (current && line.startsWith('branch refs/heads/')) {
1208
- current.branch = line.slice('branch refs/heads/'.length);
1209
- }
1210
- }
1211
- return records;
1212
- }
1213
- function publicReason(error, fallback) {
1214
- if (error instanceof Error && error.message === 'git-lifecycle-runtime-revision-conflict') {
1215
- return 'direct-yolo-runtime-revision-conflict';
1216
- }
1217
- return error instanceof Error && /^direct-yolo-[a-z0-9-]+$/.test(error.message) ? error.message : fallback;
1218
- }
1219
- function failure(reasonCode) { return { ok: false, reasonCode }; }
1220
- async function git(cwd, args) {
1221
- return String((await execFileAsync('git', [...args], { cwd, encoding: 'utf8', maxBuffer: 32 * 1024 * 1024 })).stdout).trim();
1222
- }
1223
- async function gitRaw(cwd, args) {
1224
- return String((await execFileAsync('git', [...args], { cwd, encoding: 'utf8', maxBuffer: 32 * 1024 * 1024 })).stdout);
1225
- }
1226
- async function gitExitOk(cwd, args) {
1227
- try {
1228
- await execFileAsync('git', [...args], { cwd, maxBuffer: 8 * 1024 * 1024 });
1229
- return true;
1230
- }
1231
- catch {
1232
- return false;
1233
- }
1234
- }
1
+ import{execFile as He}from"node:child_process";import{createHash as Le,randomBytes as me,timingSafeEqual as Ce}from"node:crypto";import{readFile as Z}from"node:fs/promises";import{join as te,resolve as M}from"node:path";import{promisify as Oe}from"node:util";import{readRootMemoryEffectForCheckpoint as qe}from"../private-execution/root-memory-scope.js";import{rootMemoryEffectDigest as $e,verifyRootMemoryPhysicalWriteSet as re}from"../private-execution/root-memory-git-evidence.js";import{acquireGitLifecyclePhysicalIntegrationLock as V,applyGitLifecycleRuntimeOverlay as Pe,computeImmutableStoryQaEvidenceDigest as ze,computeGitLifecyclePhysicalIntegrationLockOwnerDigest as we,computeP205StoryQaEvidenceDigest as Ne,computeLegacyDirectYoloRecoveryReceiptRef as Ke,ensureRuntimeEpic as N,ensureRuntimeStory as Re,ensureGitLifecyclePrivateChildDirectory as Me,executeP205TransientAuthority as Ve,projectP205TransientExecutionEvidence as We,publicStoryReviewArtifactFromGit as Q,LEGACY_DIRECT_YOLO_RECOVERY_COMMITS as Ge,mutateGitLifecycleRuntime as P,P205_COMPLETE_COMMAND as Fe,readGitLifecycleRuntime as C,gitLifecycleRuntimeDirectory as Te,assertGitLifecyclePrivateFile as Ie,releaseGitLifecyclePhysicalIntegrationLock as W,releaseGitLifecyclePhysicalIntegrationLockForCheckpoint as Je,verifyGitLifecyclePhysicalIntegrationLock as U,verifyAcceptedGitLifecycleStoryCheckpoint as ve}from"./git-lifecycle-store.js";import{anchoredAtomicWriteFile as Ye}from"./secure-filesystem-boundary.js";import{GIT_LIFECYCLE_COMMIT_PUSH_STEP_ID as Ze,GIT_LIFECYCLE_REVIEW_PR_STEP_ID as xe,GIT_LIFECYCLE_SETUP_BRANCH_STEP_ID as X}from"./git-lifecycle-step-ids.js";const oe=Oe(He),ie=3600*1e3;async function gt(e){let t,o=!1;try{const r=await Se(e.projectRoot,e.storyId),i=K(e.nowIso),n=Date.parse(i),g=await y(r.projectRoot,["symbolic-ref","--quiet","--short","HEAD"]),s=await y(r.projectRoot,["rev-parse","HEAD"]),h=await C(r.projectRoot,{required:!0}),c=w(h.epics[r.epicId]),d=w(c?.branch_lease),l=e.expiredSetupRecovery;if(l&&!ot(l,h.revision,d,e.storyId,n))return a("direct-yolo-expired-setup-recovery-invalid");const u=d&&Number.isFinite(Date.parse(String(d.expiresAt)))&&Date.parse(String(d.expiresAt))>n;if(u&&d?.ownerStoryId!==e.storyId)return a("direct-yolo-writer-active");let m=he(c?.origin_binding,r.executionBranch,r.originBranch);if(g===r.executionBranch){if(!m)return a("direct-yolo-origin-binding-invalid")}else{if(g!==r.originBranch)return a("direct-yolo-current-branch-mismatch");if(m&&m.originHeadSha!==s){if(!l||!await B(r.projectRoot,["merge-base","--is-ancestor",m.originHeadSha,s]))return a("direct-yolo-origin-binding-conflict");const p=`refs/heads/${r.executionBranch}`;if(!await B(r.projectRoot,["show-ref","--verify","--quiet",p]))return a("direct-yolo-execution-branch-missing");const I=await y(r.projectRoot,["rev-parse",p]);if(!await B(r.projectRoot,["merge-base","--is-ancestor",I,s]))return a("direct-yolo-execution-branch-diverged")}if(m??={schemaVersion:1,originBranch:r.originBranch,originHeadSha:s,executionBranch:r.executionBranch,capturedFromCurrentCheckout:!0,capturedAt:i},(await ye(r.projectRoot)).some(p=>p.branch===r.executionBranch&&M(p.path)!==r.projectRoot))return a("direct-yolo-execution-branch-checked-out-elsewhere");const f=await q(r.projectRoot),R=`refs/heads/${r.executionBranch}`;if(await B(r.projectRoot,["show-ref","--verify","--quiet",R])){const p=await y(r.projectRoot,["rev-parse",R]);if(p!==s){if(!await B(r.projectRoot,["merge-base","--is-ancestor",p,s]))return a("direct-yolo-execution-branch-diverged");l||await y(r.projectRoot,["update-ref",R,s,p])}l?t={checkoutBefore:f,executionRef:R,executionBranch:r.executionBranch,executionHead:p,originBranch:r.originBranch,originHead:s}:await y(r.projectRoot,["switch",r.executionBranch])}else{if(l)return a("direct-yolo-execution-branch-missing");await y(r.projectRoot,["switch","-c",r.executionBranch])}if(!t&&await q(r.projectRoot)!==f)return a("direct-yolo-checkout-payload-raced")}const S=t?.originHead??await y(r.projectRoot,["rev-parse","HEAD"]);if(!t&&await y(r.projectRoot,["symbolic-ref","--quiet","--short","HEAD"])!==r.executionBranch)return a("direct-yolo-current-branch-mismatch");if(u&&d?.ownerStoryId===e.storyId)return d.setupHeadSha!==S?a("direct-yolo-setup-lease-head-unbound"):{ok:!0,value:pe(r,m,String(d.expiresAt),!0)};const v=i,x=new Date(n+3600*1e3).toISOString(),b=`setup-${me(32).toString("hex")}`,D=l?h:await C(r.projectRoot,{required:!0});return await P(r.projectRoot,A=>{const f=N(A,r.epicId),R=w(f.branch_lease);if(l&&JSON.stringify(R)!==JSON.stringify(l.expectedLease))throw new Error("direct-yolo-expired-setup-recovery-raced");if(R&&Date.parse(String(R.expiresAt))>n&&R.ownerStoryId!==e.storyId)throw new Error("direct-yolo-writer-active");const p=he(f.origin_binding,r.executionBranch,r.originBranch);if(f.origin_binding&&!p)throw new Error("direct-yolo-origin-binding-conflict");if(p&&JSON.stringify(p)!==JSON.stringify(m))throw new Error("direct-yolo-origin-binding-conflict");f.branch_mode="epic",f.execution_branch=r.executionBranch,f.origin_branch=r.originBranch,f.graph_revision=r.graphRevision,f.origin_binding=m,f.branch_lease={schemaVersion:1,ownerStoryId:e.storyId,sessionId:b,idempotencyKey:b,acquiredAt:v,expiresAt:x,setupHeadSha:S}},{expectedRevision:l?.expectedRuntimeRevision??D.revision,preCommit:async()=>{if(t){if((await ye(r.projectRoot)).some(f=>f.branch===r.executionBranch&&M(f.path)!==r.projectRoot)||await y(r.projectRoot,["symbolic-ref","--quiet","--short","HEAD"])!==r.originBranch||await y(r.projectRoot,["rev-parse","HEAD"])!==t.originHead||await q(r.projectRoot)!==t.checkoutBefore||!await B(r.projectRoot,["show-ref","--verify","--quiet",t.executionRef])||await y(r.projectRoot,["rev-parse",t.executionRef])!==t.executionHead)throw new Error("direct-yolo-expired-setup-recovery-raced");if(o=!0,t.executionHead!==t.originHead&&await y(r.projectRoot,["update-ref",t.executionRef,t.originHead,t.executionHead]),await y(r.projectRoot,["switch",r.executionBranch]),await q(r.projectRoot)!==t.checkoutBefore)throw new Error("direct-yolo-checkout-payload-raced")}if(await y(r.projectRoot,["symbolic-ref","--quiet","--short","HEAD"])!==r.executionBranch||await y(r.projectRoot,["rev-parse","HEAD"])!==S)throw new Error("direct-yolo-setup-physical-state-raced")}}),{ok:!0,value:pe(r,m,x,!1)}}catch(r){return o&&t&&await it(M(e.projectRoot),t).catch(()=>{}),a(Y(r,"direct-yolo-setup-failed"))}}async function mt(e){let t;try{const o=await F(e.projectRoot,e.storyId),r=K(e.nowIso),i=te(o.projectRoot,".neocortex/state.json"),n=await Z(i,"utf8"),g=await q(o.projectRoot),s=await y(o.projectRoot,["rev-parse","HEAD"]),h=`refs/heads/${o.executionBranch}`,c=await C(o.projectRoot,{required:!0}),d=w(c.epics[o.epicId]),l=he(d?.origin_binding,o.executionBranch,o.originBranch),u=w(d?.branch_lease);if(!l)return a("direct-yolo-origin-binding-invalid");if(!u||!ne(u,e.storyId,r)||u?.sessionId!==u?.idempotencyKey)return a("direct-yolo-setup-lease-invalid");const m=await V(o.projectRoot,{kind:"direct",storyId:e.storyId,sessionId:String(u.sessionId),idempotencyKey:String(u.idempotencyKey),expiresAt:new Date(Date.now()+300*1e3).toISOString(),persistent:!1});if(!m.ok)return a(m.reasonCode);if(t=m,s!==u.setupHeadSha||!await B(o.projectRoot,["merge-base","--is-ancestor",l.originHeadSha,s])||await y(o.projectRoot,["rev-parse",h])!==s)return a("direct-yolo-setup-head-mismatch");if((await ye(o.projectRoot)).some(f=>f.branch===o.executionBranch&&M(f.path)!==o.projectRoot))return a("direct-yolo-execution-branch-checked-out-elsewhere");const v=w(JSON.parse(n)),x=w(w(v?.stories)?.[e.storyId]),b=w(c.stories[e.storyId]);if(!x||!["backlog","ready_to_start"].includes(String(x.status))||x.steps_completed!==void 0&&(!Array.isArray(x.steps_completed)||x.steps_completed.length>0)||x.branch_name||x.checkpoint)return a("direct-yolo-setup-story-progress-conflict");const D=b?.status==="branch-ready"&&b.last_step===X&&Array.isArray(b.steps_completed)&&b.steps_completed.length===1&&b.steps_completed[0]===X&&Object.keys(b).sort().join(",")==="last_step,status,steps_completed";if(b&&Object.keys(b).length>0&&!D)return a("direct-yolo-setup-story-progress-conflict");const A=async()=>{if(Date.parse(String(u.expiresAt))<=(e.nowIso?Date.parse(r):Date.now())||!t||!await U(t.path,t.owner)||await Z(i,"utf8")!==n||await q(o.projectRoot)!==g||await y(o.projectRoot,["symbolic-ref","--quiet","--short","HEAD"])!==o.executionBranch||await y(o.projectRoot,["rev-parse","HEAD"])!==s||!await B(o.projectRoot,["merge-base","--is-ancestor",l.originHeadSha,s])||await y(o.projectRoot,["rev-parse",h])!==s)throw new Error("direct-yolo-setup-physical-state-raced")};return D?await A():await P(o.projectRoot,f=>{const R=w(f.epics[o.epicId]),p=Re(f,e.storyId);if(JSON.stringify(R?.branch_lease)!==JSON.stringify(u)||JSON.stringify(R?.origin_binding)!==JSON.stringify(l)||R?.graph_revision!==o.graphRevision||Object.keys(p).length>0)throw new Error("direct-yolo-setup-runtime-raced");p.status="branch-ready",p.last_step=X,p.steps_completed=[X]},{expectedRevision:c.revision,preCommit:A}),{ok:!0,value:Object.freeze({...pe(o,l,String(u.expiresAt),D),checkpointAccepted:!0,graphRevision:o.graphRevision,headSha:s})}}catch(o){return a(Y(o,"direct-yolo-setup-acceptance-failed"))}finally{t&&await W(t.path,t.owner).catch(()=>{})}}async function wt(e){try{const t=await F(e.projectRoot,e.storyId),o=O(await j(t.projectRoot,["diff","--cached","--name-only","-z"])),r=T(o,t.authorizedWriteSet,e.storyId,t.rootMemoryEffect);if(!r.ok)return a(r.reasonCode);if(t.rootMemoryEffect&&re(t.projectRoot,t.rootMemoryEffect,{storyId:e.storyId,executionBranch:t.executionBranch,baseSha:await y(t.projectRoot,["rev-parse","HEAD"]),headRef:":",childPaths:t.authorizedWriteSet}),Q(t.projectRoot,e.storyId,":")?.verdict!=="approved")return a("direct-yolo-review-artifact-not-approved");if(O(await j(t.projectRoot,["diff","--name-only","-z"])).some(I=>o.includes(I)))return a("direct-yolo-user-overlap");const g=await j(t.projectRoot,["diff","--cached","--binary","--full-index"]);if(!g)return a("direct-yolo-staged-diff-empty");const s=k(g),h=k([...t.authorizedWriteSet].sort().join("\0")),c=K(e.nowIso),d=await C(t.projectRoot,{required:!0}),l=w(d.epics[t.epicId]?.branch_lease);if(!ne(l,e.storyId,c))return a("direct-yolo-setup-handoff-owner-invalid");const u=String(l.sessionId),m=String(l.expiresAt),S=new Date(Math.min(Date.parse(m),Date.parse(c)+300*1e3)).toISOString(),v=await G(t.projectRoot,e.storyId),x=v?.state==="issued"&&v.fromSessionId===u&&v.bundleDigest===s&&v.claimRevision===h&&Date.parse(v.expiresAt)>Date.parse(c),b=x?v.expiresAt:S,D=x?v.token:me(32).toString("base64url"),A=k(D),f=Object.freeze({schemaVersion:1,kind:"direct-yolo-setup-handoff",storyId:e.storyId,fromSessionId:u,tokenDigest:A,bundleDigest:s,claimRevision:h,expiresAt:b}),R=k(JSON.stringify(f)),p=await V(t.projectRoot,{kind:"direct",storyId:e.storyId,sessionId:u,idempotencyKey:u,expiresAt:b,persistent:!1,createdAt:c});if(!p.ok)return a(p.reasonCode==="git-lifecycle-integration-backpressure"?"direct-yolo-integration-backpressure":"direct-yolo-integration-lock-failed");try{await ae(t.projectRoot,{schemaVersion:1,kind:"direct-yolo-setup-handoff-token",state:"issued",storyId:e.storyId,fromSessionId:u,token:D,tokenDigest:A,bundleDigest:s,claimRevision:h,grantDigest:R,expiresAt:b}),await P(t.projectRoot,I=>{const E=w(N(I,t.epicId).branch_lease);if(!ne(E,e.storyId,c)||E?.sessionId!==u||E.idempotencyKey!==u)throw new Error("direct-yolo-setup-handoff-owner-raced");E.handoffGrant=f},{expectedRevision:d.revision,preCommit:async()=>{if(!await U(p.path,p.owner))throw new Error("direct-yolo-integration-lock-raced");const I=await G(t.projectRoot,e.storyId);if(!I||I.state!=="issued"||I.grantDigest!==R||I.tokenDigest!==A)throw new Error("direct-yolo-setup-handoff-token-raced")}})}finally{await W(p.path,p.owner).catch(()=>{})}return{ok:!0,value:Object.freeze({schemaVersion:1,kind:"direct-yolo-setup-handoff-receipt",storyId:e.storyId,sessionId:u,bundleDigest:s,claimRevision:h,grantDigest:R,expiresAt:b,reused:x,publicSafe:!0})}}catch(t){return a(t instanceof Error&&/^direct-yolo-/.test(t.message)?t.message:"direct-yolo-setup-handoff-failed")}}async function Rt(e){try{const t=await F(e.projectRoot,e.storyId),o=await y(t.projectRoot,["rev-parse","HEAD"]),r=O(await j(t.projectRoot,["diff","--cached","--name-only","-z"])),i=T(r,t.authorizedWriteSet,e.storyId,t.rootMemoryEffect);if(!i.ok)return a(i.reasonCode);t.rootMemoryEffect&&re(t.projectRoot,t.rootMemoryEffect,{storyId:e.storyId,executionBranch:t.executionBranch,baseSha:o,headRef:":",childPaths:t.authorizedWriteSet});const n=Q(t.projectRoot,e.storyId,":");if(n?.verdict!=="approved")return a("direct-yolo-review-artifact-not-approved");if(O(await j(t.projectRoot,["diff","--name-only","-z"])).some(f=>r.includes(f)))return a("direct-yolo-user-overlap");const s=await j(t.projectRoot,["diff","--cached","--binary","--full-index"]);if(!s)return a("direct-yolo-staged-diff-empty");const h=k(s),c=k([...t.authorizedWriteSet].sort().join("\0")),d=k(["direct-yolo-commit-v1",e.storyId,t.epicId,t.executionBranch,o,h,t.graphRevision,c].join("\0")),l=K(e.nowIso);let u=e.setupLeaseHandoff??await Qe(t.projectRoot,e.storyId,h,c,l);const m=await C(t.projectRoot,{required:!0}),S=w(m.epics[t.epicId]?.branch_lease),v=S?.ownerStoryId===e.storyId&&S.sessionId===d&&S.idempotencyKey===d&&typeof S.expiresAt=="string"&&Date.parse(S.expiresAt)>Date.parse(l)?S:void 0;if(v&&e.setupLeaseHandoff)return a("direct-yolo-setup-handoff-replayed");const x=w(v?.handoffConsumption);if(x){const f=await G(t.projectRoot,e.storyId);if(!f||!["consuming","consumed"].includes(f.state)||f.fromSessionId!==x.fromSessionId||f.tokenDigest!==x.tokenDigest||f.bundleDigest!==h||f.claimRevision!==c||x.bundleDigest!==h||x.claimRevision!==c||f.consumedBySessionId!==d||x.consumedBySessionId!==d)return a("direct-yolo-setup-handoff-consume-invalid");if(f.state==="consuming"){if(!f.token||k(f.token)!==f.tokenDigest)return a("direct-yolo-setup-handoff-consume-invalid");u={sessionId:f.fromSessionId,token:f.token}}}const b=v?String(v.expiresAt):new Date(Date.parse(l)+ie).toISOString(),D=await V(t.projectRoot,{kind:"direct",storyId:e.storyId,sessionId:d,idempotencyKey:d,expiresAt:b,persistent:!0,createdAt:v&&typeof v.acquiredAt=="string"?v.acquiredAt:l});if(!D.ok)return a(D.reasonCode==="git-lifecycle-integration-backpressure"?"direct-yolo-integration-backpressure":"direct-yolo-integration-lock-failed");try{u&&e.setupLeaseHandoff===void 0&&(await Ue(t.projectRoot,e.storyId,u.sessionId,u.token,d,l),await e.setupHandoffFault?.("after-consuming-persisted")),await P(t.projectRoot,f=>{const R=N(f,t.epicId),p=w(R.branch_lease),I=p&&Date.parse(String(p.expiresAt))>Date.parse(l),E=p?.ownerStoryId===e.storyId&&p.sessionId===d&&p.idempotencyKey===d,$=p?.ownerStoryId===e.storyId&&p.sessionId===p.idempotencyKey&&typeof p.idempotencyKey=="string"&&/^setup-[a-f0-9]{64}$/.test(p.idempotencyKey),_=I&&$&&et({current:p,handoff:u,storyId:e.storyId,bundleDigest:h,claimRevision:c,nowIso:l});if(u&&I&&!E&&!_)throw new Error("direct-yolo-setup-handoff-invalid");if(I&&!E&&!_)throw new Error("direct-yolo-writer-active");const H=_?w(p?.handoffGrant):void 0,L=H?{schemaVersion:1,kind:"direct-yolo-setup-handoff-consumption",state:"consumed",storyId:e.storyId,fromSessionId:String(p?.sessionId),tokenDigest:String(H.tokenDigest),bundleDigest:h,claimRevision:c,consumedBySessionId:d,consumedAt:l}:void 0;R.branch_mode="epic",R.execution_branch=t.executionBranch,R.origin_branch=t.originBranch,R.graph_revision=t.graphRevision,R.branch_lease=I&&E?p:{schemaVersion:1,ownerStoryId:e.storyId,sessionId:d,idempotencyKey:d,acquiredAt:l,expiresAt:b,...L?{handoffConsumption:L}:{}}},{expectedRevision:m.revision,preCommit:async()=>{if(!await U(D.path,D.owner))throw new Error("direct-yolo-integration-lock-raced")}})}catch(f){throw await W(D.path,D.owner).catch(()=>{}),f}u&&e.setupLeaseHandoff===void 0&&(await e.setupHandoffFault?.("before-consumed-persist"),await Xe(t.projectRoot,t.epicId,e.storyId,u.sessionId,u.token,h,c,d,l));const A=Object.freeze([`Neocortex-Story: ${e.storyId}`,`Neocortex-Epic: ${t.epicId}`,`Neocortex-Execution-Branch: ${t.executionBranch}`,`Neocortex-Prepared-Base: ${o}`,`Neocortex-Bundle-Digest: ${h}`,`Neocortex-Review-Artifact-Digest: ${n.digest}`,`Neocortex-Graph-Revision: ${t.graphRevision}`,`Neocortex-Claim-Revision: ${c}`,...t.rootMemoryEffect?[`Neocortex-Root-Memory-Effect: ${$e(t.rootMemoryEffect)}`]:[]]);return{ok:!0,value:Object.freeze({schemaVersion:1,storyId:e.storyId,epicId:t.epicId,executionBranch:t.executionBranch,baseSha:o,bundleDigest:h,reviewArtifactDigest:n.digest,graphRevision:t.graphRevision,claimRevision:c,idempotencyKey:d,changedPaths:Object.freeze(r),trailers:A})}}catch(t){return a(Y(t,"direct-yolo-begin-failed"))}}async function It(e){let t,o,r;try{const i=await F(e.projectRoot,e.storyId);t=i;const n=await y(i.projectRoot,["rev-parse","HEAD"]);let g=await C(i.projectRoot,{required:!0});const s=w(g.stories[e.storyId]?.checkpoint);if(s){const _=typeof s.headSha=="string"?s.headSha:"",H=typeof s.graphRevision=="string"?s.graphRevision:"",L=/^[a-f0-9]{40,64}$/.test(_)?O(await j(i.projectRoot,["diff-tree","--no-commit-id","--name-only","-r","-z",_])):[],z=T(L,i.authorizedWriteSet,e.storyId,i.rootMemoryEffect);if(!(/^[a-f0-9]{40,64}$/.test(_)&&await B(i.projectRoot,["merge-base","--is-ancestor",_,n]))||!/^[a-f0-9]{64}$/.test(H)||!z.ok)return a("direct-yolo-replay-invalid");const _e=await de(e,i.projectRoot,n,H);if(!await ve(i.projectRoot,s,e.storyId,i.epicId,i.executionBranch,_e.transientAuthority,i.authorizedWriteSet))return a("direct-yolo-replay-invalid");if(!await fe(i.projectRoot,i.executionBranch,n,L))return a("direct-yolo-replay-head-raced");if(e.push&&!await De(i.projectRoot,e.remote??"origin",i.executionBranch,_))return a("direct-yolo-remote-head-mismatch");const le=Array.isArray(s.receiptRefs)?s.receiptRefs.find(ge=>typeof ge=="string"&&/^physical-lock-owner:[a-f0-9]{64}$/.test(ge)):void 0;return le&&typeof s.idempotencyKey=="string"&&await Je(i.projectRoot,{storyId:e.storyId,idempotencyKey:s.idempotencyKey,ownerDigest:le.slice(20)}),{ok:!0,value:s}}const h=n,c=await y(i.projectRoot,["rev-parse",`${h}^`]),d=await j(i.projectRoot,["show","--no-patch","--format=%B",h]),l=O(await j(i.projectRoot,["diff-tree","--no-commit-id","--name-only","-r","-z",h])),u=T(l,i.authorizedWriteSet,e.storyId,i.rootMemoryEffect);if(!u.ok)return a(u.reasonCode);i.rootMemoryEffect&&re(i.projectRoot,i.rootMemoryEffect,{storyId:e.storyId,executionBranch:i.executionBranch,baseSha:c,headRef:h,childPaths:i.authorizedWriteSet});const m=J(d,"Neocortex-Bundle-Digest"),S=J(d,"Neocortex-Graph-Revision"),v=J(d,"Neocortex-Claim-Revision"),x=new Map([["Neocortex-Story",e.storyId],["Neocortex-Epic",i.epicId],["Neocortex-Execution-Branch",i.executionBranch],["Neocortex-Prepared-Base",c],["Neocortex-Graph-Revision",i.graphRevision],["Neocortex-Claim-Revision",k([...i.authorizedWriteSet].sort().join("\0"))]]);if(!m||!/^[a-f0-9]{64}$/.test(m)||!S||!v||[...x].some(([_,H])=>J(d,_)!==H))return a("direct-yolo-trailers-invalid");const b=Q(i.projectRoot,e.storyId,h);if(b?.verdict!=="approved")return a("direct-yolo-review-artifact-not-approved");const D=J(d,"Neocortex-Review-Artifact-Digest");if(D!==void 0&&D!==b.digest)return a("direct-yolo-review-artifact-digest-mismatch");const A=k(await j(i.projectRoot,["diff","--binary","--full-index",`${c}..${h}`]));if(m!==A)return a("direct-yolo-bundle-digest-mismatch");const f=k(["direct-yolo-commit-v1",e.storyId,i.epicId,i.executionBranch,c,m,S,v].join("\0")),R=w(g.epics[i.epicId]?.branch_lease),p=K(e.nowIso),I=R?.ownerStoryId===e.storyId&&R.idempotencyKey===f&&R.sessionId===f&&Date.parse(String(R.expiresAt))>Date.now()?R:void 0;if(R&&Date.parse(String(R.expiresAt))>Date.now()&&!I)return a("direct-yolo-writer-active");const E=I??{schemaVersion:1,ownerStoryId:e.storyId,sessionId:f,idempotencyKey:f,acquiredAt:p,expiresAt:new Date(Date.parse(p)+ie).toISOString()},$=await V(i.projectRoot,{kind:"direct",storyId:e.storyId,sessionId:f,idempotencyKey:f,expiresAt:String(E.expiresAt),persistent:!0,createdAt:String(E.acquiredAt)});if(!$.ok)return a($.reasonCode==="git-lifecycle-integration-backpressure"?"direct-yolo-integration-backpressure":"direct-yolo-integration-lock-failed");r=$,I||(g=(await P(i.projectRoot,H=>{const L=N(H,i.epicId),z=w(L.branch_lease);if(z&&Date.parse(String(z.expiresAt))>Date.parse(p))throw new Error("direct-yolo-writer-active");if(L.graph_revision!==i.graphRevision)throw new Error("direct-yolo-graph-revision-raced");L.branch_lease=E},{expectedRevision:g.revision})).document),o=E;{const _=await de(e,i.projectRoot,h,i.graphRevision);if(!await fe(i.projectRoot,i.executionBranch,h,l))return a("direct-yolo-head-raced");if(e.push){const z=ee(e.remote??"origin");if(await y(i.projectRoot,["push",z,i.executionBranch]),!await Ae(i.projectRoot,z,i.executionBranch,h))return a("direct-yolo-remote-head-mismatch")}const H=await be({projectRoot:i.projectRoot,storyId:e.storyId,epicId:i.epicId,executionBranch:i.executionBranch,baseSha:c,headSha:h,bundleDigest:m,graphRevision:S,claimRevision:v,idempotencyKey:f,qaExecution:_,physicalLockOwnerDigest:we($.owner),nowIso:K(e.nowIso),legacyReceiptRef:void 0,rootMemoryEffect:i.rootMemoryEffect});return await e.beforeCheckpointPersist?.(),await ke({projectRoot:i.projectRoot,checkpoint:H,remote:e.push?e.remote??"origin":void 0,expectedRevision:g.revision,expectedLease:E,expectedGraphRevision:i.graphRevision,physicalLockPath:$.path,physicalLockOwner:$.owner,transientAuthority:_.transientAuthority,authorizedWriteSet:i.authorizedWriteSet})?(await e.afterCheckpointPersist?.(),{ok:!0,value:H}):a("direct-yolo-checkpoint-persist-failed")}}catch(i){return a(Y(i,"direct-yolo-finalize-failed"))}finally{r&&await W(r.path,r.owner).catch(()=>{}),t&&o?await se(t.projectRoot,t.epicId,o).catch(()=>{}):t&&await rt(t,e.storyId).catch(()=>{})}}async function vt(e){try{const t=await F(e.projectRoot,e.storyId);if(!/^[a-f0-9]{40,64}$/.test(e.commitSha))return a("direct-yolo-recovery-commit-invalid");const o=await y(t.projectRoot,["rev-parse",e.commitSha]);if(Ge[e.storyId]!==o)return a("direct-yolo-recovery-not-allowlisted");if(await y(t.projectRoot,["rev-parse","HEAD"])!==o)return a("direct-yolo-recovery-head-mismatch");await y(t.projectRoot,["merge-base","--is-ancestor",o,`refs/heads/${t.executionBranch}`]);const r=await y(t.projectRoot,["rev-parse",`${o}^`]),i=await j(t.projectRoot,["diff","--binary","--full-index",`${r}..${o}`]),n=O(await j(t.projectRoot,["diff-tree","--no-commit-id","--name-only","-r","-z",o]));if(!T(n,t.authorizedWriteSet.length>0?t.authorizedWriteSet:n,e.storyId).ok)return a("direct-yolo-recovery-write-set-invalid");const s=k(i),h=k([...n].sort().join("\0")),c=k(["legacy-direct-yolo-recovery-v1",e.storyId,o].join("\0")),d=K(e.nowIso),l=new Date(Date.parse(d)+ie).toISOString(),u=await C(t.projectRoot,{required:!0}),m=await V(t.projectRoot,{kind:"direct",storyId:e.storyId,sessionId:c,idempotencyKey:c,expiresAt:l,persistent:!0,createdAt:d});if(!m.ok)return a(m.reasonCode==="git-lifecycle-integration-backpressure"?"direct-yolo-recovery-integration-backpressure":"direct-yolo-recovery-integration-lock-failed");const S={schemaVersion:1,ownerStoryId:e.storyId,sessionId:c,idempotencyKey:c,acquiredAt:d,expiresAt:l};let v=!1;try{const x=await P(t.projectRoot,p=>{const I=N(p,t.epicId),E=w(I.branch_lease);if(E&&Date.parse(String(E.expiresAt))>Date.parse(d)&&!ce(E,S))throw new Error("direct-yolo-recovery-writer-active");if(I.graph_revision!==t.graphRevision)throw new Error("direct-yolo-recovery-graph-raced");I.branch_mode="epic",I.execution_branch=t.executionBranch,I.origin_branch=t.originBranch,I.branch_lease=S},{expectedRevision:u.revision});v=!0;const b=await de(e,t.projectRoot,o,t.graphRevision);if(!await fe(t.projectRoot,t.executionBranch,o,n))return a("direct-yolo-recovery-head-raced");if(e.push){const p=ee(e.remote??"origin"),I=(await y(t.projectRoot,["ls-remote","--heads",p,`refs/heads/${t.executionBranch}`])).split(/\s+/)[0];if(!I||!await B(t.projectRoot,["merge-base","--is-ancestor",o,I]))return a("direct-yolo-recovery-remote-proof-missing")}const D=await be({projectRoot:t.projectRoot,storyId:e.storyId,epicId:t.epicId,executionBranch:t.executionBranch,baseSha:r,headSha:o,bundleDigest:s,graphRevision:t.graphRevision,claimRevision:h,idempotencyKey:c,qaExecution:b,physicalLockOwnerDigest:we(m.owner),nowIso:d,legacyReceiptRef:void 0}),A=await Ke(t.projectRoot,{storyId:e.storyId,epicId:t.epicId,executionBranch:t.executionBranch,baseSha:r,headSha:o,bundleDigest:s,graphRevision:t.graphRevision});if(!A)return a("direct-yolo-recovery-evidence-invalid");const f=Object.freeze({...D,receiptRefs:Object.freeze([...D.receiptRefs,A])});return await ke({projectRoot:t.projectRoot,checkpoint:f,remote:e.push?e.remote??"origin":void 0,remotePolicy:"ancestor-recovery",expectedRevision:x.document.revision,expectedLease:S,expectedGraphRevision:t.graphRevision,physicalLockPath:m.path,physicalLockOwner:m.owner,transientAuthority:b.transientAuthority,authorizedWriteSet:t.authorizedWriteSet})?{ok:!0,value:f}:a("direct-yolo-recovery-persist-failed")}finally{await W(m.path,m.owner).catch(()=>{}),v&&await se(t.projectRoot,t.epicId,S).catch(()=>{})}}catch(t){return a(Y(t,"direct-yolo-recovery-failed"))}}async function Se(e,t){if(!/^[A-Za-z][A-Za-z0-9_-]*\.[A-Za-z0-9._-]+$/.test(t))throw new Error("direct-yolo-story-id-invalid");const o=M(e),r=w(JSON.parse(await Z(te(o,".neocortex/state.json"),"utf8"))),i=await C(o,{required:!0}),n=Pe(r,i),g=w(n.stories),s=w(n.epics),h=w(g?.[t]),c=String(h?.epic_id??t.split(".")[0]),d=w(s?.[c]),l=String(d?.execution_branch??""),u=String(d?.origin_branch??""),m=String(d?.graph_revision??""),S=Array.isArray(h?.files_to_modify)?[...new Set(h.files_to_modify.filter(x=>typeof x=="string"&&ue(x)))].sort():[];if(!h)throw new Error("direct-yolo-story-state-missing");if(d?.branch_mode!=="epic")throw new Error("direct-yolo-branch-mode-invalid");if(!Be(l)||!await B(o,["check-ref-format","--branch",l]))throw new Error("direct-yolo-execution-branch-invalid");if(!Be(u)||!await B(o,["check-ref-format","--branch",u]))throw new Error("direct-yolo-origin-branch-invalid");if(!/^[a-f0-9]{64}$/.test(m))throw new Error("direct-yolo-graph-revision-invalid");const v=await qe({projectRoot:o,storyId:t,graphRevision:m,executionBranch:l,childPaths:S});return{projectRoot:o,storyId:t,story:h,epicId:c,executionBranch:l,originBranch:u,graphRevision:m,authorizedWriteSet:S,rootMemoryEffect:v}}async function F(e,t){const o=await Se(e,t);if(await y(o.projectRoot,["symbolic-ref","--quiet","--short","HEAD"])!==o.executionBranch)throw new Error("direct-yolo-current-branch-mismatch");return o}async function be(e){const t=e.nowIso,o=await j(e.projectRoot,["show","--no-patch","--format=%H%n%P%n%B",e.headSha]),r=O(await j(e.projectRoot,["diff-tree","--no-commit-id","--name-only","-r","-z",e.headSha])),i=ze({storyId:e.storyId,baseSha:e.baseSha,headSha:e.headSha,bundleDigest:e.bundleDigest,graphRevision:e.graphRevision,commitBody:o,changedPaths:r}),n=Ne({gitEvidenceDigest:i,headSha:e.headSha,graphRevision:e.graphRevision,manifestDigest:e.qaExecution.manifestDigest,gateIds:e.qaExecution.gateIds,resultDigest:e.qaExecution.resultDigest}),g=`story-qa:${n}`,s=Object.freeze({schemaVersion:1,kind:"story_qa_evidence",producer:"local-yolo-tdd",evidenceVersion:"p205-execution-v2",storyId:e.storyId,epicId:e.epicId,baseSha:e.baseSha,headSha:e.headSha,bundleDigest:e.bundleDigest,graphRevision:e.graphRevision,contract:e.qaExecution.contract,command:e.qaExecution.command,manifestDigest:e.qaExecution.manifestDigest,gateIds:e.qaExecution.gateIds,resultDigest:e.qaExecution.resultDigest,sourceEvidenceDigest:n,verdict:"passed",checkedAt:t,receiptRef:g}),h=Q(e.projectRoot,e.storyId,e.headSha);if(h?.verdict!=="approved")throw new Error("direct-yolo-review-artifact-not-approved");const c=k(["root-commit-range-review-v2",e.storyId,e.baseSha,e.headSha,e.bundleDigest,e.graphRevision,g,k(o),[...r].sort().join(","),h.digest].join("\0")),d=`story-review:${c}`,l=Object.freeze({schemaVersion:1,kind:"story_review_evidence",producer:"root-commit-range-review",storyId:e.storyId,epicId:e.epicId,baseSha:e.baseSha,headSha:e.headSha,bundleDigest:e.bundleDigest,graphRevision:e.graphRevision,sourceEvidenceDigest:c,reviewArtifactDigest:h.digest,qaReceiptRef:g,verdict:"approved",reviewedAt:t,receiptRef:d}),u=[`.neocortex/stories/${e.storyId}/qa-gate-result.json`,`.neocortex/stories/${e.storyId}/validation-evidence.md`,g,d,...e.physicalLockOwnerDigest?[`physical-lock-owner:${e.physicalLockOwnerDigest}`]:[],...e.legacyReceiptRef?[e.legacyReceiptRef]:[]];return Object.freeze({schemaVersion:1,storyId:e.storyId,epicId:e.epicId,branch:e.executionBranch,baseSha:e.baseSha,headSha:e.headSha,commits:Object.freeze([e.headSha]),idempotencyKey:e.idempotencyKey,bundleDigest:e.bundleDigest,graphRevision:e.graphRevision,claimRevision:e.claimRevision,receiptRefs:Object.freeze(u),qaVerdict:"passed",reviewVerdict:"approved",qaReceipt:s,reviewReceipt:l,recordedAt:t,...e.rootMemoryEffect?{rootMemoryEffect:e.rootMemoryEffect}:{}})}async function ke(e){const{projectRoot:t,checkpoint:o,remote:r}=e;let i=!1;return await P(t,n=>{const g=N(n,o.epicId);if(e.expectedLease){const d=w(g.branch_lease);if(!ce(d,e.expectedLease))throw new Error("direct-yolo-runtime-revision-conflict");if(Date.parse(String(d?.expiresAt))<=Date.now())throw new Error("direct-yolo-writer-lease-expired");if(g.graph_revision!==e.expectedGraphRevision||o.graphRevision!==e.expectedGraphRevision)throw new Error("direct-yolo-graph-revision-raced")}const s=Re(n,o.storyId),h=w(s.checkpoint);if(h&&h.headSha!==o.headSha){i=!0;return}s.checkpoint=o,s.status="done",s.last_step=xe;const c=Array.isArray(s.steps_completed)?s.steps_completed.filter(d=>typeof d=="string"):[];s.steps_completed=[...new Set([...c,Ze,xe])],g.branch_lease=null},{expectedRevision:e.expectedRevision,preCommit:async()=>{if(i)throw new Error("direct-yolo-checkpoint-conflict");if(await y(t,["symbolic-ref","--quiet","--short","HEAD"])!==o.branch||await y(t,["rev-parse","HEAD"])!==o.headSha)throw new Error("direct-yolo-checkpoint-head-raced");if(!await ve(t,o,o.storyId,o.epicId,o.branch,e.transientAuthority,e.authorizedWriteSet))throw new Error("direct-yolo-checkpoint-physical-invalid");if(e.physicalLockPath&&e.physicalLockOwner&&!await U(e.physicalLockPath,e.physicalLockOwner))throw new Error("direct-yolo-integration-lock-raced");if(r&&!(e.remotePolicy==="ancestor-recovery"?await De(t,r,o.branch,o.headSha):await Ae(t,r,o.branch,o.headSha)))throw new Error("direct-yolo-checkpoint-remote-raced")}}),!i}async function De(e,t,o,r){const i=ee(t),n=(await y(e,["ls-remote","--heads",i,`refs/heads/${o}`])).split(/\s+/)[0];return!!(n&&await B(e,["merge-base","--is-ancestor",r,n]))}function ne(e,t,o){return!!(e?.ownerStoryId===t&&e.sessionId===e.idempotencyKey&&typeof e.idempotencyKey=="string"&&/^setup-[a-f0-9]{64}$/.test(e.idempotencyKey)&&typeof e.expiresAt=="string"&&Date.parse(e.expiresAt)>Date.parse(o))}async function je(e,t){const o=await Te(e),r=await Me(o,"handoffs"),i=`direct-${k(t)}.json`;return{parent:r,name:i,path:te(r,i)}}async function ae(e,t){const o=await je(e,t.storyId);await Ye(o.parent,o.name,`${JSON.stringify(t,null,2)}
2
+ `),await Ie(o.path,o.parent)}async function G(e,t){try{const o=await je(e,t);await Ie(o.path,o.parent);const r=JSON.parse(await Z(o.path,"utf8")),i=r.state==="issued"?["schemaVersion","kind","state","storyId","fromSessionId","token","tokenDigest","bundleDigest","claimRevision","grantDigest","expiresAt"]:r.state==="consuming"?["schemaVersion","kind","state","storyId","fromSessionId","token","tokenDigest","bundleDigest","claimRevision","grantDigest","expiresAt","consumedBySessionId","consumeStartedAt"]:["schemaVersion","kind","state","storyId","fromSessionId","tokenDigest","bundleDigest","claimRevision","grantDigest","expiresAt","consumedBySessionId","consumedAt"];return Object.keys(r).sort().join("\0")!==[...i].sort().join("\0")||r.schemaVersion!==1||r.kind!=="direct-yolo-setup-handoff-token"||r.state!=="issued"&&r.state!=="consuming"&&r.state!=="consumed"||r.storyId!==t||typeof r.fromSessionId!="string"||!/^setup-[a-f0-9]{64}$/.test(r.fromSessionId)||![r.tokenDigest,r.bundleDigest,r.claimRevision,r.grantDigest].every(n=>typeof n=="string"&&/^[a-f0-9]{64}$/.test(n))||typeof r.expiresAt!="string"||!Number.isFinite(Date.parse(r.expiresAt))||(r.state==="issued"||r.state==="consuming")&&(typeof r.token!="string"||!/^[A-Za-z0-9_-]{32,256}$/.test(r.token))||r.state==="consuming"&&(typeof r.consumedBySessionId!="string"||!/^[a-f0-9]{64}$/.test(r.consumedBySessionId)||typeof r.consumeStartedAt!="string"||!Number.isFinite(Date.parse(r.consumeStartedAt)))||r.state==="consumed"&&(typeof r.consumedBySessionId!="string"||!/^[a-f0-9]{64}$/.test(r.consumedBySessionId)||typeof r.consumedAt!="string"||!Number.isFinite(Date.parse(r.consumedAt)))?void 0:r}catch{return}}async function Qe(e,t,o,r,i){const n=await G(e,t);if(!(!n||n.state==="consumed"||!n.token||n.bundleDigest!==o||n.claimRevision!==r||Date.parse(n.expiresAt)<=Date.parse(i)||k(n.token)!==n.tokenDigest))return{sessionId:n.fromSessionId,token:n.token}}async function Ue(e,t,o,r,i,n){const g=await G(e,t);if(!g||g.state==="consumed"||g.fromSessionId!==o||g.token!==r||g.tokenDigest!==k(r))throw new Error("direct-yolo-setup-handoff-consume-invalid");if(g.state==="consuming"){if(g.consumedBySessionId!==i)throw new Error("direct-yolo-setup-handoff-consume-raced");return}await ae(e,{...g,state:"consuming",consumedBySessionId:i,consumeStartedAt:n})}async function Xe(e,t,o,r,i,n,g,s,h){const c=await G(e,o);if(c?.state==="consumed"&&c.fromSessionId===r&&c.consumedBySessionId===s)return;const d=await C(e,{required:!0}),l=w(d.epics[t]?.branch_lease),u=w(l?.handoffConsumption);if(!c||c.state!=="consuming"||c.fromSessionId!==r||c.token!==i||c.tokenDigest!==k(i)||c.bundleDigest!==n||c.claimRevision!==g||c.consumedBySessionId!==s||l?.ownerStoryId!==o||l.sessionId!==s||u?.kind!=="direct-yolo-setup-handoff-consumption"||u.state!=="consumed"||u.fromSessionId!==r||u.tokenDigest!==c.tokenDigest||u.bundleDigest!==n||u.claimRevision!==g||u.consumedBySessionId!==s)throw new Error("direct-yolo-setup-handoff-consume-invalid");try{await ae(e,{schemaVersion:1,kind:c.kind,state:"consumed",storyId:o,fromSessionId:r,tokenDigest:c.tokenDigest,bundleDigest:c.bundleDigest,claimRevision:c.claimRevision,grantDigest:c.grantDigest,expiresAt:c.expiresAt,consumedBySessionId:s,consumedAt:h})}catch{throw new Error("direct-yolo-setup-handoff-consume-persist-failed")}}function et(e){const t=w(e.current?.handoffGrant),o=e.handoff?.token,r=typeof o=="string"&&/^[A-Za-z0-9_-]{32,256}$/.test(o)?k(o):void 0;return!!(t&&r&&e.handoff&&e.handoff.sessionId===e.current?.sessionId&&t.schemaVersion===1&&t.kind==="direct-yolo-setup-handoff"&&t.storyId===e.storyId&&t.fromSessionId===e.current?.sessionId&&t.bundleDigest===e.bundleDigest&&t.claimRevision===e.claimRevision&&typeof t.expiresAt=="string"&&Date.parse(t.expiresAt)>Date.parse(e.nowIso)&&typeof t.tokenDigest=="string"&&/^[a-f0-9]{64}$/.test(t.tokenDigest)&&tt(t.tokenDigest,r))}function tt(e,t){const o=Buffer.from(e,"hex"),r=Buffer.from(t,"hex");return o.length===32&&r.length===32&&Ce(o,r)}function ce(e,t){return!!(e&&["schemaVersion","ownerStoryId","sessionId","idempotencyKey","acquiredAt","expiresAt"].every(o=>e[o]===t[o]))}async function se(e,t,o){await P(e,r=>{const i=N(r,t),n=w(i.branch_lease);ce(n,o)&&(i.branch_lease=null)})}async function rt(e,t){const o=await C(e.projectRoot,{required:!0}),r=w(o.epics[e.epicId]?.branch_lease);if(!r||r.ownerStoryId!==t||!Ee(r.sessionId)||!Ee(r.idempotencyKey)||!Number.isFinite(Date.parse(String(r.acquiredAt)))||!Number.isFinite(Date.parse(String(r.expiresAt))))return;const i=await V(e.projectRoot,{kind:"direct",storyId:t,sessionId:String(r.sessionId),idempotencyKey:String(r.idempotencyKey),createdAt:String(r.acquiredAt),expiresAt:String(r.expiresAt),persistent:!0});if(!i.ok)return;await W(i.path,i.owner)&&await se(e.projectRoot,e.epicId,r)}async function de(e,t,o,r){const i=await Ve(t,{headSha:o,graphRevision:r,nowIso:e.nowIso});await e.qaRunner?.({projectRoot:t,storyId:e.storyId,headSha:o});const n=We(i);if(!n)throw new Error("p205-transient-authority-invalid");return{contract:"P205-CI-02",command:Fe,manifestDigest:n.manifestDigest,gateIds:n.gateIds,resultDigest:n.resultDigest,transientAuthority:i}}async function fe(e,t,o,r){return await y(e,["rev-parse","HEAD"])!==o||await y(e,["symbolic-ref","--quiet","--short","HEAD"])!==t||!await B(e,["diff","--cached","--quiet"])?!1:B(e,["diff","--quiet","HEAD","--",...r])}async function Ae(e,t,o,r){const i=ee(t);return(await y(e,["ls-remote","--heads",i,`refs/heads/${o}`])).split(/\s+/)[0]===r}function T(e,t,o,r){if(e.length===0||e.some(n=>!ue(n)))return{ok:!1,reasonCode:"direct-yolo-write-set-invalid"};const i=[".neocortex/state.json",`docs/stories/${o}.story.md`,`.neocortex/stories/${o}/qa-gate-result.json`,`.neocortex/stories/${o}/validation-evidence.md`,`.neocortex/stories/${o}/review.md`];return!i.every(n=>e.includes(n))||!i.every(n=>t.includes(n))?{ok:!1,reasonCode:"direct-yolo-write-set-invalid"}:r&&(r.storyId!==o||r.childClaimRevision!==k([...t].sort().join("\0")))?{ok:!1,reasonCode:"direct-yolo-root-memory-claim-mismatch"}:e.some(n=>!t.includes(n)&&!r?.files.some(g=>g.path===n))?{ok:!1,reasonCode:"direct-yolo-write-set-unauthorized"}:{ok:!0}}function ue(e){return e.length>0&&e.length<=1024&&!e.startsWith("/")&&!e.includes("\\")&&!e.split("/").includes("..")&&!e.startsWith(".git/")&&!e.includes("\0")}function Ee(e){return typeof e=="string"&&e.length>0&&e.length<=512&&/^[A-Za-z0-9._:@/+\-=]+$/.test(e)}function Be(e){return e.length>0&&e.length<=240&&!e.startsWith("-")&&!/[\s~^:?*[\\]/.test(e)&&!e.includes("..")}function ee(e){if(!/^[A-Za-z0-9._-]{1,128}$/.test(e))throw new Error("direct-yolo-remote-invalid");return e}function J(e,t){const o=`${t}: `,r=e.split(`
3
+ `).map(i=>i.trim()).filter(i=>i.startsWith(o)).map(i=>i.slice(o.length));return r.length===1?r[0]:void 0}function k(e){return Le("sha256").update(e).digest("hex")}function O(e){return e.split("\0").filter(Boolean)}function K(e){return e&&Number.isFinite(Date.parse(e))?new Date(Date.parse(e)).toISOString():new Date().toISOString()}function w(e){return e&&typeof e=="object"&&!Array.isArray(e)?e:void 0}function ot(e,t,o,r,i){const n=w(e.expectedLease),g=n?.sessionId,s=Date.parse(String(n?.expiresAt??""));return e.schemaVersion===1&&Number.isSafeInteger(e.expectedRuntimeRevision)&&e.expectedRuntimeRevision===t&&!!o&&JSON.stringify(o)===JSON.stringify(n)&&n?.schemaVersion===1&&n.ownerStoryId===r&&typeof g=="string"&&/^setup-[a-f0-9]{64}$/.test(g)&&n.idempotencyKey===g&&Number.isFinite(s)&&s<=i}async function it(e,t){let o=await y(e,["symbolic-ref","--quiet","--short","HEAD"]);if(o===t.executionBranch){if(await y(e,["rev-parse","HEAD"])!==t.originHead||await q(e)!==t.checkoutBefore)return;await y(e,["switch",t.originBranch]),o=t.originBranch}if(o!==t.originBranch||await y(e,["rev-parse","HEAD"])!==t.originHead||await q(e)!==t.checkoutBefore||t.executionHead===t.originHead||!await B(e,["show-ref","--verify","--quiet",t.executionRef]))return;await y(e,["rev-parse",t.executionRef])===t.originHead&&await y(e,["update-ref",t.executionRef,t.executionHead,t.originHead])}function he(e,t,o){const r=w(e);if(!(!r||r.schemaVersion!==1||r.originBranch!==o||r.executionBranch!==t||r.capturedFromCurrentCheckout!==!0||typeof r.originHeadSha!="string"||!/^[a-f0-9]{40,64}$/.test(r.originHeadSha)||typeof r.capturedAt!="string"||!Number.isFinite(Date.parse(r.capturedAt))))return r}function pe(e,t,o,r){return Object.freeze({schemaVersion:1,kind:"direct-yolo-setup-receipt",storyId:e.storyId,epicId:e.epicId,executionBranch:e.executionBranch,originBranch:e.originBranch,originHeadSha:t.originHeadSha,leaseExpiresAt:o,reused:r,publicSafe:!0})}async function q(e){const t=O(await j(e,["ls-files","--others","--exclude-standard","-z"])).sort(),o=await Promise.all(t.map(async r=>{if(!ue(r))throw new Error("direct-yolo-checkout-payload-invalid");return`${r}\0${await y(e,["hash-object","--no-filters","--",r])}`}));return k([await j(e,["diff","--cached","--binary","--full-index"]),await j(e,["diff","--binary","--full-index"]),await j(e,["status","--porcelain=v1","-z","--untracked-files=all"]),...o].join("\0"))}async function xt(e){const t=M(e),o=await y(t,["rev-parse","HEAD"]),r=await y(t,["symbolic-ref","--quiet","--short","HEAD"]),i=await q(t);if(await y(t,["rev-parse","HEAD"])!==o||await y(t,["symbolic-ref","--quiet","--short","HEAD"])!==r)throw new Error("direct-yolo-checkout-observation-raced");return Object.freeze({headSha:o,branch:r,payloadDigest:i})}async function ye(e){const t=[];let o;for(const r of(await j(e,["worktree","list","--porcelain"])).split(`
4
+ `))r.startsWith("worktree ")?(o={path:r.slice(9)},t.push(o)):o&&r.startsWith("branch refs/heads/")&&(o.branch=r.slice(18));return t}function Y(e,t){return e instanceof Error&&e.message==="git-lifecycle-runtime-revision-conflict"?"direct-yolo-runtime-revision-conflict":e instanceof Error&&/^direct-yolo-[a-z0-9-]+$/.test(e.message)?e.message:t}function a(e){return{ok:!1,reasonCode:e}}async function y(e,t){return String((await oe("git",[...t],{cwd:e,encoding:"utf8",maxBuffer:32*1024*1024})).stdout).trim()}async function j(e,t){return String((await oe("git",[...t],{cwd:e,encoding:"utf8",maxBuffer:32*1024*1024})).stdout)}async function B(e,t){try{return await oe("git",[...t],{cwd:e,maxBuffer:8*1024*1024}),!0}catch{return!1}}export{mt as acceptDirectYoloSetupCheckpoint,Rt as beginDirectYoloStoryCommit,xt as captureDirectYoloCheckoutEvidence,It as finalizeDirectYoloStoryCheckpoint,wt as issueDirectYoloSetupLeaseHandoff,gt as prepareDirectYoloSetup,vt as recoverLegacyDirectYoloStoryCheckpoint};