@ornexus/neocortex 4.60.71 → 4.60.73
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/release-source.json +1 -1
- package/dist/sbom.cdx.json +1211 -1217
- 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,2 +1,294 @@
|
|
|
1
|
-
import{isPrivateYoloSkippedPredecessors as ue,isPrivateQaSkippedPredecessors as qe,isPrivateQaStructuralAdmission as ve}from"./shared-private-admission-types.js";import{readPrivateYoloImplementationValidation as me}from"./implementation-validation.js";import{createHash as Ae}from"node:crypto";import{execFileSync as F}from"node:child_process";import{existsSync as xe,lstatSync as ye,readFileSync as S}from"node:fs";import{join as u,resolve as Ce}from"node:path";import{acquireGitLifecyclePhysicalIntegrationLock as $e,applyGitLifecycleRuntimeOverlay as De,ensureGitLifecyclePrivateChildDirectory as he,gitLifecycleRuntimeDirectory as we,mutateGitLifecycleRuntime as _e,readGitLifecycleRuntime as Ye,releaseGitLifecyclePhysicalIntegrationLock as je,assertGitLifecyclePrivateFile as ge}from"../git/git-lifecycle-store.js";import{anchoredAtomicWriteFile as Oe}from"../git/secure-filesystem-boundary.js";import{privateYoloRootOnlySetupReady as Se}from"./clean-baseline.js";import{resumePrivateYoloIsolatedSession as Ve}from"./isolated-session.js";const Ne=["analysisComplete","enrichmentReviewed","implementationBoundaryReviewed","dependenciesReviewed","smellsReviewed","warningsReviewed","mandatoryRequirementsResolved","noImplementationChange"],C=r=>Ae("sha256").update(r).digest("hex"),J={diagnosis:{schema:"PrivateYoloDiagnosisAcceptance/1",token:"step-50224b19",digest:"50224b193dae7ba2a066e54f7091d2656302b849ea7a145a3a4c556d85f3fe3c",status:"diagnose",previousStatus:"branch-ready",predecessorCount:1,artifact:"diagnosis",checks:Ne},research:{schema:"PrivateYoloResearchAcceptance/1",token:"step-f757f6bf",digest:"f757f6bf1182f23e5d7acbe47cbce9f0fa12f211d290b486892c6722bc42b4e8",status:"research",previousStatus:"diagnose",predecessorCount:2,artifact:"research",checks:["researchComplete","officialSourcesReviewed","context7AvailabilityReviewed","existingPatternsReviewed","mockStrategyReviewed","requirementsTraced","mandatoryRequirementsResolved","noImplementationChange"]},specification:{schema:"PrivateYoloSpecificationAcceptance/1",token:"step-9cd927d0",digest:"9cd927d02ff5979b5077e75b09cb34be275eca0e1c955403235b9ba0fb7a55eb",status:"write-spec",previousStatus:"research",predecessorCount:3,artifact:"spec",checks:["specificationComplete","acceptedResearchReviewed","acceptanceExamplesReviewed","contractsAndNonGoalsReviewed","implementationPlanReviewed","validationPlanReviewed","mandatoryRequirementsResolved","noImplementationChange"]},tasks:{schema:"PrivateYoloTasksAcceptance/1",token:"step-a3f880e8",digest:"a3f880e8d5bca9158a88c46f88754c0948237a3e8516c83a762ba1ee22c27d46",status:"create-tasks",previousStatus:"write-spec",predecessorCount:4,artifact:"tasks",checks:["tasksComplete","acceptedSpecificationReviewed","acceptanceTraceabilityReviewed","causalTestSequenceReviewed","implementationScopeReviewed","validationPlanReviewed","mandatoryRequirementsResolved","noImplementationChange"]},implementation:{schema:"PrivateYoloImplementationAcceptance/1",token:"step-41803e35",digest:"41803e35aae66fedeb7f3d7086f234b7d8894fa31c93088d510fd9bd1ea6a3ab",status:"implemented",previousStatus:"create-tasks",predecessorCount:5,artifact:"validation-evidence",checks:["implementationComplete","acceptedPlanningReviewed","acceptanceTraceabilityReviewed","causalTestsReviewed","implementationScopeReviewed","contractsReviewed","mandatoryRequirementsResolved","rootValidationReviewed"]},"refactor-advisory":{schema:"PrivateYoloRefactorAdvisoryAcceptance/1",token:"step-fe2e4997",digest:"fe2e499748a0a6ffa5f952b0e7f6eb0f42361b6f304722aaa9eae736ca9565e7",status:"refactor-suggest-done",previousStatus:"implemented",predecessorCount:6,artifact:"validation-evidence",checks:["advisoryComplete","registryReviewed","tierAndGatesReviewed","scopeReviewed","suggestionsReviewed","nonBlockingWarningsReviewed","traceabilityReviewed","noImplementationChange"]},"test-validation":{schema:"PrivateYoloTestValidationAcceptance/1",token:"step-b2fe7cb5",digest:"b2fe7cb5d577eecef3f9da45f9f4f11487b5a1ac27dfdf6c425a0cc5a9c9db3d",status:"tests-validated",previousStatus:"property-tests-emitted",predecessorCount:8,artifact:"validation-evidence",checks:["testCoverageComplete","acceptedPlanningReviewed","acceptanceTraceabilityReviewed","literalCommandsReviewed","skippedCoverageReviewed","contractsReviewed","mandatoryRequirementsResolved","rootValidationReviewed"]},qa:{schema:"PrivateYoloQaAcceptance/1",token:"step-8721319d",digest:"8721319de90ade506bc9b72a908d8296856b34a11bc640629178bed3fe32e373",status:"qa-gate-passed",previousStatus:"tests-validated",predecessorCount:9,artifact:"qa-gate-result",checks:["requirementsReviewed","examplesReviewed","contractsReviewed","securityAndDataReviewed","structuralIntegrityReviewed","skippedAndDeferredCoverageReviewed","findingsResolved","implementationScopeReviewed"]}};async function Ee(r,n){return R(r,n,"qa")}async function We(r,n){return R(r,n,"test-validation")}async function Ke(r,n){return R(r,n,"refactor-advisory")}async function Me(r,n){return R(r,n,"implementation")}async function Xe(r,n){return R(r,n,"diagnosis")}async function Ze(r,n){return R(r,n,"research")}async function et(r,n){return R(r,n,"specification")}async function tt(r,n){return R(r,n,"tasks")}async function R(r,n,e){const c=J[e],I=`${e}Sha256`,o=()=>{throw new Error(`private-execution-${e}-root-review-unverified`)},v=l=>l!==null&&typeof l=="object"&&!Array.isArray(l)?l:void 0,q=v(n),a=v(q?.directive),t=v(q?.review),B=a?.structuralPrecheck,$=(l,D)=>Object.keys(l).sort().join("|")===D.sort().join("|");(!q||!a||!t||!$(q,["schemaVersion","executorStopped","directive","review"])||q.schemaVersion!==c.schema||q.executorStopped!==!0||!$(a,["schemaVersion","action","storyId","executionBranch","graphRevision","stepId","publicSafe",...e==="test-validation"?["skippedPredecessors"]:[],...e==="qa"?["skippedPredecessors","structuralPrecheck"]:[]])||a.schemaVersion!==1||a.action!==`review-${e}-candidate`||e==="test-validation"&&!ue(a.skippedPredecessors)||e==="qa"&&(!qe(a.skippedPredecessors)||!ve(B)||B.graphRevision!==a.graphRevision||a.skippedPredecessors.some(l=>l.tier!==B.tier))||a.stepId!==c.token||a.publicSafe!==!0||typeof a.storyId!="string"||!/^P[1-9][0-9]*\.[0-9]+$/.test(a.storyId)||typeof a.graphRevision!="string"||!/^[a-f0-9]{64}$/.test(a.graphRevision)||!$(t,["reviewer","decision","snapshotSha",I,"sourceStateSha256","rawStepId","reviewedAt","checks",...["implementation","test-validation","qa"].includes(e)?["validationReceiptId"]:[],...["refactor-advisory","test-validation","qa"].includes(e)?["previousPhaseReceiptId"]:[]])||t.reviewer!=="root"||t.decision!==`approve-${e}`||typeof t.snapshotSha!="string"||!/^[a-f0-9]{40,64}$/.test(t.snapshotSha)||typeof t[I]!="string"||!/^[a-f0-9]{64}$/.test(t[I])||typeof t.sourceStateSha256!="string"||!/^[a-f0-9]{64}$/.test(t.sourceStateSha256)||typeof t.rawStepId!="string"||C(t.rawStepId)!==c.digest||typeof t.reviewedAt!="string"||!Number.isFinite(Date.parse(t.reviewedAt))||Date.parse(t.reviewedAt)>Date.now()||Date.now()-Date.parse(t.reviewedAt)>10*6e4||!v(t.checks)||!$(v(t.checks),[...c.checks])||c.checks.some(l=>v(t.checks)[l]!==!0))&&o();const z=e==="test-validation"&&a.skippedPredecessors.length===1,ee=c.predecessorCount-(z?1:0),te=z?"refactor-suggest-done":c.previousStatus;let k,se=[];const d=Ce(r),s=a.storyId,A=a.graphRevision,ie=s.split(".")[0],N=await Ye(d,{required:!0}),U=N.epics[ie],m=v(U?.branch_lease);(!m||typeof m.sessionId!="string"||m.ownerStoryId!==s||m.idempotencyKey!==m.sessionId||U?.graph_revision!==A||typeof m.expiresAt!="string"||!Number.isFinite(Date.parse(m.expiresAt))||Date.parse(m.expiresAt)<=Date.now())&&o();const H=await $e(d,{kind:"direct",storyId:s,sessionId:m.sessionId,idempotencyKey:m.sessionId,expiresAt:new Date(Date.now()+5*6e4).toISOString(),persistent:!1});if(!H.ok)throw new Error(`private-execution-${e}-root-review-unverified`);try{const l=async()=>{const i=await Ve(d,s,A);(i.snapshotSha!==t.snapshotSha||i.sequence<1||i.workspace.executionBranch!==a.executionBranch)&&o();const g=JSON.parse(S(u(d,".neocortex/state.json"),"utf8")),j=c.predecessorCount>=5?g.stories?.[s]?.files_to_modify:[];(!Array.isArray(j)||j.some(p=>typeof p!="string"))&&o();const K=[...j,".neocortex/state.json",`docs/stories/${s}.story.md`,`.neocortex/stories/${s}/diagnosis.md`,`.neocortex/stories/${s}/diagnose.md`,...c.predecessorCount>=2?[`.neocortex/stories/${s}/research.md`,`docs/stories/${s}.research.md`]:[],...c.predecessorCount>=3?[`docs/stories/${s}.spec.md`,`docs/stories/${s}.rtm.md`]:[],...c.predecessorCount>=4?[`docs/stories/${s}.tasks.md`]:[]];F("git",["diff","--name-only","-z",i.workspace.baseSha,i.snapshotSha,"--"],{cwd:i.workspace.workspacePath,env:Object.fromEntries(Object.entries(process.env).filter(([p])=>!p.startsWith("GIT_"))),encoding:"utf8",timeout:3e4,maxBuffer:1024*1024,stdio:["ignore","pipe","ignore"]}).split("\0").filter(Boolean).some(p=>!K.includes(p))&&o();const O=e==="qa"?u(d,".neocortex","stories",s,"qa-gate-result.json"):c.artifact==="validation-evidence"?u(d,".neocortex","stories",s,"validation-evidence.md"):c.predecessorCount<3?u(d,".neocortex","stories",s,`${c.artifact}.md`):u(d,"docs","stories",`${s}.${c.artifact}.md`),y=ye(O);if((!y.isFile()||y.isSymbolicLink()||y.nlink!==1||y.size<128||y.size>262144||C(S(O))!==t[I]||C(S(u(d,".neocortex/state.json")))!==t.sourceStateSha256)&&o(),c.predecessorCount>=5&&(F("git",["show",`${i.workspace.baseSha}:.neocortex/state.json`],{cwd:i.workspace.workspacePath,timeout:3e4,stdio:["ignore","pipe","ignore"]}).equals(S(u(d,".neocortex/state.json")))||o(),F("git",["diff","--check",i.workspace.baseSha,i.snapshotSha],{cwd:i.workspace.workspacePath,timeout:3e4,stdio:"ignore"}),(e==="implementation"||e==="test-validation"||e==="qa")&&await me(d,t.validationReceiptId,{storyId:s,graphRevision:A,snapshotSha:i.snapshotSha,sourceHeadSha:i.workspace.baseSha}),e==="refactor-advisory"||e==="test-validation"||e==="qa")){const G=t.previousPhaseReceiptId;(typeof G!="string"||!/^[a-f0-9]{64}$/.test(G))&&o();const ce=await he(await we(d),"private-execution"),ne=e==="qa"?a.skippedPredecessors:[],V=ne.some(w=>w.stepId==="step-b2fe7cb5"),M=ne.some(w=>w.stepId==="step-a8ce5c64"),de=e==="qa"?V?M?"refactor-advisory":"property-tests":"test-validation":e==="refactor-advisory"?"implementation":z?"refactor-advisory":"property-tests",pe=u(ce,`${de}-review-${G}.json`);await ge(pe,ce);const X=S(pe);(X.length>16384||C(X)!==G)&&o();const h=JSON.parse(X.toString());(h.kind!==`private-${de}-root-review`||h.storyId!==s||h.sourceHeadSha!==i.workspace.baseSha||!/^[a-f0-9]{40,64}$/.test(h.snapshotSha)||h.independentStoryQaAccepted!==!1||h.terminalAccepted!==!1)&&o();const le=(...w)=>F("git",w,{cwd:i.workspace.workspacePath,timeout:3e4,encoding:"utf8",stdio:["ignore","pipe","ignore"]});le("merge-base","--is-ancestor",h.snapshotSha,i.snapshotSha);const Pe=le("diff","--name-only","-z",h.snapshotSha,i.snapshotSha).split("\0").filter(Boolean),Ie=[...e==="qa"?[`.neocortex/stories/${s}/qa-gate-result.json`]:[],`.neocortex/stories/${s}/validation-evidence.md`,`.neocortex/stories/${s}/tdd-evidence.md`,`docs/stories/${s}.rtm.md`];if(Pe.some(w=>!Ie.includes(w))&&o(),e==="qa"){!V&&!ue(h.skippedPredecessors)&&o();const w=V?M:h.skippedPredecessors.length===1,fe=[...["diagnosis","research","specification","tasks","implementation","refactor-advisory"].map(x=>J[x].digest),...w?[]:["a8ce5c6462803364cabab06cd41304a578ca2f1d50627c7f162020159670dedf"],...V?[]:[J["test-validation"].digest]];k={digests:fe,count:fe.length+1,status:V?M?"refactor-suggest-done":"property-tests-emitted":"tests-validated"};const Q=await me(d,t.validationReceiptId,{storyId:s,graphRevision:A,snapshotSha:i.snapshotSha,sourceHeadSha:i.workspace.baseSha});(Q.structuralIntegrity?.schemaVersion!==1||Q.structuralIntegrity?.scope!=="project-snapshot"||Q.structuralIntegrity.canonicalStateParsed!==!0||Q.structuralIntegrity.trackedInputsUnchanged!==!0)&&o();const f=JSON.parse(S(O,"utf8"));(f.schemaVersion!==3||f.candidate!==!0||f.stageComplete!==!1||f.status!=="qa-review-pending"||f.independentStoryQaAccepted!==!1||!Array.isArray(f.blockingChecks)||f.blockingChecks.length!==0||!ve(f.structuralPrecheck)||["schemaVersion","kind","stepId","generationId","graphRevision","topologyGenerationId","tier","publicSafe"].some(x=>f.structuralPrecheck[x]!==a.structuralPrecheck[x])||!Array.isArray(f.followUpDebt)||f.followUpDebt.length>50)&&o();for(const x of f.followUpDebt){const P=v(x);(!P||!$(P,["id","reasonCode","ownerArea","severity","description","trigger","publicSafe"])||P.publicSafe!==!0||!["P2","P3"].includes(String(P.severity))||["id","reasonCode","ownerArea","description","trigger"].some(Z=>typeof P[Z]!="string"||P[Z].length<1||P[Z].length>1e3))&&o()}se=f.followUpDebt}}if(e==="tasks"||c.predecessorCount>=5){const p=ye(u(d,"docs/stories",`${s}.rtm.md`));(!p.isFile()||p.isSymbolicLink()||p.nlink!==1||p.size<128||p.size>262144)&&o()}return i},D=await l(),Re=JSON.parse(S(u(d,".neocortex/state.json"),"utf8")),ke=De(Re,N),b=v(v(ke.stories)?.[s]);(!b||b.checkpoint!==void 0)&&o();const L=t.rawStepId,ae=i=>{if(e==="diagnosis")return Se(i);const g=i.steps_completed,j=[...["diagnosis","research","specification","tasks","implementation","refactor-advisory"].map(y=>J[y].digest),"a8ce5c6462803364cabab06cd41304a578ca2f1d50627c7f162020159670dedf"];if(e==="qa"&&!k)return!1;const K=e==="qa"?k.status:te,re=e==="qa"?k.count:ee,O=e==="qa"?k.digests:j;return i.status===K&&Array.isArray(g)&&g.length===re&&i.last_step===g.at(-1)&&g.slice(1).every((y,p)=>typeof y=="string"&&C(y)===O[p])&&Se({status:"branch-ready",last_step:g[0],steps_completed:[g[0]]})},_=b.steps_completed,E=b?.status===c.status&&b.last_step===L&&Array.isArray(_)&&_.length===(e==="qa"?k.count:ee)+1&&_.at(-1)===L&&ae({status:e==="qa"?k.status:te,last_step:_.at(-2),steps_completed:_.slice(0,-1)});!E&&!ae(b)&&o();const be={schemaVersion:1,kind:`private-${e}-root-review`,storyId:s,graphRevision:A,sourceHeadSha:D.workspace.baseSha,snapshotSha:D.snapshotSha,[I]:t[I],sourceStateSha256:t.sourceStateSha256,reviewedAt:t.reviewedAt,checks:t.checks,...["implementation","test-validation","qa"].includes(e)?{validationReceiptId:t.validationReceiptId}:{},...["refactor-advisory","test-validation","qa"].includes(e)?{previousPhaseReceiptId:t.previousPhaseReceiptId}:{},...["test-validation","qa"].includes(e)?{skippedPredecessors:a.skippedPredecessors}:{},...e==="qa"?{structuralPrecheck:a.structuralPrecheck,precommitQaAccepted:!0,optionalDebt:se}:{},independentStoryQaAccepted:!1,terminalAccepted:!1},W=JSON.stringify(be)+`
|
|
2
|
-
|
|
1
|
+
import { isPrivateYoloSkippedPredecessors, isPrivateQaSkippedPredecessors, isPrivateQaStructuralAdmission } from './shared-private-admission-types.js';
|
|
2
|
+
/** Independent root acceptance of analysis candidates; no story QA authority. */
|
|
3
|
+
import { readPrivateYoloImplementationValidation } from './implementation-validation.js';
|
|
4
|
+
import { createHash } from 'node:crypto';
|
|
5
|
+
import { execFileSync } from 'node:child_process';
|
|
6
|
+
import { existsSync, lstatSync, readFileSync } from 'node:fs';
|
|
7
|
+
import { join, resolve } from 'node:path';
|
|
8
|
+
import { acquireGitLifecyclePhysicalIntegrationLock, applyGitLifecycleRuntimeOverlay, ensureGitLifecyclePrivateChildDirectory, gitLifecycleRuntimeDirectory, mutateGitLifecycleRuntime, readGitLifecycleRuntime, releaseGitLifecyclePhysicalIntegrationLock, assertGitLifecyclePrivateFile } from '../git/git-lifecycle-store.js';
|
|
9
|
+
import { anchoredAtomicWriteFile } from '../git/secure-filesystem-boundary.js';
|
|
10
|
+
import { privateYoloRootOnlySetupReady } from './clean-baseline.js';
|
|
11
|
+
import { resumePrivateYoloIsolatedSession } from './isolated-session.js';
|
|
12
|
+
const checks = ['analysisComplete', 'enrichmentReviewed', 'implementationBoundaryReviewed',
|
|
13
|
+
'dependenciesReviewed', 'smellsReviewed', 'warningsReviewed', 'mandatoryRequirementsResolved',
|
|
14
|
+
'noImplementationChange'];
|
|
15
|
+
const hash = (value) => createHash('sha256').update(value).digest('hex');
|
|
16
|
+
const profiles = {
|
|
17
|
+
diagnosis: { schema: 'PrivateYoloDiagnosisAcceptance/1', token: 'step-50224b19',
|
|
18
|
+
digest: '50224b193dae7ba2a066e54f7091d2656302b849ea7a145a3a4c556d85f3fe3c',
|
|
19
|
+
status: 'diagnose', previousStatus: 'branch-ready', predecessorCount: 1, artifact: 'diagnosis', checks },
|
|
20
|
+
research: { schema: 'PrivateYoloResearchAcceptance/1', token: 'step-f757f6bf',
|
|
21
|
+
digest: 'f757f6bf1182f23e5d7acbe47cbce9f0fa12f211d290b486892c6722bc42b4e8',
|
|
22
|
+
status: 'research', previousStatus: 'diagnose', predecessorCount: 2, artifact: 'research',
|
|
23
|
+
checks: ['researchComplete', 'officialSourcesReviewed', 'context7AvailabilityReviewed',
|
|
24
|
+
'existingPatternsReviewed', 'mockStrategyReviewed', 'requirementsTraced',
|
|
25
|
+
'mandatoryRequirementsResolved', 'noImplementationChange'] },
|
|
26
|
+
specification: { schema: 'PrivateYoloSpecificationAcceptance/1', token: 'step-9cd927d0',
|
|
27
|
+
digest: '9cd927d02ff5979b5077e75b09cb34be275eca0e1c955403235b9ba0fb7a55eb',
|
|
28
|
+
status: 'write-spec', previousStatus: 'research', predecessorCount: 3, artifact: 'spec',
|
|
29
|
+
checks: ['specificationComplete', 'acceptedResearchReviewed', 'acceptanceExamplesReviewed',
|
|
30
|
+
'contractsAndNonGoalsReviewed', 'implementationPlanReviewed', 'validationPlanReviewed',
|
|
31
|
+
'mandatoryRequirementsResolved', 'noImplementationChange'] },
|
|
32
|
+
tasks: { schema: 'PrivateYoloTasksAcceptance/1', token: 'step-a3f880e8',
|
|
33
|
+
digest: 'a3f880e8d5bca9158a88c46f88754c0948237a3e8516c83a762ba1ee22c27d46',
|
|
34
|
+
status: 'create-tasks', previousStatus: 'write-spec', predecessorCount: 4, artifact: 'tasks',
|
|
35
|
+
checks: ['tasksComplete', 'acceptedSpecificationReviewed', 'acceptanceTraceabilityReviewed',
|
|
36
|
+
'causalTestSequenceReviewed', 'implementationScopeReviewed', 'validationPlanReviewed',
|
|
37
|
+
'mandatoryRequirementsResolved', 'noImplementationChange'] },
|
|
38
|
+
implementation: { schema: 'PrivateYoloImplementationAcceptance/1', token: 'step-41803e35',
|
|
39
|
+
digest: '41803e35aae66fedeb7f3d7086f234b7d8894fa31c93088d510fd9bd1ea6a3ab', status: 'implemented', previousStatus: 'create-tasks', predecessorCount: 5,
|
|
40
|
+
artifact: 'validation-evidence', checks: ['implementationComplete', 'acceptedPlanningReviewed',
|
|
41
|
+
'acceptanceTraceabilityReviewed', 'causalTestsReviewed', 'implementationScopeReviewed',
|
|
42
|
+
'contractsReviewed', 'mandatoryRequirementsResolved', 'rootValidationReviewed'] },
|
|
43
|
+
'refactor-advisory': { schema: 'PrivateYoloRefactorAdvisoryAcceptance/1', token: 'step-fe2e4997',
|
|
44
|
+
digest: 'fe2e499748a0a6ffa5f952b0e7f6eb0f42361b6f304722aaa9eae736ca9565e7', status: 'refactor-suggest-done', previousStatus: 'implemented', predecessorCount: 6,
|
|
45
|
+
artifact: 'validation-evidence', checks: ['advisoryComplete', 'registryReviewed', 'tierAndGatesReviewed',
|
|
46
|
+
'scopeReviewed', 'suggestionsReviewed', 'nonBlockingWarningsReviewed', 'traceabilityReviewed', 'noImplementationChange'] },
|
|
47
|
+
'test-validation': { schema: 'PrivateYoloTestValidationAcceptance/1', token: 'step-b2fe7cb5',
|
|
48
|
+
digest: 'b2fe7cb5d577eecef3f9da45f9f4f11487b5a1ac27dfdf6c425a0cc5a9c9db3d', status: 'tests-validated', previousStatus: 'property-tests-emitted', predecessorCount: 8,
|
|
49
|
+
artifact: 'validation-evidence', checks: ['testCoverageComplete', 'acceptedPlanningReviewed',
|
|
50
|
+
'acceptanceTraceabilityReviewed', 'literalCommandsReviewed', 'skippedCoverageReviewed',
|
|
51
|
+
'contractsReviewed', 'mandatoryRequirementsResolved', 'rootValidationReviewed'] },
|
|
52
|
+
qa: { schema: 'PrivateYoloQaAcceptance/1', token: 'step-8721319d', digest: '8721319de90ade506bc9b72a908d8296856b34a11bc640629178bed3fe32e373',
|
|
53
|
+
status: 'qa-gate-passed', previousStatus: 'tests-validated', predecessorCount: 9, artifact: 'qa-gate-result',
|
|
54
|
+
checks: ['requirementsReviewed', 'examplesReviewed', 'contractsReviewed', 'securityAndDataReviewed',
|
|
55
|
+
'structuralIntegrityReviewed', 'skippedAndDeferredCoverageReviewed', 'findingsResolved', 'implementationScopeReviewed'] },
|
|
56
|
+
};
|
|
57
|
+
export async function acceptPrivateYoloQaRootReview(projectRoot, request) {
|
|
58
|
+
return acceptPrivateYoloAnalysisRootReview(projectRoot, request, 'qa');
|
|
59
|
+
}
|
|
60
|
+
export async function acceptPrivateYoloTestValidationRootReview(projectRoot, request) {
|
|
61
|
+
return acceptPrivateYoloAnalysisRootReview(projectRoot, request, 'test-validation');
|
|
62
|
+
}
|
|
63
|
+
export async function acceptPrivateYoloRefactorAdvisoryRootReview(projectRoot, request) {
|
|
64
|
+
return acceptPrivateYoloAnalysisRootReview(projectRoot, request, 'refactor-advisory');
|
|
65
|
+
}
|
|
66
|
+
export async function acceptPrivateYoloImplementationRootReview(projectRoot, request) {
|
|
67
|
+
return acceptPrivateYoloAnalysisRootReview(projectRoot, request, 'implementation');
|
|
68
|
+
}
|
|
69
|
+
export async function acceptPrivateYoloDiagnosisRootReview(projectRoot, request) {
|
|
70
|
+
return acceptPrivateYoloAnalysisRootReview(projectRoot, request, 'diagnosis');
|
|
71
|
+
}
|
|
72
|
+
export async function acceptPrivateYoloResearchRootReview(projectRoot, request) {
|
|
73
|
+
return acceptPrivateYoloAnalysisRootReview(projectRoot, request, 'research');
|
|
74
|
+
}
|
|
75
|
+
export async function acceptPrivateYoloSpecificationRootReview(projectRoot, request) {
|
|
76
|
+
return acceptPrivateYoloAnalysisRootReview(projectRoot, request, 'specification');
|
|
77
|
+
}
|
|
78
|
+
export async function acceptPrivateYoloTasksRootReview(projectRoot, request) {
|
|
79
|
+
return acceptPrivateYoloAnalysisRootReview(projectRoot, request, 'tasks');
|
|
80
|
+
}
|
|
81
|
+
async function acceptPrivateYoloAnalysisRootReview(projectRoot, request, phase) {
|
|
82
|
+
const profile = profiles[phase], artifactField = `${phase}Sha256`;
|
|
83
|
+
const fail = () => { throw new Error(`private-execution-${phase}-root-review-unverified`); };
|
|
84
|
+
const record = (v) => v !== null && typeof v === 'object' && !Array.isArray(v) ? v : undefined;
|
|
85
|
+
const r = record(request), directive = record(r?.directive), review = record(r?.review);
|
|
86
|
+
const qaStructural = directive?.structuralPrecheck;
|
|
87
|
+
const equalKeys = (v, keys) => Object.keys(v).sort().join('|') === keys.sort().join('|');
|
|
88
|
+
if (!r || !directive || !review || !equalKeys(r, ['schemaVersion', 'executorStopped', 'directive', 'review'])
|
|
89
|
+
|| r.schemaVersion !== profile.schema || r.executorStopped !== true
|
|
90
|
+
|| !equalKeys(directive, ['schemaVersion', 'action', 'storyId', 'executionBranch', 'graphRevision', 'stepId', 'publicSafe', ...(phase === 'test-validation' ? ['skippedPredecessors'] : []), ...(phase === 'qa' ? ['skippedPredecessors', 'structuralPrecheck'] : [])])
|
|
91
|
+
|| directive.schemaVersion !== 1 || directive.action !== `review-${phase}-candidate`
|
|
92
|
+
|| phase === 'test-validation' && !isPrivateYoloSkippedPredecessors(directive.skippedPredecessors)
|
|
93
|
+
|| phase === 'qa' && (!isPrivateQaSkippedPredecessors(directive.skippedPredecessors)
|
|
94
|
+
|| !isPrivateQaStructuralAdmission(qaStructural)
|
|
95
|
+
|| qaStructural.graphRevision !== directive.graphRevision
|
|
96
|
+
|| directive.skippedPredecessors.some(entry => entry.tier !== qaStructural.tier))
|
|
97
|
+
|| directive.stepId !== profile.token || directive.publicSafe !== true
|
|
98
|
+
|| typeof directive.storyId !== 'string' || !/^P[1-9][0-9]*\.[0-9]+$/.test(directive.storyId)
|
|
99
|
+
|| typeof directive.graphRevision !== 'string' || !/^[a-f0-9]{64}$/.test(directive.graphRevision)
|
|
100
|
+
|| !equalKeys(review, ['reviewer', 'decision', 'snapshotSha', artifactField, 'sourceStateSha256',
|
|
101
|
+
'rawStepId', 'reviewedAt', 'checks', ...(['implementation', 'test-validation', 'qa'].includes(phase) ? ['validationReceiptId'] : []), ...(['refactor-advisory', 'test-validation', 'qa'].includes(phase) ? ['previousPhaseReceiptId'] : [])])
|
|
102
|
+
|| review.reviewer !== 'root' || review.decision !== `approve-${phase}`
|
|
103
|
+
|| typeof review.snapshotSha !== 'string' || !/^[a-f0-9]{40,64}$/.test(review.snapshotSha)
|
|
104
|
+
|| typeof review[artifactField] !== 'string' || !/^[a-f0-9]{64}$/.test(review[artifactField])
|
|
105
|
+
|| typeof review.sourceStateSha256 !== 'string' || !/^[a-f0-9]{64}$/.test(review.sourceStateSha256)
|
|
106
|
+
|| typeof review.rawStepId !== 'string'
|
|
107
|
+
|| hash(review.rawStepId) !== profile.digest
|
|
108
|
+
|| typeof review.reviewedAt !== 'string' || !Number.isFinite(Date.parse(review.reviewedAt))
|
|
109
|
+
|| Date.parse(review.reviewedAt) > Date.now() || Date.now() - Date.parse(review.reviewedAt) > 10 * 60_000
|
|
110
|
+
|| !record(review.checks) || !equalKeys(record(review.checks), [...profile.checks])
|
|
111
|
+
|| profile.checks.some(key => record(review.checks)[key] !== true))
|
|
112
|
+
fail();
|
|
113
|
+
const skippedProperty = phase === 'test-validation' && directive.skippedPredecessors.length === 1;
|
|
114
|
+
const predecessorCount = profile.predecessorCount - (skippedProperty ? 1 : 0);
|
|
115
|
+
const previousStatus = skippedProperty ? 'refactor-suggest-done' : profile.previousStatus;
|
|
116
|
+
let qaPredecessor;
|
|
117
|
+
let qaOptionalDebt = [];
|
|
118
|
+
const root = resolve(projectRoot), storyId = directive.storyId;
|
|
119
|
+
const graphRevision = directive.graphRevision, epicId = storyId.split('.')[0];
|
|
120
|
+
const before = await readGitLifecycleRuntime(root, { required: true });
|
|
121
|
+
const epic = before.epics[epicId], lease = record(epic?.branch_lease);
|
|
122
|
+
if (!lease || typeof lease.sessionId !== 'string' || lease.ownerStoryId !== storyId
|
|
123
|
+
|| lease.idempotencyKey !== lease.sessionId || epic?.graph_revision !== graphRevision
|
|
124
|
+
|| typeof lease.expiresAt !== 'string' || !Number.isFinite(Date.parse(lease.expiresAt))
|
|
125
|
+
|| Date.parse(lease.expiresAt) <= Date.now())
|
|
126
|
+
fail();
|
|
127
|
+
const lock = await acquireGitLifecyclePhysicalIntegrationLock(root, { kind: 'direct', storyId,
|
|
128
|
+
sessionId: lease.sessionId, idempotencyKey: lease.sessionId,
|
|
129
|
+
expiresAt: new Date(Date.now() + 5 * 60_000).toISOString(), persistent: false });
|
|
130
|
+
if (!lock.ok)
|
|
131
|
+
throw new Error(`private-execution-${phase}-root-review-unverified`);
|
|
132
|
+
try {
|
|
133
|
+
const verify = async () => {
|
|
134
|
+
const session = await resumePrivateYoloIsolatedSession(root, storyId, graphRevision);
|
|
135
|
+
if (session.snapshotSha !== review.snapshotSha || session.sequence < 1
|
|
136
|
+
|| session.workspace.executionBranch !== directive.executionBranch)
|
|
137
|
+
fail();
|
|
138
|
+
const sourceState = JSON.parse(readFileSync(join(root, '.neocortex/state.json'), 'utf8'));
|
|
139
|
+
const implementationClaim = profile.predecessorCount >= 5 ? sourceState.stories?.[storyId]?.files_to_modify : [];
|
|
140
|
+
if (!Array.isArray(implementationClaim) || implementationClaim.some((p) => typeof p !== 'string'))
|
|
141
|
+
fail();
|
|
142
|
+
const allowed = [...implementationClaim, '.neocortex/state.json', `docs/stories/${storyId}.story.md`,
|
|
143
|
+
`.neocortex/stories/${storyId}/diagnosis.md`, `.neocortex/stories/${storyId}/diagnose.md`,
|
|
144
|
+
...(profile.predecessorCount >= 2 ? [`.neocortex/stories/${storyId}/research.md`, `docs/stories/${storyId}.research.md`] : []),
|
|
145
|
+
...(profile.predecessorCount >= 3 ? [`docs/stories/${storyId}.spec.md`, `docs/stories/${storyId}.rtm.md`] : []),
|
|
146
|
+
...(profile.predecessorCount >= 4 ? [`docs/stories/${storyId}.tasks.md`] : [])];
|
|
147
|
+
const delta = execFileSync('git', ['diff', '--name-only', '-z', session.workspace.baseSha, session.snapshotSha, '--'], {
|
|
148
|
+
cwd: session.workspace.workspacePath,
|
|
149
|
+
env: Object.fromEntries(Object.entries(process.env).filter(([key]) => !key.startsWith('GIT_'))),
|
|
150
|
+
encoding: 'utf8', timeout: 30_000, maxBuffer: 1024 * 1024,
|
|
151
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
152
|
+
}).split('\0').filter(Boolean);
|
|
153
|
+
if (delta.some(path => !allowed.includes(path)))
|
|
154
|
+
fail();
|
|
155
|
+
const path = phase === 'qa' ? join(root, '.neocortex', 'stories', storyId, 'qa-gate-result.json') : profile.artifact === 'validation-evidence' ? join(root, '.neocortex', 'stories', storyId, 'validation-evidence.md') : profile.predecessorCount < 3
|
|
156
|
+
? join(root, '.neocortex', 'stories', storyId, `${profile.artifact}.md`)
|
|
157
|
+
: join(root, 'docs', 'stories', `${storyId}.${profile.artifact}.md`);
|
|
158
|
+
const file = lstatSync(path);
|
|
159
|
+
if (!file.isFile() || file.isSymbolicLink() || file.nlink !== 1 || file.size < 128 || file.size > 256 * 1024
|
|
160
|
+
|| hash(readFileSync(path)) !== review[artifactField]
|
|
161
|
+
|| hash(readFileSync(join(root, '.neocortex/state.json'))) !== review.sourceStateSha256)
|
|
162
|
+
fail();
|
|
163
|
+
if (profile.predecessorCount >= 5) {
|
|
164
|
+
const baselineState = execFileSync('git', ['show', `${session.workspace.baseSha}:.neocortex/state.json`], { cwd: session.workspace.workspacePath, timeout: 30_000, stdio: ['ignore', 'pipe', 'ignore'] });
|
|
165
|
+
if (!baselineState.equals(readFileSync(join(root, '.neocortex/state.json'))))
|
|
166
|
+
fail();
|
|
167
|
+
execFileSync('git', ['diff', '--check', session.workspace.baseSha, session.snapshotSha], { cwd: session.workspace.workspacePath, timeout: 30_000, stdio: 'ignore' });
|
|
168
|
+
if (phase === 'implementation' || phase === 'test-validation' || phase === 'qa')
|
|
169
|
+
await readPrivateYoloImplementationValidation(root, review.validationReceiptId, { storyId, graphRevision, snapshotSha: session.snapshotSha, sourceHeadSha: session.workspace.baseSha });
|
|
170
|
+
if (phase === 'refactor-advisory' || phase === 'test-validation' || phase === 'qa') {
|
|
171
|
+
const id = review.previousPhaseReceiptId;
|
|
172
|
+
if (typeof id !== 'string' || !/^[a-f0-9]{64}$/.test(id))
|
|
173
|
+
fail();
|
|
174
|
+
const parent = await ensureGitLifecyclePrivateChildDirectory(await gitLifecycleRuntimeDirectory(root), 'private-execution');
|
|
175
|
+
const qaSkips = phase === 'qa' ? directive.skippedPredecessors : [];
|
|
176
|
+
const qaSkippedTests = qaSkips.some(entry => entry.stepId === 'step-b2fe7cb5');
|
|
177
|
+
const qaSkippedProperty = qaSkips.some(entry => entry.stepId === 'step-a8ce5c64');
|
|
178
|
+
const previousPhase = phase === 'qa' ? qaSkippedTests ? qaSkippedProperty ? 'refactor-advisory' : 'property-tests' : 'test-validation'
|
|
179
|
+
: phase === 'refactor-advisory' ? 'implementation' : skippedProperty ? 'refactor-advisory' : 'property-tests';
|
|
180
|
+
const predecessorPath = join(parent, `${previousPhase}-review-${id}.json`);
|
|
181
|
+
await assertGitLifecyclePrivateFile(predecessorPath, parent);
|
|
182
|
+
const bytes = readFileSync(predecessorPath);
|
|
183
|
+
if (bytes.length > 16384 || hash(bytes) !== id)
|
|
184
|
+
fail();
|
|
185
|
+
const predecessor = JSON.parse(bytes.toString());
|
|
186
|
+
if (predecessor.kind !== `private-${previousPhase}-root-review` || predecessor.storyId !== storyId
|
|
187
|
+
|| predecessor.sourceHeadSha !== session.workspace.baseSha || !/^[a-f0-9]{40,64}$/.test(predecessor.snapshotSha)
|
|
188
|
+
|| predecessor.independentStoryQaAccepted !== false || predecessor.terminalAccepted !== false)
|
|
189
|
+
fail();
|
|
190
|
+
const git = (...args) => execFileSync('git', args, { cwd: session.workspace.workspacePath, timeout: 30_000, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
|
|
191
|
+
git('merge-base', '--is-ancestor', predecessor.snapshotSha, session.snapshotSha);
|
|
192
|
+
const changes = git('diff', '--name-only', '-z', predecessor.snapshotSha, session.snapshotSha).split('\0').filter(Boolean);
|
|
193
|
+
const metadata = [...(phase === 'qa' ? [`.neocortex/stories/${storyId}/qa-gate-result.json`] : []), `.neocortex/stories/${storyId}/validation-evidence.md`, `.neocortex/stories/${storyId}/tdd-evidence.md`, `docs/stories/${storyId}.rtm.md`];
|
|
194
|
+
if (changes.some(path => !metadata.includes(path)))
|
|
195
|
+
fail();
|
|
196
|
+
if (phase === 'qa') {
|
|
197
|
+
if (!qaSkippedTests && !isPrivateYoloSkippedPredecessors(predecessor.skippedPredecessors))
|
|
198
|
+
fail();
|
|
199
|
+
const propertyWasSkipped = qaSkippedTests ? qaSkippedProperty : predecessor.skippedPredecessors.length === 1;
|
|
200
|
+
const digests = [...['diagnosis', 'research', 'specification', 'tasks', 'implementation', 'refactor-advisory'].map(key => profiles[key].digest),
|
|
201
|
+
...(propertyWasSkipped ? [] : ['a8ce5c6462803364cabab06cd41304a578ca2f1d50627c7f162020159670dedf']), ...(qaSkippedTests ? [] : [profiles['test-validation'].digest])];
|
|
202
|
+
qaPredecessor = { digests, count: digests.length + 1, status: qaSkippedTests ? qaSkippedProperty ? 'refactor-suggest-done' : 'property-tests-emitted' : 'tests-validated' };
|
|
203
|
+
const local = await readPrivateYoloImplementationValidation(root, review.validationReceiptId, { storyId, graphRevision, snapshotSha: session.snapshotSha, sourceHeadSha: session.workspace.baseSha });
|
|
204
|
+
if (local.structuralIntegrity?.schemaVersion !== 1 || local.structuralIntegrity?.scope !== 'project-snapshot'
|
|
205
|
+
|| local.structuralIntegrity.canonicalStateParsed !== true || local.structuralIntegrity.trackedInputsUnchanged !== true)
|
|
206
|
+
fail();
|
|
207
|
+
const candidate = JSON.parse(readFileSync(path, 'utf8'));
|
|
208
|
+
if (candidate.schemaVersion !== 3 || candidate.candidate !== true || candidate.stageComplete !== false
|
|
209
|
+
|| candidate.status !== 'qa-review-pending' || candidate.independentStoryQaAccepted !== false
|
|
210
|
+
|| !Array.isArray(candidate.blockingChecks) || candidate.blockingChecks.length !== 0
|
|
211
|
+
|| !isPrivateQaStructuralAdmission(candidate.structuralPrecheck)
|
|
212
|
+
|| ['schemaVersion', 'kind', 'stepId', 'generationId', 'graphRevision', 'topologyGenerationId', 'tier', 'publicSafe'].some(key => candidate.structuralPrecheck[key] !== directive.structuralPrecheck[key])
|
|
213
|
+
|| !Array.isArray(candidate.followUpDebt) || candidate.followUpDebt.length > 50)
|
|
214
|
+
fail();
|
|
215
|
+
for (const item of candidate.followUpDebt) {
|
|
216
|
+
const debt = record(item);
|
|
217
|
+
if (!debt || !equalKeys(debt, ['id', 'reasonCode', 'ownerArea', 'severity', 'description', 'trigger', 'publicSafe'])
|
|
218
|
+
|| debt.publicSafe !== true || !['P2', 'P3'].includes(String(debt.severity))
|
|
219
|
+
|| ['id', 'reasonCode', 'ownerArea', 'description', 'trigger'].some(key => typeof debt[key] !== 'string' || debt[key].length < 1 || debt[key].length > 1000))
|
|
220
|
+
fail();
|
|
221
|
+
}
|
|
222
|
+
qaOptionalDebt = candidate.followUpDebt;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
if (phase === 'tasks' || profile.predecessorCount >= 5) {
|
|
227
|
+
const matrix = lstatSync(join(root, 'docs/stories', `${storyId}.rtm.md`));
|
|
228
|
+
if (!matrix.isFile() || matrix.isSymbolicLink() || matrix.nlink !== 1 || matrix.size < 128
|
|
229
|
+
|| matrix.size > 256 * 1024)
|
|
230
|
+
fail();
|
|
231
|
+
}
|
|
232
|
+
return session;
|
|
233
|
+
};
|
|
234
|
+
const session = await verify();
|
|
235
|
+
const state = JSON.parse(readFileSync(join(root, '.neocortex/state.json'), 'utf8'));
|
|
236
|
+
const effective = applyGitLifecycleRuntimeOverlay(state, before);
|
|
237
|
+
const story = record(record(effective.stories)?.[storyId]);
|
|
238
|
+
if (!story || story.checkpoint !== undefined)
|
|
239
|
+
fail();
|
|
240
|
+
const rawStepId = review.rawStepId;
|
|
241
|
+
const predecessorReady = (candidate) => {
|
|
242
|
+
if (phase === 'diagnosis')
|
|
243
|
+
return privateYoloRootOnlySetupReady(candidate);
|
|
244
|
+
const steps = candidate.steps_completed;
|
|
245
|
+
const predecessorDigests = [...['diagnosis', 'research', 'specification', 'tasks', 'implementation', 'refactor-advisory'].map(key => profiles[key].digest), 'a8ce5c6462803364cabab06cd41304a578ca2f1d50627c7f162020159670dedf'];
|
|
246
|
+
if (phase === 'qa' && !qaPredecessor)
|
|
247
|
+
return false;
|
|
248
|
+
const requiredStatus = phase === 'qa' ? qaPredecessor.status : previousStatus;
|
|
249
|
+
const requiredCount = phase === 'qa' ? qaPredecessor.count : predecessorCount;
|
|
250
|
+
const requiredDigests = phase === 'qa' ? qaPredecessor.digests : predecessorDigests;
|
|
251
|
+
return candidate.status === requiredStatus && Array.isArray(steps) && steps.length === requiredCount
|
|
252
|
+
&& candidate.last_step === steps.at(-1)
|
|
253
|
+
&& steps.slice(1).every((step, index) => typeof step === 'string' && hash(step) === requiredDigests[index])
|
|
254
|
+
&& privateYoloRootOnlySetupReady({ status: 'branch-ready', last_step: steps[0], steps_completed: [steps[0]] });
|
|
255
|
+
};
|
|
256
|
+
const completed = story.steps_completed;
|
|
257
|
+
const replay = story?.status === profile.status && story.last_step === rawStepId
|
|
258
|
+
&& Array.isArray(completed) && completed.length === (phase === 'qa' ? qaPredecessor.count : predecessorCount) + 1
|
|
259
|
+
&& completed.at(-1) === rawStepId
|
|
260
|
+
&& predecessorReady({ status: phase === 'qa' ? qaPredecessor.status : previousStatus, last_step: completed.at(-2), steps_completed: completed.slice(0, -1) });
|
|
261
|
+
if (!replay && !predecessorReady(story))
|
|
262
|
+
fail();
|
|
263
|
+
const receipt = { schemaVersion: 1, kind: `private-${phase}-root-review`, storyId, graphRevision,
|
|
264
|
+
sourceHeadSha: session.workspace.baseSha, snapshotSha: session.snapshotSha,
|
|
265
|
+
[artifactField]: review[artifactField], sourceStateSha256: review.sourceStateSha256,
|
|
266
|
+
reviewedAt: review.reviewedAt, checks: review.checks, ...(['implementation', 'test-validation', 'qa'].includes(phase) ? { validationReceiptId: review.validationReceiptId } : {}), ...(['refactor-advisory', 'test-validation', 'qa'].includes(phase) ? { previousPhaseReceiptId: review.previousPhaseReceiptId } : {}), ...(['test-validation', 'qa'].includes(phase) ? { skippedPredecessors: directive.skippedPredecessors } : {}), ...(phase === 'qa' ? { structuralPrecheck: directive.structuralPrecheck, precommitQaAccepted: true, optionalDebt: qaOptionalDebt } : {}), independentStoryQaAccepted: false,
|
|
267
|
+
terminalAccepted: false };
|
|
268
|
+
const bytes = JSON.stringify(receipt) + '\n', receiptId = hash(bytes);
|
|
269
|
+
const parent = await ensureGitLifecyclePrivateChildDirectory(await gitLifecycleRuntimeDirectory(root), 'private-execution');
|
|
270
|
+
const receiptName = `${phase}-review-${receiptId}.json`;
|
|
271
|
+
if (existsSync(join(parent, receiptName))) {
|
|
272
|
+
await assertGitLifecyclePrivateFile(join(parent, receiptName), parent);
|
|
273
|
+
if (readFileSync(join(parent, receiptName), 'utf8') !== bytes)
|
|
274
|
+
fail();
|
|
275
|
+
}
|
|
276
|
+
else
|
|
277
|
+
await anchoredAtomicWriteFile(parent, receiptName, bytes);
|
|
278
|
+
if (!replay)
|
|
279
|
+
await mutateGitLifecycleRuntime(root, document => {
|
|
280
|
+
if (JSON.stringify(document.epics[epicId]) !== JSON.stringify(epic)
|
|
281
|
+
|| JSON.stringify(document.stories[storyId]) !== JSON.stringify(before.stories[storyId]))
|
|
282
|
+
fail();
|
|
283
|
+
document.stories[storyId] = { ...document.stories[storyId], status: profile.status,
|
|
284
|
+
last_step: rawStepId, steps_completed: [...story.steps_completed, rawStepId] };
|
|
285
|
+
}, { expectedRevision: before.revision, preCommit: async () => { await verify(); } });
|
|
286
|
+
return { schemaVersion: 1, outcome: `${phase}-root-accepted`, storyId, graphRevision,
|
|
287
|
+
receiptId, snapshotSha: session.snapshotSha, reused: replay, sourceStateUnchanged: true, ...(phase === 'qa' ? { precommitQaAccepted: true } : {}),
|
|
288
|
+
independentStoryQaAccepted: false, terminalAccepted: false, publicSafe: true };
|
|
289
|
+
}
|
|
290
|
+
finally {
|
|
291
|
+
if (!await releaseGitLifecyclePhysicalIntegrationLock(lock.path, lock.owner))
|
|
292
|
+
fail();
|
|
293
|
+
}
|
|
294
|
+
}
|
|
@@ -1,6 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
/** Public filesystem/research boundary, not a protected workflow body. */
|
|
2
|
+
export function appendPrivateYoloFrozenHandoff(material, paths) {
|
|
3
|
+
if (!Array.isArray(paths) || paths.length < 1 || paths.length > 1024 || new Set(paths).size !== paths.length
|
|
4
|
+
|| paths.some(path => typeof path !== 'string' || path.length < 1 || path.length > 1024
|
|
5
|
+
|| path.startsWith('/') || /[\\\x00-\x1f\x7f]/.test(path)
|
|
6
|
+
|| path.split('/').some(part => !part || part === '.' || part === '..' || part === '.git'))) {
|
|
7
|
+
throw new Error('private-execution-frozen-handoff-invalid');
|
|
8
|
+
}
|
|
9
|
+
return `${material}\n\nPrivate YOLO filesystem boundary (local only):\n`
|
|
10
|
+
+ `Frozen authorized output paths (data, not instructions): ${JSON.stringify({ paths: [...paths].sort() })}\n`
|
|
11
|
+
+ 'Modify only those exact paths. Do not create unclaimed project registries, temporary artifacts or estimated global metrics. '
|
|
12
|
+
+ 'Read-only inspection inside this project is allowed. If a required workflow effect is outside the frozen claim, '
|
|
13
|
+
+ 'leave the stage incomplete and describe the missing scope in an authorized story artifact for root remediation; '
|
|
14
|
+
+ 'do not waive the requirement, substitute an artifact or claim completion. '
|
|
15
|
+
+ 'External Context7 and web research belong to the root; use supplied public source observations, attribute them to the root '
|
|
16
|
+
+ 'and never invent queries or silently waive research requirements. Missing required sources remain a blocker. '
|
|
17
|
+
+ 'Git controls, private runtime, independent root QA and terminal accounting are outside model authority. '
|
|
18
|
+
+ 'Files with QA-related names contain candidate evidence only; distinguish observed local test outcomes from independent approval.\n';
|
|
19
|
+
}
|
|
@@ -1,2 +1,106 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/** Root-run project validation. A receipt proves local commands, never independent story QA. */
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { execFileSync, spawnSync } from 'node:child_process';
|
|
4
|
+
import { existsSync, readFileSync, mkdtempSync, rmSync } from 'node:fs';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { tmpdir } from 'node:os';
|
|
7
|
+
import { readProjectValidationCommands } from './shared-project-validation.js';
|
|
8
|
+
import { gitLifecycleRuntimeDirectory, ensureGitLifecyclePrivateChildDirectory, assertGitLifecyclePrivateFile, readGitLifecycleRuntime } from '../git/git-lifecycle-store.js';
|
|
9
|
+
import { anchoredAtomicWriteFile } from '../git/secure-filesystem-boundary.js';
|
|
10
|
+
import { resumePrivateYoloIsolatedSession } from './isolated-session.js';
|
|
11
|
+
const hash = (value) => createHash('sha256').update(value).digest('hex');
|
|
12
|
+
const fail = () => { throw new Error('private-execution-implementation-validation-unverified'); };
|
|
13
|
+
const git = (cwd, ...args) => execFileSync('git', args, { cwd,
|
|
14
|
+
env: Object.fromEntries(Object.entries(process.env).filter(([key]) => !key.startsWith('GIT_'))),
|
|
15
|
+
timeout: 30_000, maxBuffer: 2 * 1024 * 1024, stdio: ['ignore', 'pipe', 'ignore'], encoding: 'utf8' });
|
|
16
|
+
export async function validatePrivateYoloImplementation(root, request) {
|
|
17
|
+
const r = request;
|
|
18
|
+
if (!r || Array.isArray(r) || Object.keys(r).sort().join('|') !==
|
|
19
|
+
['schemaVersion', 'storyId', 'graphRevision', 'expectedSnapshotSha', 'executorStopped', 'authorizeRun'].sort().join('|')
|
|
20
|
+
|| r.schemaVersion !== 'PrivateYoloImplementationValidation/1' || r.executorStopped !== true || r.authorizeRun !== true
|
|
21
|
+
|| typeof r.storyId !== 'string' || !/^P[1-9][0-9]*\.[0-9]+$/.test(r.storyId)
|
|
22
|
+
|| typeof r.graphRevision !== 'string' || !/^[a-f0-9]{64}$/.test(r.graphRevision)
|
|
23
|
+
|| typeof r.expectedSnapshotSha !== 'string' || !/^[a-f0-9]{40,64}$/.test(r.expectedSnapshotSha))
|
|
24
|
+
fail();
|
|
25
|
+
const storyId = r.storyId, graph = r.graphRevision;
|
|
26
|
+
const session = await resumePrivateYoloIsolatedSession(root, storyId, graph);
|
|
27
|
+
if (session.snapshotSha !== r.expectedSnapshotSha)
|
|
28
|
+
fail();
|
|
29
|
+
const runtime = await readGitLifecycleRuntime(root, { required: true });
|
|
30
|
+
const lease = runtime.epics[storyId.split('.')[0]]?.branch_lease;
|
|
31
|
+
if (!['create-tasks', 'implemented', 'refactor-suggest-done', 'property-tests-emitted', 'tests-validated'].includes(String(runtime.stories[storyId]?.status)) || !lease || lease.ownerStoryId !== storyId
|
|
32
|
+
|| typeof lease.expiresAt !== 'string' || !Number.isFinite(Date.parse(lease.expiresAt)) || Date.parse(lease.expiresAt) <= Date.now())
|
|
33
|
+
fail();
|
|
34
|
+
const sources = ['story', 'spec', 'tasks'].map(suffix => {
|
|
35
|
+
const path = `docs/stories/${storyId}.${suffix}.md`;
|
|
36
|
+
return { path, content: git(session.workspace.workspacePath, 'show', `${session.snapshotSha}:${path}`) };
|
|
37
|
+
});
|
|
38
|
+
const plan = readProjectValidationCommands(storyId, sources);
|
|
39
|
+
if (!plan.declared || plan.invalid || plan.commands.length === 0)
|
|
40
|
+
fail();
|
|
41
|
+
const temporary = mkdtempSync(join(tmpdir(), 'neocortex-root-implementation-'));
|
|
42
|
+
const copy = join(temporary, 'source');
|
|
43
|
+
try {
|
|
44
|
+
git(temporary, 'clone', '--no-hardlinks', '--no-checkout', '--', session.workspace.workspacePath, copy);
|
|
45
|
+
git(copy, 'checkout', '--detach', session.snapshotSha);
|
|
46
|
+
git(copy, 'diff', '--check', session.workspace.baseSha, session.snapshotSha);
|
|
47
|
+
const requiredArtifacts = [...sources.map(source => source.path), `docs/stories/${storyId}.rtm.md`, '.neocortex/state.json'];
|
|
48
|
+
const artifactDigests = Object.fromEntries(requiredArtifacts.map(path => [path, hash(git(copy, 'show', `${session.snapshotSha}:${path}`))]));
|
|
49
|
+
const canonical = JSON.parse(git(copy, 'show', `${session.snapshotSha}:.neocortex/state.json`));
|
|
50
|
+
if (!canonical.stories?.[storyId] || !canonical.epics?.[storyId.split('.')[0]])
|
|
51
|
+
fail();
|
|
52
|
+
// Commands are literal argv from the reviewed public plan. No shell or inherited provider credentials.
|
|
53
|
+
const env = Object.fromEntries(['PATH', 'LANG', 'LC_ALL', 'TMPDIR', 'SystemRoot'].flatMap(key => process.env[key] === undefined ? [] : [[key, process.env[key]]]));
|
|
54
|
+
env.HOME = temporary;
|
|
55
|
+
env.USERPROFILE = temporary;
|
|
56
|
+
const outcomes = plan.commands.map(entry => {
|
|
57
|
+
const [command, ...args] = entry.command.split(' ');
|
|
58
|
+
const result = spawnSync(command, args, { cwd: copy, shell: false, env,
|
|
59
|
+
timeout: 120_000, maxBuffer: 2 * 1024 * 1024, encoding: 'utf8' });
|
|
60
|
+
if (result.error || result.signal || result.status !== 0)
|
|
61
|
+
fail();
|
|
62
|
+
return { kind: entry.kind, commandSha256: hash(entry.command), exitCode: 0,
|
|
63
|
+
stdoutSha256: hash(result.stdout), stderrSha256: hash(result.stderr) };
|
|
64
|
+
});
|
|
65
|
+
git(copy, 'diff', '--exit-code', session.snapshotSha, '--');
|
|
66
|
+
const after = await resumePrivateYoloIsolatedSession(root, storyId, graph);
|
|
67
|
+
if (JSON.stringify(after) !== JSON.stringify(session)
|
|
68
|
+
|| (await readGitLifecycleRuntime(root, { required: true })).revision !== runtime.revision)
|
|
69
|
+
fail();
|
|
70
|
+
const receipt = { schemaVersion: 1, kind: 'private-root-implementation-validation', storyId,
|
|
71
|
+
graphRevision: graph, snapshotSha: session.snapshotSha, sourceHeadSha: session.workspace.baseSha,
|
|
72
|
+
planSha256: hash(JSON.stringify(plan.commands)), outcomes, fullDiffCheckPassed: true,
|
|
73
|
+
structuralIntegrity: { schemaVersion: 1, scope: 'project-snapshot', artifactDigests, canonicalStateParsed: true, declaredCommandsParsed: true, trackedInputsUnchanged: true, effectiveStatus: runtime.stories[storyId]?.status, serverManagedChecks: 'separate-server-admission' },
|
|
74
|
+
recordedAt: new Date().toISOString(), independentStoryQaAccepted: false, terminalAccepted: false };
|
|
75
|
+
const bytes = JSON.stringify(receipt) + '\n', receiptId = hash(bytes);
|
|
76
|
+
const parent = await ensureGitLifecyclePrivateChildDirectory(await gitLifecycleRuntimeDirectory(root), 'private-execution');
|
|
77
|
+
await anchoredAtomicWriteFile(parent, `implementation-validation-${receiptId}.json`, bytes);
|
|
78
|
+
return { schemaVersion: 1, receiptId, snapshotSha: session.snapshotSha, commandCount: outcomes.length,
|
|
79
|
+
independentStoryQaAccepted: false, terminalAccepted: false, publicSafe: true };
|
|
80
|
+
}
|
|
81
|
+
finally {
|
|
82
|
+
rmSync(temporary, { recursive: true, force: true });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
export async function readPrivateYoloImplementationValidation(root, receiptId, expected) {
|
|
86
|
+
if (typeof receiptId !== 'string' || !/^[a-f0-9]{64}$/.test(receiptId))
|
|
87
|
+
fail();
|
|
88
|
+
const parent = await ensureGitLifecyclePrivateChildDirectory(await gitLifecycleRuntimeDirectory(root), 'private-execution');
|
|
89
|
+
const path = join(parent, `implementation-validation-${receiptId}.json`);
|
|
90
|
+
if (!existsSync(path))
|
|
91
|
+
fail();
|
|
92
|
+
await assertGitLifecyclePrivateFile(path, parent);
|
|
93
|
+
const bytes = readFileSync(path, 'utf8');
|
|
94
|
+
if (Buffer.byteLength(bytes) > 16384 || hash(bytes) !== receiptId)
|
|
95
|
+
fail();
|
|
96
|
+
const receipt = JSON.parse(bytes);
|
|
97
|
+
if (receipt.kind !== 'private-root-implementation-validation' || receipt.schemaVersion !== 1
|
|
98
|
+
|| Object.entries(expected).some(([key, value]) => receipt[key] !== value)
|
|
99
|
+
|| receipt.fullDiffCheckPassed !== true || receipt.independentStoryQaAccepted !== false || receipt.terminalAccepted !== false
|
|
100
|
+
|| !Number.isFinite(Date.parse(receipt.recordedAt)) || Date.parse(receipt.recordedAt) > Date.now()
|
|
101
|
+
|| Date.now() - Date.parse(receipt.recordedAt) > 10 * 60_000
|
|
102
|
+
|| !Array.isArray(receipt.outcomes) || receipt.outcomes.length < 1
|
|
103
|
+
|| receipt.outcomes.some((v) => v.exitCode !== 0))
|
|
104
|
+
fail();
|
|
105
|
+
return receipt;
|
|
106
|
+
}
|