@ornexus/neocortex 4.60.73 → 4.60.74

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 (241) 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.js +1 -108
  22. package/packages/client/dist/cache/in-memory-asset-cache.js +1 -73
  23. package/packages/client/dist/cache/index.js +1 -13
  24. package/packages/client/dist/cache/protected-pi-boundary.js +1 -44
  25. package/packages/client/dist/checkpoint/checkpoint-client-reader.js +2 -127
  26. package/packages/client/dist/checkpoint/index.js +1 -11
  27. package/packages/client/dist/checkpoint/shared-checkpoint-types.js +1 -32
  28. package/packages/client/dist/cli.js +5 -358
  29. package/packages/client/dist/commands/activate.js +8 -394
  30. package/packages/client/dist/commands/architecture-answers.js +7 -274
  31. package/packages/client/dist/commands/architecture-memory-terminal.js +4 -253
  32. package/packages/client/dist/commands/architecture-non-ui-continuation.js +1 -100
  33. package/packages/client/dist/commands/architecture-root-binding.js +1 -51
  34. package/packages/client/dist/commands/cache-status.js +2 -112
  35. package/packages/client/dist/commands/config.js +1 -88
  36. package/packages/client/dist/commands/doctor.js +2 -635
  37. package/packages/client/dist/commands/invoke.js +143 -7200
  38. package/packages/client/dist/commands/legacy-epic-terminal.js +1 -71
  39. package/packages/client/dist/commands/legacy-story-branch.js +1 -96
  40. package/packages/client/dist/commands/legacy-story-pr.js +1 -276
  41. package/packages/client/dist/commands/mutation-qa-checkpoint.js +5 -285
  42. package/packages/client/dist/commands/mutation-qa-gate.js +1 -54
  43. package/packages/client/dist/commands/mutation-qa-git.js +1 -41
  44. package/packages/client/dist/commands/mutation-qa-policy.js +1 -102
  45. package/packages/client/dist/commands/mutation-qa-run.js +3 -219
  46. package/packages/client/dist/commands/mutation-qa-verdict.js +1 -149
  47. package/packages/client/dist/commands/mutation-qa.js +1 -220
  48. package/packages/client/dist/commands/native-agent.js +2 -136
  49. package/packages/client/dist/commands/planning-diagnosis.js +1 -43
  50. package/packages/client/dist/commands/planning-generation.js +1 -53
  51. package/packages/client/dist/commands/planning-memory.js +1 -40
  52. package/packages/client/dist/commands/planning-research.js +1 -42
  53. package/packages/client/dist/commands/private-session.js +1 -75
  54. package/packages/client/dist/commands/property-qa.js +1 -139
  55. package/packages/client/dist/commands/qa-control.js +1 -16
  56. package/packages/client/dist/commands/qa-scan-browser-launch.js +1 -55
  57. package/packages/client/dist/commands/qa-scan-browser-route.js +1 -28
  58. package/packages/client/dist/commands/qa-scan-capture.js +5 -663
  59. package/packages/client/dist/commands/qa-scan-discovery.js +3 -376
  60. package/packages/client/dist/commands/qa-scan-review.js +5 -826
  61. package/packages/client/dist/commands/qa-synthetic-backend-capture.js +1 -266
  62. package/packages/client/dist/commands/qa-synthetic-backend-review.js +1 -297
  63. package/packages/client/dist/commands/qa-synthetic-backend.js +2 -340
  64. package/packages/client/dist/commands/qa-synthetic-broker-port.js +1 -85
  65. package/packages/client/dist/commands/qa-synthetic-browser.js +1 -39
  66. package/packages/client/dist/commands/refactor-characterization.js +21 -671
  67. package/packages/client/dist/commands/refactor-commit.js +13 -399
  68. package/packages/client/dist/commands/refactor-control.js +3 -142
  69. package/packages/client/dist/commands/refactor-cpp-assertions.js +3 -93
  70. package/packages/client/dist/commands/refactor-review.js +5 -283
  71. package/packages/client/dist/commands/refactor-root.js +7 -588
  72. package/packages/client/dist/commands/refresh-memory.js +3 -156
  73. package/packages/client/dist/commands/shared-architecture-non-ui.js +1 -24
  74. package/packages/client/dist/commands/story-checkpoint.js +1 -171
  75. package/packages/client/dist/config/profile-directory.js +1 -13
  76. package/packages/client/dist/config/resolver-selection.js +1 -278
  77. package/packages/client/dist/config/secure-config.js +12 -281
  78. package/packages/client/dist/config.js +2 -223
  79. package/packages/client/dist/constants.js +1 -25
  80. package/packages/client/dist/context/context-collector.js +2 -222
  81. package/packages/client/dist/context/context-sanitizer.js +1 -145
  82. package/packages/client/dist/continuity/autonomous-governance.js +1 -285
  83. package/packages/client/dist/continuity/continuity-client-state-store.js +1 -1587
  84. package/packages/client/dist/continuity/invoke-hooks.js +1 -129
  85. package/packages/client/dist/continuity/migrations/001-initial-schema.js +2 -12
  86. package/packages/client/dist/continuity/migrations/002-processing-output-summary-indexes.js +2 -12
  87. package/packages/client/dist/continuity/migrations/003-headless-execution-attempts.js +2 -12
  88. package/packages/client/dist/continuity/migrations/004-outbox-delivery-state.js +2 -12
  89. package/packages/client/dist/continuity/migrations/005-bounded-headless-queue.js +2 -12
  90. package/packages/client/dist/continuity/migrations/006-native-agent-dispatches.js +2 -10
  91. package/packages/client/dist/continuity/native-agent-store.js +1 -69
  92. package/packages/client/dist/continuity/native-agent-types.js +1 -90
  93. package/packages/client/dist/continuity/outbox.js +1 -117
  94. package/packages/client/dist/continuity/runner-bootstrap-policy.js +1 -172
  95. package/packages/client/dist/continuity/sqlite-store.js +81 -2612
  96. package/packages/client/dist/effects/invoke-effect-gateways.js +1 -62
  97. package/packages/client/dist/errors/error-messages.js +2 -228
  98. package/packages/client/dist/errors/frontmatter-diagnostics.js +1 -125
  99. package/packages/client/dist/evidence/collector.js +1 -347
  100. package/packages/client/dist/evidence/index.js +1 -1
  101. package/packages/client/dist/evidence/types.js +0 -2
  102. package/packages/client/dist/fs/atomic-state-replace.js +1 -152
  103. package/packages/client/dist/generated/command-registry.generated.js +1 -4076
  104. package/packages/client/dist/generated/public-projections.generated.js +1 -149
  105. package/packages/client/dist/git/direct-yolo-c01-retry.js +4 -138
  106. package/packages/client/dist/git/direct-yolo-checkpoint.js +4 -1234
  107. package/packages/client/dist/git/epic-operational-state.js +2 -672
  108. package/packages/client/dist/git/git-lifecycle-step-ids.js +1 -14
  109. package/packages/client/dist/git/git-lifecycle-store.d.ts +23 -0
  110. package/packages/client/dist/git/git-lifecycle-store.js +10 -1981
  111. package/packages/client/dist/git/legacy-story-branch-checkpoint.js +9 -259
  112. package/packages/client/dist/git/legacy-story-branch-setup.js +4 -348
  113. package/packages/client/dist/git/legacy-story-pr.js +70 -3538
  114. package/packages/client/dist/git/secure-filesystem-boundary.js +5 -182
  115. package/packages/client/dist/graph-retrieval/pre-command-hook.js +1 -284
  116. package/packages/client/dist/graph-retrieval/shared-graph-retrieval-contract.js +1 -237
  117. package/packages/client/dist/i18n/first-run.js +2 -60
  118. package/packages/client/dist/index.js +1 -47
  119. package/packages/client/dist/invocation/invoke-input.js +1 -24
  120. package/packages/client/dist/license/index.js +1 -5
  121. package/packages/client/dist/license/license-client.js +1 -304
  122. package/packages/client/dist/machine/fingerprint.js +2 -163
  123. package/packages/client/dist/machine/index.js +1 -5
  124. package/packages/client/dist/memory/project-memory-path-scope.js +1 -62
  125. package/packages/client/dist/memory/project-memory-writer.js +40 -1007
  126. package/packages/client/dist/memory/shared-project-memory-types.js +2 -253
  127. package/packages/client/dist/migrations/epic-lifecycle-backfill.js +3 -1060
  128. package/packages/client/dist/migrations/project-identity-capability.js +1 -50
  129. package/packages/client/dist/migrations/project-identity.js +7 -493
  130. package/packages/client/dist/migrations/state-json.js +4 -393
  131. package/packages/client/dist/policy/architecture-policy.js +2 -184
  132. package/packages/client/dist/policy/index.js +1 -7
  133. package/packages/client/dist/policy/shared-policy-types.js +0 -38
  134. package/packages/client/dist/policy/subagent-approval-policy.js +1 -55
  135. package/packages/client/dist/private-execution/clean-baseline.js +2 -279
  136. package/packages/client/dist/private-execution/codex-executor.js +4 -574
  137. package/packages/client/dist/private-execution/codex-host-runtime.js +1 -137
  138. package/packages/client/dist/private-execution/codex-lifecycle-guard.js +1 -97
  139. package/packages/client/dist/private-execution/codex-scoped-permissions.js +1 -128
  140. package/packages/client/dist/private-execution/delivery.js +13 -410
  141. package/packages/client/dist/private-execution/diagnosis-root-review.js +2 -294
  142. package/packages/client/dist/private-execution/frozen-handoff.js +6 -19
  143. package/packages/client/dist/private-execution/implementation-validation.js +2 -106
  144. package/packages/client/dist/private-execution/isolated-admission.js +23 -1432
  145. package/packages/client/dist/private-execution/isolated-session.js +5 -725
  146. package/packages/client/dist/private-execution/isolated-workspace.js +1 -122
  147. package/packages/client/dist/private-execution/pact-consumer-runtime.js +3 -409
  148. package/packages/client/dist/private-execution/pact-test-source.js +1 -258
  149. package/packages/client/dist/private-execution/pact-toolchain.js +3 -290
  150. package/packages/client/dist/private-execution/pact-write-set.js +1 -172
  151. package/packages/client/dist/private-execution/planning-diagnosis-checkpoint.js +5 -406
  152. package/packages/client/dist/private-execution/planning-generation-checkpoint.js +5 -462
  153. package/packages/client/dist/private-execution/planning-memory-checkpoint.js +3 -139
  154. package/packages/client/dist/private-execution/planning-research-checkpoint.js +4 -325
  155. package/packages/client/dist/private-execution/private-report-store.js +1 -42
  156. package/packages/client/dist/private-execution/property-test-candidate.js +1 -103
  157. package/packages/client/dist/private-execution/property-test-choices.js +3 -147
  158. package/packages/client/dist/private-execution/property-test-plan.js +7 -271
  159. package/packages/client/dist/private-execution/property-test-review.js +4 -258
  160. package/packages/client/dist/private-execution/property-test-runtime.js +9 -372
  161. package/packages/client/dist/private-execution/property-test-source.js +1 -139
  162. package/packages/client/dist/private-execution/root-checkpoint.js +1 -80
  163. package/packages/client/dist/private-execution/root-memory-git-evidence.js +3 -135
  164. package/packages/client/dist/private-execution/root-memory-recovery.js +1 -93
  165. package/packages/client/dist/private-execution/root-memory-request.js +1 -29
  166. package/packages/client/dist/private-execution/root-memory-scope.js +1 -178
  167. package/packages/client/dist/private-execution/root-memory-writer.js +1 -102
  168. package/packages/client/dist/private-execution/session-lease.js +1 -63
  169. package/packages/client/dist/private-execution/shared-private-admission-types.js +1 -50
  170. package/packages/client/dist/private-execution/shared-project-validation.js +1 -66
  171. package/packages/client/dist/private-execution/shared-root-memory-types.js +1 -61
  172. package/packages/client/dist/private-execution/ux-source-validation.js +1 -372
  173. package/packages/client/dist/private-execution/ux-write-set.js +2 -191
  174. package/packages/client/dist/registry/registry-reconciliation.js +1 -234
  175. package/packages/client/dist/resilience/circuit-breaker.js +1 -170
  176. package/packages/client/dist/resilience/degradation-manager.js +1 -164
  177. package/packages/client/dist/resilience/freshness-indicator.js +1 -100
  178. package/packages/client/dist/resilience/index.js +1 -8
  179. package/packages/client/dist/resilience/recovery-detector.js +1 -74
  180. package/packages/client/dist/resolvers/asset-resolver.js +0 -13
  181. package/packages/client/dist/resolvers/local-resolver.js +8 -255
  182. package/packages/client/dist/resolvers/remote-resolver.js +1 -233
  183. package/packages/client/dist/runner/binary.js +1 -404
  184. package/packages/client/dist/runner/cli.js +17 -1571
  185. package/packages/client/dist/runner/headless/adapters.js +1 -49
  186. package/packages/client/dist/runner/headless/claude-code-adapter.js +1 -79
  187. package/packages/client/dist/runner/headless/continuous-story-workspace.js +11 -1628
  188. package/packages/client/dist/runner/headless/epic-git-reconciliation-inventory.js +1 -69
  189. package/packages/client/dist/runner/headless/epic-merge-candidate-qa.d.ts +1 -1
  190. package/packages/client/dist/runner/headless/epic-merge-candidate-qa.js +2 -110
  191. package/packages/client/dist/runner/headless/epic-terminal-git-cli-adapter.d.ts +2 -0
  192. package/packages/client/dist/runner/headless/epic-terminal-git-cli-adapter.js +10 -1236
  193. package/packages/client/dist/runner/headless/epic-terminal-git-runner.d.ts +18 -2
  194. package/packages/client/dist/runner/headless/epic-terminal-git-runner.js +2 -318
  195. package/packages/client/dist/runner/headless/fake-adapter.js +1 -62
  196. package/packages/client/dist/runner/headless/managed-profile.js +1 -132
  197. package/packages/client/dist/runner/headless/opencode-adapter.js +2 -145
  198. package/packages/client/dist/runner/headless/prepared-story-bundle.js +1 -43
  199. package/packages/client/dist/runner/headless/resolver.js +1 -231
  200. package/packages/client/dist/runner/headless/run-yolo.js +1 -885
  201. package/packages/client/dist/runner/headless/run-yoloop.d.ts +1 -0
  202. package/packages/client/dist/runner/headless/run-yoloop.js +2 -2574
  203. package/packages/client/dist/runner/headless/types.js +2 -574
  204. package/packages/client/dist/runner/local-console.js +13 -396
  205. package/packages/client/dist/runner/portal-sync-worker.js +1 -1487
  206. package/packages/client/dist/runner/project-identity.js +1 -106
  207. package/packages/client/dist/runner/scheduler.js +7 -995
  208. package/packages/client/dist/runner-cli.js +2 -18
  209. package/packages/client/dist/state/invocation-shape.js +1 -13
  210. package/packages/client/dist/state/project-state-snapshot.js +1 -1731
  211. package/packages/client/dist/state/state-json-repair.js +3 -785
  212. package/packages/client/dist/state/structural-dag-projection.js +1 -118
  213. package/packages/client/dist/telemetry/index.js +1 -5
  214. package/packages/client/dist/telemetry/offline-queue.js +1 -131
  215. package/packages/client/dist/tier/index.js +1 -5
  216. package/packages/client/dist/tier/tier-aware-client.js +1 -405
  217. package/packages/client/dist/types/index.js +1 -38
  218. package/packages/client/dist/version.js +1 -27
  219. package/packages/client/dist/yolo/change-evidence-collector.js +1 -126
  220. package/packages/client/dist/yolo/invoke-envelope-capability.js +1 -62
  221. package/packages/client/dist/yolo/spec-envelope-collector.js +5 -184
  222. package/packages/client/dist/yoloop/discovery-hook.js +2 -300
  223. package/packages/client/dist/yoloop/index.js +1 -8
  224. package/packages/client/dist/yoloop/invoke-hooks.js +12 -669
  225. package/packages/client/dist/yoloop/shared-discover-epics.js +1 -1007
  226. package/packages/client/dist/yoloop/shared-yoloop-types.js +1 -33
  227. package/packages/client/dist/yoloop/target-normalization.js +1 -180
  228. package/packages/client/dist/yoloop/yoloop-client-state-store.js +1 -1920
  229. package/packages/shared/dist/yolo/change-evidence.js +1 -1
  230. package/packages/shared/dist/yoloop/epic-terminal-git.js +144 -2
  231. package/targets-stubs/antigravity/gemini.md +1 -1
  232. package/targets-stubs/antigravity/skill/SKILL.md +1 -1
  233. package/targets-stubs/claude-code/neocortex-root.agent.yaml +1 -1
  234. package/targets-stubs/claude-code/neocortex-root.md +2 -2
  235. package/targets-stubs/claude-code/neocortex.agent.yaml +1 -1
  236. package/targets-stubs/claude-code/neocortex.md +2 -2
  237. package/targets-stubs/codex/AGENTS.md +1 -1
  238. package/targets-stubs/cursor/agent.md +2 -2
  239. package/targets-stubs/gemini-cli/agent.md +2 -2
  240. package/targets-stubs/opencode/neocortex-root.md +1 -1
  241. package/targets-stubs/vscode/neocortex.agent.md +2 -2
@@ -1,1731 +1 @@
1
- /**
2
- * @license FSL-1.1
3
- * Copyright (c) 2026 OrNexus AI
4
- *
5
- * Pure request-scoped projection helpers for invoke state snapshots.
6
- */
7
- import { createHash } from 'node:crypto';
8
- import { analyzeDependencyDag, StructuralDagError, } from './structural-dag-projection.js';
9
- import { classifyInvocationShapeV2 } from './invocation-shape.js';
10
- const RECENT_STORY_LIMIT = 12;
11
- const REASON_CODE_LIMIT = 16;
12
- const EPIC_LIFECYCLE_CLASSIFICATIONS = ['active', 'archived', 'rolled-back', 'synthetic', 'orphan'];
13
- // Publish-safe shadow of packages/shared/src/yoloop/status-policy.ts. The
14
- // standalone client tarball cannot take a runtime dependency on @neocortex/shared.
15
- const YOLOOP_ACTIVE_STORY_STATUS_SET = new Set([
16
- 'in-progress', 'branch-ready', 'diagnose', 'research', 'write-spec',
17
- 'create-tasks', 'implemented', 'refactor-suggest-done',
18
- 'property-tests-emitted', 'tests-validated', 'qa-gate-passed',
19
- 'memory-updated', 'committed', 'synced', 'pr-created', 'pr-reviewed',
20
- 'partial',
21
- ]);
22
- /**
23
- * P224.01 bounded common-authority evidence for a future invoke handoff.
24
- * This module remains pure: callers read the private store elsewhere and pass
25
- * only the already-redacted projection. Absolute paths, source bytes, unknown
26
- * private fields and certificates are intentionally impossible to project.
27
- */
28
- export function projectEpicOperationalEvidenceForRequest(value) {
29
- const hash = /^[a-f0-9]{64}$/;
30
- const safeId = /^P[1-9][0-9]*(?:\.[0-9]+)*$/;
31
- const safeBranch = /^[A-Za-z0-9][A-Za-z0-9._/-]{0,239}$/;
32
- if (value.schemaVersion !== 'EpicOperationalPublicProjection/1.0'
33
- || !hash.test(value.repositoryFingerprint)
34
- || !hash.test(value.identityHash)
35
- || !safeId.test(value.epicId)
36
- || !safeBranch.test(value.executionBranch)
37
- || !hash.test(value.graphRevision)
38
- || !Number.isSafeInteger(value.storeRevision) || value.storeRevision < 0
39
- || !Number.isSafeInteger(value.writerEpoch) || value.writerEpoch < 1
40
- || (value.runId !== null && value.runId.length > 128)
41
- || (value.runStatus !== null && value.runStatus.length > 32)
42
- || (value.runRevision !== null && (!Number.isSafeInteger(value.runRevision) || value.runRevision < 0))
43
- || (value.snapshotHash !== null && !hash.test(value.snapshotHash))
44
- || !hash.test(value.continuationSessionHash)
45
- || !hash.test(value.pendingAuthorityHash)
46
- || !Number.isSafeInteger(value.pendingAuthorityCount) || value.pendingAuthorityCount < 0
47
- || value.pendingAuthorityCount > 1_024
48
- || value.reasonCodes.length > 16
49
- || value.reasonCodes.some((reason) => !/^[a-z0-9][a-z0-9-]{0,79}$/.test(reason))
50
- || value.publicSafe !== true) {
51
- throw new Error('epic-operational-projection-invalid');
52
- }
53
- return Object.freeze({
54
- schemaVersion: value.schemaVersion,
55
- repositoryFingerprint: value.repositoryFingerprint,
56
- identityHash: value.identityHash,
57
- epicId: value.epicId,
58
- executionBranch: value.executionBranch,
59
- graphRevision: value.graphRevision,
60
- storeRevision: value.storeRevision,
61
- writerEpoch: value.writerEpoch,
62
- runId: value.runId,
63
- runStatus: value.runStatus,
64
- runRevision: value.runRevision,
65
- snapshotHash: value.snapshotHash,
66
- continuationSessionHash: value.continuationSessionHash,
67
- pendingAuthorityHash: value.pendingAuthorityHash,
68
- pendingAuthorityCount: value.pendingAuthorityCount,
69
- lockActive: value.lockActive,
70
- currentSessionActive: value.currentSessionActive,
71
- journalActive: value.journalActive,
72
- reasonCodes: [...value.reasonCodes],
73
- publicSafe: true,
74
- });
75
- }
76
- const policy = (commandId, stateProjectionPolicy, options = {}) => ({
77
- commandId,
78
- stateProjectionPolicy,
79
- ...options,
80
- });
81
- // P198.03: local publish-safe shadow of the P198.01 shared command-context
82
- // projection policies. The published CLI does not ship `packages/shared/dist`, so
83
- // this file must not add a runtime `@neocortex/shared` import until packaging is
84
- // changed. Keep ids/aliases/numbers aligned with the public command registry.
85
- const COMMAND_POLICY_BY_ID = Object.freeze({
86
- status: policy('status', 'full'),
87
- 'arch-plan': policy('arch-plan', 'full'),
88
- 'arch-prd': policy('arch-prd', 'full'),
89
- 'arch-architecture': policy('arch-architecture', 'epic-targeted', { structuralRequired: true }),
90
- 'arch-api-contracts': policy('arch-api-contracts', 'epic-targeted', { structuralRequired: true }),
91
- 'arch-api-integrations': policy('arch-api-integrations', 'epic-targeted', { structuralRequired: true }),
92
- 'arch-database': policy('arch-database', 'epic-targeted', { structuralRequired: true }),
93
- 'arch-security': policy('arch-security', 'epic-targeted', { structuralRequired: true }),
94
- 'arch-design-system': policy('arch-design-system', 'epic-targeted', { structuralRequired: true }),
95
- 'arch-ux-design': policy('arch-ux-design', 'epic-targeted', { structuralRequired: true }),
96
- 'arch-performance': policy('arch-performance', 'epic-targeted', { structuralRequired: true }),
97
- 'arch-testing': policy('arch-testing', 'epic-targeted', { structuralRequired: true }),
98
- 'arch-infrastructure': policy('arch-infrastructure', 'epic-targeted', { structuralRequired: true }),
99
- 'arch-review': policy('arch-review', 'epic-targeted', { structuralRequired: true }),
100
- 'arch-fitness-functions': policy('arch-fitness-functions', 'epic-targeted', { structuralRequired: true }),
101
- 'create-epic': policy('create-epic', 'epic-summary'),
102
- 'idea-diagnose': policy('idea-diagnose', 'full'),
103
- 'idea-research': policy('idea-research', 'epic-targeted', { structuralRequired: true }),
104
- 'generate-epic': policy('generate-epic', 'epic-targeted', { structuralRequired: true }),
105
- exec: policy('exec', 'story-targeted', { structuralRequired: true }),
106
- yolo: policy('yolo', 'story-targeted', { structuralRequired: true }),
107
- 'yolo-inline': policy('yolo-inline', 'story-targeted', { structuralRequired: true }),
108
- epic: policy('epic', 'epic-targeted', { structuralRequired: true }),
109
- 'setup-branch': policy('setup-branch', 'story-targeted', { structuralRequired: true }),
110
- diagnose: policy('diagnose', 'adaptive-targeted'),
111
- 'diagnose-inline': policy('diagnose-inline', 'full'),
112
- research: policy('research', 'adaptive-targeted'),
113
- 'research-inline': policy('research-inline', 'full'),
114
- 'write-spec': policy('write-spec', 'story-targeted', { structuralRequired: true }),
115
- 'create-tasks': policy('create-tasks', 'story-targeted', { structuralRequired: true }),
116
- implement: policy('implement', 'story-targeted', { structuralRequired: true }),
117
- commit: policy('commit', 'story-targeted', { structuralRequired: true }),
118
- 'commit-push': policy('commit', 'story-targeted', { structuralRequired: true }),
119
- sync: policy('sync', 'story-targeted', { structuralRequired: true }),
120
- 'sync-main': policy('sync', 'story-targeted', { structuralRequired: true }),
121
- 'create-pr': policy('create-pr', 'story-targeted', { structuralRequired: true }),
122
- review: policy('review', 'story-targeted'),
123
- 'review-pr': policy('review', 'story-targeted'),
124
- merge: policy('merge', 'story-targeted', { structuralRequired: true }),
125
- 'merge-pr': policy('merge', 'story-targeted', { structuralRequired: true }),
126
- 'fix-blocked': policy('fix-blocked', 'story-targeted'),
127
- 'finalize-epic': policy('finalize-epic', 'epic-targeted'),
128
- 'resolve-conflicts': policy('resolve-conflicts', 'story-targeted'),
129
- 'update-memory': policy('update-memory', 'full'),
130
- 'update-project-memory': policy('update-memory', 'story-targeted', { structuralRequired: true }),
131
- refactor: policy('refactor', 'adaptive-targeted'),
132
- 'refactor-suggest': policy('refactor', 'story-targeted', { structuralRequired: true }),
133
- cleanup: policy('cleanup', 'full'),
134
- audit: policy('audit', 'full'),
135
- 'tech-debt': policy('tech-debt', 'full'),
136
- menu: policy('menu', 'full'),
137
- plans: policy('plans', 'full'),
138
- billing: policy('billing', 'full'),
139
- subscribe: policy('subscribe', 'full'),
140
- 'qa-scan': policy('qa-scan', 'full'),
141
- yoloop: policy('yoloop', 'yoloop-targeted', { structuralRequired: true }),
142
- 'migrate-state': policy('migrate-state', 'full'),
143
- 'migrate-project-id': policy('migrate-project-id', 'full'),
144
- 'policy-audit': policy('policy-audit', 'full'),
145
- 'mutation-test': policy('mutation-test', 'full'),
146
- 'sync-openapi': policy('sync-openapi', 'full'),
147
- 'generate-pacts': policy('generate-pacts', 'full'),
148
- 'verify-pacts': policy('verify-pacts', 'full'),
149
- 'property-test': policy('property-test', 'full'),
150
- 'property-tests': policy('property-test', 'story-targeted', { structuralRequired: true }),
151
- 'chaos-plan': policy('chaos-plan', 'full'),
152
- 'ui-ux-review': policy('ui-ux-review', 'adaptive-targeted'),
153
- 'solyd-system': policy('solyd-system', 'adaptive-targeted'),
154
- goal: policy('goal', 'continuity-scoped'),
155
- loop: policy('loop', 'continuity-scoped'),
156
- jobs: policy('jobs', 'continuity-scoped'),
157
- runner: policy('runner', 'continuity-scoped'),
158
- 'goal-tick': policy('goal-tick', 'continuity-scoped'),
159
- 'loop-tick': policy('loop-tick', 'continuity-scoped'),
160
- 'validate-tests': policy('validate-tests', 'story-targeted', { structuralRequired: true }),
161
- 'qa-gate': policy('qa-gate', 'story-targeted', { structuralRequired: true }),
162
- });
163
- const COMMAND_ALIAS_TO_ID = Object.freeze({
164
- st: 'status',
165
- arquitetura: 'arch-plan',
166
- architecture: 'arch-plan',
167
- arch: 'arch-plan',
168
- prd: 'arch-prd',
169
- 'product-requirements': 'arch-prd',
170
- 'arch-doc': 'arch-architecture',
171
- 'tech-architecture': 'arch-architecture',
172
- 'api-contracts': 'arch-api-contracts',
173
- 'api-contract': 'arch-api-contracts',
174
- 'api-integrations': 'arch-api-integrations',
175
- 'api-int': 'arch-api-integrations',
176
- 'database-schema': 'arch-database',
177
- 'db-schema': 'arch-database',
178
- db: 'arch-database',
179
- 'cyber-security': 'arch-security',
180
- security: 'arch-security',
181
- sec: 'arch-security',
182
- 'design-system': 'arch-design-system',
183
- ds: 'arch-design-system',
184
- 'atomic-design': 'arch-design-system',
185
- 'ux-design': 'arch-ux-design',
186
- ux: 'arch-ux-design',
187
- wireframes: 'arch-ux-design',
188
- wireframe: 'arch-ux-design',
189
- 'user-flows': 'arch-ux-design',
190
- performance: 'arch-performance',
191
- perf: 'arch-performance',
192
- 'capacity-planning': 'arch-performance',
193
- capacity: 'arch-performance',
194
- 'arch-perf': 'arch-performance',
195
- 'performance-strategy': 'arch-performance',
196
- 'testing-strategy': 'arch-testing',
197
- 'test-plan': 'arch-testing',
198
- 'test-strategy': 'arch-testing',
199
- 'arch-test': 'arch-testing',
200
- infrastructure: 'arch-infrastructure',
201
- infra: 'arch-infrastructure',
202
- 'deploy-plan': 'arch-infrastructure',
203
- deployment: 'arch-infrastructure',
204
- 'arch-infra': 'arch-infrastructure',
205
- 'arch-deploy': 'arch-infrastructure',
206
- 'review-consolidation': 'arch-review',
207
- consolidation: 'arch-review',
208
- 'architecture-review': 'arch-review',
209
- 'arch-consolidation': 'arch-review',
210
- fitness: 'arch-fitness-functions',
211
- 'fitness-functions': 'arch-fitness-functions',
212
- archfit: 'arch-fitness-functions',
213
- 'arch-fit': 'arch-fitness-functions',
214
- 'fit-funcs': 'arch-fitness-functions',
215
- 'criar-epic': 'create-epic',
216
- planejamento: 'create-epic',
217
- 'idea-diag': 'idea-diagnose',
218
- 'idea-pesq': 'idea-research',
219
- 'gerar-epic': 'generate-epic',
220
- executar: 'exec',
221
- pipeline: 'exec',
222
- 'parallel-epic': 'epic',
223
- setup: 'setup-branch',
224
- branch: 'setup-branch',
225
- diag: 'diagnose',
226
- diagnostico: 'diagnose',
227
- c02: 'diagnose',
228
- pesquisar: 'research',
229
- pesq: 'research',
230
- 'research-fix': 'research',
231
- c03: 'research',
232
- spec: 'write-spec',
233
- especificacao: 'write-spec',
234
- c04: 'write-spec',
235
- tasks: 'create-tasks',
236
- tarefas: 'create-tasks',
237
- c05: 'create-tasks',
238
- impl: 'implement',
239
- implementar: 'implement',
240
- c06: 'implement',
241
- push: 'commit',
242
- 'commit_push': 'commit',
243
- cp: 'commit',
244
- c07: 'commit',
245
- sincronizar: 'sync',
246
- c08: 'sync',
247
- pr: 'create-pr',
248
- c09: 'create-pr',
249
- revisar: 'review',
250
- c11: 'review',
251
- mesclar: 'merge',
252
- c12: 'merge',
253
- fix: 'fix-blocked',
254
- corrigir: 'fix-blocked',
255
- finalizar: 'finalize-epic',
256
- conflitos: 'resolve-conflicts',
257
- memoria: 'update-memory',
258
- refactoring: 'refactor',
259
- refact: 'refactor',
260
- refac: 'refactor',
261
- rf: 'refactor',
262
- 'cleanup-code': 'refactor',
263
- 'code-cleanup': 'refactor',
264
- 'code-quality': 'refactor',
265
- 'improve-code': 'refactor',
266
- modernize: 'refactor',
267
- modernization: 'refactor',
268
- legacy: 'refactor',
269
- 'legacy-code': 'refactor',
270
- 'debt-pay': 'refactor',
271
- 'pay-debt': 'refactor',
272
- 'debt-reduction': 'refactor',
273
- 'smell-fix': 'refactor',
274
- 'fix-smells': 'refactor',
275
- 'code-smells': 'refactor',
276
- strangler: 'refactor',
277
- 'strangler-fig': 'refactor',
278
- limpar: 'cleanup',
279
- limpeza: 'cleanup',
280
- auditar: 'audit',
281
- auditoria: 'audit',
282
- debt: 'tech-debt',
283
- debts: 'tech-debt',
284
- 'debt-list': 'tech-debt',
285
- 'tech-debts': 'tech-debt',
286
- td: 'tech-debt',
287
- help: 'menu',
288
- ajuda: 'menu',
289
- '?': 'menu',
290
- commands: 'menu',
291
- cmds: 'menu',
292
- planos: 'plans',
293
- pricing: 'plans',
294
- bill: 'billing',
295
- subscription: 'billing',
296
- sub: 'billing',
297
- upgrade: 'subscribe',
298
- checkout: 'subscribe',
299
- qa: 'qa-scan',
300
- 'qa-test': 'qa-scan',
301
- 'visual-qa': 'qa-scan',
302
- 'e2e-scan': 'qa-scan',
303
- troubleshoot: 'qa-scan',
304
- 'debug-ui': 'qa-scan',
305
- scan: 'qa-scan',
306
- a11y: 'qa-scan',
307
- accessibility: 'qa-scan',
308
- wcag: 'qa-scan',
309
- 'auto-yolo': 'yoloop',
310
- 'epic-runner': 'yoloop',
311
- 'story-runner': 'yoloop',
312
- 'loop-yolo': 'yoloop',
313
- 'auto-epic': 'yoloop',
314
- migrate: 'migrate-state',
315
- 'state-migrate': 'migrate-state',
316
- 'schema-migrate': 'migrate-state',
317
- policy: 'policy-audit',
318
- 'audit-policy': 'policy-audit',
319
- 'policy-history': 'policy-audit',
320
- policies: 'policy-audit',
321
- mutation: 'mutation-test',
322
- stryker: 'mutation-test',
323
- 'kill-mutants': 'mutation-test',
324
- 'openapi-sync': 'sync-openapi',
325
- 'api-sync': 'sync-openapi',
326
- 'spec-sync': 'sync-openapi',
327
- 'regen-openapi': 'sync-openapi',
328
- pacts: 'generate-pacts',
329
- 'gen-pacts': 'generate-pacts',
330
- 'regen-pacts': 'generate-pacts',
331
- 'create-pacts': 'generate-pacts',
332
- 'verify-pact': 'verify-pacts',
333
- 'pact-verify': 'verify-pacts',
334
- 'check-pacts': 'verify-pacts',
335
- 'pact-check': 'verify-pacts',
336
- property: 'property-test',
337
- pbt: 'property-test',
338
- proptest: 'property-test',
339
- 'invariant-test': 'property-test',
340
- chaos: 'chaos-plan',
341
- gameday: 'chaos-plan',
342
- 'resilience-test': 'chaos-plan',
343
- 'ux-review': 'ui-ux-review',
344
- 'design-review': 'ui-ux-review',
345
- 'visual-review': 'ui-ux-review',
346
- 'product-polish': 'ui-ux-review',
347
- 'security-system': 'solyd-system',
348
- 'authorized-security': 'solyd-system',
349
- 'sec-system': 'solyd-system',
350
- goals: 'goal',
351
- 'durable-goal': 'goal',
352
- 'continuity-goal': 'goal',
353
- loops: 'loop',
354
- 'durable-loop': 'loop',
355
- 'continuity-loop': 'loop',
356
- job: 'jobs',
357
- 'continuity-jobs': 'jobs',
358
- 'neocortex-runner': 'runner',
359
- 'continuity-runner': 'runner',
360
- });
361
- const COMMAND_NUMBER_TO_ID = Object.freeze({
362
- '1': 'status',
363
- '3': 'arch-plan',
364
- '4': 'arch-prd',
365
- '5': 'arch-architecture',
366
- '6': 'arch-api-contracts',
367
- '7': 'arch-api-integrations',
368
- '8': 'arch-database',
369
- '9': 'arch-security',
370
- '10': 'arch-design-system',
371
- '11': 'arch-ux-design',
372
- '12': 'arch-performance',
373
- '13': 'arch-testing',
374
- '14': 'arch-infrastructure',
375
- '15': 'arch-review',
376
- '16': 'arch-fitness-functions',
377
- '17': 'create-epic',
378
- '18': 'idea-diagnose',
379
- '19': 'idea-research',
380
- '20': 'generate-epic',
381
- '21': 'exec',
382
- '22': 'yolo',
383
- '23': 'epic',
384
- '24': 'setup-branch',
385
- '25': 'diagnose',
386
- '26': 'research',
387
- '27': 'write-spec',
388
- '28': 'create-tasks',
389
- '29': 'implement',
390
- '30': 'commit',
391
- '31': 'sync',
392
- '32': 'create-pr',
393
- '33': 'review',
394
- '34': 'merge',
395
- '35': 'fix-blocked',
396
- '36': 'finalize-epic',
397
- '37': 'resolve-conflicts',
398
- '38': 'update-memory',
399
- '39': 'refactor',
400
- '40': 'cleanup',
401
- '41': 'audit',
402
- '42': 'tech-debt',
403
- '43': 'menu',
404
- '44': 'plans',
405
- '45': 'billing',
406
- '46': 'subscribe',
407
- '47': 'qa-scan',
408
- '48': 'yoloop',
409
- '49': 'migrate-state',
410
- '50': 'policy-audit',
411
- '51': 'mutation-test',
412
- '52': 'sync-openapi',
413
- '53': 'generate-pacts',
414
- '54': 'verify-pacts',
415
- '55': 'property-test',
416
- '56': 'chaos-plan',
417
- '57': 'ui-ux-review',
418
- '58': 'solyd-system',
419
- '59': 'goal',
420
- '60': 'loop',
421
- '61': 'jobs',
422
- '62': 'runner',
423
- });
424
- function safeToken(value, maxLength = 120) {
425
- if (typeof value !== 'string')
426
- return undefined;
427
- const trimmed = value.trim();
428
- if (!trimmed)
429
- return undefined;
430
- return trimmed.replace(/[^a-z0-9_.:-]+/gi, '_').slice(0, maxLength);
431
- }
432
- function safeCanonicalToken(value, maxLength = 120) {
433
- if (typeof value !== 'string')
434
- return undefined;
435
- const trimmed = value.trim();
436
- const safe = safeToken(value, maxLength);
437
- return safe === trimmed ? safe : undefined;
438
- }
439
- function safeGitRef(value) {
440
- if (typeof value !== 'string')
441
- return undefined;
442
- const trimmed = value.trim();
443
- if (!trimmed || trimmed.length > 240 || !/^[A-Za-z0-9][A-Za-z0-9._/-]*$/.test(trimmed)
444
- || trimmed.includes('..') || trimmed.includes('//') || trimmed.includes('@{')
445
- || trimmed.endsWith('/') || trimmed.endsWith('.')
446
- || trimmed.split('/').some((part) => !part || part.startsWith('.') || part.endsWith('.lock')))
447
- return undefined;
448
- return trimmed;
449
- }
450
- function safeWriteRef(value) {
451
- if (typeof value !== 'string')
452
- return undefined;
453
- const normalized = value.replace(/\\/g, '/');
454
- if (!normalized || normalized.length > 240 || normalized.startsWith('/')
455
- || /^[A-Za-z]:/.test(normalized) || normalized.includes('..') || normalized.includes('//')
456
- || normalized.split('/').includes('.') || !/^[A-Za-z0-9._/:@#-]+$/.test(normalized))
457
- return undefined;
458
- return normalized;
459
- }
460
- function safeWriteRefs(value) {
461
- if (value === undefined)
462
- return undefined;
463
- if (!Array.isArray(value) || value.length > 128)
464
- return undefined;
465
- const refs = value.map(safeWriteRef);
466
- if (refs.some((ref) => ref === undefined))
467
- return undefined;
468
- return [...new Set(refs)].sort((left, right) => left.localeCompare(right, 'en'));
469
- }
470
- function isActiveYoloopStory(status) {
471
- return typeof status === 'string'
472
- && YOLOOP_ACTIVE_STORY_STATUS_SET.has(status.trim().toLowerCase().replace(/_/g, '-'));
473
- }
474
- function pickPublicScalarFields(value, allowlist) {
475
- const result = {};
476
- for (const key of allowlist) {
477
- const field = value[key];
478
- if (typeof field === 'string' || typeof field === 'boolean'
479
- || typeof field === 'number' && Number.isFinite(field))
480
- result[key] = field;
481
- else if (Array.isArray(field) && field.every((item) => typeof item === 'string'))
482
- result[key] = [...field];
483
- }
484
- return result;
485
- }
486
- function publicResumeCheckpoint(storyId, story) {
487
- const value = story.checkpoint;
488
- if (!isRecord(value) || value.schemaVersion !== 1 || value.storyId !== storyId
489
- || value.epicId !== story.epic_id || !safeGitRef(value.branch)
490
- || typeof value.baseSha !== 'string' || !/^[0-9a-f]{7,64}$/i.test(value.baseSha)
491
- || typeof value.headSha !== 'string' || !/^[0-9a-f]{7,64}$/i.test(value.headSha)
492
- || value.baseSha === value.headSha || !safeCanonicalToken(value.idempotencyKey)
493
- || typeof value.recordedAt !== 'string' || !Number.isFinite(Date.parse(value.recordedAt))
494
- || !Array.isArray(value.commits) || value.commits.length === 0 || value.commits.length > 256
495
- || value.commits.some((sha) => typeof sha !== 'string' || !/^[0-9a-f]{7,64}$/i.test(sha))
496
- || !Array.isArray(value.receiptRefs) || value.receiptRefs.length === 0 || value.receiptRefs.length > 256
497
- || value.receiptRefs.some((ref) => typeof ref !== 'string' || ref.length > 240 || ref.includes('..')
498
- || !/^[A-Za-z0-9._:/#\-[\]]+$/.test(ref))
499
- || !isRecord(value.qaReceipt) || value.qaReceipt.kind !== 'story_qa_evidence'
500
- || value.qaReceipt.producer !== 'local-yolo-tdd'
501
- || !['passed', 'blocked'].includes(String(value.qaVerdict))
502
- || !['approved', 'changes-requested', 'pending'].includes(String(value.reviewVerdict)))
503
- return undefined;
504
- if (value.reviewVerdict === 'pending' ? value.reviewReceipt !== undefined : !isRecord(value.reviewReceipt))
505
- return undefined;
506
- // collectStateSnapshot already validated field values. Re-project the exact
507
- // public allowlist so later hooks or synthetic callers cannot append content.
508
- const result = pickPublicScalarFields(value, [
509
- 'schemaVersion', 'storyId', 'epicId', 'branch', 'baseSha', 'headSha',
510
- 'idempotencyKey', 'dispatchId', 'bundleId', 'bundleDigest', 'graphRevision',
511
- 'claimRevision', 'qaVerdict', 'reviewVerdict', 'recordedAt', 'commits',
512
- 'receiptRefs',
513
- ]);
514
- result.qaReceipt = pickPublicScalarFields(value.qaReceipt, [
515
- 'schemaVersion', 'kind', 'producer', 'evidenceVersion', 'storyId', 'epicId',
516
- 'contract', 'command', 'manifestDigest', 'gateIds', 'resultDigest', 'baseSha',
517
- 'headSha', 'bundleDigest', 'graphRevision', 'sourceEvidenceDigest', 'verdict',
518
- 'checkedAt', 'receiptRef',
519
- ]);
520
- if (isRecord(value.reviewReceipt)) {
521
- result.reviewReceipt = pickPublicScalarFields(value.reviewReceipt, [
522
- 'schemaVersion', 'kind', 'producer', 'storyId', 'epicId', 'baseSha',
523
- 'headSha', 'bundleDigest', 'graphRevision', 'sourceEvidenceDigest',
524
- 'qaReceiptRef', 'verdict', 'reviewedAt', 'receiptRef',
525
- ]);
526
- }
527
- return result;
528
- }
529
- function publicCheckpointVerification(storyId, story) {
530
- const value = story.checkpoint_verification;
531
- if (!isRecord(value) || value.schemaVersion !== 1
532
- || value.kind !== 'story_checkpoint_physical_verification'
533
- || value.producer !== 'client-p205-transient-v1' || value.publicSafe !== true
534
- || value.storyId !== storyId || value.epicId !== story.epic_id
535
- || !['verified', 'rejected'].includes(String(value.verdict)))
536
- return undefined;
537
- return pickPublicScalarFields(value, [
538
- 'schemaVersion', 'kind', 'producer', 'storyId', 'epicId', 'branch',
539
- 'baseSha', 'headSha', 'aggregateHeadSha', 'commitBodyDigest',
540
- 'changedPathsDigest', 'claimVerification', 'effectiveClaimRevision',
541
- 'command', 'manifestDigest', 'gateIds', 'resultDigest', 'graphRevision',
542
- 'executionEvidenceDigest', 'sourceEvidenceDigest', 'verifiedAt', 'verdict',
543
- 'reasonCodes', 'publicSafe',
544
- ]);
545
- }
546
- function publicSchedulingCheckpointEvidence(storyId, story) {
547
- const value = story.scheduling_checkpoint_evidence;
548
- const allowed = new Set([
549
- 'schemaVersion', 'kind', 'producer', 'storyId', 'epicId', 'branch', 'baseSha', 'headSha',
550
- 'graphRevision', 'effectiveClaimRevision', 'sourceEvidenceDigest', 'verifiedAt', 'reasonCodes',
551
- 'integrationAuthorized', 'publicSafe',
552
- ]);
553
- if (!isRecord(value) || Object.keys(value).some((key) => !allowed.has(key))
554
- || value.schemaVersion !== 1 || value.kind !== 'story_scheduling_checkpoint_evidence'
555
- || value.producer !== 'client-git-physical-v1' || value.publicSafe !== true
556
- || value.integrationAuthorized !== false || value.storyId !== storyId || value.epicId !== story.epic_id
557
- || !safeGitRef(value.branch) || typeof value.baseSha !== 'string' || !/^[0-9a-f]{40,64}$/i.test(value.baseSha)
558
- || typeof value.headSha !== 'string' || !/^[0-9a-f]{40,64}$/i.test(value.headSha)
559
- || typeof value.graphRevision !== 'string' || !/^[0-9a-f]{64}$/i.test(value.graphRevision)
560
- || typeof value.effectiveClaimRevision !== 'string' || !/^[0-9a-f]{64}$/i.test(value.effectiveClaimRevision)
561
- || typeof value.sourceEvidenceDigest !== 'string' || !/^[0-9a-f]{64}$/i.test(value.sourceEvidenceDigest)
562
- || typeof value.verifiedAt !== 'string' || !Number.isFinite(Date.parse(value.verifiedAt))
563
- || !Array.isArray(value.reasonCodes) || value.reasonCodes.length !== 1
564
- || value.reasonCodes[0] !== 'checkpoint-git-scheduling-verified')
565
- return undefined;
566
- return pickPublicScalarFields(value, [...allowed]);
567
- }
568
- function projectSchedulingLane(storyId, story) {
569
- const projected = { ...story };
570
- const evidence = publicSchedulingCheckpointEvidence(storyId, story);
571
- if (evidence) {
572
- projected.scheduling_checkpoint_evidence = evidence;
573
- delete projected.checkpoint;
574
- delete projected.checkpoint_verification;
575
- }
576
- else {
577
- delete projected.scheduling_checkpoint_evidence;
578
- }
579
- return projected;
580
- }
581
- function isRecord(value) {
582
- return value !== null && typeof value === 'object' && !Array.isArray(value);
583
- }
584
- function safeReasonCodes(...groups) {
585
- const seen = new Set();
586
- const reasonCodes = [];
587
- for (const group of groups) {
588
- for (const reasonCode of group ?? []) {
589
- const safe = safeToken(reasonCode, 80);
590
- if (!safe || seen.has(safe))
591
- continue;
592
- seen.add(safe);
593
- reasonCodes.push(safe);
594
- if (reasonCodes.length >= REASON_CODE_LIMIT)
595
- return reasonCodes;
596
- }
597
- }
598
- return reasonCodes;
599
- }
600
- function normalizeCommandToken(input) {
601
- return input.trim().replace(/^@?\*/, '').replace(/^@/, '').toLowerCase();
602
- }
603
- function leadingCommandToken(input) {
604
- return input.trim().split(/\s+/, 1)[0] ?? '';
605
- }
606
- function resolveCommandProjectionPolicy(args) {
607
- // The exact published @epic-P{N} spelling is a one-token command and its
608
- // own target. It must receive the same structural projection as *epic.
609
- if (/^@epic-P[0-9]{1,12}$/.test(args.trim()))
610
- return COMMAND_POLICY_BY_ID.epic;
611
- const token = leadingCommandToken(args);
612
- if (!token)
613
- return COMMAND_POLICY_BY_ID.menu;
614
- const normalized = normalizeCommandToken(token);
615
- const commandId = COMMAND_NUMBER_TO_ID[normalized]
616
- ?? COMMAND_ALIAS_TO_ID[normalized]
617
- ?? normalized;
618
- return COMMAND_POLICY_BY_ID[commandId] ?? null;
619
- }
620
- function createFullScope(trigger, snapshot, commandPolicy, projectionMode = 'full-compatible', reasonCodes = ['full-state']) {
621
- return {
622
- kind: 'full',
623
- commandId: commandPolicy?.commandId ?? null,
624
- stateProjectionPolicy: commandPolicy?.stateProjectionPolicy ?? null,
625
- projectionMode,
626
- trigger,
627
- targetEpicId: null,
628
- targetStoryId: null,
629
- includedStoryCount: Object.keys(snapshot.stories).length,
630
- includedEpicCount: Object.keys(snapshot.epics).length,
631
- omittedStoryCount: 0,
632
- omittedEpicCount: 0,
633
- reason: 'full-state',
634
- reasonCodes: safeReasonCodes(reasonCodes),
635
- };
636
- }
637
- function fullSnapshot(trigger, snapshot, commandPolicy, projectionMode = 'full-compatible', reasonCodes = ['full-state']) {
638
- const scope = createFullScope(trigger, snapshot, commandPolicy, projectionMode, reasonCodes);
639
- return withStateSummary({
640
- ...snapshot,
641
- scope,
642
- }, snapshot, scope);
643
- }
644
- function firstTokenIsStoryReference(args) {
645
- const [firstToken] = args.trim().split(/\s+/, 1);
646
- if (!firstToken)
647
- return false;
648
- return (/^[A-Z]?\d+(?:\.\d+)+$/i.test(firstToken.replace(/^@/, '')) ||
649
- /(?:^|\/)[A-Z]?\d+(?:\.\d+)+\.story\.md$/i.test(firstToken.replace(/^@/, '')));
650
- }
651
- function extractStoryId(args) {
652
- const storyFileMatch = args.match(/(?:^|[/@\s])([A-Z]?\d+(?:\.\d+)+)\.story\.md\b/i);
653
- if (storyFileMatch?.[1])
654
- return storyFileMatch[1];
655
- const storyIdMatch = args.match(/(?:^|\s|@)([A-Z]?\d+(?:\.\d+)+)(?=$|\s)/i);
656
- return storyIdMatch?.[1] ?? null;
657
- }
658
- function triggerRemainder(args) {
659
- const trimmed = args.trim();
660
- const firstWhitespace = trimmed.search(/\s/);
661
- return firstWhitespace === -1 ? '' : trimmed.slice(firstWhitespace).trim();
662
- }
663
- function positionalTokens(args) {
664
- return triggerRemainder(args)
665
- .split(/\s+/)
666
- .map((token) => token.replace(/^@/, '').replace(/^['"]|['"]$/g, ''))
667
- .filter((token) => token.length > 0 && !token.startsWith('-'));
668
- }
669
- function firstPositionalArg(args) {
670
- return positionalTokens(args)[0] ?? null;
671
- }
672
- function resolveEpicId(candidate, snapshot) {
673
- const normalized = candidate.replace(/^@/, '').replace(/^epic-/i, '');
674
- const withoutExtension = normalized.replace(/\.md$/i, '');
675
- const candidates = [
676
- withoutExtension,
677
- withoutExtension.toUpperCase(),
678
- /^p\d+$/i.test(withoutExtension)
679
- ? `P${withoutExtension.replace(/^p/i, '')}`
680
- : null,
681
- /^\d+$/.test(withoutExtension) ? `P${withoutExtension}` : null,
682
- ].filter((entry) => typeof entry === 'string' && entry.length > 0);
683
- for (const entry of candidates) {
684
- if (snapshot.epics[entry])
685
- return entry;
686
- }
687
- return null;
688
- }
689
- function extractEpicIdFromCanonicalFile(args, snapshot) {
690
- const match = args.match(/(?:^|[/@\s])epic-(P?\d+)\.md\b/i);
691
- if (!match?.[1])
692
- return null;
693
- return resolveEpicId(match[1], snapshot);
694
- }
695
- function extractTargetEpicId(args, snapshot) {
696
- const directEpic = args.trim().match(/^@epic-(P[0-9]{1,12})$/);
697
- if (directEpic)
698
- return resolveEpicId(directEpic[1], snapshot);
699
- const fromFile = extractEpicIdFromCanonicalFile(args, snapshot);
700
- if (fromFile)
701
- return fromFile;
702
- for (const token of positionalTokens(args)) {
703
- if (/^[A-Z]?\d+(?:\.\d+)+$/i.test(token))
704
- continue;
705
- const fromToken = resolveEpicId(token, snapshot);
706
- if (fromToken)
707
- return fromToken;
708
- }
709
- return null;
710
- }
711
- function extractYoloopTargetEpicId(args, snapshot) {
712
- const fromTarget = extractTargetEpicId(args, snapshot);
713
- if (fromTarget)
714
- return fromTarget;
715
- const yoloop = snapshot.yoloop;
716
- const sessionEpicId = yoloop?.state?.currentSession?.currentEpicId;
717
- if (typeof sessionEpicId === 'string' && snapshot.epics[sessionEpicId]) {
718
- return sessionEpicId;
719
- }
720
- const lockEpicId = yoloop?.lock?.currentEpicId ?? yoloop?.lock?.epicId;
721
- if (typeof lockEpicId === 'string' && snapshot.epics[lockEpicId]) {
722
- return lockEpicId;
723
- }
724
- return null;
725
- }
726
- function explicitYoloopEpicCandidate(args) {
727
- const fromFile = args.match(/(?:^|[/@\s])epic-(P?\d+)\.md\b/i)?.[1];
728
- if (fromFile)
729
- return /^P/i.test(fromFile) ? fromFile.toUpperCase() : `P${fromFile}`;
730
- for (const token of positionalTokens(args)) {
731
- if (/^P\d+$/i.test(token))
732
- return token.toUpperCase();
733
- if (/^\d+$/.test(token))
734
- return `P${token}`;
735
- }
736
- return null;
737
- }
738
- function storyEpicId(story, storyId) {
739
- if (typeof story.epic_id === 'string' && story.epic_id.length > 0) {
740
- return story.epic_id;
741
- }
742
- const derived = storyId.split('.').slice(0, -1).join('.');
743
- return derived.length > 0 ? derived : null;
744
- }
745
- function isTerminalStoryStatus(status) {
746
- return typeof status === 'string' && ['done', 'completed', 'merged', 'cancelled', 'canceled', 'abandoned'].includes(status.toLowerCase());
747
- }
748
- function addStoryAndDependencies(storyId, snapshot, includedStoryIds) {
749
- if (includedStoryIds.has(storyId))
750
- return;
751
- const story = snapshot.stories[storyId];
752
- if (!story)
753
- return;
754
- includedStoryIds.add(storyId);
755
- const dependsOn = story.depends_on;
756
- if (!Array.isArray(dependsOn))
757
- return;
758
- for (const dependencyId of dependsOn) {
759
- if (typeof dependencyId === 'string') {
760
- addStoryAndDependencies(dependencyId, snapshot, includedStoryIds);
761
- }
762
- }
763
- }
764
- function buildProjectedStories(targetStoryId, snapshot) {
765
- const analysis = analyzeDependencyDag(snapshot.stories, [targetStoryId]);
766
- const projectedStories = {};
767
- for (const storyId of analysis.orderedIds) {
768
- const story = snapshot.stories[storyId];
769
- if (story) {
770
- projectedStories[storyId] = story;
771
- }
772
- }
773
- return projectedStories;
774
- }
775
- function buildProjectedEpics(projectedStories, snapshot, requiredEpicIds = []) {
776
- const projectedEpics = {};
777
- for (const epicId of requiredEpicIds) {
778
- if (snapshot.epics[epicId]) {
779
- projectedEpics[epicId] = snapshot.epics[epicId];
780
- }
781
- }
782
- for (const [storyId, story] of Object.entries(projectedStories)) {
783
- const epicId = storyEpicId(story, storyId);
784
- if (epicId && snapshot.epics[epicId]) {
785
- projectedEpics[epicId] = snapshot.epics[epicId];
786
- }
787
- }
788
- return projectedEpics;
789
- }
790
- function addEpicAndDependencies(epicId, snapshot, includedEpicIds) {
791
- if (includedEpicIds.has(epicId))
792
- return;
793
- const epic = snapshot.epics[epicId];
794
- if (!epic)
795
- return;
796
- includedEpicIds.add(epicId);
797
- const dependsOn = epic.depends_on;
798
- if (!Array.isArray(dependsOn))
799
- return;
800
- for (const dependencyId of dependsOn) {
801
- if (typeof dependencyId === 'string' && dependencyId.length > 0) {
802
- addEpicAndDependencies(dependencyId, snapshot, includedEpicIds);
803
- }
804
- }
805
- }
806
- function buildProjectedYoloopEpics(targetEpicId, projectedStories, snapshot) {
807
- const epicAnalysis = analyzeDependencyDag(snapshot.epics, [targetEpicId]);
808
- const requiredEpicIds = new Set(epicAnalysis.orderedIds);
809
- for (const [storyId, story] of Object.entries(projectedStories)) {
810
- const parentEpicId = storyEpicId(story, storyId);
811
- if (parentEpicId && snapshot.epics[parentEpicId])
812
- requiredEpicIds.add(parentEpicId);
813
- }
814
- const projectedEpics = {};
815
- for (const epicId of [...requiredEpicIds].sort((left, right) => left.localeCompare(right, 'en'))) {
816
- const epic = snapshot.epics[epicId];
817
- if (!epic)
818
- continue;
819
- projectedEpics[epicId] = epicId === targetEpicId ? epic : compactStructuralEpic(epicId, epic);
820
- }
821
- return projectedEpics;
822
- }
823
- function buildProjectedYoloopStories(targetEpicId, targetEpic, snapshot) {
824
- const storyIds = targetEpic.stories;
825
- if (!Array.isArray(storyIds)
826
- || storyIds.some((storyId) => typeof storyId !== 'string' || storyId.length === 0)) {
827
- throw new StructuralDagError('STRUCTURAL_DEPENDENCY_MISSING', {
828
- nodes: 0,
829
- edges: 0,
830
- malformedStoryMemberships: 1,
831
- });
832
- }
833
- const analysis = analyzeDependencyDag(snapshot.stories, storyIds);
834
- const projectedStories = {};
835
- for (const storyId of analysis.orderedIds) {
836
- const story = snapshot.stories[storyId];
837
- if (story) {
838
- projectedStories[storyId] = storyEpicId(story, storyId) === targetEpicId
839
- ? projectSchedulingLane(storyId, story)
840
- : compactStructuralStory(storyId, story);
841
- }
842
- }
843
- return projectedStories;
844
- }
845
- function buildTargetEpicCoverage(targetEpicId, targetEpic, projectedStories) {
846
- const declaredStoryIds = [...targetEpic.stories]
847
- .sort((left, right) => left.localeCompare(right, 'en'));
848
- const includedStoryIds = Object.entries(projectedStories)
849
- .filter(([storyId, story]) => storyEpicId(story, storyId) === targetEpicId)
850
- .map(([storyId]) => storyId)
851
- .sort((left, right) => left.localeCompare(right, 'en'));
852
- const membershipSha256 = createHash('sha256')
853
- .update(`target-epic-coverage-v1\0${JSON.stringify({ targetEpicId, declaredStoryIds, includedStoryIds })}`)
854
- .digest('hex');
855
- return {
856
- schemaVersion: 'TargetEpicCoverage/1.0',
857
- targetEpicId,
858
- declaredStoryIds,
859
- includedStoryIds,
860
- membershipSha256,
861
- publicSafe: true,
862
- };
863
- }
864
- function compactStructuralStory(storyId, story) {
865
- const dependsOn = Array.isArray(story.depends_on)
866
- ? [...new Set(story.depends_on.filter((value) => typeof value === 'string' && value.length > 0))]
867
- .sort((left, right) => left.localeCompare(right, 'en'))
868
- : [];
869
- const compact = {
870
- id: safeToken(story.id) ?? storyId,
871
- ...(safeToken(story.epic_id) ? { epic_id: safeToken(story.epic_id) } : {}),
872
- ...(safeToken(story.status, 40) ? { status: safeToken(story.status, 40) } : {}),
873
- depends_on: dependsOn,
874
- };
875
- const schedulingEvidence = publicSchedulingCheckpointEvidence(storyId, story);
876
- if (!isActiveYoloopStory(story.status)) {
877
- return { ...compact, ...(schedulingEvidence ? { scheduling_checkpoint_evidence: schedulingEvidence } : {}) };
878
- }
879
- const source = story.source === 'create-epic' || story.source === 'discovered'
880
- ? story.source : undefined;
881
- const branchName = safeGitRef(story.branch_name);
882
- const lastStep = safeCanonicalToken(story.last_step, 120);
883
- const steps = Array.isArray(story.steps_completed) && story.steps_completed.length <= 64
884
- ? story.steps_completed.map((step) => safeCanonicalToken(step, 120))
885
- : [];
886
- const validSteps = steps.length > 0 && steps.every((step) => step !== undefined)
887
- ? steps : undefined;
888
- const prNumber = typeof story.pr_number === 'number' && Number.isSafeInteger(story.pr_number)
889
- && story.pr_number > 0 ? story.pr_number : undefined;
890
- const checkpoint = publicResumeCheckpoint(storyId, story);
891
- const checkpointVerification = checkpoint ? publicCheckpointVerification(storyId, story) : undefined;
892
- return {
893
- ...compact,
894
- ...(source ? { source } : {}),
895
- ...(branchName ? { branch_name: branchName } : {}),
896
- ...(lastStep ? { last_step: lastStep } : {}),
897
- ...(validSteps ? { steps_completed: validSteps } : {}),
898
- ...(prNumber ? { pr_number: prNumber } : {}),
899
- ...(checkpoint ? { checkpoint } : {}),
900
- ...(checkpointVerification ? { checkpoint_verification: checkpointVerification } : {}),
901
- ...(schedulingEvidence ? { scheduling_checkpoint_evidence: schedulingEvidence } : {}),
902
- ...(safeWriteRefs(story.files_to_modify) !== undefined
903
- ? { files_to_modify: safeWriteRefs(story.files_to_modify) } : {}),
904
- ...(safeWriteRefs(story.contracts) !== undefined
905
- ? { contracts: safeWriteRefs(story.contracts) } : {}),
906
- ...(safeWriteRefs(story.contract_refs) !== undefined
907
- ? { contract_refs: safeWriteRefs(story.contract_refs) } : {}),
908
- ...(safeWriteRefs(story.shared_surfaces) !== undefined
909
- ? { shared_surfaces: safeWriteRefs(story.shared_surfaces) } : {}),
910
- };
911
- }
912
- function compactStructuralEpic(epicId, epic) {
913
- const stories = Array.isArray(epic.stories)
914
- ? [...new Set(epic.stories.filter((value) => typeof value === 'string' && value.length > 0))]
915
- .sort((left, right) => left.localeCompare(right, 'en'))
916
- : [];
917
- const dependsOn = Array.isArray(epic.depends_on)
918
- ? [...new Set(epic.depends_on.filter((value) => typeof value === 'string' && value.length > 0))]
919
- .sort((left, right) => left.localeCompare(right, 'en'))
920
- : [];
921
- return {
922
- id: safeToken(epic.id) ?? epicId,
923
- ...(safeToken(epic.status, 40) ? { status: safeToken(epic.status, 40) } : {}),
924
- ...(safeToken(epic.lifecycle_classification, 40)
925
- ? { lifecycle_classification: safeToken(epic.lifecycle_classification, 40) }
926
- : {}),
927
- stories,
928
- depends_on: dependsOn,
929
- };
930
- }
931
- function buildYoloopDiscoveryProjection(trigger, snapshot, commandPolicy) {
932
- const storyAnalysis = analyzeDependencyDag(snapshot.stories);
933
- const epicAnalysis = analyzeDependencyDag(snapshot.epics);
934
- const projectedStories = Object.fromEntries(storyAnalysis.orderedIds.map((storyId) => [
935
- storyId,
936
- compactStructuralStory(storyId, snapshot.stories[storyId]),
937
- ]));
938
- const projectedEpics = Object.fromEntries(epicAnalysis.orderedIds.map((epicId) => [
939
- epicId,
940
- compactStructuralEpic(epicId, snapshot.epics[epicId]),
941
- ]));
942
- const scope = {
943
- kind: 'projected',
944
- commandId: commandPolicy.commandId,
945
- stateProjectionPolicy: commandPolicy.stateProjectionPolicy,
946
- projectionMode: 'yoloop-discovery',
947
- trigger,
948
- targetEpicId: null,
949
- targetStoryId: null,
950
- includedStoryCount: storyAnalysis.orderedIds.length,
951
- includedEpicCount: epicAnalysis.orderedIds.length,
952
- omittedStoryCount: 0,
953
- omittedEpicCount: 0,
954
- reason: 'targeted-invocation',
955
- reasonCodes: safeReasonCodes([
956
- 'structural-required',
957
- 'yoloop-discovery',
958
- `structural-edges-${storyAnalysis.edgeCount + epicAnalysis.edgeCount}`,
959
- 'content-fields-omitted',
960
- ]),
961
- };
962
- return withStateSummary({
963
- ...snapshot,
964
- stories: projectedStories,
965
- epics: projectedEpics,
966
- scope,
967
- }, snapshot, scope);
968
- }
969
- function buildProjectedTargetEpicStories(targetEpic, snapshot) {
970
- const includedStoryIds = new Set();
971
- const storyIds = targetEpic.stories;
972
- if (Array.isArray(storyIds)) {
973
- for (const storyId of storyIds) {
974
- if (typeof storyId === 'string') {
975
- addStoryAndDependencies(storyId, snapshot, includedStoryIds);
976
- }
977
- }
978
- }
979
- const projectedStories = {};
980
- for (const storyId of includedStoryIds) {
981
- const story = snapshot.stories[storyId];
982
- if (story) {
983
- projectedStories[storyId] = story;
984
- }
985
- }
986
- return projectedStories;
987
- }
988
- function buildEpicStubs(snapshot) {
989
- const projectedEpics = {};
990
- for (const [epicId, epic] of Object.entries(snapshot.epics)) {
991
- projectedEpics[epicId] = {
992
- id: safeToken(epic.id) ?? epicId,
993
- ...(safeToken(epic.status) ? { status: safeToken(epic.status) } : {}),
994
- ...(EPIC_LIFECYCLE_CLASSIFICATIONS.includes(epic.lifecycle_classification)
995
- ? { lifecycle_classification: epic.lifecycle_classification }
996
- : {}),
997
- ...(typeof epic.total_stories === 'number' && Number.isFinite(epic.total_stories)
998
- ? { total_stories: epic.total_stories }
999
- : {}),
1000
- ...(typeof epic.completed_stories === 'number' && Number.isFinite(epic.completed_stories)
1001
- ? { completed_stories: epic.completed_stories }
1002
- : {}),
1003
- };
1004
- }
1005
- return projectedEpics;
1006
- }
1007
- function countStatuses(records) {
1008
- const counts = {};
1009
- for (const record of Object.values(records)) {
1010
- const status = safeToken(record.status, 40) ?? 'unknown';
1011
- counts[status] = (counts[status] ?? 0) + 1;
1012
- }
1013
- return counts;
1014
- }
1015
- function countEpicLifecycles(records) {
1016
- const counts = Object.fromEntries(EPIC_LIFECYCLE_CLASSIFICATIONS.map((classification) => [classification, 0]));
1017
- for (const record of Object.values(records)) {
1018
- const classification = EPIC_LIFECYCLE_CLASSIFICATIONS.includes(record.lifecycle_classification)
1019
- ? record.lifecycle_classification
1020
- : 'active';
1021
- counts[classification] = (counts[classification] ?? 0) + 1;
1022
- }
1023
- return counts;
1024
- }
1025
- function isActiveEpicLifecycle(snapshot, epicId) {
1026
- if (!epicId)
1027
- return true;
1028
- const epic = snapshot.epics[epicId];
1029
- if (!epic)
1030
- return true;
1031
- return !EPIC_LIFECYCLE_CLASSIFICATIONS.includes(epic.lifecycle_classification) || epic.lifecycle_classification === 'active';
1032
- }
1033
- function sortableStoryTimestamp(story) {
1034
- for (const key of ['last_updated', 'updated_at', 'completed_at', 'created_at', 'discovered_at']) {
1035
- const value = story[key];
1036
- if (typeof value !== 'string')
1037
- continue;
1038
- const parsed = Date.parse(value);
1039
- if (Number.isFinite(parsed))
1040
- return parsed;
1041
- }
1042
- return 0;
1043
- }
1044
- function recentStories(snapshot) {
1045
- return Object.entries(snapshot.stories)
1046
- .map(([id, story]) => ({ id, story, timestamp: sortableStoryTimestamp(story) }))
1047
- .sort((left, right) => right.timestamp - left.timestamp || left.id.localeCompare(right.id))
1048
- .slice(0, RECENT_STORY_LIMIT)
1049
- .map(({ id, story }) => ({
1050
- id,
1051
- ...(safeToken(story.epic_id) ? { epicId: safeToken(story.epic_id) } : {}),
1052
- ...(safeToken(story.status, 40) ? { status: safeToken(story.status, 40) } : {}),
1053
- ...(safeToken(story.last_step ?? story.lastStep) ? { lastStep: safeToken(story.last_step ?? story.lastStep) } : {}),
1054
- ...(safeToken(story.last_updated ?? story.updated_at ?? story.completed_at ?? story.created_at)
1055
- ? { lastUpdated: safeToken(story.last_updated ?? story.updated_at ?? story.completed_at ?? story.created_at) }
1056
- : {}),
1057
- }));
1058
- }
1059
- function currentSessionRef(snapshot) {
1060
- const session = snapshot.current_session;
1061
- if (!session)
1062
- return undefined;
1063
- const ref = {
1064
- activeEpicId: safeToken(session.active_epic_id) ?? null,
1065
- activeStoryId: safeToken(session.active_story_id) ?? null,
1066
- activeStepId: safeToken(session.active_step_id) ?? null,
1067
- sessionId: safeToken(session.session_id) ?? null,
1068
- startedAt: safeToken(session.started_at) ?? null,
1069
- };
1070
- return Object.values(ref).some((value) => value !== null && value !== undefined) ? ref : undefined;
1071
- }
1072
- function yoloopSessionRef(snapshot) {
1073
- const yoloop = snapshot.yoloop;
1074
- if (!isRecord(yoloop))
1075
- return undefined;
1076
- const state = isRecord(yoloop.state) ? yoloop.state : undefined;
1077
- const currentSession = isRecord(state?.currentSession) ? state.currentSession : undefined;
1078
- const lock = isRecord(yoloop.lock) ? yoloop.lock : undefined;
1079
- const ref = {
1080
- ...(typeof yoloop.active === 'boolean' ? { active: yoloop.active } : {}),
1081
- currentEpicId: safeToken(currentSession?.currentEpicId ?? yoloop.currentEpicId) ?? null,
1082
- currentStoryId: safeToken(currentSession?.currentStoryId ?? yoloop.currentStoryId) ?? null,
1083
- sessionId: safeToken(currentSession?.sessionId ?? yoloop.sessionId) ?? null,
1084
- lockEpicId: safeToken(lock?.currentEpicId ?? lock?.epicId) ?? null,
1085
- };
1086
- return Object.keys(ref).length > 0 ? ref : undefined;
1087
- }
1088
- function continuitySessionRef(snapshot) {
1089
- const continuity = snapshot.continuity;
1090
- if (!isRecord(continuity))
1091
- return undefined;
1092
- const reasonCodes = Array.isArray(continuity.reasonCodes)
1093
- ? continuity.reasonCodes.filter((entry) => typeof entry === 'string')
1094
- : undefined;
1095
- return {
1096
- present: true,
1097
- activeJobId: safeToken(continuity.activeJobId ?? continuity.jobId) ?? null,
1098
- activeGoalId: safeToken(continuity.activeGoalId ?? continuity.goalId) ?? null,
1099
- activeLoopId: safeToken(continuity.activeLoopId ?? continuity.loopId) ?? null,
1100
- ...(reasonCodes && reasonCodes.length > 0 ? { reasonCodes: safeReasonCodes(reasonCodes) } : {}),
1101
- };
1102
- }
1103
- function deriveActiveRef(snapshot, session, yoloop) {
1104
- const sessionStoryId = safeToken(session?.activeStoryId);
1105
- const sessionEpicId = safeToken(session?.activeEpicId);
1106
- const sessionStepId = safeToken(session?.activeStepId);
1107
- const sessionStoryEpicId = sessionStoryId && snapshot.stories[sessionStoryId]
1108
- ? storyEpicId(snapshot.stories[sessionStoryId], sessionStoryId)
1109
- : null;
1110
- if ((sessionStoryId || sessionEpicId || sessionStepId) && isActiveEpicLifecycle(snapshot, sessionEpicId ?? sessionStoryEpicId)) {
1111
- return {
1112
- epicId: sessionEpicId ?? null,
1113
- storyId: sessionStoryId ?? null,
1114
- stepId: sessionStepId ?? null,
1115
- };
1116
- }
1117
- const yoloopStoryId = safeToken(yoloop?.currentStoryId);
1118
- const yoloopEpicId = safeToken(yoloop?.currentEpicId ?? yoloop?.lockEpicId);
1119
- if ((yoloopStoryId || yoloopEpicId) && isActiveEpicLifecycle(snapshot, yoloopEpicId)) {
1120
- return {
1121
- epicId: yoloopEpicId ?? null,
1122
- storyId: yoloopStoryId ?? null,
1123
- stepId: null,
1124
- };
1125
- }
1126
- const latestNonTerminal = Object.entries(snapshot.stories)
1127
- .map(([id, story]) => ({ id, story, timestamp: sortableStoryTimestamp(story) }))
1128
- .filter(({ id, story }) => !isTerminalStoryStatus(story.status) && isActiveEpicLifecycle(snapshot, storyEpicId(story, id)))
1129
- .sort((left, right) => right.timestamp - left.timestamp || left.id.localeCompare(right.id))[0];
1130
- if (latestNonTerminal) {
1131
- return {
1132
- epicId: storyEpicId(latestNonTerminal.story, latestNonTerminal.id),
1133
- storyId: latestNonTerminal.id,
1134
- stepId: safeToken(latestNonTerminal.story.last_step ?? latestNonTerminal.story.lastStep) ?? null,
1135
- };
1136
- }
1137
- return { epicId: null, storyId: null, stepId: null };
1138
- }
1139
- function buildStateSummary(sourceSnapshot, scope) {
1140
- const storyStatus = countStatuses(sourceSnapshot.stories);
1141
- const epicStatus = countStatuses(sourceSnapshot.epics);
1142
- const epicLifecycle = countEpicLifecycles(sourceSnapshot.epics);
1143
- const activeEpics = Object.values(sourceSnapshot.epics).filter((epic) => {
1144
- const lifecycle = EPIC_LIFECYCLE_CLASSIFICATIONS.includes(epic.lifecycle_classification)
1145
- ? epic.lifecycle_classification
1146
- : 'active';
1147
- return lifecycle === 'active' && !isTerminalStoryStatus(epic.status);
1148
- }).length;
1149
- const session = currentSessionRef(sourceSnapshot);
1150
- const yoloop = yoloopSessionRef(sourceSnapshot);
1151
- const continuity = continuitySessionRef(sourceSnapshot);
1152
- const active = deriveActiveRef(sourceSnapshot, session, yoloop);
1153
- const recent = recentStories(sourceSnapshot);
1154
- const reasonCodes = safeReasonCodes(scope.reasonCodes, [
1155
- 'state-summary-attached',
1156
- 'state-summary-bounded',
1157
- `projection-${scope.projectionMode ?? scope.kind}`,
1158
- scope.stateProjectionPolicy ? `policy-${scope.stateProjectionPolicy}` : 'policy-unknown',
1159
- recent.length >= RECENT_STORY_LIMIT ? 'recent-stories-capped' : 'recent-stories-bounded',
1160
- active.storyId || active.epicId ? 'active-ref-derived' : 'active-ref-empty',
1161
- yoloop ? 'yoloop-session-present' : 'yoloop-session-absent',
1162
- continuity ? 'continuity-session-present' : 'continuity-session-absent',
1163
- ]);
1164
- return {
1165
- schemaVersion: 1,
1166
- totals: {
1167
- stories: Object.keys(sourceSnapshot.stories).length,
1168
- epics: Object.keys(sourceSnapshot.epics).length,
1169
- storyStatus,
1170
- epicStatus,
1171
- epicLifecycle,
1172
- activeEpics,
1173
- blockedStories: storyStatus.blocked ?? 0,
1174
- },
1175
- active,
1176
- blockedCount: storyStatus.blocked ?? 0,
1177
- recentStories: recent,
1178
- ...(session ? { currentSession: session } : {}),
1179
- ...(yoloop ? { currentYoloopSession: yoloop } : {}),
1180
- ...(continuity ? { currentContinuitySession: continuity } : {}),
1181
- projection: {
1182
- commandId: scope.commandId ?? null,
1183
- stateProjectionPolicy: scope.stateProjectionPolicy ?? null,
1184
- projectionMode: scope.projectionMode,
1185
- scopeKind: scope.kind,
1186
- includedStoryCount: scope.includedStoryCount,
1187
- includedEpicCount: scope.includedEpicCount ?? Object.keys(sourceSnapshot.epics).length,
1188
- omittedStoryCount: scope.omittedStoryCount,
1189
- omittedEpicCount: scope.omittedEpicCount,
1190
- reason: scope.reason,
1191
- },
1192
- reasonCodes,
1193
- publicSafe: true,
1194
- };
1195
- }
1196
- function withStateSummary(projected, sourceSnapshot, scope) {
1197
- return {
1198
- ...projected,
1199
- stateSummary: buildStateSummary(sourceSnapshot, scope),
1200
- };
1201
- }
1202
- function buildCreateEpicProjection(trigger, snapshot, commandPolicy) {
1203
- const projectedEpics = {};
1204
- for (const epicId of Object.keys(snapshot.epics)) {
1205
- projectedEpics[epicId] = { id: epicId };
1206
- }
1207
- const scope = {
1208
- kind: 'projected',
1209
- commandId: commandPolicy.commandId,
1210
- stateProjectionPolicy: commandPolicy.stateProjectionPolicy,
1211
- projectionMode: 'summary-only',
1212
- trigger,
1213
- targetEpicId: null,
1214
- targetStoryId: null,
1215
- includedStoryCount: 0,
1216
- includedEpicCount: Object.keys(projectedEpics).length,
1217
- omittedStoryCount: Object.keys(snapshot.stories).length,
1218
- omittedEpicCount: 0,
1219
- reason: 'epic-summary',
1220
- reasonCodes: ['epic-summary', 'stories-omitted', 'epic-stubs-only'],
1221
- };
1222
- return withStateSummary({
1223
- ...snapshot,
1224
- stories: {},
1225
- epics: projectedEpics,
1226
- scope,
1227
- }, snapshot, scope);
1228
- }
1229
- function buildSummaryOnlyProjection(trigger, snapshot, commandPolicy, reasonCodes = ['summary-only', 'stories-omitted'], projectionMode = 'summary-only') {
1230
- const projectedEpics = buildEpicStubs(snapshot);
1231
- const scope = {
1232
- kind: 'projected',
1233
- commandId: commandPolicy.commandId,
1234
- stateProjectionPolicy: commandPolicy.stateProjectionPolicy,
1235
- projectionMode,
1236
- trigger,
1237
- targetEpicId: null,
1238
- targetStoryId: null,
1239
- includedStoryCount: 0,
1240
- includedEpicCount: Object.keys(projectedEpics).length,
1241
- omittedStoryCount: Object.keys(snapshot.stories).length,
1242
- omittedEpicCount: Math.max(0, Object.keys(snapshot.epics).length - Object.keys(projectedEpics).length),
1243
- reason: 'summary-only',
1244
- reasonCodes: safeReasonCodes(reasonCodes),
1245
- };
1246
- return withStateSummary({
1247
- ...snapshot,
1248
- stories: {},
1249
- epics: projectedEpics,
1250
- scope,
1251
- }, snapshot, scope);
1252
- }
1253
- function buildStoryProjection(trigger, snapshot, commandPolicy, targetStoryId, projectionMode, reasonCodes) {
1254
- const targetStory = snapshot.stories[targetStoryId];
1255
- if (!targetStory) {
1256
- throw new StructuralDagError('STRUCTURAL_TARGET_NOT_FOUND', {
1257
- stories: Object.keys(snapshot.stories).length,
1258
- epics: Object.keys(snapshot.epics).length,
1259
- targetKind: 'story',
1260
- });
1261
- }
1262
- const targetEpicId = storyEpicId(targetStory, targetStoryId);
1263
- if (!targetEpicId) {
1264
- throw new StructuralDagError('STRUCTURAL_TARGET_NOT_FOUND', {
1265
- stories: Object.keys(snapshot.stories).length,
1266
- epics: Object.keys(snapshot.epics).length,
1267
- targetKind: 'epic',
1268
- });
1269
- }
1270
- const targetEpic = snapshot.epics[targetEpicId];
1271
- if (!targetEpic) {
1272
- throw new StructuralDagError('STRUCTURAL_TARGET_NOT_FOUND', {
1273
- stories: Object.keys(snapshot.stories).length,
1274
- epics: Object.keys(snapshot.epics).length,
1275
- targetKind: 'epic',
1276
- });
1277
- }
1278
- // Direct *yolo needs the same epic topology seen by *yoloop so a standalone
1279
- // invocation can bootstrap the canonical P207 graph epoch. Other story
1280
- // commands remain dependency-scoped. Stories outside the owning epic are
1281
- // retained only as compact structural dependencies by the yoloop projector.
1282
- const yoloEpicGraph = projectionMode === 'yolo-story';
1283
- const projectedStories = yoloEpicGraph
1284
- ? buildProjectedYoloopStories(targetEpicId, targetEpic, snapshot)
1285
- : buildProjectedStories(targetStoryId, snapshot);
1286
- const projectedEpics = yoloEpicGraph
1287
- ? buildProjectedYoloopEpics(targetEpicId, projectedStories, snapshot)
1288
- : buildProjectedEpics(projectedStories, snapshot);
1289
- if (!projectedStories[targetStoryId] || !projectedEpics[targetEpicId]) {
1290
- throw new StructuralDagError('STRUCTURAL_TARGET_NOT_FOUND', {
1291
- stories: Object.keys(projectedStories).length,
1292
- epics: Object.keys(projectedEpics).length,
1293
- targetKind: 'story',
1294
- });
1295
- }
1296
- const scope = {
1297
- kind: 'projected',
1298
- commandId: commandPolicy.commandId,
1299
- stateProjectionPolicy: commandPolicy.stateProjectionPolicy,
1300
- projectionMode,
1301
- trigger,
1302
- targetEpicId,
1303
- targetStoryId,
1304
- includedStoryCount: Object.keys(projectedStories).length,
1305
- includedEpicCount: Object.keys(projectedEpics).length,
1306
- omittedStoryCount: Math.max(0, Object.keys(snapshot.stories).length - Object.keys(projectedStories).length),
1307
- omittedEpicCount: Math.max(0, Object.keys(snapshot.epics).length - Object.keys(projectedEpics).length),
1308
- reason: 'targeted-invocation',
1309
- reasonCodes: safeReasonCodes(reasonCodes, yoloEpicGraph ? ['epic-graph-authority-complete'] : []),
1310
- };
1311
- return withStateSummary({
1312
- ...snapshot,
1313
- stories: projectedStories,
1314
- epics: projectedEpics,
1315
- scope,
1316
- }, snapshot, scope);
1317
- }
1318
- function buildTargetedEpicProjection(trigger, snapshot, commandPolicy, targetEpicId, projectionMode, reasonCodes) {
1319
- const targetEpic = snapshot.epics[targetEpicId];
1320
- if (!targetEpic) {
1321
- throw new StructuralDagError('STRUCTURAL_TARGET_NOT_FOUND', {
1322
- stories: Object.keys(snapshot.stories).length,
1323
- epics: Object.keys(snapshot.epics).length,
1324
- targetKind: 'epic',
1325
- });
1326
- }
1327
- const projectedStories = buildProjectedTargetEpicStories(targetEpic, snapshot);
1328
- const projectedEpics = buildProjectedEpics(projectedStories, snapshot, [targetEpicId]);
1329
- if (!projectedEpics[targetEpicId]) {
1330
- throw new StructuralDagError('STRUCTURAL_TARGET_NOT_FOUND', {
1331
- stories: Object.keys(projectedStories).length,
1332
- epics: Object.keys(projectedEpics).length,
1333
- targetKind: 'epic',
1334
- });
1335
- }
1336
- const scope = {
1337
- kind: 'projected',
1338
- commandId: commandPolicy.commandId,
1339
- stateProjectionPolicy: commandPolicy.stateProjectionPolicy,
1340
- projectionMode,
1341
- trigger,
1342
- targetEpicId,
1343
- targetStoryId: null,
1344
- includedStoryCount: Object.keys(projectedStories).length,
1345
- includedEpicCount: Object.keys(projectedEpics).length,
1346
- omittedStoryCount: Math.max(0, Object.keys(snapshot.stories).length - Object.keys(projectedStories).length),
1347
- omittedEpicCount: Math.max(0, Object.keys(snapshot.epics).length - Object.keys(projectedEpics).length),
1348
- reason: 'targeted-invocation',
1349
- reasonCodes: safeReasonCodes(reasonCodes),
1350
- };
1351
- return withStateSummary({
1352
- ...snapshot,
1353
- stories: projectedStories,
1354
- epics: projectedEpics,
1355
- scope,
1356
- }, snapshot, scope);
1357
- }
1358
- function buildYoloopProjection(trigger, snapshot, commandPolicy) {
1359
- const explicitCandidate = explicitYoloopEpicCandidate(trigger);
1360
- const explicitTargetEpicId = extractTargetEpicId(trigger, snapshot);
1361
- if (explicitCandidate && !explicitTargetEpicId) {
1362
- throw new StructuralDagError('STRUCTURAL_TARGET_NOT_FOUND', {
1363
- stories: Object.keys(snapshot.stories).length,
1364
- epics: Object.keys(snapshot.epics).length,
1365
- targetKind: 'epic',
1366
- });
1367
- }
1368
- const targetEpicId = extractYoloopTargetEpicId(trigger, snapshot);
1369
- const shape = classifyInvocationShapeV2({
1370
- commandId: commandPolicy.commandId,
1371
- explicitYoloopEpicId: explicitTargetEpicId,
1372
- sessionYoloopEpicId: explicitTargetEpicId ? null : targetEpicId,
1373
- });
1374
- if (shape === 'yoloop-discovery')
1375
- return buildYoloopDiscoveryProjection(trigger, snapshot, commandPolicy);
1376
- if (!targetEpicId || (shape !== 'yoloop-target' && shape !== 'yoloop-session')) {
1377
- throw new StructuralDagError('STRUCTURAL_TARGET_NOT_FOUND', {
1378
- stories: Object.keys(snapshot.stories).length,
1379
- epics: Object.keys(snapshot.epics).length,
1380
- targetKind: 'epic',
1381
- });
1382
- }
1383
- const targetEpic = snapshot.epics[targetEpicId];
1384
- if (!targetEpic) {
1385
- throw new StructuralDagError('STRUCTURAL_TARGET_NOT_FOUND', {
1386
- stories: Object.keys(snapshot.stories).length,
1387
- epics: Object.keys(snapshot.epics).length,
1388
- targetKind: 'epic',
1389
- });
1390
- }
1391
- const projectedStories = buildProjectedYoloopStories(targetEpicId, targetEpic, snapshot);
1392
- const projectedEpics = buildProjectedYoloopEpics(targetEpicId, projectedStories, snapshot);
1393
- if (!projectedEpics[targetEpicId]) {
1394
- throw new StructuralDagError('STRUCTURAL_TARGET_NOT_FOUND', {
1395
- stories: Object.keys(projectedStories).length,
1396
- epics: Object.keys(projectedEpics).length,
1397
- targetKind: 'epic',
1398
- });
1399
- }
1400
- const projectionMode = shape;
1401
- const scope = {
1402
- kind: 'projected',
1403
- commandId: commandPolicy.commandId,
1404
- stateProjectionPolicy: commandPolicy.stateProjectionPolicy,
1405
- projectionMode,
1406
- trigger,
1407
- targetEpicId,
1408
- targetStoryId: null,
1409
- targetEpicCoverage: buildTargetEpicCoverage(targetEpicId, targetEpic, projectedStories),
1410
- includedStoryCount: Object.keys(projectedStories).length,
1411
- includedEpicCount: Object.keys(projectedEpics).length,
1412
- omittedStoryCount: Math.max(0, Object.keys(snapshot.stories).length - Object.keys(projectedStories).length),
1413
- omittedEpicCount: Math.max(0, Object.keys(snapshot.epics).length - Object.keys(projectedEpics).length),
1414
- reason: 'targeted-invocation',
1415
- reasonCodes: ['structural-required', projectionMode, 'yoloop-targeted'],
1416
- };
1417
- return withStateSummary({
1418
- ...snapshot,
1419
- stories: projectedStories,
1420
- epics: projectedEpics,
1421
- scope,
1422
- }, snapshot, scope);
1423
- }
1424
- function buildPayloadGuardrailEpics(snapshot, retainedStoryIds) {
1425
- const projectedEpics = {};
1426
- for (const [epicId, epic] of Object.entries(snapshot.epics)) {
1427
- const retainedEpicStories = Array.isArray(epic.stories)
1428
- ? epic.stories.filter((storyId) => typeof storyId === 'string' && retainedStoryIds.has(storyId))
1429
- : [];
1430
- projectedEpics[epicId] = {
1431
- id: safeToken(epic.id) ?? epicId,
1432
- ...(safeToken(epic.status, 40) ? { status: safeToken(epic.status, 40) } : {}),
1433
- ...(typeof epic.total_stories === 'number' && Number.isFinite(epic.total_stories)
1434
- ? { total_stories: epic.total_stories }
1435
- : {}),
1436
- ...(typeof epic.completed_stories === 'number' && Number.isFinite(epic.completed_stories)
1437
- ? { completed_stories: epic.completed_stories }
1438
- : {}),
1439
- ...(retainedEpicStories.length > 0 ? { stories: retainedEpicStories } : {}),
1440
- ...(retainedEpicStories.length > 0 && Array.isArray(epic.depends_on)
1441
- ? { depends_on: epic.depends_on.filter((value) => typeof value === 'string' && value.length > 0) }
1442
- : {}),
1443
- ...(retainedEpicStories.length > 0 && (epic.branch_mode === 'epic' || epic.branch_mode === 'story')
1444
- ? { branch_mode: epic.branch_mode }
1445
- : {}),
1446
- ...(retainedEpicStories.length > 0 && typeof epic.execution_branch === 'string'
1447
- ? { execution_branch: epic.execution_branch }
1448
- : {}),
1449
- ...(retainedEpicStories.length > 0 && typeof epic.origin_branch === 'string'
1450
- ? { origin_branch: epic.origin_branch }
1451
- : {}),
1452
- ...(retainedEpicStories.length > 0 && (epic.branch_lease === null || typeof epic.branch_lease === 'object')
1453
- ? { branch_lease: epic.branch_lease }
1454
- : {}),
1455
- ...(retainedEpicStories.length > 0 && (epic.origin_binding === null || typeof epic.origin_binding === 'object')
1456
- ? { origin_binding: epic.origin_binding }
1457
- : {}),
1458
- ...(retainedEpicStories.length > 0 && (epic.aggregate_qa_receipt === null || typeof epic.aggregate_qa_receipt === 'object')
1459
- ? { aggregate_qa_receipt: epic.aggregate_qa_receipt }
1460
- : {}),
1461
- };
1462
- }
1463
- return projectedEpics;
1464
- }
1465
- function sourceStoryTotal(snapshot) {
1466
- return snapshot.stateSummary?.totals.stories
1467
- ?? ((snapshot.scope?.includedStoryCount ?? Object.keys(snapshot.stories).length) + (snapshot.scope?.omittedStoryCount ?? 0));
1468
- }
1469
- function sourceEpicTotal(snapshot) {
1470
- return snapshot.stateSummary?.totals.epics
1471
- ?? ((snapshot.scope?.includedEpicCount ?? Object.keys(snapshot.epics).length) + (snapshot.scope?.omittedEpicCount ?? 0));
1472
- }
1473
- function updateStateSummaryForPayloadGuardrail(snapshot, scope) {
1474
- if (!snapshot.stateSummary)
1475
- return undefined;
1476
- return {
1477
- ...snapshot.stateSummary,
1478
- projection: {
1479
- ...snapshot.stateSummary.projection,
1480
- scopeKind: 'projected',
1481
- projectionMode: scope.projectionMode,
1482
- includedStoryCount: scope.includedStoryCount,
1483
- includedEpicCount: scope.includedEpicCount ?? snapshot.stateSummary.projection.includedEpicCount,
1484
- omittedStoryCount: scope.omittedStoryCount,
1485
- omittedEpicCount: scope.omittedEpicCount,
1486
- reason: 'body-size',
1487
- },
1488
- reasonCodes: safeReasonCodes(snapshot.stateSummary.reasonCodes, ['payload-size-guardrail', 'state-summary-preserved', 'nonessential-state-degraded']),
1489
- };
1490
- }
1491
- /**
1492
- * P198.04 -- deterministic post-assembly state degradation.
1493
- *
1494
- * This helper is intentionally pure and request-only. It removes historical story
1495
- * bodies that are not structural for the already-projected request, converts the
1496
- * epic map to public stubs, keeps `stateSummary`, and preserves structural-required
1497
- * story/yolo/yoloop target state instead of silently corrupting it.
1498
- */
1499
- export function degradeStateSnapshotForPayloadLimit(snapshot) {
1500
- const scope = snapshot.scope;
1501
- const retainedStoryIds = new Set();
1502
- const preserveProjectedStructuralStories = scope?.projectionMode === 'structural-required'
1503
- || scope?.projectionMode === 'yolo-story'
1504
- || scope?.projectionMode === 'yoloop-target'
1505
- || scope?.projectionMode === 'yoloop-session'
1506
- || scope?.projectionMode === 'yoloop-discovery';
1507
- if (preserveProjectedStructuralStories) {
1508
- for (const storyId of Object.keys(snapshot.stories)) {
1509
- retainedStoryIds.add(storyId);
1510
- }
1511
- }
1512
- else if (scope?.targetStoryId) {
1513
- addStoryAndDependencies(scope.targetStoryId, snapshot, retainedStoryIds);
1514
- }
1515
- const projectedStories = {};
1516
- for (const storyId of retainedStoryIds) {
1517
- const story = snapshot.stories[storyId];
1518
- if (story)
1519
- projectedStories[storyId] = story;
1520
- }
1521
- const projectedEpics = buildPayloadGuardrailEpics(snapshot, retainedStoryIds);
1522
- const includedStoryCount = Object.keys(projectedStories).length;
1523
- const includedEpicCount = Object.keys(projectedEpics).length;
1524
- const omittedStoryCount = Math.max(0, sourceStoryTotal(snapshot) - includedStoryCount);
1525
- const omittedEpicCount = Math.max(0, sourceEpicTotal(snapshot) - includedEpicCount);
1526
- const projectionMode = preserveProjectedStructuralStories
1527
- ? scope?.projectionMode ?? 'structural-required'
1528
- : scope?.projectionMode === 'yolo-story'
1529
- ? 'yolo-story'
1530
- : 'summary-only';
1531
- const nextScope = {
1532
- kind: 'projected',
1533
- commandId: scope?.commandId ?? null,
1534
- stateProjectionPolicy: scope?.stateProjectionPolicy ?? null,
1535
- projectionMode,
1536
- targetEpicId: scope?.targetEpicId ?? null,
1537
- targetStoryId: scope?.targetStoryId ?? null,
1538
- ...(scope?.targetEpicCoverage ? { targetEpicCoverage: scope.targetEpicCoverage } : {}),
1539
- includedStoryCount,
1540
- includedEpicCount,
1541
- omittedStoryCount,
1542
- omittedEpicCount,
1543
- reason: 'body-size',
1544
- reasonCodes: safeReasonCodes(scope?.reasonCodes, [
1545
- 'payload-size-guardrail',
1546
- retainedStoryIds.size > 0 ? 'structural-targets-preserved' : 'stories-omitted',
1547
- 'epics-stubbed',
1548
- 'state-summary-preserved',
1549
- 'redundant-trigger-omitted',
1550
- ]),
1551
- };
1552
- const stateSummary = updateStateSummaryForPayloadGuardrail(snapshot, nextScope);
1553
- return {
1554
- ...snapshot,
1555
- stories: projectedStories,
1556
- epics: projectedEpics,
1557
- scope: nextScope,
1558
- ...(stateSummary ? { stateSummary } : {}),
1559
- };
1560
- }
1561
- /**
1562
- * Return a request-scoped state snapshot for invoke commands.
1563
- *
1564
- * The function is intentionally pure. Structural uncertainty fails closed;
1565
- * callers must surface the public code instead of sending a full-state fallback.
1566
- */
1567
- export function projectStateSnapshotForInvoke(args, snapshot) {
1568
- const trigger = args.trim();
1569
- const commandPolicy = resolveCommandProjectionPolicy(trigger);
1570
- try {
1571
- if (!commandPolicy) {
1572
- if (firstTokenIsStoryReference(trigger)) {
1573
- const storyReferencePolicy = policy('story-reference', 'story-targeted');
1574
- const targetStoryId = extractStoryId(trigger);
1575
- if (targetStoryId) {
1576
- return buildStoryProjection(trigger, snapshot, storyReferencePolicy, targetStoryId, 'targeted-story', ['story-reference-targeted']);
1577
- }
1578
- }
1579
- return buildSummaryOnlyProjection(trigger, snapshot, policy('unknown', 'adaptive-targeted'), ['unknown-command-summary-only']);
1580
- }
1581
- if (commandPolicy.stateProjectionPolicy === 'epic-summary') {
1582
- return buildCreateEpicProjection(trigger, snapshot, commandPolicy);
1583
- }
1584
- if (commandPolicy.stateProjectionPolicy === 'yoloop-targeted') {
1585
- return buildYoloopProjection(trigger, snapshot, commandPolicy);
1586
- }
1587
- const targetStoryId = extractStoryId(trigger);
1588
- if (commandPolicy.stateProjectionPolicy === 'story-targeted') {
1589
- if (targetStoryId) {
1590
- const invocationShape = classifyInvocationShapeV2({ commandId: commandPolicy.commandId, targetStoryId });
1591
- const yoloStory = invocationShape === 'yolo-story';
1592
- return buildStoryProjection(trigger, snapshot, commandPolicy, targetStoryId, yoloStory ? 'yolo-story' : commandPolicy.structuralRequired ? 'structural-required' : 'targeted-story', [yoloStory ? 'yolo-story' : commandPolicy.structuralRequired ? 'structural-required' : 'targeted-story']);
1593
- }
1594
- if (commandPolicy.commandId === 'yolo' || commandPolicy.commandId === 'yolo-inline') {
1595
- const invocationShape = classifyInvocationShapeV2({ commandId: commandPolicy.commandId });
1596
- return buildSummaryOnlyProjection(trigger, snapshot, commandPolicy, ['yolo-inline', 'stories-omitted', 'historical-content-omitted'], invocationShape ?? 'yolo-inline');
1597
- }
1598
- // Commit without a story is an exact root working-tree workflow. It
1599
- // needs no story graph, but a malformed or unknown target must not gain
1600
- // this summary-only escape from structural target validation.
1601
- if (commandPolicy.commandId === 'commit' && /^\*?(?:commit|commit-push)$/i.test(trigger)) {
1602
- return buildSummaryOnlyProjection(trigger, snapshot, commandPolicy, ['working-tree-commit', 'stories-omitted', 'historical-content-omitted']);
1603
- }
1604
- return commandPolicy.structuralRequired
1605
- ? (() => {
1606
- throw new StructuralDagError('STRUCTURAL_TARGET_NOT_FOUND', {
1607
- stories: Object.keys(snapshot.stories).length,
1608
- epics: Object.keys(snapshot.epics).length,
1609
- targetKind: 'story',
1610
- });
1611
- })()
1612
- : buildSummaryOnlyProjection(trigger, snapshot, commandPolicy, ['story-target-missing-summary-only']);
1613
- }
1614
- if (commandPolicy.stateProjectionPolicy === 'adaptive-targeted') {
1615
- if (targetStoryId && snapshot.stories[targetStoryId]) {
1616
- return buildStoryProjection(trigger, snapshot, commandPolicy, targetStoryId, 'targeted-story', ['adaptive-story-targeted']);
1617
- }
1618
- const targetEpicId = extractTargetEpicId(trigger, snapshot);
1619
- if (targetEpicId) {
1620
- return buildTargetedEpicProjection(trigger, snapshot, commandPolicy, targetEpicId, 'targeted-epic', ['adaptive-epic-targeted']);
1621
- }
1622
- return buildSummaryOnlyProjection(trigger, snapshot, commandPolicy, ['adaptive-summary-only', 'target-unresolved-summary-only']);
1623
- }
1624
- if (commandPolicy.stateProjectionPolicy === 'epic-targeted') {
1625
- // The root *arch-plan pipeline delegates its current stage with the
1626
- // internal marker `epic-arch`. It has no user-selected epic yet; keep
1627
- // this exact marker only when the bounded root checkpoint names the
1628
- // same stage. The server still verifies the accounting receipt.
1629
- if (commandPolicy.commandId.startsWith('arch-')
1630
- && /(?:^|\s)epic-arch\s*$/i.test(trigger)
1631
- && snapshot.archPlan?.execution?.currentStageCommand === commandPolicy.commandId) {
1632
- return buildSummaryOnlyProjection(trigger, snapshot, commandPolicy, ['architecture-root-delegated']);
1633
- }
1634
- const targetEpicId = extractTargetEpicId(trigger, snapshot);
1635
- if (targetEpicId) {
1636
- return buildTargetedEpicProjection(trigger, snapshot, commandPolicy, targetEpicId, commandPolicy.structuralRequired ? 'structural-required' : 'targeted-epic', [commandPolicy.structuralRequired ? 'structural-required' : 'targeted-epic']);
1637
- }
1638
- return commandPolicy.structuralRequired
1639
- ? (() => {
1640
- throw new StructuralDagError('STRUCTURAL_TARGET_NOT_FOUND', {
1641
- stories: Object.keys(snapshot.stories).length,
1642
- epics: Object.keys(snapshot.epics).length,
1643
- targetKind: 'epic',
1644
- });
1645
- })()
1646
- : buildSummaryOnlyProjection(trigger, snapshot, commandPolicy, ['epic-target-missing-summary-only']);
1647
- }
1648
- if (commandPolicy.stateProjectionPolicy === 'continuity-scoped') {
1649
- return buildSummaryOnlyProjection(trigger, snapshot, commandPolicy, ['continuity-scoped-summary-only']);
1650
- }
1651
- return buildSummaryOnlyProjection(trigger, snapshot, commandPolicy, ['policy-full-summary-only']);
1652
- }
1653
- catch (error) {
1654
- if (error instanceof StructuralDagError)
1655
- throw error;
1656
- if (commandPolicy?.structuralRequired) {
1657
- throw new StructuralDagError('STRUCTURAL_DEPENDENCY_MISSING', {
1658
- stories: Object.keys(snapshot.stories).length,
1659
- epics: Object.keys(snapshot.epics).length,
1660
- projectionErrors: 1,
1661
- });
1662
- }
1663
- return buildSummaryOnlyProjection(trigger, snapshot, commandPolicy ?? policy('unknown', 'adaptive-targeted'), ['projection-error-summary-only']);
1664
- }
1665
- }
1666
- const V2_INVOCATION_SHAPES = new Set([
1667
- 'yolo-story',
1668
- 'yolo-inline',
1669
- 'yoloop-target',
1670
- 'yoloop-session',
1671
- 'yoloop-discovery',
1672
- ]);
1673
- function projectedEdgeCount(snapshot) {
1674
- let edges = 0;
1675
- for (const collection of [snapshot.stories, snapshot.epics]) {
1676
- for (const entry of Object.values(collection)) {
1677
- if (Array.isArray(entry.depends_on)) {
1678
- edges += entry.depends_on.filter((value) => typeof value === 'string').length;
1679
- }
1680
- }
1681
- }
1682
- return edges;
1683
- }
1684
- /**
1685
- * Versioned result contract for the five structural YOLO/yoloop shapes.
1686
- * Other command families retain their existing projection policy and return
1687
- * null because they are outside StateProjectionV2's closed shape union.
1688
- */
1689
- export function projectStateSnapshotResultV2ForInvoke(args, snapshot) {
1690
- const commandPolicy = resolveCommandProjectionPolicy(args.trim());
1691
- if (!commandPolicy
1692
- || !['yolo', 'yolo-inline', 'yoloop'].includes(commandPolicy.commandId)) {
1693
- return null;
1694
- }
1695
- try {
1696
- const projected = projectStateSnapshotForInvoke(args, snapshot);
1697
- const shape = projected.scope?.projectionMode;
1698
- if (!shape || !V2_INVOCATION_SHAPES.has(shape)) {
1699
- throw new StructuralDagError('STRUCTURAL_DEPENDENCY_MISSING', {
1700
- stories: Object.keys(projected.stories).length,
1701
- epics: Object.keys(projected.epics).length,
1702
- projectionShapeErrors: 1,
1703
- });
1704
- }
1705
- return Object.freeze({
1706
- ok: true,
1707
- version: 2,
1708
- shape,
1709
- snapshot: projected,
1710
- included: {
1711
- epics: projected.scope?.includedEpicCount ?? Object.keys(projected.epics).length,
1712
- stories: projected.scope?.includedStoryCount ?? Object.keys(projected.stories).length,
1713
- edges: projectedEdgeCount(projected),
1714
- },
1715
- omitted: {
1716
- epics: projected.scope?.omittedEpicCount ?? 0,
1717
- stories: projected.scope?.omittedStoryCount ?? 0,
1718
- },
1719
- cycleCount: 0,
1720
- });
1721
- }
1722
- catch (error) {
1723
- if (!(error instanceof StructuralDagError))
1724
- throw error;
1725
- return Object.freeze({
1726
- ok: false,
1727
- code: error.code,
1728
- publicDetails: error.publicDetails,
1729
- });
1730
- }
1731
- }
1
+ import{createHash as B}from"node:crypto";import{analyzeDependencyDag as k,StructuralDagError as y}from"./structural-dag-projection.js";import{classifyInvocationShapeV2 as O}from"./invocation-shape.js";const U=12,J=16,C=["active","archived","rolled-back","synthetic","orphan"],Q=new Set(["in-progress","branch-ready","diagnose","research","write-spec","create-tasks","implemented","refactor-suggest-done","property-tests-emitted","tests-validated","qa-gate-passed","memory-updated","committed","synced","pr-created","pr-reviewed","partial"]);function ze(r){const e=/^[a-f0-9]{64}$/,t=/^P[1-9][0-9]*(?:\.[0-9]+)*$/,i=/^[A-Za-z0-9][A-Za-z0-9._/-]{0,239}$/;if(r.schemaVersion!=="EpicOperationalPublicProjection/1.0"||!e.test(r.repositoryFingerprint)||!e.test(r.identityHash)||!t.test(r.epicId)||!i.test(r.executionBranch)||!e.test(r.graphRevision)||!Number.isSafeInteger(r.storeRevision)||r.storeRevision<0||!Number.isSafeInteger(r.writerEpoch)||r.writerEpoch<1||r.runId!==null&&r.runId.length>128||r.runStatus!==null&&r.runStatus.length>32||r.runRevision!==null&&(!Number.isSafeInteger(r.runRevision)||r.runRevision<0)||r.snapshotHash!==null&&!e.test(r.snapshotHash)||!e.test(r.continuationSessionHash)||!e.test(r.pendingAuthorityHash)||!Number.isSafeInteger(r.pendingAuthorityCount)||r.pendingAuthorityCount<0||r.pendingAuthorityCount>1024||r.reasonCodes.length>16||r.reasonCodes.some(c=>!/^[a-z0-9][a-z0-9-]{0,79}$/.test(c))||r.publicSafe!==!0)throw new Error("epic-operational-projection-invalid");return Object.freeze({schemaVersion:r.schemaVersion,repositoryFingerprint:r.repositoryFingerprint,identityHash:r.identityHash,epicId:r.epicId,executionBranch:r.executionBranch,graphRevision:r.graphRevision,storeRevision:r.storeRevision,writerEpoch:r.writerEpoch,runId:r.runId,runStatus:r.runStatus,runRevision:r.runRevision,snapshotHash:r.snapshotHash,continuationSessionHash:r.continuationSessionHash,pendingAuthorityHash:r.pendingAuthorityHash,pendingAuthorityCount:r.pendingAuthorityCount,lockActive:r.lockActive,currentSessionActive:r.currentSessionActive,journalActive:r.journalActive,reasonCodes:[...r.reasonCodes],publicSafe:!0})}const n=(r,e,t={})=>({commandId:r,stateProjectionPolicy:e,...t}),A=Object.freeze({status:n("status","full"),"arch-plan":n("arch-plan","full"),"arch-prd":n("arch-prd","full"),"arch-architecture":n("arch-architecture","epic-targeted",{structuralRequired:!0}),"arch-api-contracts":n("arch-api-contracts","epic-targeted",{structuralRequired:!0}),"arch-api-integrations":n("arch-api-integrations","epic-targeted",{structuralRequired:!0}),"arch-database":n("arch-database","epic-targeted",{structuralRequired:!0}),"arch-security":n("arch-security","epic-targeted",{structuralRequired:!0}),"arch-design-system":n("arch-design-system","epic-targeted",{structuralRequired:!0}),"arch-ux-design":n("arch-ux-design","epic-targeted",{structuralRequired:!0}),"arch-performance":n("arch-performance","epic-targeted",{structuralRequired:!0}),"arch-testing":n("arch-testing","epic-targeted",{structuralRequired:!0}),"arch-infrastructure":n("arch-infrastructure","epic-targeted",{structuralRequired:!0}),"arch-review":n("arch-review","epic-targeted",{structuralRequired:!0}),"arch-fitness-functions":n("arch-fitness-functions","epic-targeted",{structuralRequired:!0}),"create-epic":n("create-epic","epic-summary"),"idea-diagnose":n("idea-diagnose","full"),"idea-research":n("idea-research","epic-targeted",{structuralRequired:!0}),"generate-epic":n("generate-epic","epic-targeted",{structuralRequired:!0}),exec:n("exec","story-targeted",{structuralRequired:!0}),yolo:n("yolo","story-targeted",{structuralRequired:!0}),"yolo-inline":n("yolo-inline","story-targeted",{structuralRequired:!0}),epic:n("epic","epic-targeted",{structuralRequired:!0}),"setup-branch":n("setup-branch","story-targeted",{structuralRequired:!0}),diagnose:n("diagnose","adaptive-targeted"),"diagnose-inline":n("diagnose-inline","full"),research:n("research","adaptive-targeted"),"research-inline":n("research-inline","full"),"write-spec":n("write-spec","story-targeted",{structuralRequired:!0}),"create-tasks":n("create-tasks","story-targeted",{structuralRequired:!0}),implement:n("implement","story-targeted",{structuralRequired:!0}),commit:n("commit","story-targeted",{structuralRequired:!0}),"commit-push":n("commit","story-targeted",{structuralRequired:!0}),sync:n("sync","story-targeted",{structuralRequired:!0}),"sync-main":n("sync","story-targeted",{structuralRequired:!0}),"create-pr":n("create-pr","story-targeted",{structuralRequired:!0}),review:n("review","story-targeted"),"review-pr":n("review","story-targeted"),merge:n("merge","story-targeted",{structuralRequired:!0}),"merge-pr":n("merge","story-targeted",{structuralRequired:!0}),"fix-blocked":n("fix-blocked","story-targeted"),"finalize-epic":n("finalize-epic","epic-targeted"),"resolve-conflicts":n("resolve-conflicts","story-targeted"),"update-memory":n("update-memory","full"),"update-project-memory":n("update-memory","story-targeted",{structuralRequired:!0}),refactor:n("refactor","adaptive-targeted"),"refactor-suggest":n("refactor","story-targeted",{structuralRequired:!0}),cleanup:n("cleanup","full"),audit:n("audit","full"),"tech-debt":n("tech-debt","full"),menu:n("menu","full"),plans:n("plans","full"),billing:n("billing","full"),subscribe:n("subscribe","full"),"qa-scan":n("qa-scan","full"),yoloop:n("yoloop","yoloop-targeted",{structuralRequired:!0}),"migrate-state":n("migrate-state","full"),"migrate-project-id":n("migrate-project-id","full"),"policy-audit":n("policy-audit","full"),"mutation-test":n("mutation-test","full"),"sync-openapi":n("sync-openapi","full"),"generate-pacts":n("generate-pacts","full"),"verify-pacts":n("verify-pacts","full"),"property-test":n("property-test","full"),"property-tests":n("property-test","story-targeted",{structuralRequired:!0}),"chaos-plan":n("chaos-plan","full"),"ui-ux-review":n("ui-ux-review","adaptive-targeted"),"solyd-system":n("solyd-system","adaptive-targeted"),goal:n("goal","continuity-scoped"),loop:n("loop","continuity-scoped"),jobs:n("jobs","continuity-scoped"),runner:n("runner","continuity-scoped"),"goal-tick":n("goal-tick","continuity-scoped"),"loop-tick":n("loop-tick","continuity-scoped"),"validate-tests":n("validate-tests","story-targeted",{structuralRequired:!0}),"qa-gate":n("qa-gate","story-targeted",{structuralRequired:!0})}),X=Object.freeze({st:"status",arquitetura:"arch-plan",architecture:"arch-plan",arch:"arch-plan",prd:"arch-prd","product-requirements":"arch-prd","arch-doc":"arch-architecture","tech-architecture":"arch-architecture","api-contracts":"arch-api-contracts","api-contract":"arch-api-contracts","api-integrations":"arch-api-integrations","api-int":"arch-api-integrations","database-schema":"arch-database","db-schema":"arch-database",db:"arch-database","cyber-security":"arch-security",security:"arch-security",sec:"arch-security","design-system":"arch-design-system",ds:"arch-design-system","atomic-design":"arch-design-system","ux-design":"arch-ux-design",ux:"arch-ux-design",wireframes:"arch-ux-design",wireframe:"arch-ux-design","user-flows":"arch-ux-design",performance:"arch-performance",perf:"arch-performance","capacity-planning":"arch-performance",capacity:"arch-performance","arch-perf":"arch-performance","performance-strategy":"arch-performance","testing-strategy":"arch-testing","test-plan":"arch-testing","test-strategy":"arch-testing","arch-test":"arch-testing",infrastructure:"arch-infrastructure",infra:"arch-infrastructure","deploy-plan":"arch-infrastructure",deployment:"arch-infrastructure","arch-infra":"arch-infrastructure","arch-deploy":"arch-infrastructure","review-consolidation":"arch-review",consolidation:"arch-review","architecture-review":"arch-review","arch-consolidation":"arch-review",fitness:"arch-fitness-functions","fitness-functions":"arch-fitness-functions",archfit:"arch-fitness-functions","arch-fit":"arch-fitness-functions","fit-funcs":"arch-fitness-functions","criar-epic":"create-epic",planejamento:"create-epic","idea-diag":"idea-diagnose","idea-pesq":"idea-research","gerar-epic":"generate-epic",executar:"exec",pipeline:"exec","parallel-epic":"epic",setup:"setup-branch",branch:"setup-branch",diag:"diagnose",diagnostico:"diagnose",c02:"diagnose",pesquisar:"research",pesq:"research","research-fix":"research",c03:"research",spec:"write-spec",especificacao:"write-spec",c04:"write-spec",tasks:"create-tasks",tarefas:"create-tasks",c05:"create-tasks",impl:"implement",implementar:"implement",c06:"implement",push:"commit",commit_push:"commit",cp:"commit",c07:"commit",sincronizar:"sync",c08:"sync",pr:"create-pr",c09:"create-pr",revisar:"review",c11:"review",mesclar:"merge",c12:"merge",fix:"fix-blocked",corrigir:"fix-blocked",finalizar:"finalize-epic",conflitos:"resolve-conflicts",memoria:"update-memory",refactoring:"refactor",refact:"refactor",refac:"refactor",rf:"refactor","cleanup-code":"refactor","code-cleanup":"refactor","code-quality":"refactor","improve-code":"refactor",modernize:"refactor",modernization:"refactor",legacy:"refactor","legacy-code":"refactor","debt-pay":"refactor","pay-debt":"refactor","debt-reduction":"refactor","smell-fix":"refactor","fix-smells":"refactor","code-smells":"refactor",strangler:"refactor","strangler-fig":"refactor",limpar:"cleanup",limpeza:"cleanup",auditar:"audit",auditoria:"audit",debt:"tech-debt",debts:"tech-debt","debt-list":"tech-debt","tech-debts":"tech-debt",td:"tech-debt",help:"menu",ajuda:"menu","?":"menu",commands:"menu",cmds:"menu",planos:"plans",pricing:"plans",bill:"billing",subscription:"billing",sub:"billing",upgrade:"subscribe",checkout:"subscribe",qa:"qa-scan","qa-test":"qa-scan","visual-qa":"qa-scan","e2e-scan":"qa-scan",troubleshoot:"qa-scan","debug-ui":"qa-scan",scan:"qa-scan",a11y:"qa-scan",accessibility:"qa-scan",wcag:"qa-scan","auto-yolo":"yoloop","epic-runner":"yoloop","story-runner":"yoloop","loop-yolo":"yoloop","auto-epic":"yoloop",migrate:"migrate-state","state-migrate":"migrate-state","schema-migrate":"migrate-state",policy:"policy-audit","audit-policy":"policy-audit","policy-history":"policy-audit",policies:"policy-audit",mutation:"mutation-test",stryker:"mutation-test","kill-mutants":"mutation-test","openapi-sync":"sync-openapi","api-sync":"sync-openapi","spec-sync":"sync-openapi","regen-openapi":"sync-openapi",pacts:"generate-pacts","gen-pacts":"generate-pacts","regen-pacts":"generate-pacts","create-pacts":"generate-pacts","verify-pact":"verify-pacts","pact-verify":"verify-pacts","check-pacts":"verify-pacts","pact-check":"verify-pacts",property:"property-test",pbt:"property-test",proptest:"property-test","invariant-test":"property-test",chaos:"chaos-plan",gameday:"chaos-plan","resilience-test":"chaos-plan","ux-review":"ui-ux-review","design-review":"ui-ux-review","visual-review":"ui-ux-review","product-polish":"ui-ux-review","security-system":"solyd-system","authorized-security":"solyd-system","sec-system":"solyd-system",goals:"goal","durable-goal":"goal","continuity-goal":"goal",loops:"loop","durable-loop":"loop","continuity-loop":"loop",job:"jobs","continuity-jobs":"jobs","neocortex-runner":"runner","continuity-runner":"runner"}),ee=Object.freeze({1:"status",3:"arch-plan",4:"arch-prd",5:"arch-architecture",6:"arch-api-contracts",7:"arch-api-integrations",8:"arch-database",9:"arch-security",10:"arch-design-system",11:"arch-ux-design",12:"arch-performance",13:"arch-testing",14:"arch-infrastructure",15:"arch-review",16:"arch-fitness-functions",17:"create-epic",18:"idea-diagnose",19:"idea-research",20:"generate-epic",21:"exec",22:"yolo",23:"epic",24:"setup-branch",25:"diagnose",26:"research",27:"write-spec",28:"create-tasks",29:"implement",30:"commit",31:"sync",32:"create-pr",33:"review",34:"merge",35:"fix-blocked",36:"finalize-epic",37:"resolve-conflicts",38:"update-memory",39:"refactor",40:"cleanup",41:"audit",42:"tech-debt",43:"menu",44:"plans",45:"billing",46:"subscribe",47:"qa-scan",48:"yoloop",49:"migrate-state",50:"policy-audit",51:"mutation-test",52:"sync-openapi",53:"generate-pacts",54:"verify-pacts",55:"property-test",56:"chaos-plan",57:"ui-ux-review",58:"solyd-system",59:"goal",60:"loop",61:"jobs",62:"runner"});function d(r,e=120){if(typeof r!="string")return;const t=r.trim();if(t)return t.replace(/[^a-z0-9_.:-]+/gi,"_").slice(0,e)}function w(r,e=120){if(typeof r!="string")return;const t=r.trim(),i=d(r,e);return i===t?i:void 0}function T(r){if(typeof r!="string")return;const e=r.trim();if(!(!e||e.length>240||!/^[A-Za-z0-9][A-Za-z0-9._/-]*$/.test(e)||e.includes("..")||e.includes("//")||e.includes("@{")||e.endsWith("/")||e.endsWith(".")||e.split("/").some(t=>!t||t.startsWith(".")||t.endsWith(".lock"))))return e}function te(r){if(typeof r!="string")return;const e=r.replace(/\\/g,"/");if(!(!e||e.length>240||e.startsWith("/")||/^[A-Za-z]:/.test(e)||e.includes("..")||e.includes("//")||e.split("/").includes(".")||!/^[A-Za-z0-9._/:@#-]+$/.test(e)))return e}function S(r){if(r===void 0||!Array.isArray(r)||r.length>128)return;const e=r.map(te);if(!e.some(t=>t===void 0))return[...new Set(e)].sort((t,i)=>t.localeCompare(i,"en"))}function re(r){return typeof r=="string"&&Q.has(r.trim().toLowerCase().replace(/_/g,"-"))}function v(r,e){const t={};for(const i of e){const c=r[i];typeof c=="string"||typeof c=="boolean"||typeof c=="number"&&Number.isFinite(c)?t[i]=c:Array.isArray(c)&&c.every(o=>typeof o=="string")&&(t[i]=[...c])}return t}function ie(r,e){const t=e.checkpoint;if(!h(t)||t.schemaVersion!==1||t.storyId!==r||t.epicId!==e.epic_id||!T(t.branch)||typeof t.baseSha!="string"||!/^[0-9a-f]{7,64}$/i.test(t.baseSha)||typeof t.headSha!="string"||!/^[0-9a-f]{7,64}$/i.test(t.headSha)||t.baseSha===t.headSha||!w(t.idempotencyKey)||typeof t.recordedAt!="string"||!Number.isFinite(Date.parse(t.recordedAt))||!Array.isArray(t.commits)||t.commits.length===0||t.commits.length>256||t.commits.some(c=>typeof c!="string"||!/^[0-9a-f]{7,64}$/i.test(c))||!Array.isArray(t.receiptRefs)||t.receiptRefs.length===0||t.receiptRefs.length>256||t.receiptRefs.some(c=>typeof c!="string"||c.length>240||c.includes("..")||!/^[A-Za-z0-9._:/#\-[\]]+$/.test(c))||!h(t.qaReceipt)||t.qaReceipt.kind!=="story_qa_evidence"||t.qaReceipt.producer!=="local-yolo-tdd"||!["passed","blocked"].includes(String(t.qaVerdict))||!["approved","changes-requested","pending"].includes(String(t.reviewVerdict))||(t.reviewVerdict==="pending"?t.reviewReceipt!==void 0:!h(t.reviewReceipt)))return;const i=v(t,["schemaVersion","storyId","epicId","branch","baseSha","headSha","idempotencyKey","dispatchId","bundleId","bundleDigest","graphRevision","claimRevision","qaVerdict","reviewVerdict","recordedAt","commits","receiptRefs"]);return i.qaReceipt=v(t.qaReceipt,["schemaVersion","kind","producer","evidenceVersion","storyId","epicId","contract","command","manifestDigest","gateIds","resultDigest","baseSha","headSha","bundleDigest","graphRevision","sourceEvidenceDigest","verdict","checkedAt","receiptRef"]),h(t.reviewReceipt)&&(i.reviewReceipt=v(t.reviewReceipt,["schemaVersion","kind","producer","storyId","epicId","baseSha","headSha","bundleDigest","graphRevision","sourceEvidenceDigest","qaReceiptRef","verdict","reviewedAt","receiptRef"])),i}function ce(r,e){const t=e.checkpoint_verification;if(!(!h(t)||t.schemaVersion!==1||t.kind!=="story_checkpoint_physical_verification"||t.producer!=="client-p205-transient-v1"||t.publicSafe!==!0||t.storyId!==r||t.epicId!==e.epic_id||!["verified","rejected"].includes(String(t.verdict))))return v(t,["schemaVersion","kind","producer","storyId","epicId","branch","baseSha","headSha","aggregateHeadSha","commitBodyDigest","changedPathsDigest","claimVerification","effectiveClaimRevision","command","manifestDigest","gateIds","resultDigest","graphRevision","executionEvidenceDigest","sourceEvidenceDigest","verifiedAt","verdict","reasonCodes","publicSafe"])}function M(r,e){const t=e.scheduling_checkpoint_evidence,i=new Set(["schemaVersion","kind","producer","storyId","epicId","branch","baseSha","headSha","graphRevision","effectiveClaimRevision","sourceEvidenceDigest","verifiedAt","reasonCodes","integrationAuthorized","publicSafe"]);if(!(!h(t)||Object.keys(t).some(c=>!i.has(c))||t.schemaVersion!==1||t.kind!=="story_scheduling_checkpoint_evidence"||t.producer!=="client-git-physical-v1"||t.publicSafe!==!0||t.integrationAuthorized!==!1||t.storyId!==r||t.epicId!==e.epic_id||!T(t.branch)||typeof t.baseSha!="string"||!/^[0-9a-f]{40,64}$/i.test(t.baseSha)||typeof t.headSha!="string"||!/^[0-9a-f]{40,64}$/i.test(t.headSha)||typeof t.graphRevision!="string"||!/^[0-9a-f]{64}$/i.test(t.graphRevision)||typeof t.effectiveClaimRevision!="string"||!/^[0-9a-f]{64}$/i.test(t.effectiveClaimRevision)||typeof t.sourceEvidenceDigest!="string"||!/^[0-9a-f]{64}$/i.test(t.sourceEvidenceDigest)||typeof t.verifiedAt!="string"||!Number.isFinite(Date.parse(t.verifiedAt))||!Array.isArray(t.reasonCodes)||t.reasonCodes.length!==1||t.reasonCodes[0]!=="checkpoint-git-scheduling-verified"))return v(t,[...i])}function oe(r,e){const t={...e},i=M(r,e);return i?(t.scheduling_checkpoint_evidence=i,delete t.checkpoint,delete t.checkpoint_verification):delete t.scheduling_checkpoint_evidence,t}function h(r){return r!==null&&typeof r=="object"&&!Array.isArray(r)}function _(...r){const e=new Set,t=[];for(const i of r)for(const c of i??[]){const o=d(c,80);if(!(!o||e.has(o))&&(e.add(o),t.push(o),t.length>=J))return t}return t}function ne(r){return r.trim().replace(/^@?\*/,"").replace(/^@/,"").toLowerCase()}function se(r){return r.trim().split(/\s+/,1)[0]??""}function z(r){if(/^@epic-P[0-9]{1,12}$/.test(r.trim()))return A.epic;const e=se(r);if(!e)return A.menu;const t=ne(e),i=ee[t]??X[t]??t;return A[i]??null}function ae(r,e,t,i="full-compatible",c=["full-state"]){return{kind:"full",commandId:t?.commandId??null,stateProjectionPolicy:t?.stateProjectionPolicy??null,projectionMode:i,trigger:r,targetEpicId:null,targetStoryId:null,includedStoryCount:Object.keys(e.stories).length,includedEpicCount:Object.keys(e.epics).length,omittedStoryCount:0,omittedEpicCount:0,reason:"full-state",reasonCodes:_(c)}}function Fe(r,e,t,i="full-compatible",c=["full-state"]){const o=ae(r,e,t,i,c);return I({...e,scope:o},e,o)}function de(r){const[e]=r.trim().split(/\s+/,1);return e?/^[A-Z]?\d+(?:\.\d+)+$/i.test(e.replace(/^@/,""))||/(?:^|\/)[A-Z]?\d+(?:\.\d+)+\.story\.md$/i.test(e.replace(/^@/,"")):!1}function F(r){const e=r.match(/(?:^|[/@\s])([A-Z]?\d+(?:\.\d+)+)\.story\.md\b/i);return e?.[1]?e[1]:r.match(/(?:^|\s|@)([A-Z]?\d+(?:\.\d+)+)(?=$|\s)/i)?.[1]??null}function ue(r){const e=r.trim(),t=e.search(/\s/);return t===-1?"":e.slice(t).trim()}function q(r){return ue(r).split(/\s+/).map(e=>e.replace(/^@/,"").replace(/^['"]|['"]$/g,"")).filter(e=>e.length>0&&!e.startsWith("-"))}function $e(r){return q(r)[0]??null}function P(r,e){const i=r.replace(/^@/,"").replace(/^epic-/i,"").replace(/\.md$/i,""),c=[i,i.toUpperCase(),/^p\d+$/i.test(i)?`P${i.replace(/^p/i,"")}`:null,/^\d+$/.test(i)?`P${i}`:null].filter(o=>typeof o=="string"&&o.length>0);for(const o of c)if(e.epics[o])return o;return null}function le(r,e){const t=r.match(/(?:^|[/@\s])epic-(P?\d+)\.md\b/i);return t?.[1]?P(t[1],e):null}function R(r,e){const t=r.trim().match(/^@epic-(P[0-9]{1,12})$/);if(t)return P(t[1],e);const i=le(r,e);if(i)return i;for(const c of q(r)){if(/^[A-Z]?\d+(?:\.\d+)+$/i.test(c))continue;const o=P(c,e);if(o)return o}return null}function pe(r,e){const t=R(r,e);if(t)return t;const i=e.yoloop,c=i?.state?.currentSession?.currentEpicId;if(typeof c=="string"&&e.epics[c])return c;const o=i?.lock?.currentEpicId??i?.lock?.epicId;return typeof o=="string"&&e.epics[o]?o:null}function fe(r){const e=r.match(/(?:^|[/@\s])epic-(P?\d+)\.md\b/i)?.[1];if(e)return/^P/i.test(e)?e.toUpperCase():`P${e}`;for(const t of q(r)){if(/^P\d+$/i.test(t))return t.toUpperCase();if(/^\d+$/.test(t))return`P${t}`}return null}function j(r,e){if(typeof r.epic_id=="string"&&r.epic_id.length>0)return r.epic_id;const t=e.split(".").slice(0,-1).join(".");return t.length>0?t:null}function $(r){return typeof r=="string"&&["done","completed","merged","cancelled","canceled","abandoned"].includes(r.toLowerCase())}function x(r,e,t){if(t.has(r))return;const i=e.stories[r];if(!i)return;t.add(r);const c=i.depends_on;if(Array.isArray(c))for(const o of c)typeof o=="string"&&x(o,e,t)}function ye(r,e){const t=k(e.stories,[r]),i={};for(const c of t.orderedIds){const o=e.stories[c];o&&(i[c]=o)}return i}function L(r,e,t=[]){const i={};for(const c of t)e.epics[c]&&(i[c]=e.epics[c]);for(const[c,o]of Object.entries(r)){const s=j(o,c);s&&e.epics[s]&&(i[s]=e.epics[s])}return i}function me(r,e,t){if(t.has(r))return;const i=e.epics[r];if(!i)return;t.add(r);const c=i.depends_on;if(Array.isArray(c))for(const o of c)typeof o=="string"&&o.length>0&&me(o,e,t)}function V(r,e,t){const i=k(t.epics,[r]),c=new Set(i.orderedIds);for(const[s,a]of Object.entries(e)){const u=j(a,s);u&&t.epics[u]&&c.add(u)}const o={};for(const s of[...c].sort((a,u)=>a.localeCompare(u,"en"))){const a=t.epics[s];a&&(o[s]=s===r?a:H(s,a))}return o}function G(r,e,t){const i=e.stories;if(!Array.isArray(i)||i.some(s=>typeof s!="string"||s.length===0))throw new y("STRUCTURAL_DEPENDENCY_MISSING",{nodes:0,edges:0,malformedStoryMemberships:1});const c=k(t.stories,i),o={};for(const s of c.orderedIds){const a=t.stories[s];a&&(o[s]=j(a,s)===r?oe(s,a):Y(s,a))}return o}function ge(r,e,t){const i=[...e.stories].sort((s,a)=>s.localeCompare(a,"en")),c=Object.entries(t).filter(([s,a])=>j(a,s)===r).map(([s])=>s).sort((s,a)=>s.localeCompare(a,"en")),o=B("sha256").update(`target-epic-coverage-v1\0${JSON.stringify({targetEpicId:r,declaredStoryIds:i,includedStoryIds:c})}`).digest("hex");return{schemaVersion:"TargetEpicCoverage/1.0",targetEpicId:r,declaredStoryIds:i,includedStoryIds:c,membershipSha256:o,publicSafe:!0}}function Y(r,e){const t=Array.isArray(e.depends_on)?[...new Set(e.depends_on.filter(m=>typeof m=="string"&&m.length>0))].sort((m,E)=>m.localeCompare(E,"en")):[],i={id:d(e.id)??r,...d(e.epic_id)?{epic_id:d(e.epic_id)}:{},...d(e.status,40)?{status:d(e.status,40)}:{},depends_on:t},c=M(r,e);if(!re(e.status))return{...i,...c?{scheduling_checkpoint_evidence:c}:{}};const o=e.source==="create-epic"||e.source==="discovered"?e.source:void 0,s=T(e.branch_name),a=w(e.last_step,120),u=Array.isArray(e.steps_completed)&&e.steps_completed.length<=64?e.steps_completed.map(m=>w(m,120)):[],l=u.length>0&&u.every(m=>m!==void 0)?u:void 0,f=typeof e.pr_number=="number"&&Number.isSafeInteger(e.pr_number)&&e.pr_number>0?e.pr_number:void 0,p=ie(r,e),g=p?ce(r,e):void 0;return{...i,...o?{source:o}:{},...s?{branch_name:s}:{},...a?{last_step:a}:{},...l?{steps_completed:l}:{},...f?{pr_number:f}:{},...p?{checkpoint:p}:{},...g?{checkpoint_verification:g}:{},...c?{scheduling_checkpoint_evidence:c}:{},...S(e.files_to_modify)!==void 0?{files_to_modify:S(e.files_to_modify)}:{},...S(e.contracts)!==void 0?{contracts:S(e.contracts)}:{},...S(e.contract_refs)!==void 0?{contract_refs:S(e.contract_refs)}:{},...S(e.shared_surfaces)!==void 0?{shared_surfaces:S(e.shared_surfaces)}:{}}}function H(r,e){const t=Array.isArray(e.stories)?[...new Set(e.stories.filter(c=>typeof c=="string"&&c.length>0))].sort((c,o)=>c.localeCompare(o,"en")):[],i=Array.isArray(e.depends_on)?[...new Set(e.depends_on.filter(c=>typeof c=="string"&&c.length>0))].sort((c,o)=>c.localeCompare(o,"en")):[];return{id:d(e.id)??r,...d(e.status,40)?{status:d(e.status,40)}:{},...d(e.lifecycle_classification,40)?{lifecycle_classification:d(e.lifecycle_classification,40)}:{},stories:t,depends_on:i}}function he(r,e,t){const i=k(e.stories),c=k(e.epics),o=Object.fromEntries(i.orderedIds.map(u=>[u,Y(u,e.stories[u])])),s=Object.fromEntries(c.orderedIds.map(u=>[u,H(u,e.epics[u])])),a={kind:"projected",commandId:t.commandId,stateProjectionPolicy:t.stateProjectionPolicy,projectionMode:"yoloop-discovery",trigger:r,targetEpicId:null,targetStoryId:null,includedStoryCount:i.orderedIds.length,includedEpicCount:c.orderedIds.length,omittedStoryCount:0,omittedEpicCount:0,reason:"targeted-invocation",reasonCodes:_(["structural-required","yoloop-discovery",`structural-edges-${i.edgeCount+c.edgeCount}`,"content-fields-omitted"])};return I({...e,stories:o,epics:s,scope:a},e,a)}function be(r,e){const t=new Set,i=r.stories;if(Array.isArray(i))for(const o of i)typeof o=="string"&&x(o,e,t);const c={};for(const o of t){const s=e.stories[o];s&&(c[o]=s)}return c}function _e(r){const e={};for(const[t,i]of Object.entries(r.epics))e[t]={id:d(i.id)??t,...d(i.status)?{status:d(i.status)}:{},...C.includes(i.lifecycle_classification)?{lifecycle_classification:i.lifecycle_classification}:{},...typeof i.total_stories=="number"&&Number.isFinite(i.total_stories)?{total_stories:i.total_stories}:{},...typeof i.completed_stories=="number"&&Number.isFinite(i.completed_stories)?{completed_stories:i.completed_stories}:{}};return e}function K(r){const e={};for(const t of Object.values(r)){const i=d(t.status,40)??"unknown";e[i]=(e[i]??0)+1}return e}function Se(r){const e=Object.fromEntries(C.map(t=>[t,0]));for(const t of Object.values(r)){const i=C.includes(t.lifecycle_classification)?t.lifecycle_classification:"active";e[i]=(e[i]??0)+1}return e}function N(r,e){if(!e)return!0;const t=r.epics[e];return t?!C.includes(t.lifecycle_classification)||t.lifecycle_classification==="active":!0}function Z(r){for(const e of["last_updated","updated_at","completed_at","created_at","discovered_at"]){const t=r[e];if(typeof t!="string")continue;const i=Date.parse(t);if(Number.isFinite(i))return i}return 0}function je(r){return Object.entries(r.stories).map(([e,t])=>({id:e,story:t,timestamp:Z(t)})).sort((e,t)=>t.timestamp-e.timestamp||e.id.localeCompare(t.id)).slice(0,U).map(({id:e,story:t})=>({id:e,...d(t.epic_id)?{epicId:d(t.epic_id)}:{},...d(t.status,40)?{status:d(t.status,40)}:{},...d(t.last_step??t.lastStep)?{lastStep:d(t.last_step??t.lastStep)}:{},...d(t.last_updated??t.updated_at??t.completed_at??t.created_at)?{lastUpdated:d(t.last_updated??t.updated_at??t.completed_at??t.created_at)}:{}}))}function Ie(r){const e=r.current_session;if(!e)return;const t={activeEpicId:d(e.active_epic_id)??null,activeStoryId:d(e.active_story_id)??null,activeStepId:d(e.active_step_id)??null,sessionId:d(e.session_id)??null,startedAt:d(e.started_at)??null};return Object.values(t).some(i=>i!=null)?t:void 0}function ke(r){const e=r.yoloop;if(!h(e))return;const t=h(e.state)?e.state:void 0,i=h(t?.currentSession)?t.currentSession:void 0,c=h(e.lock)?e.lock:void 0,o={...typeof e.active=="boolean"?{active:e.active}:{},currentEpicId:d(i?.currentEpicId??e.currentEpicId)??null,currentStoryId:d(i?.currentStoryId??e.currentStoryId)??null,sessionId:d(i?.sessionId??e.sessionId)??null,lockEpicId:d(c?.currentEpicId??c?.epicId)??null};return Object.keys(o).length>0?o:void 0}function Ce(r){const e=r.continuity;if(!h(e))return;const t=Array.isArray(e.reasonCodes)?e.reasonCodes.filter(i=>typeof i=="string"):void 0;return{present:!0,activeJobId:d(e.activeJobId??e.jobId)??null,activeGoalId:d(e.activeGoalId??e.goalId)??null,activeLoopId:d(e.activeLoopId??e.loopId)??null,...t&&t.length>0?{reasonCodes:_(t)}:{}}}function ve(r,e,t){const i=d(e?.activeStoryId),c=d(e?.activeEpicId),o=d(e?.activeStepId),s=i&&r.stories[i]?j(r.stories[i],i):null;if((i||c||o)&&N(r,c??s))return{epicId:c??null,storyId:i??null,stepId:o??null};const a=d(t?.currentStoryId),u=d(t?.currentEpicId??t?.lockEpicId);if((a||u)&&N(r,u))return{epicId:u??null,storyId:a??null,stepId:null};const l=Object.entries(r.stories).map(([f,p])=>({id:f,story:p,timestamp:Z(p)})).filter(({id:f,story:p})=>!$(p.status)&&N(r,j(p,f))).sort((f,p)=>p.timestamp-f.timestamp||f.id.localeCompare(p.id))[0];return l?{epicId:j(l.story,l.id),storyId:l.id,stepId:d(l.story.last_step??l.story.lastStep)??null}:{epicId:null,storyId:null,stepId:null}}function Ee(r,e){const t=K(r.stories),i=K(r.epics),c=Se(r.epics),o=Object.values(r.epics).filter(g=>(C.includes(g.lifecycle_classification)?g.lifecycle_classification:"active")==="active"&&!$(g.status)).length,s=Ie(r),a=ke(r),u=Ce(r),l=ve(r,s,a),f=je(r),p=_(e.reasonCodes,["state-summary-attached","state-summary-bounded",`projection-${e.projectionMode??e.kind}`,e.stateProjectionPolicy?`policy-${e.stateProjectionPolicy}`:"policy-unknown",f.length>=U?"recent-stories-capped":"recent-stories-bounded",l.storyId||l.epicId?"active-ref-derived":"active-ref-empty",a?"yoloop-session-present":"yoloop-session-absent",u?"continuity-session-present":"continuity-session-absent"]);return{schemaVersion:1,totals:{stories:Object.keys(r.stories).length,epics:Object.keys(r.epics).length,storyStatus:t,epicStatus:i,epicLifecycle:c,activeEpics:o,blockedStories:t.blocked??0},active:l,blockedCount:t.blocked??0,recentStories:f,...s?{currentSession:s}:{},...a?{currentYoloopSession:a}:{},...u?{currentContinuitySession:u}:{},projection:{commandId:e.commandId??null,stateProjectionPolicy:e.stateProjectionPolicy??null,projectionMode:e.projectionMode,scopeKind:e.kind,includedStoryCount:e.includedStoryCount,includedEpicCount:e.includedEpicCount??Object.keys(r.epics).length,omittedStoryCount:e.omittedStoryCount,omittedEpicCount:e.omittedEpicCount,reason:e.reason},reasonCodes:p,publicSafe:!0}}function I(r,e,t){return{...r,stateSummary:Ee(e,t)}}function Re(r,e,t){const i={};for(const o of Object.keys(e.epics))i[o]={id:o};const c={kind:"projected",commandId:t.commandId,stateProjectionPolicy:t.stateProjectionPolicy,projectionMode:"summary-only",trigger:r,targetEpicId:null,targetStoryId:null,includedStoryCount:0,includedEpicCount:Object.keys(i).length,omittedStoryCount:Object.keys(e.stories).length,omittedEpicCount:0,reason:"epic-summary",reasonCodes:["epic-summary","stories-omitted","epic-stubs-only"]};return I({...e,stories:{},epics:i,scope:c},e,c)}function b(r,e,t,i=["summary-only","stories-omitted"],c="summary-only"){const o=_e(e),s={kind:"projected",commandId:t.commandId,stateProjectionPolicy:t.stateProjectionPolicy,projectionMode:c,trigger:r,targetEpicId:null,targetStoryId:null,includedStoryCount:0,includedEpicCount:Object.keys(o).length,omittedStoryCount:Object.keys(e.stories).length,omittedEpicCount:Math.max(0,Object.keys(e.epics).length-Object.keys(o).length),reason:"summary-only",reasonCodes:_(i)};return I({...e,stories:{},epics:o,scope:s},e,s)}function D(r,e,t,i,c,o){const s=e.stories[i];if(!s)throw new y("STRUCTURAL_TARGET_NOT_FOUND",{stories:Object.keys(e.stories).length,epics:Object.keys(e.epics).length,targetKind:"story"});const a=j(s,i);if(!a)throw new y("STRUCTURAL_TARGET_NOT_FOUND",{stories:Object.keys(e.stories).length,epics:Object.keys(e.epics).length,targetKind:"epic"});const u=e.epics[a];if(!u)throw new y("STRUCTURAL_TARGET_NOT_FOUND",{stories:Object.keys(e.stories).length,epics:Object.keys(e.epics).length,targetKind:"epic"});const l=c==="yolo-story",f=l?G(a,u,e):ye(i,e),p=l?V(a,f,e):L(f,e);if(!f[i]||!p[a])throw new y("STRUCTURAL_TARGET_NOT_FOUND",{stories:Object.keys(f).length,epics:Object.keys(p).length,targetKind:"story"});const g={kind:"projected",commandId:t.commandId,stateProjectionPolicy:t.stateProjectionPolicy,projectionMode:c,trigger:r,targetEpicId:a,targetStoryId:i,includedStoryCount:Object.keys(f).length,includedEpicCount:Object.keys(p).length,omittedStoryCount:Math.max(0,Object.keys(e.stories).length-Object.keys(f).length),omittedEpicCount:Math.max(0,Object.keys(e.epics).length-Object.keys(p).length),reason:"targeted-invocation",reasonCodes:_(o,l?["epic-graph-authority-complete"]:[])};return I({...e,stories:f,epics:p,scope:g},e,g)}function W(r,e,t,i,c,o){const s=e.epics[i];if(!s)throw new y("STRUCTURAL_TARGET_NOT_FOUND",{stories:Object.keys(e.stories).length,epics:Object.keys(e.epics).length,targetKind:"epic"});const a=be(s,e),u=L(a,e,[i]);if(!u[i])throw new y("STRUCTURAL_TARGET_NOT_FOUND",{stories:Object.keys(a).length,epics:Object.keys(u).length,targetKind:"epic"});const l={kind:"projected",commandId:t.commandId,stateProjectionPolicy:t.stateProjectionPolicy,projectionMode:c,trigger:r,targetEpicId:i,targetStoryId:null,includedStoryCount:Object.keys(a).length,includedEpicCount:Object.keys(u).length,omittedStoryCount:Math.max(0,Object.keys(e.stories).length-Object.keys(a).length),omittedEpicCount:Math.max(0,Object.keys(e.epics).length-Object.keys(u).length),reason:"targeted-invocation",reasonCodes:_(o)};return I({...e,stories:a,epics:u,scope:l},e,l)}function Oe(r,e,t){const i=fe(r),c=R(r,e);if(i&&!c)throw new y("STRUCTURAL_TARGET_NOT_FOUND",{stories:Object.keys(e.stories).length,epics:Object.keys(e.epics).length,targetKind:"epic"});const o=pe(r,e),s=O({commandId:t.commandId,explicitYoloopEpicId:c,sessionYoloopEpicId:c?null:o});if(s==="yoloop-discovery")return he(r,e,t);if(!o||s!=="yoloop-target"&&s!=="yoloop-session")throw new y("STRUCTURAL_TARGET_NOT_FOUND",{stories:Object.keys(e.stories).length,epics:Object.keys(e.epics).length,targetKind:"epic"});const a=e.epics[o];if(!a)throw new y("STRUCTURAL_TARGET_NOT_FOUND",{stories:Object.keys(e.stories).length,epics:Object.keys(e.epics).length,targetKind:"epic"});const u=G(o,a,e),l=V(o,u,e);if(!l[o])throw new y("STRUCTURAL_TARGET_NOT_FOUND",{stories:Object.keys(u).length,epics:Object.keys(l).length,targetKind:"epic"});const f=s,p={kind:"projected",commandId:t.commandId,stateProjectionPolicy:t.stateProjectionPolicy,projectionMode:f,trigger:r,targetEpicId:o,targetStoryId:null,targetEpicCoverage:ge(o,a,u),includedStoryCount:Object.keys(u).length,includedEpicCount:Object.keys(l).length,omittedStoryCount:Math.max(0,Object.keys(e.stories).length-Object.keys(u).length),omittedEpicCount:Math.max(0,Object.keys(e.epics).length-Object.keys(l).length),reason:"targeted-invocation",reasonCodes:["structural-required",f,"yoloop-targeted"]};return I({...e,stories:u,epics:l,scope:p},e,p)}function Ae(r,e){const t={};for(const[i,c]of Object.entries(r.epics)){const o=Array.isArray(c.stories)?c.stories.filter(s=>typeof s=="string"&&e.has(s)):[];t[i]={id:d(c.id)??i,...d(c.status,40)?{status:d(c.status,40)}:{},...typeof c.total_stories=="number"&&Number.isFinite(c.total_stories)?{total_stories:c.total_stories}:{},...typeof c.completed_stories=="number"&&Number.isFinite(c.completed_stories)?{completed_stories:c.completed_stories}:{},...o.length>0?{stories:o}:{},...o.length>0&&Array.isArray(c.depends_on)?{depends_on:c.depends_on.filter(s=>typeof s=="string"&&s.length>0)}:{},...o.length>0&&(c.branch_mode==="epic"||c.branch_mode==="story")?{branch_mode:c.branch_mode}:{},...o.length>0&&typeof c.execution_branch=="string"?{execution_branch:c.execution_branch}:{},...o.length>0&&typeof c.origin_branch=="string"?{origin_branch:c.origin_branch}:{},...o.length>0&&(c.branch_lease===null||typeof c.branch_lease=="object")?{branch_lease:c.branch_lease}:{},...o.length>0&&(c.origin_binding===null||typeof c.origin_binding=="object")?{origin_binding:c.origin_binding}:{},...o.length>0&&(c.aggregate_qa_receipt===null||typeof c.aggregate_qa_receipt=="object")?{aggregate_qa_receipt:c.aggregate_qa_receipt}:{},...o.length>0&&(c.head_reconciliation_receipt===null||typeof c.head_reconciliation_receipt=="object")?{head_reconciliation_receipt:c.head_reconciliation_receipt}:{}}}return t}function we(r){return r.stateSummary?.totals.stories??(r.scope?.includedStoryCount??Object.keys(r.stories).length)+(r.scope?.omittedStoryCount??0)}function Te(r){return r.stateSummary?.totals.epics??(r.scope?.includedEpicCount??Object.keys(r.epics).length)+(r.scope?.omittedEpicCount??0)}function qe(r,e){if(r.stateSummary)return{...r.stateSummary,projection:{...r.stateSummary.projection,scopeKind:"projected",projectionMode:e.projectionMode,includedStoryCount:e.includedStoryCount,includedEpicCount:e.includedEpicCount??r.stateSummary.projection.includedEpicCount,omittedStoryCount:e.omittedStoryCount,omittedEpicCount:e.omittedEpicCount,reason:"body-size"},reasonCodes:_(r.stateSummary.reasonCodes,["payload-size-guardrail","state-summary-preserved","nonessential-state-degraded"])}}function Le(r){const e=r.scope,t=new Set,i=e?.projectionMode==="structural-required"||e?.projectionMode==="yolo-story"||e?.projectionMode==="yoloop-target"||e?.projectionMode==="yoloop-session"||e?.projectionMode==="yoloop-discovery";if(i)for(const m of Object.keys(r.stories))t.add(m);else e?.targetStoryId&&x(e.targetStoryId,r,t);const c={};for(const m of t){const E=r.stories[m];E&&(c[m]=E)}const o=Ae(r,t),s=Object.keys(c).length,a=Object.keys(o).length,u=Math.max(0,we(r)-s),l=Math.max(0,Te(r)-a),f=i?e?.projectionMode??"structural-required":e?.projectionMode==="yolo-story"?"yolo-story":"summary-only",p={kind:"projected",commandId:e?.commandId??null,stateProjectionPolicy:e?.stateProjectionPolicy??null,projectionMode:f,targetEpicId:e?.targetEpicId??null,targetStoryId:e?.targetStoryId??null,...e?.targetEpicCoverage?{targetEpicCoverage:e.targetEpicCoverage}:{},includedStoryCount:s,includedEpicCount:a,omittedStoryCount:u,omittedEpicCount:l,reason:"body-size",reasonCodes:_(e?.reasonCodes,["payload-size-guardrail",t.size>0?"structural-targets-preserved":"stories-omitted","epics-stubbed","state-summary-preserved","redundant-trigger-omitted"])},g=qe(r,p);return{...r,stories:c,epics:o,scope:p,...g?{stateSummary:g}:{}}}function Pe(r,e){const t=r.trim(),i=z(t);try{if(!i){if(de(t)){const o=n("story-reference","story-targeted"),s=F(t);if(s)return D(t,e,o,s,"targeted-story",["story-reference-targeted"])}return b(t,e,n("unknown","adaptive-targeted"),["unknown-command-summary-only"])}if(i.stateProjectionPolicy==="epic-summary")return Re(t,e,i);if(i.stateProjectionPolicy==="yoloop-targeted")return Oe(t,e,i);const c=F(t);if(i.stateProjectionPolicy==="story-targeted"){if(c){const s=O({commandId:i.commandId,targetStoryId:c})==="yolo-story";return D(t,e,i,c,s?"yolo-story":i.structuralRequired?"structural-required":"targeted-story",[s?"yolo-story":i.structuralRequired?"structural-required":"targeted-story"])}if(i.commandId==="yolo"||i.commandId==="yolo-inline"){const o=O({commandId:i.commandId});return b(t,e,i,["yolo-inline","stories-omitted","historical-content-omitted"],o??"yolo-inline")}return i.commandId==="commit"&&/^\*?(?:commit|commit-push)$/i.test(t)?b(t,e,i,["working-tree-commit","stories-omitted","historical-content-omitted"]):i.structuralRequired?(()=>{throw new y("STRUCTURAL_TARGET_NOT_FOUND",{stories:Object.keys(e.stories).length,epics:Object.keys(e.epics).length,targetKind:"story"})})():b(t,e,i,["story-target-missing-summary-only"])}if(i.stateProjectionPolicy==="adaptive-targeted"){if(c&&e.stories[c])return D(t,e,i,c,"targeted-story",["adaptive-story-targeted"]);const o=R(t,e);return o?W(t,e,i,o,"targeted-epic",["adaptive-epic-targeted"]):b(t,e,i,["adaptive-summary-only","target-unresolved-summary-only"])}if(i.stateProjectionPolicy==="epic-targeted"){if(i.commandId.startsWith("arch-")&&/(?:^|\s)epic-arch\s*$/i.test(t)&&e.archPlan?.execution?.currentStageCommand===i.commandId)return b(t,e,i,["architecture-root-delegated"]);const o=R(t,e);return o?W(t,e,i,o,i.structuralRequired?"structural-required":"targeted-epic",[i.structuralRequired?"structural-required":"targeted-epic"]):i.structuralRequired?(()=>{throw new y("STRUCTURAL_TARGET_NOT_FOUND",{stories:Object.keys(e.stories).length,epics:Object.keys(e.epics).length,targetKind:"epic"})})():b(t,e,i,["epic-target-missing-summary-only"])}return i.stateProjectionPolicy==="continuity-scoped"?b(t,e,i,["continuity-scoped-summary-only"]):b(t,e,i,["policy-full-summary-only"])}catch(c){if(c instanceof y)throw c;if(i?.structuralRequired)throw new y("STRUCTURAL_DEPENDENCY_MISSING",{stories:Object.keys(e.stories).length,epics:Object.keys(e.epics).length,projectionErrors:1});return b(t,e,i??n("unknown","adaptive-targeted"),["projection-error-summary-only"])}}const xe=new Set(["yolo-story","yolo-inline","yoloop-target","yoloop-session","yoloop-discovery"]);function Ne(r){let e=0;for(const t of[r.stories,r.epics])for(const i of Object.values(t))Array.isArray(i.depends_on)&&(e+=i.depends_on.filter(c=>typeof c=="string").length);return e}function Ve(r,e){const t=z(r.trim());if(!t||!["yolo","yolo-inline","yoloop"].includes(t.commandId))return null;try{const i=Pe(r,e),c=i.scope?.projectionMode;if(!c||!xe.has(c))throw new y("STRUCTURAL_DEPENDENCY_MISSING",{stories:Object.keys(i.stories).length,epics:Object.keys(i.epics).length,projectionShapeErrors:1});return Object.freeze({ok:!0,version:2,shape:c,snapshot:i,included:{epics:i.scope?.includedEpicCount??Object.keys(i.epics).length,stories:i.scope?.includedStoryCount??Object.keys(i.stories).length,edges:Ne(i)},omitted:{epics:i.scope?.omittedEpicCount??0,stories:i.scope?.omittedStoryCount??0},cycleCount:0})}catch(i){if(!(i instanceof y))throw i;return Object.freeze({ok:!1,code:i.code,publicDetails:i.publicDetails})}}export{Le as degradeStateSnapshotForPayloadLimit,ze as projectEpicOperationalEvidenceForRequest,Pe as projectStateSnapshotForInvoke,Ve as projectStateSnapshotResultV2ForInvoke};