@ornexus/neocortex 4.60.72 → 4.60.73
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/release-source.json +1 -1
- package/dist/sbom.cdx.json +24 -24
- package/install.ps1 +1 -1
- package/install.sh +1 -1
- package/package.json +2 -2
- package/packages/client/dist/adapters/adapter-registry.js +108 -1
- package/packages/client/dist/adapters/antigravity-adapter.js +77 -2
- package/packages/client/dist/adapters/claude-code-adapter.js +79 -3
- package/packages/client/dist/adapters/codex-adapter.js +80 -2
- package/packages/client/dist/adapters/cursor-adapter.js +115 -4
- package/packages/client/dist/adapters/gemini-adapter.js +71 -2
- package/packages/client/dist/adapters/grok-adapter.js +42 -2
- package/packages/client/dist/adapters/index.js +22 -1
- package/packages/client/dist/adapters/platform-detector.js +212 -1
- package/packages/client/dist/adapters/target-adapter.js +12 -0
- package/packages/client/dist/adapters/vscode-adapter.js +72 -2
- package/packages/client/dist/agent/refresh-stubs.js +417 -2
- package/packages/client/dist/agent/update-agent-yaml.js +102 -1
- package/packages/client/dist/agent/update-description.js +251 -1
- package/packages/client/dist/cache/crypto-utils.js +78 -1
- package/packages/client/dist/cache/encrypted-cache.js +108 -1
- package/packages/client/dist/cache/in-memory-asset-cache.js +73 -1
- package/packages/client/dist/cache/index.js +13 -1
- package/packages/client/dist/cache/protected-pi-boundary.js +44 -1
- package/packages/client/dist/checkpoint/checkpoint-client-reader.js +127 -2
- package/packages/client/dist/checkpoint/index.js +11 -1
- package/packages/client/dist/checkpoint/shared-checkpoint-types.js +32 -1
- package/packages/client/dist/cli.js +358 -5
- package/packages/client/dist/commands/activate.js +394 -8
- package/packages/client/dist/commands/architecture-answers.js +274 -7
- package/packages/client/dist/commands/architecture-memory-terminal.js +253 -4
- package/packages/client/dist/commands/architecture-non-ui-continuation.js +100 -1
- package/packages/client/dist/commands/architecture-root-binding.js +51 -1
- package/packages/client/dist/commands/cache-status.js +112 -2
- package/packages/client/dist/commands/config.js +88 -1
- package/packages/client/dist/commands/doctor.js +635 -2
- package/packages/client/dist/commands/invoke.js +7200 -143
- package/packages/client/dist/commands/legacy-epic-terminal.js +71 -1
- package/packages/client/dist/commands/legacy-story-branch.js +96 -1
- package/packages/client/dist/commands/legacy-story-pr.js +276 -1
- package/packages/client/dist/commands/mutation-qa-checkpoint.js +285 -5
- package/packages/client/dist/commands/mutation-qa-gate.js +54 -1
- package/packages/client/dist/commands/mutation-qa-git.js +41 -1
- package/packages/client/dist/commands/mutation-qa-policy.js +102 -1
- package/packages/client/dist/commands/mutation-qa-run.js +219 -3
- package/packages/client/dist/commands/mutation-qa-verdict.js +149 -1
- package/packages/client/dist/commands/mutation-qa.js +220 -1
- package/packages/client/dist/commands/native-agent.js +136 -2
- package/packages/client/dist/commands/planning-diagnosis.js +43 -1
- package/packages/client/dist/commands/planning-generation.js +53 -1
- package/packages/client/dist/commands/planning-memory.js +40 -1
- package/packages/client/dist/commands/planning-research.js +42 -1
- package/packages/client/dist/commands/private-session.js +75 -1
- package/packages/client/dist/commands/property-qa.js +139 -1
- package/packages/client/dist/commands/qa-control.js +16 -1
- package/packages/client/dist/commands/qa-scan-browser-launch.js +55 -1
- package/packages/client/dist/commands/qa-scan-browser-route.js +28 -1
- package/packages/client/dist/commands/qa-scan-capture.js +663 -5
- package/packages/client/dist/commands/qa-scan-discovery.js +376 -3
- package/packages/client/dist/commands/qa-scan-review.js +826 -5
- package/packages/client/dist/commands/qa-synthetic-backend-capture.js +266 -1
- package/packages/client/dist/commands/qa-synthetic-backend-review.js +297 -1
- package/packages/client/dist/commands/qa-synthetic-backend.js +340 -2
- package/packages/client/dist/commands/qa-synthetic-broker-port.js +85 -1
- package/packages/client/dist/commands/qa-synthetic-browser.js +39 -1
- package/packages/client/dist/commands/refactor-characterization.js +671 -21
- package/packages/client/dist/commands/refactor-commit.js +399 -13
- package/packages/client/dist/commands/refactor-control.js +142 -3
- package/packages/client/dist/commands/refactor-cpp-assertions.js +93 -3
- package/packages/client/dist/commands/refactor-review.js +283 -5
- package/packages/client/dist/commands/refactor-root.js +588 -7
- package/packages/client/dist/commands/refresh-memory.js +156 -3
- package/packages/client/dist/commands/shared-architecture-non-ui.js +24 -1
- package/packages/client/dist/commands/story-checkpoint.js +171 -1
- package/packages/client/dist/config/profile-directory.js +13 -1
- package/packages/client/dist/config/resolver-selection.js +278 -1
- package/packages/client/dist/config/secure-config.js +281 -12
- package/packages/client/dist/config.js +223 -2
- package/packages/client/dist/constants.js +25 -1
- package/packages/client/dist/context/context-collector.js +222 -2
- package/packages/client/dist/context/context-sanitizer.js +145 -1
- package/packages/client/dist/continuity/autonomous-governance.js +285 -1
- package/packages/client/dist/continuity/continuity-client-state-store.js +1587 -1
- package/packages/client/dist/continuity/invoke-hooks.js +129 -1
- package/packages/client/dist/continuity/migrations/001-initial-schema.js +12 -2
- package/packages/client/dist/continuity/migrations/002-processing-output-summary-indexes.js +12 -2
- package/packages/client/dist/continuity/migrations/003-headless-execution-attempts.js +12 -2
- package/packages/client/dist/continuity/migrations/004-outbox-delivery-state.js +12 -2
- package/packages/client/dist/continuity/migrations/005-bounded-headless-queue.js +12 -2
- package/packages/client/dist/continuity/migrations/006-native-agent-dispatches.js +10 -2
- package/packages/client/dist/continuity/native-agent-store.js +69 -1
- package/packages/client/dist/continuity/native-agent-types.js +90 -1
- package/packages/client/dist/continuity/outbox.js +117 -1
- package/packages/client/dist/continuity/runner-bootstrap-policy.js +172 -1
- package/packages/client/dist/continuity/sqlite-store.d.ts +22 -1
- package/packages/client/dist/continuity/sqlite-store.js +2612 -81
- package/packages/client/dist/effects/invoke-effect-gateways.js +62 -1
- package/packages/client/dist/errors/error-messages.js +228 -2
- package/packages/client/dist/errors/frontmatter-diagnostics.js +125 -1
- package/packages/client/dist/evidence/collector.js +347 -1
- package/packages/client/dist/evidence/index.js +1 -1
- package/packages/client/dist/evidence/types.js +2 -0
- package/packages/client/dist/fs/atomic-state-replace.js +152 -1
- package/packages/client/dist/generated/command-registry.generated.js +4076 -1
- package/packages/client/dist/generated/public-projections.generated.js +149 -1
- package/packages/client/dist/git/direct-yolo-c01-retry.js +138 -4
- package/packages/client/dist/git/direct-yolo-checkpoint.js +1234 -4
- package/packages/client/dist/git/epic-operational-state.d.ts +198 -0
- package/packages/client/dist/git/epic-operational-state.js +672 -0
- package/packages/client/dist/git/git-lifecycle-step-ids.js +14 -1
- package/packages/client/dist/git/git-lifecycle-store.d.ts +9 -1
- package/packages/client/dist/git/git-lifecycle-store.js +1981 -9
- package/packages/client/dist/git/legacy-story-branch-checkpoint.js +259 -9
- package/packages/client/dist/git/legacy-story-branch-setup.js +348 -4
- package/packages/client/dist/git/legacy-story-pr.js +3538 -70
- package/packages/client/dist/git/secure-filesystem-boundary.js +182 -5
- package/packages/client/dist/graph-retrieval/pre-command-hook.js +284 -1
- package/packages/client/dist/graph-retrieval/shared-graph-retrieval-contract.js +237 -1
- package/packages/client/dist/i18n/first-run.js +60 -2
- package/packages/client/dist/index.js +47 -1
- package/packages/client/dist/invocation/invoke-input.js +24 -1
- package/packages/client/dist/license/index.js +5 -1
- package/packages/client/dist/license/license-client.js +304 -1
- package/packages/client/dist/machine/fingerprint.js +163 -2
- package/packages/client/dist/machine/index.js +5 -1
- package/packages/client/dist/memory/project-memory-path-scope.js +62 -1
- package/packages/client/dist/memory/project-memory-writer.js +1007 -40
- package/packages/client/dist/memory/shared-project-memory-types.js +253 -2
- package/packages/client/dist/migrations/epic-lifecycle-backfill.d.ts +97 -0
- package/packages/client/dist/migrations/epic-lifecycle-backfill.js +1060 -0
- package/packages/client/dist/migrations/project-identity-capability.js +50 -1
- package/packages/client/dist/migrations/project-identity.js +493 -7
- package/packages/client/dist/migrations/state-json.d.ts +22 -0
- package/packages/client/dist/migrations/state-json.js +393 -4
- package/packages/client/dist/policy/architecture-policy.js +184 -2
- package/packages/client/dist/policy/index.js +7 -1
- package/packages/client/dist/policy/shared-policy-types.js +38 -0
- package/packages/client/dist/policy/subagent-approval-policy.js +55 -1
- package/packages/client/dist/private-execution/clean-baseline.js +279 -2
- package/packages/client/dist/private-execution/codex-executor.js +574 -4
- package/packages/client/dist/private-execution/codex-host-runtime.js +137 -1
- package/packages/client/dist/private-execution/codex-lifecycle-guard.js +97 -1
- package/packages/client/dist/private-execution/codex-scoped-permissions.js +128 -1
- package/packages/client/dist/private-execution/delivery.js +410 -13
- package/packages/client/dist/private-execution/diagnosis-root-review.js +294 -2
- package/packages/client/dist/private-execution/frozen-handoff.js +19 -6
- package/packages/client/dist/private-execution/implementation-validation.js +106 -2
- package/packages/client/dist/private-execution/isolated-admission.js +1432 -23
- package/packages/client/dist/private-execution/isolated-session.js +725 -5
- package/packages/client/dist/private-execution/isolated-workspace.js +122 -1
- package/packages/client/dist/private-execution/pact-consumer-runtime.js +409 -3
- package/packages/client/dist/private-execution/pact-test-source.js +258 -1
- package/packages/client/dist/private-execution/pact-toolchain.js +290 -3
- package/packages/client/dist/private-execution/pact-write-set.js +172 -1
- package/packages/client/dist/private-execution/planning-diagnosis-checkpoint.js +406 -5
- package/packages/client/dist/private-execution/planning-generation-checkpoint.js +462 -5
- package/packages/client/dist/private-execution/planning-memory-checkpoint.js +139 -3
- package/packages/client/dist/private-execution/planning-research-checkpoint.js +325 -4
- package/packages/client/dist/private-execution/private-report-store.js +42 -1
- package/packages/client/dist/private-execution/property-test-candidate.js +103 -1
- package/packages/client/dist/private-execution/property-test-choices.js +147 -3
- package/packages/client/dist/private-execution/property-test-plan.js +271 -7
- package/packages/client/dist/private-execution/property-test-review.js +258 -4
- package/packages/client/dist/private-execution/property-test-runtime.js +372 -9
- package/packages/client/dist/private-execution/property-test-source.js +139 -1
- package/packages/client/dist/private-execution/root-checkpoint.js +80 -1
- package/packages/client/dist/private-execution/root-memory-git-evidence.js +135 -3
- package/packages/client/dist/private-execution/root-memory-recovery.js +93 -1
- package/packages/client/dist/private-execution/root-memory-request.js +29 -1
- package/packages/client/dist/private-execution/root-memory-scope.js +178 -1
- package/packages/client/dist/private-execution/root-memory-writer.js +102 -1
- package/packages/client/dist/private-execution/session-lease.js +63 -1
- package/packages/client/dist/private-execution/shared-private-admission-types.js +50 -1
- package/packages/client/dist/private-execution/shared-project-validation.js +66 -1
- package/packages/client/dist/private-execution/shared-root-memory-types.js +61 -1
- package/packages/client/dist/private-execution/ux-source-validation.js +372 -1
- package/packages/client/dist/private-execution/ux-write-set.js +191 -2
- package/packages/client/dist/registry/registry-reconciliation.js +234 -1
- package/packages/client/dist/resilience/circuit-breaker.js +170 -1
- package/packages/client/dist/resilience/degradation-manager.js +164 -1
- package/packages/client/dist/resilience/freshness-indicator.js +100 -1
- package/packages/client/dist/resilience/index.js +8 -1
- package/packages/client/dist/resilience/recovery-detector.js +74 -1
- package/packages/client/dist/resolvers/asset-resolver.js +13 -0
- package/packages/client/dist/resolvers/local-resolver.js +255 -8
- package/packages/client/dist/resolvers/remote-resolver.js +233 -1
- package/packages/client/dist/runner/binary.js +404 -1
- package/packages/client/dist/runner/cli.js +1571 -17
- package/packages/client/dist/runner/headless/adapters.js +49 -1
- package/packages/client/dist/runner/headless/claude-code-adapter.js +79 -1
- package/packages/client/dist/runner/headless/continuous-story-workspace.js +1628 -11
- package/packages/client/dist/runner/headless/epic-git-reconciliation-inventory.js +69 -1
- package/packages/client/dist/runner/headless/epic-merge-candidate-qa.js +110 -2
- package/packages/client/dist/runner/headless/epic-terminal-git-cli-adapter.js +1236 -8
- package/packages/client/dist/runner/headless/epic-terminal-git-runner.js +318 -2
- package/packages/client/dist/runner/headless/fake-adapter.js +62 -1
- package/packages/client/dist/runner/headless/managed-profile.js +132 -1
- package/packages/client/dist/runner/headless/opencode-adapter.js +145 -2
- package/packages/client/dist/runner/headless/prepared-story-bundle.js +43 -1
- package/packages/client/dist/runner/headless/resolver.js +231 -1
- package/packages/client/dist/runner/headless/run-yolo.js +885 -1
- package/packages/client/dist/runner/headless/run-yoloop.js +2574 -2
- package/packages/client/dist/runner/headless/types.js +574 -2
- package/packages/client/dist/runner/local-console.js +396 -13
- package/packages/client/dist/runner/portal-sync-worker.js +1487 -1
- package/packages/client/dist/runner/project-identity.js +106 -1
- package/packages/client/dist/runner/scheduler.js +995 -7
- package/packages/client/dist/runner-cli.js +18 -2
- package/packages/client/dist/state/invocation-shape.js +13 -1
- package/packages/client/dist/state/project-state-snapshot.d.ts +8 -0
- package/packages/client/dist/state/project-state-snapshot.js +1731 -1
- package/packages/client/dist/state/state-json-repair.js +785 -3
- package/packages/client/dist/state/structural-dag-projection.js +118 -1
- package/packages/client/dist/telemetry/index.js +5 -1
- package/packages/client/dist/telemetry/offline-queue.js +131 -1
- package/packages/client/dist/tier/index.js +5 -1
- package/packages/client/dist/tier/tier-aware-client.js +405 -1
- package/packages/client/dist/types/index.js +38 -1
- package/packages/client/dist/version.js +27 -1
- package/packages/client/dist/yolo/change-evidence-collector.js +126 -1
- package/packages/client/dist/yolo/invoke-envelope-capability.js +62 -1
- package/packages/client/dist/yolo/spec-envelope-collector.js +184 -5
- package/packages/client/dist/yoloop/discovery-hook.js +300 -2
- package/packages/client/dist/yoloop/index.js +8 -1
- package/packages/client/dist/yoloop/invoke-hooks.js +669 -12
- package/packages/client/dist/yoloop/shared-discover-epics.js +1007 -1
- package/packages/client/dist/yoloop/shared-yoloop-types.d.ts +65 -1
- package/packages/client/dist/yoloop/shared-yoloop-types.js +33 -1
- package/packages/client/dist/yoloop/target-normalization.js +180 -1
- package/packages/client/dist/yoloop/yoloop-client-state-store.d.ts +8 -0
- package/packages/client/dist/yoloop/yoloop-client-state-store.js +1920 -1
- package/packages/shared/dist/yolo/spec-envelope.js +3 -6
- package/targets-stubs/antigravity/gemini.md +1 -1
- package/targets-stubs/antigravity/skill/SKILL.md +1 -1
- package/targets-stubs/claude-code/neocortex-root.agent.yaml +1 -1
- package/targets-stubs/claude-code/neocortex-root.md +2 -2
- package/targets-stubs/claude-code/neocortex.agent.yaml +1 -1
- package/targets-stubs/claude-code/neocortex.md +2 -2
- package/targets-stubs/codex/AGENTS.md +1 -1
- package/targets-stubs/cursor/agent.md +2 -2
- package/targets-stubs/gemini-cli/agent.md +2 -2
- package/targets-stubs/opencode/neocortex-root.md +1 -1
- package/targets-stubs/vscode/neocortex.agent.md +2 -2
|
@@ -1 +1,1731 @@
|
|
|
1
|
-
import{createHash as H}from"node:crypto";import{analyzeDependencyDag as I,StructuralDagError as y}from"./structural-dag-projection.js";import{classifyInvocationShapeV2 as O}from"./invocation-shape.js";const U=12,J=16,k=["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"]),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 C(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=C(t,["schemaVersion","storyId","epicId","branch","baseSha","headSha","idempotencyKey","dispatchId","bundleId","bundleDigest","graphRevision","claimRevision","qaVerdict","reviewVerdict","recordedAt","commits","receiptRefs"]);return i.qaReceipt=C(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=C(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 C(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 C(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 ze(r,e,t,i="full-compatible",c=["full-state"]){const o=ae(r,e,t,i,c);return v({...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 Fe(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 L(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=I(e.stories,[r]),i={};for(const c of t.orderedIds){const o=e.stories[c];o&&(i[c]=o)}return i}function $(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=I(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:K(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=I(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=H("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 K(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=I(e.stories),c=I(e.epics),o=Object.fromEntries(i.orderedIds.map(u=>[u,Y(u,e.stories[u])])),s=Object.fromEntries(c.orderedIds.map(u=>[u,K(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 v({...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)}:{},...k.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 W(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(k.map(t=>[t,0]));for(const t of Object.values(r)){const i=k.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?!k.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 ve(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 Ie(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 ke(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 Ce(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})=>!L(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=W(r.stories),i=W(r.epics),c=Se(r.epics),o=Object.values(r.epics).filter(g=>(k.includes(g.lifecycle_classification)?g.lifecycle_classification:"active")==="active"&&!L(g.status)).length,s=ve(r),a=Ie(r),u=ke(r),l=Ce(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 v(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 v({...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 v({...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):$(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 v({...e,stories:f,epics:p,scope:g},e,g)}function B(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=$(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 v({...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 v({...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}:{}}}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?B(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?B(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 $e(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,Pe as projectStateSnapshotForInvoke,$e as projectStateSnapshotResultV2ForInvoke};
|
|
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
|
+
}
|