@ornexus/neocortex 4.60.71 → 4.60.73

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (243) hide show
  1. package/dist/release-source.json +1 -1
  2. package/dist/sbom.cdx.json +1211 -1217
  3. package/install.ps1 +1 -1
  4. package/install.sh +1 -1
  5. package/package.json +2 -2
  6. package/packages/client/dist/adapters/adapter-registry.js +108 -1
  7. package/packages/client/dist/adapters/antigravity-adapter.js +77 -2
  8. package/packages/client/dist/adapters/claude-code-adapter.js +79 -3
  9. package/packages/client/dist/adapters/codex-adapter.js +80 -2
  10. package/packages/client/dist/adapters/cursor-adapter.js +115 -4
  11. package/packages/client/dist/adapters/gemini-adapter.js +71 -2
  12. package/packages/client/dist/adapters/grok-adapter.js +42 -2
  13. package/packages/client/dist/adapters/index.js +22 -1
  14. package/packages/client/dist/adapters/platform-detector.js +212 -1
  15. package/packages/client/dist/adapters/target-adapter.js +12 -0
  16. package/packages/client/dist/adapters/vscode-adapter.js +72 -2
  17. package/packages/client/dist/agent/refresh-stubs.js +417 -2
  18. package/packages/client/dist/agent/update-agent-yaml.js +102 -1
  19. package/packages/client/dist/agent/update-description.js +251 -1
  20. package/packages/client/dist/cache/crypto-utils.js +78 -1
  21. package/packages/client/dist/cache/encrypted-cache.js +108 -1
  22. package/packages/client/dist/cache/in-memory-asset-cache.js +73 -1
  23. package/packages/client/dist/cache/index.js +13 -1
  24. package/packages/client/dist/cache/protected-pi-boundary.js +44 -1
  25. package/packages/client/dist/checkpoint/checkpoint-client-reader.js +127 -2
  26. package/packages/client/dist/checkpoint/index.js +11 -1
  27. package/packages/client/dist/checkpoint/shared-checkpoint-types.js +32 -1
  28. package/packages/client/dist/cli.js +358 -5
  29. package/packages/client/dist/commands/activate.js +394 -8
  30. package/packages/client/dist/commands/architecture-answers.js +274 -7
  31. package/packages/client/dist/commands/architecture-memory-terminal.js +253 -4
  32. package/packages/client/dist/commands/architecture-non-ui-continuation.js +100 -1
  33. package/packages/client/dist/commands/architecture-root-binding.js +51 -1
  34. package/packages/client/dist/commands/cache-status.js +112 -2
  35. package/packages/client/dist/commands/config.js +88 -1
  36. package/packages/client/dist/commands/doctor.js +635 -2
  37. package/packages/client/dist/commands/invoke.js +7200 -143
  38. package/packages/client/dist/commands/legacy-epic-terminal.js +71 -1
  39. package/packages/client/dist/commands/legacy-story-branch.js +96 -1
  40. package/packages/client/dist/commands/legacy-story-pr.js +276 -1
  41. package/packages/client/dist/commands/mutation-qa-checkpoint.js +285 -5
  42. package/packages/client/dist/commands/mutation-qa-gate.js +54 -1
  43. package/packages/client/dist/commands/mutation-qa-git.js +41 -1
  44. package/packages/client/dist/commands/mutation-qa-policy.js +102 -1
  45. package/packages/client/dist/commands/mutation-qa-run.js +219 -3
  46. package/packages/client/dist/commands/mutation-qa-verdict.js +149 -1
  47. package/packages/client/dist/commands/mutation-qa.js +220 -1
  48. package/packages/client/dist/commands/native-agent.js +136 -2
  49. package/packages/client/dist/commands/planning-diagnosis.js +43 -1
  50. package/packages/client/dist/commands/planning-generation.js +53 -1
  51. package/packages/client/dist/commands/planning-memory.js +40 -1
  52. package/packages/client/dist/commands/planning-research.js +42 -1
  53. package/packages/client/dist/commands/private-session.js +75 -1
  54. package/packages/client/dist/commands/property-qa.js +139 -1
  55. package/packages/client/dist/commands/qa-control.js +16 -1
  56. package/packages/client/dist/commands/qa-scan-browser-launch.js +55 -1
  57. package/packages/client/dist/commands/qa-scan-browser-route.js +28 -1
  58. package/packages/client/dist/commands/qa-scan-capture.js +663 -5
  59. package/packages/client/dist/commands/qa-scan-discovery.js +376 -3
  60. package/packages/client/dist/commands/qa-scan-review.js +826 -5
  61. package/packages/client/dist/commands/qa-synthetic-backend-capture.js +266 -1
  62. package/packages/client/dist/commands/qa-synthetic-backend-review.js +297 -1
  63. package/packages/client/dist/commands/qa-synthetic-backend.js +340 -2
  64. package/packages/client/dist/commands/qa-synthetic-broker-port.js +85 -1
  65. package/packages/client/dist/commands/qa-synthetic-browser.js +39 -1
  66. package/packages/client/dist/commands/refactor-characterization.js +671 -21
  67. package/packages/client/dist/commands/refactor-commit.js +399 -13
  68. package/packages/client/dist/commands/refactor-control.js +142 -3
  69. package/packages/client/dist/commands/refactor-cpp-assertions.js +93 -3
  70. package/packages/client/dist/commands/refactor-review.js +283 -5
  71. package/packages/client/dist/commands/refactor-root.js +588 -7
  72. package/packages/client/dist/commands/refresh-memory.js +156 -3
  73. package/packages/client/dist/commands/shared-architecture-non-ui.js +24 -1
  74. package/packages/client/dist/commands/story-checkpoint.js +171 -1
  75. package/packages/client/dist/config/profile-directory.js +13 -1
  76. package/packages/client/dist/config/resolver-selection.js +278 -1
  77. package/packages/client/dist/config/secure-config.js +281 -12
  78. package/packages/client/dist/config.js +223 -2
  79. package/packages/client/dist/constants.js +25 -1
  80. package/packages/client/dist/context/context-collector.js +222 -2
  81. package/packages/client/dist/context/context-sanitizer.js +145 -1
  82. package/packages/client/dist/continuity/autonomous-governance.js +285 -1
  83. package/packages/client/dist/continuity/continuity-client-state-store.js +1587 -1
  84. package/packages/client/dist/continuity/invoke-hooks.js +129 -1
  85. package/packages/client/dist/continuity/migrations/001-initial-schema.js +12 -2
  86. package/packages/client/dist/continuity/migrations/002-processing-output-summary-indexes.js +12 -2
  87. package/packages/client/dist/continuity/migrations/003-headless-execution-attempts.js +12 -2
  88. package/packages/client/dist/continuity/migrations/004-outbox-delivery-state.js +12 -2
  89. package/packages/client/dist/continuity/migrations/005-bounded-headless-queue.js +12 -2
  90. package/packages/client/dist/continuity/migrations/006-native-agent-dispatches.js +10 -2
  91. package/packages/client/dist/continuity/native-agent-store.js +69 -1
  92. package/packages/client/dist/continuity/native-agent-types.js +90 -1
  93. package/packages/client/dist/continuity/outbox.js +117 -1
  94. package/packages/client/dist/continuity/runner-bootstrap-policy.js +172 -1
  95. package/packages/client/dist/continuity/sqlite-store.d.ts +22 -1
  96. package/packages/client/dist/continuity/sqlite-store.js +2612 -81
  97. package/packages/client/dist/effects/invoke-effect-gateways.js +62 -1
  98. package/packages/client/dist/errors/error-messages.js +228 -2
  99. package/packages/client/dist/errors/frontmatter-diagnostics.js +125 -1
  100. package/packages/client/dist/evidence/collector.js +347 -1
  101. package/packages/client/dist/evidence/index.js +1 -1
  102. package/packages/client/dist/evidence/types.js +2 -0
  103. package/packages/client/dist/fs/atomic-state-replace.js +152 -1
  104. package/packages/client/dist/generated/command-registry.generated.js +4076 -1
  105. package/packages/client/dist/generated/public-projections.generated.js +149 -1
  106. package/packages/client/dist/git/direct-yolo-c01-retry.js +138 -4
  107. package/packages/client/dist/git/direct-yolo-checkpoint.js +1234 -4
  108. package/packages/client/dist/git/epic-operational-state.d.ts +198 -0
  109. package/packages/client/dist/git/epic-operational-state.js +672 -0
  110. package/packages/client/dist/git/git-lifecycle-step-ids.js +14 -1
  111. package/packages/client/dist/git/git-lifecycle-store.d.ts +9 -1
  112. package/packages/client/dist/git/git-lifecycle-store.js +1981 -9
  113. package/packages/client/dist/git/legacy-story-branch-checkpoint.js +259 -9
  114. package/packages/client/dist/git/legacy-story-branch-setup.js +348 -4
  115. package/packages/client/dist/git/legacy-story-pr.js +3538 -70
  116. package/packages/client/dist/git/secure-filesystem-boundary.js +182 -5
  117. package/packages/client/dist/graph-retrieval/pre-command-hook.js +284 -1
  118. package/packages/client/dist/graph-retrieval/shared-graph-retrieval-contract.js +237 -1
  119. package/packages/client/dist/i18n/first-run.js +60 -2
  120. package/packages/client/dist/index.js +47 -1
  121. package/packages/client/dist/invocation/invoke-input.js +24 -1
  122. package/packages/client/dist/license/index.js +5 -1
  123. package/packages/client/dist/license/license-client.js +304 -1
  124. package/packages/client/dist/machine/fingerprint.js +163 -2
  125. package/packages/client/dist/machine/index.js +5 -1
  126. package/packages/client/dist/memory/project-memory-path-scope.js +62 -1
  127. package/packages/client/dist/memory/project-memory-writer.js +1007 -40
  128. package/packages/client/dist/memory/shared-project-memory-types.js +253 -2
  129. package/packages/client/dist/migrations/epic-lifecycle-backfill.d.ts +97 -0
  130. package/packages/client/dist/migrations/epic-lifecycle-backfill.js +1060 -0
  131. package/packages/client/dist/migrations/project-identity-capability.js +50 -1
  132. package/packages/client/dist/migrations/project-identity.js +493 -7
  133. package/packages/client/dist/migrations/state-json.d.ts +22 -0
  134. package/packages/client/dist/migrations/state-json.js +393 -4
  135. package/packages/client/dist/policy/architecture-policy.js +184 -2
  136. package/packages/client/dist/policy/index.js +7 -1
  137. package/packages/client/dist/policy/shared-policy-types.js +38 -0
  138. package/packages/client/dist/policy/subagent-approval-policy.js +55 -1
  139. package/packages/client/dist/private-execution/clean-baseline.js +279 -2
  140. package/packages/client/dist/private-execution/codex-executor.js +574 -4
  141. package/packages/client/dist/private-execution/codex-host-runtime.js +137 -1
  142. package/packages/client/dist/private-execution/codex-lifecycle-guard.js +97 -1
  143. package/packages/client/dist/private-execution/codex-scoped-permissions.js +128 -1
  144. package/packages/client/dist/private-execution/delivery.js +410 -13
  145. package/packages/client/dist/private-execution/diagnosis-root-review.js +294 -2
  146. package/packages/client/dist/private-execution/frozen-handoff.js +19 -6
  147. package/packages/client/dist/private-execution/implementation-validation.js +106 -2
  148. package/packages/client/dist/private-execution/isolated-admission.js +1432 -23
  149. package/packages/client/dist/private-execution/isolated-session.js +725 -5
  150. package/packages/client/dist/private-execution/isolated-workspace.js +122 -1
  151. package/packages/client/dist/private-execution/pact-consumer-runtime.js +409 -3
  152. package/packages/client/dist/private-execution/pact-test-source.js +258 -1
  153. package/packages/client/dist/private-execution/pact-toolchain.js +290 -3
  154. package/packages/client/dist/private-execution/pact-write-set.js +172 -1
  155. package/packages/client/dist/private-execution/planning-diagnosis-checkpoint.js +406 -5
  156. package/packages/client/dist/private-execution/planning-generation-checkpoint.js +462 -5
  157. package/packages/client/dist/private-execution/planning-memory-checkpoint.js +139 -3
  158. package/packages/client/dist/private-execution/planning-research-checkpoint.js +325 -4
  159. package/packages/client/dist/private-execution/private-report-store.js +42 -1
  160. package/packages/client/dist/private-execution/property-test-candidate.js +103 -1
  161. package/packages/client/dist/private-execution/property-test-choices.js +147 -3
  162. package/packages/client/dist/private-execution/property-test-plan.js +271 -7
  163. package/packages/client/dist/private-execution/property-test-review.js +258 -4
  164. package/packages/client/dist/private-execution/property-test-runtime.js +372 -9
  165. package/packages/client/dist/private-execution/property-test-source.js +139 -1
  166. package/packages/client/dist/private-execution/root-checkpoint.js +80 -1
  167. package/packages/client/dist/private-execution/root-memory-git-evidence.js +135 -3
  168. package/packages/client/dist/private-execution/root-memory-recovery.js +93 -1
  169. package/packages/client/dist/private-execution/root-memory-request.js +29 -1
  170. package/packages/client/dist/private-execution/root-memory-scope.js +178 -1
  171. package/packages/client/dist/private-execution/root-memory-writer.js +102 -1
  172. package/packages/client/dist/private-execution/session-lease.js +63 -1
  173. package/packages/client/dist/private-execution/shared-private-admission-types.js +50 -1
  174. package/packages/client/dist/private-execution/shared-project-validation.js +66 -1
  175. package/packages/client/dist/private-execution/shared-root-memory-types.js +61 -1
  176. package/packages/client/dist/private-execution/ux-source-validation.js +372 -1
  177. package/packages/client/dist/private-execution/ux-write-set.js +191 -2
  178. package/packages/client/dist/registry/registry-reconciliation.js +234 -1
  179. package/packages/client/dist/resilience/circuit-breaker.js +170 -1
  180. package/packages/client/dist/resilience/degradation-manager.js +164 -1
  181. package/packages/client/dist/resilience/freshness-indicator.js +100 -1
  182. package/packages/client/dist/resilience/index.js +8 -1
  183. package/packages/client/dist/resilience/recovery-detector.js +74 -1
  184. package/packages/client/dist/resolvers/asset-resolver.js +13 -0
  185. package/packages/client/dist/resolvers/local-resolver.js +255 -8
  186. package/packages/client/dist/resolvers/remote-resolver.js +233 -1
  187. package/packages/client/dist/runner/binary.js +404 -1
  188. package/packages/client/dist/runner/cli.js +1571 -17
  189. package/packages/client/dist/runner/headless/adapters.js +49 -1
  190. package/packages/client/dist/runner/headless/claude-code-adapter.js +79 -1
  191. package/packages/client/dist/runner/headless/continuous-story-workspace.js +1628 -11
  192. package/packages/client/dist/runner/headless/epic-git-reconciliation-inventory.js +69 -1
  193. package/packages/client/dist/runner/headless/epic-merge-candidate-qa.js +110 -2
  194. package/packages/client/dist/runner/headless/epic-terminal-git-cli-adapter.js +1236 -8
  195. package/packages/client/dist/runner/headless/epic-terminal-git-runner.js +318 -2
  196. package/packages/client/dist/runner/headless/fake-adapter.js +62 -1
  197. package/packages/client/dist/runner/headless/managed-profile.js +132 -1
  198. package/packages/client/dist/runner/headless/opencode-adapter.js +145 -2
  199. package/packages/client/dist/runner/headless/prepared-story-bundle.js +43 -1
  200. package/packages/client/dist/runner/headless/resolver.js +231 -1
  201. package/packages/client/dist/runner/headless/run-yolo.js +885 -1
  202. package/packages/client/dist/runner/headless/run-yoloop.js +2574 -2
  203. package/packages/client/dist/runner/headless/types.js +574 -2
  204. package/packages/client/dist/runner/local-console.js +396 -13
  205. package/packages/client/dist/runner/portal-sync-worker.js +1487 -1
  206. package/packages/client/dist/runner/project-identity.js +106 -1
  207. package/packages/client/dist/runner/scheduler.js +995 -7
  208. package/packages/client/dist/runner-cli.js +18 -2
  209. package/packages/client/dist/state/invocation-shape.js +13 -1
  210. package/packages/client/dist/state/project-state-snapshot.d.ts +8 -0
  211. package/packages/client/dist/state/project-state-snapshot.js +1731 -1
  212. package/packages/client/dist/state/state-json-repair.js +785 -3
  213. package/packages/client/dist/state/structural-dag-projection.js +118 -1
  214. package/packages/client/dist/telemetry/index.js +5 -1
  215. package/packages/client/dist/telemetry/offline-queue.js +131 -1
  216. package/packages/client/dist/tier/index.js +5 -1
  217. package/packages/client/dist/tier/tier-aware-client.js +405 -1
  218. package/packages/client/dist/types/index.js +38 -1
  219. package/packages/client/dist/version.js +27 -1
  220. package/packages/client/dist/yolo/change-evidence-collector.js +126 -1
  221. package/packages/client/dist/yolo/invoke-envelope-capability.js +62 -1
  222. package/packages/client/dist/yolo/spec-envelope-collector.js +184 -5
  223. package/packages/client/dist/yoloop/discovery-hook.js +300 -2
  224. package/packages/client/dist/yoloop/index.js +8 -1
  225. package/packages/client/dist/yoloop/invoke-hooks.js +669 -12
  226. package/packages/client/dist/yoloop/shared-discover-epics.js +1007 -1
  227. package/packages/client/dist/yoloop/shared-yoloop-types.d.ts +65 -1
  228. package/packages/client/dist/yoloop/shared-yoloop-types.js +33 -1
  229. package/packages/client/dist/yoloop/target-normalization.js +180 -1
  230. package/packages/client/dist/yoloop/yoloop-client-state-store.d.ts +8 -0
  231. package/packages/client/dist/yoloop/yoloop-client-state-store.js +1920 -1
  232. package/packages/shared/dist/yolo/spec-envelope.js +3 -6
  233. package/targets-stubs/antigravity/gemini.md +1 -1
  234. package/targets-stubs/antigravity/skill/SKILL.md +1 -1
  235. package/targets-stubs/claude-code/neocortex-root.agent.yaml +1 -1
  236. package/targets-stubs/claude-code/neocortex-root.md +2 -2
  237. package/targets-stubs/claude-code/neocortex.agent.yaml +1 -1
  238. package/targets-stubs/claude-code/neocortex.md +2 -2
  239. package/targets-stubs/codex/AGENTS.md +1 -1
  240. package/targets-stubs/cursor/agent.md +2 -2
  241. package/targets-stubs/gemini-cli/agent.md +2 -2
  242. package/targets-stubs/opencode/neocortex-root.md +1 -1
  243. package/targets-stubs/vscode/neocortex.agent.md +2 -2
@@ -0,0 +1,672 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ import { createHash } from 'node:crypto';
3
+ import { existsSync, fsyncSync, lstatSync, mkdirSync, openSync, closeSync, readFileSync, realpathSync, writeFileSync, } from 'node:fs';
4
+ import { isAbsolute, join, relative, resolve } from 'node:path';
5
+ import { GIT_LIFECYCLE_RUNTIME_DIRECTORY, mutateGitLifecycleRuntime, readGitLifecycleRuntime, readGitLifecycleRuntimeSync, } from './git-lifecycle-store.js';
6
+ export const EPIC_OPERATIONAL_SOURCE_LIMIT_BYTES = 4 * 1024 * 1024;
7
+ export const EPIC_OPERATIONAL_PAYLOAD_LIMIT_BYTES = 1024 * 1024;
8
+ export const EPIC_OPERATIONAL_RECEIPT_CAP = 64;
9
+ export const EPIC_OPERATIONAL_IDENTITY_CAP = 2_048;
10
+ const HASH = /^[a-f0-9]{64}$/;
11
+ const ID = /^P[1-9][0-9]*(?:\.[0-9]+)*$/;
12
+ const SAFE_TOKEN = /^[A-Za-z0-9._:/#\-[\]]{1,240}$/;
13
+ export function epicOperationalCanonicalJson(value) {
14
+ if (value === null || typeof value !== 'object')
15
+ return JSON.stringify(value);
16
+ if (Array.isArray(value))
17
+ return `[${value.map(epicOperationalCanonicalJson).join(',')}]`;
18
+ return `{${Object.entries(value)
19
+ .filter(([, child]) => child !== undefined)
20
+ .sort(([left], [right]) => left.localeCompare(right))
21
+ .map(([key, child]) => `${JSON.stringify(key)}:${epicOperationalCanonicalJson(child)}`)
22
+ .join(',')}}`;
23
+ }
24
+ export function epicOperationalHash(domain, value) {
25
+ return createHash('sha256').update(`${domain}\0${epicOperationalCanonicalJson(value)}`).digest('hex');
26
+ }
27
+ export function deriveEpicOperationalRepositoryFingerprint(projectRoot) {
28
+ const canonicalRoot = realpathSync(resolve(projectRoot));
29
+ const raw = execFileSync('git', ['rev-parse', '--git-common-dir'], {
30
+ cwd: canonicalRoot,
31
+ encoding: 'utf8',
32
+ stdio: ['ignore', 'pipe', 'ignore'],
33
+ }).trim();
34
+ const absolute = isAbsolute(raw) ? resolve(raw) : resolve(canonicalRoot, raw);
35
+ const commonDir = realpathSync(absolute);
36
+ if (commonDir !== absolute)
37
+ throw new Error('epic-operational-path-unsafe');
38
+ const info = lstatSync(commonDir);
39
+ if (!info.isDirectory() || info.isSymbolicLink())
40
+ throw new Error('epic-operational-path-unsafe');
41
+ return epicOperationalHash('p224-epic-operational-repository-v1', {
42
+ commonDir,
43
+ dev: Number(info.dev),
44
+ ino: Number(info.ino),
45
+ });
46
+ }
47
+ export function createEpicOperationalIdentity(projectRoot, input) {
48
+ const repositoryFingerprint = deriveEpicOperationalRepositoryFingerprint(projectRoot);
49
+ const base = {
50
+ schemaVersion: 'EpicOperationalIdentity/1.0',
51
+ epicId: input.epicId,
52
+ executionBranch: input.executionBranch,
53
+ graphRevision: input.graphRevision,
54
+ originBindingDigest: input.originBindingDigest,
55
+ repositoryFingerprint,
56
+ };
57
+ const identity = Object.freeze({
58
+ ...base,
59
+ identityHash: epicOperationalHash('p224-epic-operational-identity-v1', base),
60
+ });
61
+ assertEpicOperationalIdentity(identity);
62
+ return identity;
63
+ }
64
+ export function createInitialEpicOperationalState(input) {
65
+ const value = {
66
+ schemaVersion: 'EpicOperationalState/1.0',
67
+ repositoryFingerprint: input.identity.repositoryFingerprint,
68
+ writerEpoch: 1,
69
+ capabilities: ['epic-operational-state-v1', 'epic-lifecycle-backfill-v1'],
70
+ identities: { [input.identity.identityHash]: input.identity },
71
+ yoloop: { schemaVersion: 'CommonYoloopAuthority/1.0', ...input.yoloop },
72
+ projectionBindings: {
73
+ schemaVersion: 'EpicOperationalProjectionBindings/1.0',
74
+ trackedState: {
75
+ status: 'bound',
76
+ sourceDigest: input.trackedStateDigest,
77
+ authorityRevision: 0,
78
+ reasonCodes: ['epic-operational-tracked-state-bound'],
79
+ },
80
+ sqlite: {
81
+ status: input.sqliteStatus ?? 'unbound',
82
+ sourceDigest: null,
83
+ authorityRevision: 0,
84
+ reasonCodes: input.sqliteReasonCodes ?? ['epic-operational-projection-unbound'],
85
+ },
86
+ },
87
+ activeJournal: null,
88
+ backfillReceipts: [],
89
+ reconciliationReceipts: [],
90
+ rollbackReceipts: [],
91
+ };
92
+ assertEpicOperationalState(value);
93
+ return Object.freeze(value);
94
+ }
95
+ export function assertEpicOperationalState(value) {
96
+ const item = record(value);
97
+ if (!item)
98
+ throw new Error('epic-operational-store-corrupt');
99
+ exact(item, [
100
+ 'schemaVersion', 'repositoryFingerprint', 'writerEpoch', 'capabilities',
101
+ 'identities', 'yoloop', 'projectionBindings', 'activeJournal',
102
+ 'backfillReceipts', 'reconciliationReceipts', 'rollbackReceipts',
103
+ ]);
104
+ const identities = record(item.identities);
105
+ const yoloop = record(item.yoloop);
106
+ const bindings = record(item.projectionBindings);
107
+ if (item.schemaVersion !== 'EpicOperationalState/1.0' || !hash(item.repositoryFingerprint)
108
+ || !Number.isSafeInteger(item.writerEpoch) || Number(item.writerEpoch) < 1
109
+ || !Array.isArray(item.capabilities)
110
+ || !sameStringSet(item.capabilities, ['epic-operational-state-v1', 'epic-lifecycle-backfill-v1'])
111
+ || !identities || Object.keys(identities).length > EPIC_OPERATIONAL_IDENTITY_CAP
112
+ || !yoloop || !hasExactKeys(yoloop, ['schemaVersion', 'lock', 'state', 'capabilities'])
113
+ || yoloop.schemaVersion !== 'CommonYoloopAuthority/1.0'
114
+ || !record(yoloop.state) || !(yoloop.lock === null || record(yoloop.lock))
115
+ || !Array.isArray(yoloop.capabilities) || yoloop.capabilities.length > 16
116
+ || new Set(yoloop.capabilities).size !== yoloop.capabilities.length
117
+ || yoloop.capabilities.some((capability) => ![
118
+ 'rollover-session-v1', 'successor-transition-v1', 'continuation-replan-v1',
119
+ 'cross-epic-continuation-reconcile-v1',
120
+ ].includes(String(capability)))
121
+ || !bindings || !hasExactKeys(bindings, ['schemaVersion', 'trackedState', 'sqlite'])
122
+ || bindings.schemaVersion !== 'EpicOperationalProjectionBindings/1.0'
123
+ || !validProjectionBinding(bindings.trackedState) || !validProjectionBinding(bindings.sqlite)
124
+ || !(item.activeJournal === null || validJournal(item.activeJournal))
125
+ || !boundedReceipts(item.backfillReceipts, 'EpicLifecycleBackfillReceipt/1.0')
126
+ || !boundedReceipts(item.reconciliationReceipts, 'YoloopCrossEpicReconciliationReceipt/1.0')
127
+ || !boundedReceipts(item.rollbackReceipts, 'EpicOperationalRollbackReceipt/1.0')) {
128
+ throw new Error('epic-operational-store-corrupt');
129
+ }
130
+ for (const [identityHash, identity] of Object.entries(identities)) {
131
+ assertEpicOperationalIdentity(identity);
132
+ if (identityHash !== identity.identityHash || identity.repositoryFingerprint !== item.repositoryFingerprint) {
133
+ throw new Error('epic-operational-identity-mismatch');
134
+ }
135
+ }
136
+ if (Buffer.byteLength(epicOperationalCanonicalJson(item), 'utf8') > EPIC_OPERATIONAL_SOURCE_LIMIT_BYTES) {
137
+ throw new Error('epic-operational-source-oversized');
138
+ }
139
+ }
140
+ export function readEpicOperationalState(projectRoot, context) {
141
+ const document = readGitLifecycleRuntimeSync(projectRoot, { required: true });
142
+ if (document.schemaVersion !== 2 || document.operational === undefined) {
143
+ throw new Error('epic-operational-store-missing');
144
+ }
145
+ assertEpicOperationalState(document.operational);
146
+ assertOperationalContext(document, document.operational, context);
147
+ return { document, operational: document.operational };
148
+ }
149
+ export async function mutateEpicOperationalState(projectRoot, context, mutate) {
150
+ try {
151
+ return await mutateGitLifecycleRuntime(projectRoot, (document) => {
152
+ if (document.schemaVersion !== 2 || document.operational === undefined) {
153
+ throw new Error('epic-operational-store-missing');
154
+ }
155
+ assertEpicOperationalState(document.operational);
156
+ assertOperationalContext(document, document.operational, context);
157
+ return mutate(document.operational, document);
158
+ }, { expectedRevision: context.expectedRevision });
159
+ }
160
+ catch (error) {
161
+ if (error instanceof Error && error.message === 'git-lifecycle-runtime-revision-conflict') {
162
+ throw new Error('epic-operational-revision-conflict');
163
+ }
164
+ throw error;
165
+ }
166
+ }
167
+ export function projectEpicOperationalState(document, identityHash) {
168
+ if (document.schemaVersion !== 2 || document.operational === undefined) {
169
+ throw new Error('epic-operational-store-missing');
170
+ }
171
+ assertEpicOperationalState(document.operational);
172
+ const state = document.operational;
173
+ const identity = state.identities[identityHash];
174
+ if (!identity)
175
+ throw new Error('epic-operational-identity-mismatch');
176
+ const run = record(state.yoloop.state.runControl);
177
+ const pending = state.yoloop.state.pendingContinuationAuthorities ?? [];
178
+ return Object.freeze({
179
+ schemaVersion: 'EpicOperationalPublicProjection/1.0',
180
+ repositoryFingerprint: state.repositoryFingerprint,
181
+ identityHash,
182
+ epicId: identity.epicId,
183
+ executionBranch: identity.executionBranch,
184
+ graphRevision: identity.graphRevision,
185
+ storeRevision: document.revision,
186
+ writerEpoch: state.writerEpoch,
187
+ runId: typeof run?.runId === 'string' ? run.runId : null,
188
+ runStatus: typeof run?.status === 'string' ? run.status : null,
189
+ runRevision: Number.isSafeInteger(run?.revision) ? Number(run.revision) : null,
190
+ snapshotHash: run?.latestSnapshotHash === null || hash(run?.latestSnapshotHash)
191
+ ? run?.latestSnapshotHash : null,
192
+ continuationSessionHash: epicOperationalHash('p224-common-continuation-session-v1', state.yoloop.state.continuationSession ?? null),
193
+ pendingAuthorityHash: epicOperationalHash('p224-common-pending-authorities-v1', pending),
194
+ pendingAuthorityCount: pending.length,
195
+ lockActive: state.yoloop.lock !== null,
196
+ currentSessionActive: state.yoloop.state.currentSession !== null,
197
+ journalActive: state.activeJournal !== null,
198
+ reasonCodes: ['epic-operational-projection-bounded'],
199
+ publicSafe: true,
200
+ });
201
+ }
202
+ export function classifyCrossEpicContinuationReconciliation(input) {
203
+ let projection;
204
+ try {
205
+ projection = projectEpicOperationalState(input.document, input.targetIdentityHash);
206
+ }
207
+ catch (error) {
208
+ return { kind: 'blocked', reasonCodes: [publicOperationalReason(error)] };
209
+ }
210
+ const operational = input.document.operational;
211
+ const targetIdentity = operational.identities[input.targetIdentityHash];
212
+ const session = record(operational.yoloop.state.continuationSession);
213
+ if (!session)
214
+ return { kind: 'not-applicable', reasonCodes: ['yoloop-cross-epic-session-missing'] };
215
+ const sourceEpicId = typeof session.epicId === 'string' ? session.epicId : '';
216
+ const sourceGraphRevision = typeof session.graphFingerprint === 'string' ? session.graphFingerprint : '';
217
+ if (sourceEpicId === input.targetEpicId && sourceGraphRevision === input.targetGraphRevision) {
218
+ return { kind: 'not-applicable', reasonCodes: ['yoloop-cross-epic-target-unchanged'] };
219
+ }
220
+ const reasons = [];
221
+ const run = record(operational.yoloop.state.runControl);
222
+ const pending = operational.yoloop.state.pendingContinuationAuthorities ?? [];
223
+ if (!targetIdentity || targetIdentity.epicId !== input.targetEpicId
224
+ || targetIdentity.graphRevision !== input.targetGraphRevision)
225
+ reasons.push('epic-operational-identity-mismatch');
226
+ if (!operational.yoloop.capabilities.includes('cross-epic-continuation-reconcile-v1'))
227
+ reasons.push('yoloop-cross-epic-capability-missing');
228
+ if (!run || !['success', 'exhausted'].includes(String(run.status)))
229
+ reasons.push('yoloop-cross-epic-predecessor-active');
230
+ if (operational.yoloop.lock !== null || operational.yoloop.state.currentSession !== null
231
+ || input.legacyActivity !== 'inactive')
232
+ reasons.push('yoloop-cross-epic-lock-active');
233
+ if (operational.activeJournal !== null)
234
+ reasons.push('epic-operational-journal-conflict');
235
+ if (operational.reconciliationReceipts.length >= EPIC_OPERATIONAL_RECEIPT_CAP)
236
+ reasons.push('yoloop-cross-epic-receipt-capacity');
237
+ if (pending.length > 1)
238
+ reasons.push('yoloop-cross-epic-authority-ambiguous');
239
+ if (pending.length === 1) {
240
+ const authority = record(pending[0]);
241
+ const certificate = record(authority?.certificate);
242
+ if (!authority || !certificate || authority.sessionId !== session.sessionId
243
+ || certificate.sessionId !== session.sessionId)
244
+ reasons.push('yoloop-cross-epic-authority-ambiguous');
245
+ else if (authority.delegated !== false)
246
+ reasons.push('yoloop-cross-epic-authority-delegated');
247
+ }
248
+ if (!hash(sourceGraphRevision) || !hash(session.sessionId)
249
+ || projection.runId === null || projection.runRevision === null)
250
+ reasons.push('yoloop-cross-epic-state-corrupt');
251
+ if (reasons.length > 0)
252
+ return { kind: 'blocked', reasonCodes: [...new Set(reasons)] };
253
+ return {
254
+ kind: 'eligible',
255
+ evidence: Object.freeze({
256
+ ...projection,
257
+ schemaVersion: 'YoloopCrossEpicReconciliationEvidence/1.0',
258
+ sourceEpicId,
259
+ sourceGraphRevision,
260
+ targetEpicId: input.targetEpicId,
261
+ targetGraphRevision: input.targetGraphRevision,
262
+ decision: 'eligible',
263
+ }),
264
+ };
265
+ }
266
+ export function buildCrossEpicContinuationPayloadHash(payload) {
267
+ return epicOperationalHash('p224-cross-epic-continuation-reconcile-v1', payload);
268
+ }
269
+ export async function applyCrossEpicContinuationReconciliation(projectRoot, operation) {
270
+ validateCrossEpicOperation(operation);
271
+ let current = await readGitLifecycleRuntime(projectRoot, { required: true });
272
+ if (current.schemaVersion !== 2 || current.operational === undefined)
273
+ throw new Error('epic-operational-store-missing');
274
+ assertEpicOperationalState(current.operational);
275
+ let operational = current.operational;
276
+ const expectedReceipt = crossEpicReceipt(operation);
277
+ const replay = operational.reconciliationReceipts.find((receipt) => receipt.operationId === operation.operationId || receipt.idempotencyKey === operation.idempotencyKey);
278
+ if (replay) {
279
+ if (epicOperationalCanonicalJson(replay) !== epicOperationalCanonicalJson(expectedReceipt)
280
+ || epicOperationalHash('p224-common-continuation-session-v1', operational.yoloop.state.continuationSession ?? null)
281
+ !== operation.nextSessionHash
282
+ || (operational.yoloop.state.pendingContinuationAuthorities?.length ?? 0) !== 0) {
283
+ throw new Error('yoloop-cross-epic-replay-conflict');
284
+ }
285
+ return { applied: false, replay: true, receipt: replay };
286
+ }
287
+ if (operational.reconciliationReceipts.length >= EPIC_OPERATIONAL_RECEIPT_CAP) {
288
+ throw new Error('yoloop-cross-epic-receipt-capacity');
289
+ }
290
+ const existingJournal = operational.activeJournal;
291
+ if (existingJournal && (existingJournal.operationId !== operation.operationId
292
+ || existingJournal.idempotencyKey !== operation.idempotencyKey
293
+ || existingJournal.payloadHash !== operation.payloadHash)) {
294
+ throw new Error('epic-operational-journal-conflict');
295
+ }
296
+ if (!existingJournal)
297
+ assertCrossEpicPreimage(current, operational, operation);
298
+ const backup = existingJournal
299
+ ? verifyOperationalBackup(projectRoot, existingJournal.backupManifestRef, existingJournal.preimageDigest)
300
+ : persistOperationalBackup(projectRoot, operation.operationId, operational);
301
+ if (!existingJournal) {
302
+ const journal = crossEpicJournal(operation, backup.digest, backup.ref, 'prepared');
303
+ const result = await mutateEpicOperationalState(projectRoot, {
304
+ identityHash: operation.expectedIdentityHash,
305
+ expectedRevision: current.revision,
306
+ expectedWriterEpoch: operation.expectedWriterEpoch,
307
+ }, (state) => {
308
+ state.activeJournal = journal;
309
+ });
310
+ current = result.document;
311
+ operational = current.operational;
312
+ }
313
+ if (operational.activeJournal?.phase === 'prepared') {
314
+ assertCrossEpicPreimage(current, operational, operation, true);
315
+ const nextState = {
316
+ ...operational.yoloop.state,
317
+ continuationSession: operation.nextSession,
318
+ pendingContinuationAuthorities: [],
319
+ };
320
+ const result = await mutateEpicOperationalState(projectRoot, {
321
+ identityHash: operation.expectedIdentityHash,
322
+ expectedRevision: current.revision,
323
+ expectedWriterEpoch: operation.expectedWriterEpoch,
324
+ }, (state) => {
325
+ state.yoloop = { ...state.yoloop, state: nextState };
326
+ state.activeJournal = {
327
+ ...state.activeJournal, phase: 'authority-applied',
328
+ };
329
+ });
330
+ current = result.document;
331
+ operational = current.operational;
332
+ }
333
+ if (operational.activeJournal?.phase === 'authority-applied') {
334
+ const result = await mutateEpicOperationalState(projectRoot, {
335
+ identityHash: operation.expectedIdentityHash,
336
+ expectedRevision: current.revision,
337
+ expectedWriterEpoch: operation.expectedWriterEpoch,
338
+ }, (state) => {
339
+ state.activeJournal = {
340
+ ...state.activeJournal, phase: 'projections-applied',
341
+ projectionStatuses: { trackedState: 'not-applicable', sqlite: 'not-applicable' },
342
+ };
343
+ });
344
+ current = result.document;
345
+ operational = current.operational;
346
+ }
347
+ if (operational.activeJournal?.phase !== 'projections-applied') {
348
+ throw new Error('epic-operational-journal-corrupt');
349
+ }
350
+ const result = await mutateEpicOperationalState(projectRoot, {
351
+ identityHash: operation.expectedIdentityHash,
352
+ expectedRevision: current.revision,
353
+ expectedWriterEpoch: operation.expectedWriterEpoch,
354
+ }, (state) => {
355
+ if (state.reconciliationReceipts.length >= EPIC_OPERATIONAL_RECEIPT_CAP) {
356
+ throw new Error('yoloop-cross-epic-receipt-capacity');
357
+ }
358
+ state.reconciliationReceipts = [
359
+ ...state.reconciliationReceipts,
360
+ expectedReceipt,
361
+ ];
362
+ state.activeJournal = null;
363
+ });
364
+ assertEpicOperationalState(result.document.operational);
365
+ return { applied: true, replay: false, receipt: expectedReceipt };
366
+ }
367
+ function assertCrossEpicPreimage(document, state, operation, allowPrepared = false) {
368
+ const run = record(state.yoloop.state.runControl);
369
+ const pending = state.yoloop.state.pendingContinuationAuthorities ?? [];
370
+ const identity = state.identities[operation.expectedIdentityHash];
371
+ const session = record(state.yoloop.state.continuationSession);
372
+ if ((document.revision !== operation.expectedCommonRevision && !allowPrepared)
373
+ || state.writerEpoch !== operation.expectedWriterEpoch
374
+ || !identity || identity.epicId !== operation.targetEpicId
375
+ || identity.graphRevision !== operation.targetGraphRevision
376
+ || !state.yoloop.capabilities.includes('cross-epic-continuation-reconcile-v1')
377
+ || state.yoloop.lock !== null || state.yoloop.state.currentSession !== null
378
+ || !run || run.runId !== operation.expectedRunId || run.status !== operation.expectedRunStatus
379
+ || run.revision !== operation.expectedRunRevision
380
+ || (run.latestSnapshotHash ?? null) !== operation.expectedSnapshotHash
381
+ || !session || session.epicId !== operation.sourceEpicId
382
+ || session.graphFingerprint !== operation.sourceGraphRevision
383
+ || session.sessionId !== operation.sourceSessionId
384
+ || epicOperationalHash('p224-common-continuation-session-v1', state.yoloop.state.continuationSession ?? null)
385
+ !== operation.expectedSessionHash
386
+ || epicOperationalHash('p224-common-pending-authorities-v1', pending) !== operation.expectedPendingHash
387
+ || pending.length !== operation.expectedPendingCount) {
388
+ throw new Error('yoloop-cross-epic-revision-conflict');
389
+ }
390
+ if (pending.length > 1 || pending.some((entry) => {
391
+ const authority = record(entry);
392
+ const certificate = record(authority?.certificate);
393
+ return !authority || authority.delegated !== false || !certificate
394
+ || authority.sessionId !== operation.sourceSessionId
395
+ || certificate.sessionId !== operation.sourceSessionId;
396
+ }))
397
+ throw new Error('yoloop-cross-epic-authority-ambiguous');
398
+ }
399
+ function crossEpicReceipt(operation) {
400
+ return Object.freeze({
401
+ schemaVersion: 'YoloopCrossEpicReconciliationReceipt/1.0',
402
+ operationId: operation.operationId,
403
+ idempotencyKey: operation.idempotencyKey,
404
+ payloadHash: operation.payloadHash,
405
+ sourceEpicId: operation.sourceEpicId,
406
+ sourceGraphRevision: operation.sourceGraphRevision,
407
+ targetEpicId: operation.targetEpicId,
408
+ targetGraphRevision: operation.targetGraphRevision,
409
+ runId: operation.expectedRunId,
410
+ previousSessionHash: operation.expectedSessionHash,
411
+ nextSessionHash: operation.nextSessionHash,
412
+ retiredAuthorityHash: operation.expectedPendingHash,
413
+ retiredAuthorityCount: operation.expectedPendingCount,
414
+ outcome: 'cross-epic-reconciled',
415
+ publicSafe: true,
416
+ });
417
+ }
418
+ function crossEpicJournal(operation, preimageDigest, backupManifestRef, phase) {
419
+ return {
420
+ schemaVersion: 'EpicOperationalJournal/1.0',
421
+ kind: 'cross-epic-reconcile',
422
+ operationId: operation.operationId,
423
+ idempotencyKey: operation.idempotencyKey,
424
+ payloadHash: operation.payloadHash,
425
+ identityHash: operation.expectedIdentityHash,
426
+ expectedRevision: operation.expectedCommonRevision,
427
+ expectedWriterEpoch: operation.expectedWriterEpoch,
428
+ preimageDigest,
429
+ backupManifestRef,
430
+ phase,
431
+ projectionStatuses: { trackedState: 'not-applicable', sqlite: 'not-applicable' },
432
+ reasonCodes: ['epic-operational-backup-verified'],
433
+ };
434
+ }
435
+ function validateCrossEpicOperation(operation) {
436
+ const { type: _type, payloadHash: _payloadHash, receipt: _receipt, ...payload } = operation;
437
+ const nextSession = record(operation.nextSession);
438
+ if (!safeOperationId(operation.operationId) || !safeOperationId(operation.idempotencyKey)
439
+ || buildCrossEpicContinuationPayloadHash(payload) !== operation.payloadHash
440
+ || Buffer.byteLength(epicOperationalCanonicalJson(payload), 'utf8') > EPIC_OPERATIONAL_PAYLOAD_LIMIT_BYTES
441
+ || operation.expectedPendingCount < 0 || operation.expectedPendingCount > 1
442
+ || !nextSession || nextSession.epicId !== operation.targetEpicId
443
+ || nextSession.graphFingerprint !== operation.targetGraphRevision
444
+ || !hash(nextSession.sessionId)
445
+ || operation.nextSessionHash !== epicOperationalHash('p224-common-continuation-session-v1', operation.nextSession)
446
+ || epicOperationalCanonicalJson(operation.receipt) !== epicOperationalCanonicalJson(crossEpicReceipt(operation))) {
447
+ throw new Error('yoloop-cross-epic-replay-conflict');
448
+ }
449
+ }
450
+ function persistOperationalBackup(projectRoot, operationId, operational) {
451
+ if (!safeOperationId(operationId))
452
+ throw new Error('epic-operational-backup-failed');
453
+ const canonicalRoot = realpathSync(resolve(projectRoot));
454
+ const rawCommon = execFileSync('git', ['rev-parse', '--git-common-dir'], { cwd: canonicalRoot, encoding: 'utf8' }).trim();
455
+ const commonDir = realpathSync(isAbsolute(rawCommon) ? resolve(rawCommon) : resolve(canonicalRoot, rawCommon));
456
+ const runtimeDir = join(commonDir, GIT_LIFECYCLE_RUNTIME_DIRECTORY);
457
+ const backupsDir = join(runtimeDir, 'backups');
458
+ assertPrivateDirectory(runtimeDir, commonDir);
459
+ if (!existsSync(backupsDir))
460
+ mkdirSync(backupsDir, { mode: 0o700 });
461
+ assertPrivateDirectory(backupsDir, runtimeDir);
462
+ const name = `${operationId}.operational.json`;
463
+ const path = join(backupsDir, name);
464
+ const raw = `${JSON.stringify(operational, null, 2)}\n`;
465
+ const digest = createHash('sha256').update(raw).digest('hex');
466
+ if (existsSync(path)) {
467
+ const info = lstatSync(path);
468
+ if (!info.isFile() || info.isSymbolicLink()
469
+ || createHash('sha256').update(readFileSync(path)).digest('hex') !== digest) {
470
+ throw new Error('epic-operational-backup-failed');
471
+ }
472
+ }
473
+ else {
474
+ const fd = openSync(path, 'wx', 0o600);
475
+ try {
476
+ writeFileSync(fd, raw);
477
+ fsyncSync(fd);
478
+ }
479
+ finally {
480
+ closeSync(fd);
481
+ }
482
+ const directoryFd = openSync(backupsDir, 'r');
483
+ try {
484
+ fsyncSync(directoryFd);
485
+ }
486
+ finally {
487
+ closeSync(directoryFd);
488
+ }
489
+ }
490
+ return { ref: `backups/${name}`, digest };
491
+ }
492
+ function verifyOperationalBackup(projectRoot, backupRef, expectedDigest) {
493
+ if (!/^backups\/[A-Za-z0-9._-]{1,240}$/.test(backupRef) || !hash(expectedDigest)) {
494
+ throw new Error('epic-operational-backup-failed');
495
+ }
496
+ const canonicalRoot = realpathSync(resolve(projectRoot));
497
+ const rawCommon = execFileSync('git', ['rev-parse', '--git-common-dir'], {
498
+ cwd: canonicalRoot,
499
+ encoding: 'utf8',
500
+ }).trim();
501
+ const commonDir = realpathSync(isAbsolute(rawCommon) ? resolve(rawCommon) : resolve(canonicalRoot, rawCommon));
502
+ const runtimeDir = join(commonDir, GIT_LIFECYCLE_RUNTIME_DIRECTORY);
503
+ const path = join(runtimeDir, backupRef);
504
+ assertPrivateDirectory(runtimeDir, commonDir);
505
+ const info = lstatSync(path);
506
+ if (!info.isFile() || info.isSymbolicLink()
507
+ || createHash('sha256').update(readFileSync(path)).digest('hex') !== expectedDigest) {
508
+ throw new Error('epic-operational-backup-failed');
509
+ }
510
+ return { ref: backupRef, digest: expectedDigest };
511
+ }
512
+ function assertPrivateDirectory(path, parent) {
513
+ const canonicalParent = realpathSync(parent);
514
+ if (relative(canonicalParent, resolve(path)).startsWith('..'))
515
+ throw new Error('epic-operational-path-unsafe');
516
+ const info = lstatSync(path);
517
+ if (!info.isDirectory() || info.isSymbolicLink() || realpathSync(path) !== resolve(path)
518
+ || (typeof process.getuid === 'function' && (info.uid !== process.getuid() || (info.mode & 0o077) !== 0))) {
519
+ throw new Error('epic-operational-path-unsafe');
520
+ }
521
+ }
522
+ function assertOperationalContext(document, state, context) {
523
+ if (context?.expectedRevision !== undefined && document.revision !== context.expectedRevision) {
524
+ throw new Error('epic-operational-revision-conflict');
525
+ }
526
+ if (context?.expectedWriterEpoch !== undefined && state.writerEpoch !== context.expectedWriterEpoch) {
527
+ throw new Error('epic-operational-epoch-conflict');
528
+ }
529
+ if (context?.identityHash !== undefined && !state.identities[context.identityHash]) {
530
+ throw new Error('epic-operational-identity-mismatch');
531
+ }
532
+ }
533
+ function assertEpicOperationalIdentity(value) {
534
+ const item = record(value);
535
+ if (!item)
536
+ throw new Error('epic-operational-identity-mismatch');
537
+ exact(item, [
538
+ 'schemaVersion', 'epicId', 'executionBranch', 'graphRevision',
539
+ 'originBindingDigest', 'repositoryFingerprint', 'identityHash',
540
+ ]);
541
+ const { identityHash, ...base } = item;
542
+ if (item.schemaVersion !== 'EpicOperationalIdentity/1.0' || !ID.test(String(item.epicId))
543
+ || !safeGitRef(item.executionBranch) || !hash(item.graphRevision)
544
+ || !hash(item.originBindingDigest) || !hash(item.repositoryFingerprint)
545
+ || identityHash !== epicOperationalHash('p224-epic-operational-identity-v1', base)) {
546
+ throw new Error('epic-operational-identity-mismatch');
547
+ }
548
+ }
549
+ function validProjectionBinding(value) {
550
+ const item = record(value);
551
+ return Boolean(item && hasExactKeys(item, ['status', 'sourceDigest', 'authorityRevision', 'reasonCodes'])
552
+ && ['bound', 'unbound', 'unavailable'].includes(String(item.status))
553
+ && (item.sourceDigest === null || hash(item.sourceDigest))
554
+ && Number.isSafeInteger(item.authorityRevision) && Number(item.authorityRevision) >= 0
555
+ && Array.isArray(item.reasonCodes) && item.reasonCodes.length <= 16
556
+ && item.reasonCodes.every(safeReasonCode));
557
+ }
558
+ function validJournal(value) {
559
+ const item = record(value);
560
+ const statuses = record(item?.projectionStatuses);
561
+ return Boolean(item && hasExactKeys(item, [
562
+ 'schemaVersion', 'kind', 'operationId', 'idempotencyKey', 'payloadHash',
563
+ 'identityHash', 'expectedRevision', 'expectedWriterEpoch', 'preimageDigest',
564
+ 'backupManifestRef', 'phase', 'projectionStatuses', 'reasonCodes',
565
+ ]) && item.schemaVersion === 'EpicOperationalJournal/1.0'
566
+ && ['backfill', 'cross-epic-reconcile', 'rollback'].includes(String(item.kind))
567
+ && safeToken(item.operationId) && safeToken(item.idempotencyKey) && hash(item.payloadHash)
568
+ && hash(item.identityHash) && Number.isSafeInteger(item.expectedRevision)
569
+ && Number.isSafeInteger(item.expectedWriterEpoch) && hash(item.preimageDigest)
570
+ && typeof item.backupManifestRef === 'string' && item.backupManifestRef.length <= 1_024
571
+ && !isAbsolute(item.backupManifestRef) && !item.backupManifestRef.includes('..')
572
+ && ['prepared', 'authority-applied', 'projections-applied', 'committed', 'aborted-before-authority'].includes(String(item.phase))
573
+ && statuses && hasExactKeys(statuses, ['trackedState', 'sqlite'])
574
+ && ['pending', 'applied', 'not-applicable'].includes(String(statuses.trackedState))
575
+ && ['pending', 'applied', 'unavailable', 'not-applicable'].includes(String(statuses.sqlite))
576
+ && Array.isArray(item.reasonCodes) && item.reasonCodes.length <= 16
577
+ && item.reasonCodes.every(safeReasonCode));
578
+ }
579
+ function boundedReceipts(value, schema) {
580
+ return Array.isArray(value) && value.length <= EPIC_OPERATIONAL_RECEIPT_CAP
581
+ && value.every((entry) => schema === 'EpicLifecycleBackfillReceipt/1.0'
582
+ ? validBackfillReceipt(entry)
583
+ : schema === 'YoloopCrossEpicReconciliationReceipt/1.0'
584
+ ? validReconciliationReceipt(entry)
585
+ : validRollbackReceipt(entry));
586
+ }
587
+ function validBackfillReceipt(value) {
588
+ const item = record(value);
589
+ return Boolean(item && hasExactKeys(item, [
590
+ 'schemaVersion', 'operationId', 'idempotencyKey', 'payloadHash', 'planDigest',
591
+ 'sourceInventoryDigest', 'preimageDigest', 'postimageDigest',
592
+ 'authorityPostimageDigest', 'backupManifestDigest', 'identityHash',
593
+ 'appliedRevision', 'writerEpoch', 'stateMigrationOperationId', 'outcome',
594
+ 'reasonCodes', 'publicSafe',
595
+ ]) && item.schemaVersion === 'EpicLifecycleBackfillReceipt/1.0'
596
+ && safeOperationId(item.operationId) && safeOperationId(item.idempotencyKey)
597
+ && item.operationId === item.idempotencyKey
598
+ && [item.payloadHash, item.planDigest, item.sourceInventoryDigest, item.preimageDigest,
599
+ item.postimageDigest, item.authorityPostimageDigest, item.backupManifestDigest,
600
+ item.identityHash].every(hash)
601
+ && Number.isSafeInteger(item.appliedRevision) && Number(item.appliedRevision) >= 1
602
+ && Number.isSafeInteger(item.writerEpoch) && Number(item.writerEpoch) >= 1
603
+ && (item.stateMigrationOperationId === null || safeOperationId(item.stateMigrationOperationId))
604
+ && ['applied', 'already-applied'].includes(String(item.outcome))
605
+ && validReasonCodes(item.reasonCodes) && item.publicSafe === true);
606
+ }
607
+ function validReconciliationReceipt(value) {
608
+ const item = record(value);
609
+ return Boolean(item && hasExactKeys(item, [
610
+ 'schemaVersion', 'operationId', 'idempotencyKey', 'payloadHash', 'sourceEpicId',
611
+ 'sourceGraphRevision', 'targetEpicId', 'targetGraphRevision', 'runId',
612
+ 'previousSessionHash', 'nextSessionHash', 'retiredAuthorityHash',
613
+ 'retiredAuthorityCount', 'outcome', 'publicSafe',
614
+ ]) && item.schemaVersion === 'YoloopCrossEpicReconciliationReceipt/1.0'
615
+ && safeOperationId(item.operationId) && safeOperationId(item.idempotencyKey)
616
+ && hash(item.payloadHash) && ID.test(String(item.sourceEpicId)) && ID.test(String(item.targetEpicId))
617
+ && hash(item.sourceGraphRevision) && hash(item.targetGraphRevision)
618
+ && safeOperationId(item.runId) && hash(item.previousSessionHash) && hash(item.nextSessionHash)
619
+ && hash(item.retiredAuthorityHash) && [0, 1].includes(Number(item.retiredAuthorityCount))
620
+ && item.outcome === 'cross-epic-reconciled' && item.publicSafe === true);
621
+ }
622
+ function validRollbackReceipt(value) {
623
+ const item = record(value);
624
+ return Boolean(item && hasExactKeys(item, [
625
+ 'schemaVersion', 'operationId', 'targetOperationId', 'expectedPostimageDigest',
626
+ 'restoredPreimageDigest', 'reasonCodes', 'publicSafe',
627
+ ]) && item.schemaVersion === 'EpicOperationalRollbackReceipt/1.0'
628
+ && safeOperationId(item.operationId) && safeOperationId(item.targetOperationId)
629
+ && hash(item.expectedPostimageDigest) && hash(item.restoredPreimageDigest)
630
+ && validReasonCodes(item.reasonCodes) && item.publicSafe === true);
631
+ }
632
+ function validReasonCodes(value) {
633
+ return Array.isArray(value) && value.length <= 16 && value.every(safeReasonCode);
634
+ }
635
+ function sameStringSet(value, expected) {
636
+ return value.length === expected.length && expected.every((item) => value.includes(item));
637
+ }
638
+ function exact(item, keys) {
639
+ const allowed = new Set(keys);
640
+ if (Object.keys(item).some((key) => !allowed.has(key)))
641
+ throw new Error('epic-operational-store-corrupt');
642
+ }
643
+ function hasExactKeys(item, keys) {
644
+ const actual = Object.keys(item).sort();
645
+ const expected = [...keys].sort();
646
+ return actual.length === expected.length && actual.every((key, index) => key === expected[index]);
647
+ }
648
+ function record(value) {
649
+ return value && typeof value === 'object' && !Array.isArray(value)
650
+ ? value : undefined;
651
+ }
652
+ function hash(value) {
653
+ return typeof value === 'string' && HASH.test(value);
654
+ }
655
+ function safeToken(value) {
656
+ return typeof value === 'string' && SAFE_TOKEN.test(value) && !value.includes('..');
657
+ }
658
+ function safeOperationId(value) {
659
+ return typeof value === 'string' && /^[A-Za-z0-9._:-]{1,128}$/.test(value);
660
+ }
661
+ function safeReasonCode(value) {
662
+ return typeof value === 'string' && /^[a-z0-9][a-z0-9-]{0,79}$/.test(value);
663
+ }
664
+ function safeGitRef(value) {
665
+ return typeof value === 'string' && /^[A-Za-z0-9][A-Za-z0-9._/-]{0,239}$/.test(value)
666
+ && !value.includes('..') && !value.includes('//') && !value.includes('@{')
667
+ && !value.endsWith('/') && !value.endsWith('.');
668
+ }
669
+ function publicOperationalReason(error) {
670
+ const reason = error instanceof Error ? error.message : '';
671
+ return safeReasonCode(reason) ? reason : 'epic-operational-store-corrupt';
672
+ }