@ornexus/neocortex 4.60.41 → 4.60.42
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/sbom.cdx.json +31 -31
- package/install.ps1 +1 -1
- package/install.sh +1 -1
- package/package.json +14 -2
- package/packages/client/dist/commands/invoke.js +39 -38
- package/packages/client/dist/generated/command-registry.generated.d.ts +66 -23
- package/packages/client/dist/generated/command-registry.generated.js +1 -1
- package/packages/client/dist/generated/public-projections.generated.d.ts +100 -0
- package/packages/client/dist/generated/public-projections.generated.js +1 -0
- package/packages/client/dist/git/git-lifecycle-store.d.ts +17 -0
- package/packages/client/dist/git/git-lifecycle-store.js +4 -4
- package/packages/client/dist/index.d.ts +1 -0
- package/packages/client/dist/index.js +1 -1
- package/packages/client/dist/migrations/project-identity.js +7 -7
- package/packages/client/dist/migrations/state-json.d.ts +14 -0
- package/packages/client/dist/migrations/state-json.js +4 -0
- package/packages/client/dist/runner/headless/epic-git-reconciliation-inventory.d.ts +2 -0
- package/packages/client/dist/runner/headless/epic-git-reconciliation-inventory.js +1 -1
- package/packages/client/dist/runner/headless/epic-terminal-git-cli-adapter.js +8 -8
- package/packages/client/dist/state/state-json-repair.js +3 -3
- package/packages/client/dist/yoloop/invoke-hooks.d.ts +2 -3
- package/packages/client/dist/yoloop/invoke-hooks.js +9 -8
- package/packages/client/dist/yoloop/shared-yoloop-types.d.ts +87 -1
- package/packages/client/dist/yoloop/yoloop-client-state-store.d.ts +27 -3
- package/packages/client/dist/yoloop/yoloop-client-state-store.js +1 -1
- package/packages/shared/dist/command-registry-contract.js +38 -0
- package/packages/shared/dist/continuity/goal-operation-authority.js +48 -0
- package/packages/shared/dist/executable-graph-catalog/contracts.js +935 -0
- package/packages/shared/dist/generated/command-registry.generated.js +82 -23
- package/packages/shared/dist/generated/invocation-schema.generated.js +12 -4
- package/packages/shared/dist/generated/public-projections.generated.js +149 -0
- package/packages/shared/dist/invocation/executable-policy-recovery.d.ts +83 -0
- package/packages/shared/dist/invocation/executable-policy-recovery.js +272 -0
- package/packages/shared/dist/invocation/index.d.ts +3 -1
- package/packages/shared/dist/invocation/index.js +40 -1
- package/packages/shared/dist/invocation/recovery-route-compiler.d.ts +56 -0
- package/packages/shared/dist/invocation/recovery-route-compiler.js +214 -0
- package/packages/shared/dist/state/index.d.ts +1 -0
- package/packages/shared/dist/state/index.js +1 -0
- package/packages/shared/dist/state/state-migration.d.ts +58 -0
- package/packages/shared/dist/state/state-migration.js +404 -0
- package/packages/shared/dist/yoloop/canonical.js +15 -0
- package/packages/shared/dist/yoloop/operational-budget.js +164 -0
- package/packages/shared/dist/yoloop/recovery-types.js +39 -0
- 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 +11 -2
- package/targets-stubs/codex/README.md +7 -4
- package/targets-stubs/codex/neocortex.toml +53 -6
- 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 @@
|
|
|
1
|
-
const I=2048;function
|
|
1
|
+
const I=2048,S=1e3;function N(e){const s=new Map,u=new Set,f=Object.entries(e.epics??{}),b=Object.entries(e.stories??{});if(f.length+b.length>1e4)throw new Error("git-reconciliation-state-inventory-limit");const _=t=>{!t.branch||!t.origin||s.set(t.branch,[...s.get(t.branch)??[],t])};for(const[t,r]of f){const i=c(r.execution_branch),o=c(r.origin_branch);o&&u.add(o);const n=d(r.terminal_git_receipt),a=n?.physicalStatus==="pushed"&&n.epicId===t&&n.executionBranch===i&&n.originBranch===o&&p(n.executionHeadSha)&&p(n.mergeSha)&&p(n.pushedSha),h=m(r.branch_lease)||(r.status===void 0?!a:!g(r.status));_({epicId:t,branch:i,origin:o,active:h,proofs:a?[{head:c(n.executionHeadSha),evidence:["terminal_receipt","canonical_convention","managed_metadata"],mergeSha:c(n.mergeSha),pushedSha:c(n.pushedSha)}]:[],prNumbers:l(r.pr_number)})}for(const[t,r]of b){const i=c(r.epic_id)||t.split(".")[0],o=e.epics?.[i],n=d(r.checkpoint),a=c(r.branch_name)||c(n?.branch);if(!o||a===c(o.execution_branch))continue;const h=n?.branch===a&&p(n.headSha);_({epicId:i,branch:a,origin:c(o.origin_branch),active:!g(r.status)||m(r.branch_lease),proofs:h?[{head:c(n.headSha),evidence:["state_reference","canonical_convention","managed_metadata"]}]:[],prNumbers:l(r.pr_number)})}for(const[t,r]of s)s.set(t,r.map(i=>({...i,proofs:i.proofs.map(o=>({...o,evidence:o.evidence.filter(n=>n!=="canonical_convention"||t===`epic/${i.epicId}`||t.startsWith(`epic/${i.epicId}/`)||t.startsWith(`story/${i.epicId}.`)||t.startsWith(`story/${i.epicId}/`))}))})));return{claims:s,origins:u}}function c(e){return typeof e=="string"?e:""}function d(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)?e:void 0}function p(e){return typeof e=="string"&&/^[a-f0-9]{40,64}$/.test(e)}function g(e){return["done","completed","merged","superseded","cancelled","archived","rolled-back"].includes(c(e))}function m(e){const s=d(e);return!!(s&&Date.parse(c(s.expiresAt))>Date.now())}function l(e){return Number.isSafeInteger(e)&&Number(e)>0?[Number(e)]:[]}export{S as GIT_INVENTORY_PR_LIMIT,I as GIT_INVENTORY_REF_LIMIT,N as collectGitResourceOwnership};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import{execFile as pe}from"node:child_process";import{createHash as I}from"node:crypto";import{readFile as te}from"node:fs/promises";import{join as me}from"node:path";import{promisify as ye}from"node:util";import{classifyGitReconciliationCandidate as ae,reconcileTerminalReceipt as we}from"../../../../shared/dist/yoloop/epic-terminal-git.js";import{collectGitResourceOwnership as be,GIT_INVENTORY_REF_LIMIT as L}from"./epic-git-reconciliation-inventory.js";import{executeEpicMergeCandidateQa as ie,verifiedEpicMergeCandidateReceipt as W}from"./epic-merge-candidate-qa.js";import{applyGitLifecycleRuntimeOverlay as Se,ensureRuntimeEpic as T,executeP205TransientAuthority as _e,P205_COMPLETE_COMMAND as Q,projectP205TransientExecutionEvidence as xe,verifyAcceptedGitLifecycleStoryCheckpoint as ne,mutateGitLifecycleRuntime as J,readGitLifecycleRuntime as ve}from"../../git/git-lifecycle-store.js";const Re=ye(pe);function We(r,a={}){const c=me(r,".neocortex","state.json"),t=a.gitRunner??((...e)=>se("git",e,r)),d=a.providerRunner??((...e)=>se("gh",e,r)),g=new Map;return{async prepareLifecycleEvidence(e){try{try{await te(c,"utf8")}catch(y){if(Ee(y,"ENOENT"))return{ok:!0,originBindingCaptured:!1,aggregateQaRecorded:!1};throw y}const n=(await D(r,c)).epics?.[e.epicId];if(!n)return o("epic-state-missing",!1,!1);if(_(n.branch_mode)!=="epic")return{ok:!0,originBindingCaptured:!1,aggregateQaRecorded:!1};const s=_(n.execution_branch),u=_(n.origin_branch);if(!s||!A(s))return o("epic-execution-branch-missing",!1,!1);let h=await t("symbolic-ref","--quiet","--short","HEAD").catch(()=>""),f=await t("rev-parse","HEAD"),p=S(n.origin_binding)?n.origin_binding:null,l=!1;if(p){if(p.executionBranch!==s||p.originBranch!==u||h!==s&&h!==p.originBranch||!await U(t,p.originHeadSha)||!await R(t,p.originHeadSha,f))return o("epic-origin-binding-physical-drift",!1,!1)}else{if(!h||h===s||!A(h))return o("epic-origin-binding-capture-window-missed",!1,!1);if(u&&u!==h)return o("epic-origin-binding-recorded-origin-drift",!1,!1);p={schemaVersion:1,originBranch:h,originHeadSha:f,executionBranch:s,capturedFromCurrentCheckout:!0,capturedAt:e.nowIso},await J(r,y=>{const v=T(y,e.epicId);if(v.origin_binding&&JSON.stringify(v.origin_binding)!==JSON.stringify(p))throw new Error("origin-binding-cas-conflict");v.origin_branch=h,v.origin_binding=p}),l=!0}if(h!==s){const y=await ue(t,`refs/heads/${s}`);try{if(y){if(!await R(t,p.originHeadSha,y))return o("epic-execution-branch-origin-drift",!1,!1);await t("switch",s)}else await t("switch","-c",s,p.originHeadSha)}catch{const v=await t("status","--porcelain","--untracked-files=normal").catch(()=>"");return o(v?"epic-execution-branch-switch-blocked-by-user-changes":"epic-execution-branch-switch-failed",!0,!1)}if(h=await t("symbolic-ref","--quiet","--short","HEAD"),f=await t("rev-parse","HEAD"),h!==s)return o("epic-execution-branch-switch-unverified",!0,!1)}const m=await D(r,c,!0),w=m.epics?.[e.epicId];if(!w)return o("epic-state-missing",!1,!1);const x=j(w),b=await Promise.all(x.map(y=>ne(r,m.stories?.[y]?.checkpoint,y,e.epicId,s).then(v=>v&&le(m.stories?.[y]?.checkpoint))));if(!(x.length>0&&b.every(Boolean)))return{ok:!0,originBindingCaptured:l,aggregateQaRecorded:!1};if(h!==s)return o("epic-aggregate-qa-execution-checkout-required",!0,!1);const $=_(w.graph_revision);if(!/^[a-f0-9]{64}$/i.test($))return o("epic-aggregate-qa-graph-required",!1,!1);if(await fe(t))return o("epic-aggregate-qa-checkout-dirty",!0,!1);const q=await _e(r,{headSha:f,graphRevision:$,nowIso:e.nowIso}),B=xe(q);if(!B)return o("epic-aggregate-qa-authority-invalid",!1,!1);if(await a.aggregateQaRunner?.({projectRoot:r,gateIds:B.gateIds}),await t("rev-parse","HEAD")!==f||await fe(t))return o("epic-aggregate-qa-head-raced",!0,!1);const M=`local-yolo-tdd:${I("sha256").update(["epic-aggregate-p205-execution-v2",Q,f,$,B.manifestDigest,B.gateIds.join(","),B.resultDigest].join("\0")).digest("hex")}`,G={schemaVersion:1,evidenceVersion:"p205-execution-v2",producer:"local-yolo-tdd",contract:"P205-CI-02",command:Q,verdict:"passed",executionHeadSha:f,graphRevision:$,manifestDigest:B.manifestDigest,resultDigest:B.resultDigest,checkedAt:B.executedAt,receiptRef:M,gateIds:B.gateIds};return await J(r,y=>{const v=T(y,e.epicId);v.aggregate_qa_receipt=G}),{ok:!0,originBindingCaptured:l,aggregateQaRecorded:!0,p205Authority:q}}catch{return o("epic-lifecycle-evidence-production-failed",!0,!1)}},async inspect(e){try{const i=await D(r,c,!0),n=i.epics?.[e.epicId];if(!n)return o("epic-state-missing",!1,!1);if(_(n.execution_branch)!==e.executionBranch||_(n.origin_branch)!==e.originBranch)return o("epic-branch-boundary-drift",!1,!1);const s=await t("rev-parse",`refs/heads/${e.executionBranch}`),u=await N(t,e.originBranch);await P(t,e.originBranch,u);const h=await Y(r,i,n,e.epicId,e.executionBranch);let f=S(n.terminal_git_receipt)?n.terminal_git_receipt:null;f?.physicalStatus==="pushed"&&(f.pushedSha&&await R(t,f.executionHeadSha,f.pushedSha)&&await R(t,f.pushedSha,u)||(f=null)),f||(f=await Ne({directive:e,executionHeadSha:s,originHeadSha:u,git:t}));const p=new Set((Array.isArray(n.git_reconciliation_receipts)?n.git_reconciliation_receipts:[]).filter(S).filter(z).map(H=>_(H.resourceId)).filter(Boolean)),l=await t("symbolic-ref","--quiet","--short","HEAD").catch(()=>""),m=e.reconcile.enabled?await oe({state:i,epic:n,directive:e,currentBranch:l,originHeadSha:u,receiptIds:p,git:t,gh:d}):[],w=S(n.origin_binding)?n.origin_binding:null,x=w&&w.originBranch===e.originBranch&&w.executionBranch===e.executionBranch&&await U(t,w.originHeadSha)&&await R(t,w.originHeadSha,s)?w:null;return{ok:!0,snapshot:{executionHeadSha:s,originHeadSha:u,..._(n.graph_revision)?{graphRevision:_(n.graph_revision)}:{},writerLeaseActive:ee(n.branch_lease),acceptedCheckpointIds:h,existingReceipt:f,aggregateQaReceipt:S(n.aggregate_qa_receipt)?n.aggregate_qa_receipt:null,originBinding:x,reconciliationCandidates:m}}}catch(i){return o(ce(i)??"epic-terminal-inspection-failed",!0,!1)}},async prepareMergeObject(e){let i;try{const n=await t("merge-tree","--write-tree",e.originHeadSha,e.executionHeadSha);i=await t("commit-tree",n,"-p",e.originHeadSha,"-p",e.executionHeadSha,"-m",e.message)}catch{return o("epic-terminal-merge-conflict",!1,!1)}try{const n=await ie(r,{mergeSha:i,originHeadSha:e.originHeadSha,executionHeadSha:e.executionHeadSha,graphRevision:e.graphRevision},e.nowIso);return g.set(i,n.candidateAuthority),{ok:!0,mergeSha:i,...n}}catch{return o("epic-merge-candidate-qa-failed",!0,!1)}},async inspectReconciliation(e){try{const i=await D(r,c,!0),n=i.epics?.[e.epicId];if(!n)return o("epic-state-missing",!1,!1);if(_(n.execution_branch)!==e.executionBranch||_(n.origin_branch)!==e.originBranch)return o("epic-branch-boundary-drift",!1,!1);const s=await N(t,e.originBranch);await P(t,e.originBranch,s);const u=new Set((Array.isArray(n.git_reconciliation_receipts)?n.git_reconciliation_receipts:[]).filter(S).filter(z).map(p=>_(p.resourceId)).filter(Boolean)),h=await t("symbolic-ref","--quiet","--short","HEAD").catch(()=>"");return{ok:!0,candidates:await oe({state:i,epic:n,directive:e,currentBranch:h,originHeadSha:s,receiptIds:u,git:t,gh:d})}}catch(i){return o(ce(i)??"git-reconciliation-inspection-failed",!0,!1)}},async pushMergeObject(e){try{if(!W(e.candidateAuthority,{mergeSha:e.mergeSha,originHeadSha:e.expectedOriginSha,executionHeadSha:e.executionHeadSha,graphRevision:e.graphRevision}))return o("epic-merge-candidate-authority-required",!1,!1);const n=await D(r,c,!0),s=n.epics?.[e.epicId];if(!s||s.execution_branch!==e.executionBranch||s.origin_branch!==e.originBranch||s.graph_revision!==e.graphRevision||ee(s.branch_lease)||await t("rev-parse",`refs/heads/${e.executionBranch}`)!==e.executionHeadSha)return o("epic-merge-candidate-authority-raced",!0,!1);const u=await Y(r,n,s,e.epicId,e.executionBranch);if(!X(u,e.storyCheckpointIds)||!X(j(s),e.storyCheckpointIds))return o("epic-checkpoint-set-revalidation-drift",!0,!1);const h=S(s.aggregate_qa_receipt)?s.aggregate_qa_receipt:null;if(!de(h,e.executionHeadSha,e.graphRevision,e.nowIso)||h.receiptRef!==e.aggregateQaReceipt.receiptRef)return o("epic-terminal-aggregate-qa-revalidation-drift",!0,!1);if(await N(t,e.originBranch)!==e.expectedOriginSha)return o("epic-origin-head-raced",!0,!1);await t("push","--porcelain","origin",`${e.mergeSha}:refs/heads/${e.originBranch}`);const p=await N(t,e.originBranch);return p!==e.mergeSha?o("epic-terminal-push-unverified",!0,!1):{ok:!0,pushedSha:p}}catch(i){return re(i)?o("epic-terminal-remote-policy-denied",!0,!0):o("epic-terminal-push-failed",!0,!1)}},async ensureSingleEpicPullRequest(e){try{const i=W(e.candidateAuthority,{mergeSha:e.expectedPullRequestHeadSha,originHeadSha:e.candidateQa.originHeadSha,executionHeadSha:e.expectedStoryHeadSha,graphRevision:e.candidateQa.graphRevision});if(!i||i!==e.candidateQa||!await R(t,e.candidateQa.originHeadSha,e.expectedPullRequestHeadSha)||!await R(t,e.expectedStoryHeadSha,e.expectedPullRequestHeadSha))return o("epic-merge-candidate-authority-required",!1,!1);const n=await O(t,d);if(!n)return o("git-resource-provider-repository-unverified",!0,!0);const s=`https://github.com/${n.nameWithOwner}`,u=await C(t,e.executionBranch);if(u&&u!==e.expectedPullRequestHeadSha){if(await P(t,e.executionBranch,u),!await R(t,u,e.expectedPullRequestHeadSha))return o("epic-terminal-execution-remote-head-drift",!0,!0);await t("push","--porcelain","origin",`${e.expectedPullRequestHeadSha}:refs/heads/${e.executionBranch}`)}if(u||await t("push","--porcelain","origin",`${e.expectedPullRequestHeadSha}:refs/heads/${e.executionBranch}`),await N(t,e.executionBranch)!==e.expectedPullRequestHeadSha)return o("epic-terminal-execution-publish-unverified",!0,!0);const h=await d("pr","list","--repo",s,"--state","all","--head",e.executionBranch,"--base",e.originBranch,"--json","number,state,headRefName,headRefOid,baseRefName,mergeCommit","--limit","100"),f=JSON.parse(h),p=f.filter(b=>b.headRefName===e.executionBranch&&b.baseRefName===e.originBranch&&b.headRefOid===e.expectedPullRequestHeadSha&&(b.state==="OPEN"||b.state==="MERGED"));if(p.length>1)return o("epic-terminal-duplicate-prs",!1,!0);if(typeof p[0]?.number=="number")return{ok:!0,prNumber:p[0].number};if(f.some(b=>b.state==="OPEN"))return o("epic-terminal-stale-pr",!1,!0);const m=(await d("pr","create","--repo",s,"--head",e.executionBranch,"--base",e.originBranch,"--title",`[${e.epicId}] Terminal epic integration`,"--body",`Autonomous terminal integration receipt: ${e.idempotencyKey}`)).match(/\/(\d+)(?:\s*)$/);if(!m)return o("epic-terminal-pr-number-unverified",!0,!0);const w=Number(m[1]),x=await K(d,n.nameWithOwner,w);return!x||x.state!=="OPEN"||x.headRefName!==e.executionBranch||x.baseRefName!==e.originBranch||x.headRefOid!==e.expectedPullRequestHeadSha?o("epic-terminal-pr-create-unverified",!0,!0):{ok:!0,prNumber:w}}catch(i){return o("epic-terminal-pr-create-denied",!0,re(i))}},async mergeEpicPullRequest(e){try{const i=await O(t,d);if(!i)return o("git-resource-provider-repository-unverified",!0,!0);const n=e.candidateQa&&W(e.candidateAuthority,{mergeSha:e.expectedPullRequestHeadSha,graphRevision:e.candidateQa.graphRevision,originHeadSha:e.expectedOriginHeadSha,executionHeadSha:e.expectedStoryHeadSha});if(!n||n!==e.candidateQa)return o("epic-merge-candidate-authority-required",!1,!1);const s=await K(d,i.nameWithOwner,e.prNumber),u=await C(t,e.executionBranch),h=await N(t,e.originBranch);if(!s||s.headRefName!==e.executionBranch||s.baseRefName!==e.originBranch||s.headRefOid!==e.expectedPullRequestHeadSha||u&&u!==e.expectedPullRequestHeadSha||!await R(t,e.expectedOriginHeadSha,e.expectedPullRequestHeadSha)||!await R(t,e.expectedStoryHeadSha,e.expectedPullRequestHeadSha))return o("epic-terminal-pr-revalidation-drift",!0,!0);if(s.state==="OPEN"&&h!==e.expectedOriginHeadSha)return o("epic-terminal-pr-origin-raced",!0,!0);let f;if(s.state==="OPEN"){const b=await $e(d,i.nameWithOwner,e.originBranch,e.expectedPullRequestHeadSha,e.expectedOriginHeadSha,e.nowIso);if(!b.ok)return o(b.reasonCode,!0,!0);if(f=b.receipt,await N(t,e.originBranch)!==e.expectedOriginHeadSha)return o("epic-terminal-pr-origin-raced",!0,!0);await d("pr","merge",String(e.prNumber),"--repo",`https://github.com/${i.nameWithOwner}`,"--merge","--match-head-commit",e.expectedPullRequestHeadSha)}else{if(s.state!=="MERGED")return o("epic-terminal-pr-not-open-or-merged",!1,!0);f={schemaVersion:1,provider:"github",mode:"recovered_exact_tree",source:"physical_recovery",prHeadSha:e.expectedPullRequestHeadSha,originHeadSha:e.expectedOriginHeadSha,checkedAt:e.nowIso,publicSafe:!0}}const p=await K(d,i.nameWithOwner,e.prNumber),l=p?.mergeCommit?.oid;if(p?.state!=="MERGED"||!l)return o("epic-terminal-pr-merge-unverified",!0,!0);const m=await N(t,e.originBranch);if(await P(t,e.originBranch,m),!await R(t,e.expectedStoryHeadSha,m)||!await R(t,e.expectedPullRequestHeadSha,m)||!await R(t,l,m))return o("epic-terminal-pr-merge-ancestry-unproven",!0,!0);const[w,x]=await Promise.all([t("rev-parse",`${e.expectedPullRequestHeadSha}^{tree}`),t("rev-parse",`${l}^{tree}`)]);return w!==e.candidateQa.treeSha||x!==w?o("epic-terminal-pr-merged-tree-mismatch",!1,!0):{ok:!0,mergeSha:e.expectedPullRequestHeadSha,pushedSha:l,providerGuard:f}}catch(i){return o("epic-terminal-pr-merge-denied",!0,re(i))}},async verifyTerminalReceipt(e,i,n){try{if(i.physicalStatus!=="pushed"||!i.pushedSha||!i.mergeSha)return o("epic-terminal-physical-revalidation-required",!0,!1);const s=await D(r,c,!0),u=s.epics?.[e.epicId];if(!u||_(u.execution_branch)!==e.executionBranch||_(u.origin_branch)!==e.originBranch||_(u.graph_revision)!==e.graphRevision)return o("epic-terminal-state-revalidation-drift",!0,!1);const h=await t("rev-parse",`refs/heads/${e.executionBranch}`);if(h!==e.expectedExecutionHeadSha||i.executionHeadSha!==h)return o("epic-terminal-execution-revalidation-drift",!0,!1);const f=S(u.origin_binding)?u.origin_binding:null;if(!f||f.originBranch!==e.originBranch||f.executionBranch!==e.executionBranch||!await U(t,f.originHeadSha)||!await R(t,f.originHeadSha,h))return o("epic-origin-binding-revalidation-drift",!0,!1);const p=await Y(r,s,u,e.epicId,e.executionBranch);if(ee(u.branch_lease)||!X(p,e.storyCheckpointIds))return o("epic-checkpoint-set-revalidation-drift",!0,!1);const l=S(u.aggregate_qa_receipt)?u.aggregate_qa_receipt:null;if(!de(l,h,e.graphRevision,n)||l?.receiptRef!==e.localGates.aggregateQaReceipt.receiptRef)return o("epic-terminal-aggregate-qa-revalidation-drift",!0,!1);const m=await N(t,e.originBranch);if(await P(t,e.originBranch,m),!await R(t,h,m)||!await R(t,i.mergeSha,m)||!await R(t,i.pushedSha,m))return o("epic-terminal-physical-ancestry-unproven",!0,!1);const w={originHeadSha:i.baseSha,executionHeadSha:h,mergeSha:i.mergeSha,graphRevision:e.graphRevision};let x=g.get(i.mergeSha),b=W(x,w);if(!b){const $=await ie(r,w,n);x=$.candidateAuthority,b=$.candidateQa,g.set(i.mergeSha,x)}if(i.candidateQa&&(i.candidateQa.originHeadSha!==b.originHeadSha||i.candidateQa.executionHeadSha!==b.executionHeadSha||i.candidateQa.graphRevision!==b.graphRevision||i.candidateQa.treeSha!==b.treeSha))return o("epic-merge-candidate-receipt-drift",!0,!1);let H=i.providerGuard;if(i.strategy==="single_pr"){if(!i.prNumber)return o("epic-terminal-pr-merge-unverified",!0,!1);if(await t("rev-parse",`${i.pushedSha}^{tree}`)!==b.treeSha)return o("epic-terminal-pr-merged-tree-mismatch",!1,!1);if(H&&!qe(H,i))return o("epic-terminal-pr-base-guard-unverifiable",!1,!1);H??={schemaVersion:1,provider:"github",mode:"recovered_exact_tree",source:"physical_recovery",prHeadSha:i.mergeSha,originHeadSha:i.baseSha,checkedAt:n,publicSafe:!0}}else if(H)return o("epic-terminal-pr-base-guard-unverifiable",!1,!1);return{ok:!0,candidateQa:b,candidateAuthority:x,...H?{providerGuard:H}:{}}}catch{return o("epic-terminal-physical-revalidation-failed",!0,!1)}},async applyReconciliation(e,i,n){if(i.action==="preserve")return o("git-reconciliation-preserve-not-actionable",!1,!1);if(n)return{ok:!0,outcome:"dry_run"};if(i.action==="no_op")return{ok:!0,outcome:"already_absent"};if(i.action==="delete_remote_branch")return He(e,i,t,d);try{const s=await F({candidate:e,decision:i,git:t,gh:d});if(!s.ok)return o(s.reasonCode,!0,!1);if(s.alreadyAbsent)return{ok:!0,outcome:"already_absent"};if(i.action==="delete_local_branch")await t("update-ref","-d",`refs/heads/${e.branchName}`,e.headSha);else if(i.action==="close_pull_request"){if(!e.prNumber)return o("git-reconciliation-pr-number-missing",!1,!1);const u=await O(t,d);if(!u)return o("git-resource-provider-repository-unverified",!0,!0);const h=`https://github.com/${u.nameWithOwner}`,f=`neocortex-reconciliation:${I("sha256").update(e.resourceId).digest("hex")}`,p=JSON.parse(await d("pr","view",String(e.prNumber),"--repo",h,"--json","comments"));if(!S(p)||!Array.isArray(p.comments)||p.comments.length>1e3||p.comments.some(w=>!S(w)||typeof w.body!="string"))return o("git-reconciliation-pr-comments-unverified",!0,!0);p.comments.some(w=>String(w.body).includes(f))||await d("pr","comment",String(e.prNumber),"--repo",h,"--body",`Neocortex reconciliation verified managed ownership and integrated content; requesting closure. [${f}]`);const l=await F({candidate:e,decision:i,git:t,gh:d});if(!l.ok)return o(l.reasonCode,!0,!1);if(l.alreadyAbsent)return{ok:!0,outcome:"already_absent"};await d("pr","close",String(e.prNumber),"--repo",h);const m=await F({candidate:e,decision:i,git:t,gh:d});if(!m.ok)return o(m.reasonCode,!0,!1);if(!m.alreadyAbsent)return o("git-reconciliation-pr-close-unverified",!0,!0)}return{ok:!0,outcome:"applied"}}catch{return o("git-reconciliation-effect-failed",!0,!1)}},async persist(e){try{let i;return await J(r,n=>{const s=T(n,e.terminalReceipt.epicId),u=Array.isArray(s.git_reconciliation_receipts)?s.git_reconciliation_receipts.filter(S):[],h=new Map(u.map(l=>[l.idempotencyKey,l]));for(const l of e.reconciliationReceipts)z(h.get(l.idempotencyKey))||h.set(l.idempotencyKey,l);const f=S(s.terminal_git_receipt)?s.terminal_git_receipt:null,p=we(f,e.terminalReceipt);if(p.kind==="blocked"){i=p.reasonCode;return}s.terminal_git_receipt=p.receipt,s.git_reconciliation_receipts=[...h.values()],s.physicalMergeStatus=e.terminalReceipt.physicalStatus==="pushed"?"merged":e.terminalReceipt.physicalStatus,s.pr_number=e.terminalReceipt.prNumber??s.pr_number??null,s.git_reconciliation_debt=ge(s.git_reconciliation_debt,e.preservedDecisions)}),i?o(i,!1,!1):{ok:!0}}catch{return o("epic-terminal-state-persist-failed",!0,!1)}},async persistReconciliation(e){try{return await J(r,i=>{const n=T(i,e.epicId),s=Array.isArray(n.git_reconciliation_receipts)?n.git_reconciliation_receipts.filter(S):[],u=new Map(s.map(h=>[h.idempotencyKey,h]));for(const h of e.reconciliationReceipts)z(u.get(h.idempotencyKey))||u.set(h.idempotencyKey,h);n.git_reconciliation_receipts=[...u.values()],n.git_reconciliation_debt=ge(n.git_reconciliation_debt,e.preservedDecisions)}),{ok:!0}}catch{return o("git-reconciliation-state-persist-failed",!0,!1)}}}}async function O(r,a){try{const c=await r("remote","get-url","origin"),t=c.match(/^https:\/\/github\.com\/([A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+?)(?:\.git)?\/?$/)?.[1]??c.match(/^git@github\.com:([A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+?)(?:\.git)?$/)?.[1];if(!t)return null;const d=JSON.parse(await a("repo","view",`https://github.com/${t}`,"--json","id,nameWithOwner,url"));return d.nameWithOwner!==t||d.url!==`https://github.com/${t}`||typeof d.id!="string"||!/^[A-Za-z0-9_=-]{1,256}$/.test(d.id)?null:{id:d.id,nameWithOwner:t}}catch{return null}}async function He(r,a,c,t){try{if(r.kind!=="remote_branch"||a.resourceId!==r.resourceId||!r.baseBranch||r.baseBranch===r.branchName||!A(r.branchName)||!A(r.baseBranch)||!/^[a-f0-9]{40}$/.test(r.headSha)||!/^[a-f0-9]{40}$/.test(r.integrationHeadSha))return o("git-reconciliation-candidate-no-longer-safe",!1,!1);const d=await O(c,t);if(!d)return o("git-resource-remote-delete-cas-unavailable",!1,!1);if(!await C(c,r.branchName))return{ok:!0,outcome:"already_absent"};const e=await F({candidate:r,decision:a,git:c,gh:t});if(!e.ok)return o(e.reasonCode,!0,!1);if((await O(c,t))?.id!==d.id)return o("git-resource-provider-repository-raced",!0,!1);const n=I("sha256").update(["managed-remote-delete-v1",d.id,r.branchName,r.headSha,r.baseBranch,r.integrationHeadSha].join("\0")).digest("hex"),s=(f,p,l)=>`{name:${JSON.stringify(`refs/heads/${f}`)},beforeOid:${JSON.stringify(p)},afterOid:${JSON.stringify(l)},force:false}`,u=`mutation{updateRefs(input:{repositoryId:${JSON.stringify(d.id)},clientMutationId:${JSON.stringify(n)},refUpdates:[`+s(r.branchName,r.headSha,"0".repeat(40))+","+s(r.baseBranch,r.integrationHeadSha,r.integrationHeadSha)+"]}){clientMutationId}}",h=JSON.parse(await t("api","graphql","--hostname","github.com","-f",`query=${u}`));return h.errors||h.data?.updateRefs?.clientMutationId!==n?o("git-resource-remote-delete-cas-unverified",!0,!0):await C(c,r.branchName)?o("git-resource-remote-delete-recreated-or-unverified",!0,!1):{ok:!0,outcome:"applied"}}catch{return o("git-resource-remote-delete-cas-rejected",!0,!0)}}async function F(r){const{candidate:a,decision:c,git:t,gh:d}=r;if(!c.managed||!a.providerVerified||a.current||a.origin||a.protected||a.lifecycle==="active"||a.hasExclusiveCommits||a.worktreeActive)return{ok:!1,reasonCode:"git-reconciliation-candidate-no-longer-safe"};const g=await t("symbolic-ref","--quiet","--short","HEAD").catch(()=>""),e=await he(t);if(g===a.branchName||e.has(a.branchName))return{ok:!1,reasonCode:"git-resource-worktree-active"};if(a.kind==="local_branch"){const u=await ue(t,`refs/heads/${a.branchName}`);return u?u!==a.headSha?{ok:!1,reasonCode:"git-resource-expected-oid-raced"}:!a.baseBranch||await N(t,a.baseBranch)!==a.integrationHeadSha||!await R(t,u,a.integrationHeadSha)?{ok:!1,reasonCode:"git-resource-integration-unproven"}:{ok:!0}:{ok:!0,alreadyAbsent:!0}}if(a.kind==="remote_branch"){const u=await C(t,a.branchName);if(!u)return{ok:!0};if(u!==a.headSha)return{ok:!1,reasonCode:"git-resource-expected-oid-raced"};const h=await O(t,d);return h?await Z(d,h.nameWithOwner,a.branchName)!=="unprotected"?{ok:!1,reasonCode:"git-resource-protected"}:!a.baseBranch||await N(t,a.baseBranch)!==a.integrationHeadSha?{ok:!1,reasonCode:"git-resource-integration-unproven"}:(await t("fetch","--quiet","--no-tags","origin",`refs/heads/${a.branchName}`),await t("rev-parse","FETCH_HEAD")!==a.headSha||!await R(t,a.headSha,a.integrationHeadSha)?{ok:!1,reasonCode:"git-resource-integration-unproven"}:{ok:!0}):{ok:!1,reasonCode:"git-resource-provider-repository-unverified"}}if(!a.prNumber||!a.baseBranch)return{ok:!1,reasonCode:"git-reconciliation-pr-proof-missing"};const i=await O(t,d);if(!i)return{ok:!1,reasonCode:"git-resource-provider-repository-unverified"};const n=await K(d,i.nameWithOwner,a.prNumber);if(!n||n.headRefName!==a.branchName||n.baseRefName!==a.baseBranch||n.headRefOid!==a.headSha)return{ok:!1,reasonCode:"git-resource-expected-oid-raced"};if(n.state==="CLOSED"||n.state==="MERGED")return{ok:!0,alreadyAbsent:!0};if(await Z(d,i.nameWithOwner,a.branchName)!=="unprotected")return{ok:!1,reasonCode:"git-resource-protected"};const s=await C(t,a.branchName);return!n||n.state!=="OPEN"||n.headRefName!==a.branchName||n.baseRefName!==a.baseBranch||n.headRefOid!==a.headSha||s!==a.headSha?{ok:!1,reasonCode:"git-resource-expected-oid-raced"}:(await t("fetch","--quiet","--no-tags","origin",`refs/heads/${a.branchName}`),await t("rev-parse","FETCH_HEAD")!==a.headSha||await N(t,a.baseBranch)!==a.integrationHeadSha||!await R(t,a.headSha,a.integrationHeadSha)?{ok:!1,reasonCode:"git-resource-integration-unproven"}:{ok:!0})}async function oe(r){const a=[],c=await he(r.git),t=be(r.state),d=await O(r.git,r.gh),g=l=>{const m=l.split(`
|
|
2
|
-
`).filter(Boolean).map(
|
|
3
|
-
`).filter(s=>/^[a-f0-9]{40,64}$/i.test(s));if(a.length!==1)return null;const c=a[0],t=(await
|
|
4
|
-
`),
|
|
5
|
-
`).map(s=>s.trim()));for(const s of[`Neocortex-Epic: ${
|
|
6
|
-
`).filter(Boolean).map(
|
|
7
|
-
`))t.startsWith("branch refs/heads/")&&a.add(t.slice(18));return a}function o(
|
|
8
|
-
`).filter(Boolean).some(c=>!c.startsWith("?? ")||!a.has(c.slice(3)))}function
|
|
1
|
+
import{execFile as ye}from"node:child_process";import{createHash as O}from"node:crypto";import{readFile as ae}from"node:fs/promises";import{join as we}from"node:path";import{promisify as be}from"node:util";import{classifyGitReconciliationCandidate as ie,reconcileTerminalReceipt as Se}from"../../../../shared/dist/yoloop/epic-terminal-git.js";import{collectGitResourceOwnership as _e,GIT_INVENTORY_PR_LIMIT as ne,GIT_INVENTORY_REF_LIMIT as Z}from"./epic-git-reconciliation-inventory.js";import{executeEpicMergeCandidateQa as oe,verifiedEpicMergeCandidateReceipt as W}from"./epic-merge-candidate-qa.js";import{applyGitLifecycleRuntimeOverlay as xe,ensureRuntimeEpic as z,executeP205TransientAuthority as ve,P205_COMPLETE_COMMAND as Q,projectP205TransientExecutionEvidence as Re,verifyAcceptedGitLifecycleStoryCheckpoint as ce,mutateGitLifecycleRuntime as J,readGitLifecycleRuntime as He}from"../../git/git-lifecycle-store.js";const ke=be(ye);function ze(e,a={}){const c=we(e,".neocortex","state.json"),t=a.gitRunner??((...r)=>ue("git",r,e)),u=a.providerRunner??((...r)=>ue("gh",r,e)),m=new Map;return{async prepareLifecycleEvidence(r){try{try{await ae(c,"utf8")}catch(k){if(Oe(k,"ENOENT"))return{ok:!0,originBindingCaptured:!1,aggregateQaRecorded:!1};throw k}const n=(await D(e,c)).epics?.[r.epicId];if(!n)return o("epic-state-missing",!1,!1);if(S(n.branch_mode)!=="epic")return{ok:!0,originBindingCaptured:!1,aggregateQaRecorded:!1};const s=S(n.execution_branch),d=S(n.origin_branch);if(!s||!E(s))return o("epic-execution-branch-missing",!1,!1);let l=await t("symbolic-ref","--quiet","--short","HEAD").catch(()=>""),f=await t("rev-parse","HEAD"),h=_(n.origin_binding)?n.origin_binding:null,b=!1;if(h){if(h.executionBranch!==s||h.originBranch!==d||l!==s&&l!==h.originBranch||!await j(t,h.originHeadSha)||!await v(t,h.originHeadSha,f))return o("epic-origin-binding-physical-drift",!1,!1)}else{if(!l||l===s||!E(l))return o("epic-origin-binding-capture-window-missed",!1,!1);if(d&&d!==l)return o("epic-origin-binding-recorded-origin-drift",!1,!1);h={schemaVersion:1,originBranch:l,originHeadSha:f,executionBranch:s,capturedFromCurrentCheckout:!0,capturedAt:r.nowIso},await J(e,k=>{const y=z(k,r.epicId);if(y.origin_binding&&JSON.stringify(y.origin_binding)!==JSON.stringify(h))throw new Error("origin-binding-cas-conflict");y.origin_branch=l,y.origin_binding=h}),b=!0}if(l!==s){const k=await le(t,`refs/heads/${s}`);try{if(k){if(!await v(t,h.originHeadSha,k))return o("epic-execution-branch-origin-drift",!1,!1);await t("switch",s)}else await t("switch","-c",s,h.originHeadSha)}catch{const y=await t("status","--porcelain","--untracked-files=normal").catch(()=>"");return o(y?"epic-execution-branch-switch-blocked-by-user-changes":"epic-execution-branch-switch-failed",!0,!1)}if(l=await t("symbolic-ref","--quiet","--short","HEAD"),f=await t("rev-parse","HEAD"),l!==s)return o("epic-execution-branch-switch-unverified",!0,!1)}const g=await D(e,c,!0),p=g.epics?.[r.epicId];if(!p)return o("epic-state-missing",!1,!1);const x=Y(p),w=await Promise.all(x.map(k=>ce(e,g.stories?.[k]?.checkpoint,k,r.epicId,s).then(y=>y&&ge(g.stories?.[k]?.checkpoint))));if(!(x.length>0&&w.every(Boolean)))return{ok:!0,originBindingCaptured:b,aggregateQaRecorded:!1};if(l!==s)return o("epic-aggregate-qa-execution-checkout-required",!0,!1);const H=S(p.graph_revision);if(!/^[a-f0-9]{64}$/i.test(H))return o("epic-aggregate-qa-graph-required",!1,!1);if(await pe(t))return o("epic-aggregate-qa-checkout-dirty",!0,!1);const G=await ve(e,{headSha:f,graphRevision:H,nowIso:r.nowIso}),B=Re(G);if(!B)return o("epic-aggregate-qa-authority-invalid",!1,!1);if(await a.aggregateQaRunner?.({projectRoot:e,gateIds:B.gateIds}),await t("rev-parse","HEAD")!==f||await pe(t))return o("epic-aggregate-qa-head-raced",!0,!1);const M=`local-yolo-tdd:${O("sha256").update(["epic-aggregate-p205-execution-v2",Q,f,H,B.manifestDigest,B.gateIds.join(","),B.resultDigest].join("\0")).digest("hex")}`,T={schemaVersion:1,evidenceVersion:"p205-execution-v2",producer:"local-yolo-tdd",contract:"P205-CI-02",command:Q,verdict:"passed",executionHeadSha:f,graphRevision:H,manifestDigest:B.manifestDigest,resultDigest:B.resultDigest,checkedAt:B.executedAt,receiptRef:M,gateIds:B.gateIds};return await J(e,k=>{const y=z(k,r.epicId);y.aggregate_qa_receipt=T}),{ok:!0,originBindingCaptured:b,aggregateQaRecorded:!0,p205Authority:G}}catch{return o("epic-lifecycle-evidence-production-failed",!0,!1)}},async inspect(r){try{const i=await D(e,c,!0),n=i.epics?.[r.epicId];if(!n)return o("epic-state-missing",!1,!1);if(S(n.execution_branch)!==r.executionBranch||S(n.origin_branch)!==r.originBranch)return o("epic-branch-boundary-drift",!1,!1);const s=await t("rev-parse",`refs/heads/${r.executionBranch}`),d=await A(t,r.originBranch);await P(t,r.originBranch,d);const l=await X(e,i,n,r.epicId,r.executionBranch);let f=_(n.terminal_git_receipt)?n.terminal_git_receipt:null;f?.physicalStatus==="pushed"&&(f.pushedSha&&await v(t,f.executionHeadSha,f.pushedSha)&&await v(t,f.pushedSha,d)||(f=null)),f||(f=await Ae({directive:r,executionHeadSha:s,originHeadSha:d,git:t}));const h=new Set((Array.isArray(n.git_reconciliation_receipts)?n.git_reconciliation_receipts:[]).filter(_).filter(K).map(R=>S(R.resourceId)).filter(Boolean)),b=await t("symbolic-ref","--quiet","--short","HEAD").catch(()=>""),g=r.reconcile.enabled?await se({state:i,epic:n,directive:r,currentBranch:b,originHeadSha:d,receiptIds:h,git:t,gh:u}):[],p=_(n.origin_binding)?n.origin_binding:null,x=p&&p.originBranch===r.originBranch&&p.executionBranch===r.executionBranch&&await j(t,p.originHeadSha)&&await v(t,p.originHeadSha,s)?p:null;return{ok:!0,snapshot:{executionHeadSha:s,originHeadSha:d,...S(n.graph_revision)?{graphRevision:S(n.graph_revision)}:{},writerLeaseActive:re(n.branch_lease),acceptedCheckpointIds:l,existingReceipt:f,aggregateQaReceipt:_(n.aggregate_qa_receipt)?n.aggregate_qa_receipt:null,originBinding:x,reconciliationCandidates:g}}}catch(i){return o(de(i)??"epic-terminal-inspection-failed",!0,!1)}},async prepareMergeObject(r){let i;try{const n=await t("merge-tree","--write-tree",r.originHeadSha,r.executionHeadSha);i=await t("commit-tree",n,"-p",r.originHeadSha,"-p",r.executionHeadSha,"-m",r.message)}catch{return o("epic-terminal-merge-conflict",!1,!1)}try{const n=await oe(e,{mergeSha:i,originHeadSha:r.originHeadSha,executionHeadSha:r.executionHeadSha,graphRevision:r.graphRevision},r.nowIso);return m.set(i,n.candidateAuthority),{ok:!0,mergeSha:i,...n}}catch{return o("epic-merge-candidate-qa-failed",!0,!1)}},async inspectReconciliation(r){try{const i=await D(e,c,!0),n=i.epics?.[r.epicId];if(!n)return o("epic-state-missing",!1,!1);if(S(n.execution_branch)!==r.executionBranch||S(n.origin_branch)!==r.originBranch)return o("epic-branch-boundary-drift",!1,!1);const s=await A(t,r.originBranch);await P(t,r.originBranch,s);const d=new Set((Array.isArray(n.git_reconciliation_receipts)?n.git_reconciliation_receipts:[]).filter(_).filter(K).map(h=>S(h.resourceId)).filter(Boolean)),l=await t("symbolic-ref","--quiet","--short","HEAD").catch(()=>"");return{ok:!0,candidates:await se({state:i,epic:n,directive:r,currentBranch:l,originHeadSha:s,receiptIds:d,git:t,gh:u})}}catch(i){return o(de(i)??"git-reconciliation-inspection-failed",!0,!1)}},async pushMergeObject(r){try{if(!W(r.candidateAuthority,{mergeSha:r.mergeSha,originHeadSha:r.expectedOriginSha,executionHeadSha:r.executionHeadSha,graphRevision:r.graphRevision}))return o("epic-merge-candidate-authority-required",!1,!1);const n=await D(e,c,!0),s=n.epics?.[r.epicId];if(!s||s.execution_branch!==r.executionBranch||s.origin_branch!==r.originBranch||s.graph_revision!==r.graphRevision||re(s.branch_lease)||await t("rev-parse",`refs/heads/${r.executionBranch}`)!==r.executionHeadSha)return o("epic-merge-candidate-authority-raced",!0,!1);const d=await X(e,n,s,r.epicId,r.executionBranch);if(!ee(d,r.storyCheckpointIds)||!ee(Y(s),r.storyCheckpointIds))return o("epic-checkpoint-set-revalidation-drift",!0,!1);const l=_(s.aggregate_qa_receipt)?s.aggregate_qa_receipt:null;if(!he(l,r.executionHeadSha,r.graphRevision,r.nowIso)||l.receiptRef!==r.aggregateQaReceipt.receiptRef)return o("epic-terminal-aggregate-qa-revalidation-drift",!0,!1);if(await A(t,r.originBranch)!==r.expectedOriginSha)return o("epic-origin-head-raced",!0,!1);await t("push","--porcelain","origin",`${r.mergeSha}:refs/heads/${r.originBranch}`);const h=await A(t,r.originBranch);return h!==r.mergeSha?o("epic-terminal-push-unverified",!0,!1):{ok:!0,pushedSha:h}}catch(i){return te(i)?o("epic-terminal-remote-policy-denied",!0,!0):o("epic-terminal-push-failed",!0,!1)}},async ensureSingleEpicPullRequest(r){try{const i=W(r.candidateAuthority,{mergeSha:r.expectedPullRequestHeadSha,originHeadSha:r.candidateQa.originHeadSha,executionHeadSha:r.expectedStoryHeadSha,graphRevision:r.candidateQa.graphRevision});if(!i||i!==r.candidateQa||!await v(t,r.candidateQa.originHeadSha,r.expectedPullRequestHeadSha)||!await v(t,r.expectedStoryHeadSha,r.expectedPullRequestHeadSha))return o("epic-merge-candidate-authority-required",!1,!1);const n=await I(t,u);if(!n)return o("git-resource-provider-repository-unverified",!0,!0);const s=`https://github.com/${n.nameWithOwner}`,d=await C(t,r.executionBranch);if(d&&d!==r.expectedPullRequestHeadSha){if(await P(t,r.executionBranch,d),!await v(t,d,r.expectedPullRequestHeadSha))return o("epic-terminal-execution-remote-head-drift",!0,!0);await t("push","--porcelain","origin",`${r.expectedPullRequestHeadSha}:refs/heads/${r.executionBranch}`)}if(d||await t("push","--porcelain","origin",`${r.expectedPullRequestHeadSha}:refs/heads/${r.executionBranch}`),await A(t,r.executionBranch)!==r.expectedPullRequestHeadSha)return o("epic-terminal-execution-publish-unverified",!0,!0);const l=await u("pr","list","--repo",s,"--state","all","--head",r.executionBranch,"--base",r.originBranch,"--json","number,state,headRefName,headRefOid,baseRefName,mergeCommit","--limit","100"),f=JSON.parse(l),h=f.filter(w=>w.headRefName===r.executionBranch&&w.baseRefName===r.originBranch&&w.headRefOid===r.expectedPullRequestHeadSha&&(w.state==="OPEN"||w.state==="MERGED"));if(h.length>1)return o("epic-terminal-duplicate-prs",!1,!0);if(typeof h[0]?.number=="number")return{ok:!0,prNumber:h[0].number};if(f.some(w=>w.state==="OPEN"))return o("epic-terminal-stale-pr",!1,!0);const g=(await u("pr","create","--repo",s,"--head",r.executionBranch,"--base",r.originBranch,"--title",`[${r.epicId}] Terminal epic integration`,"--body",`Autonomous terminal integration receipt: ${r.idempotencyKey}`)).match(/\/(\d+)(?:\s*)$/);if(!g)return o("epic-terminal-pr-number-unverified",!0,!0);const p=Number(g[1]),x=await L(u,n.nameWithOwner,p);return!x||x.state!=="OPEN"||x.headRefName!==r.executionBranch||x.baseRefName!==r.originBranch||x.headRefOid!==r.expectedPullRequestHeadSha?o("epic-terminal-pr-create-unverified",!0,!0):{ok:!0,prNumber:p}}catch(i){return o("epic-terminal-pr-create-denied",!0,te(i))}},async mergeEpicPullRequest(r){try{const i=await I(t,u);if(!i)return o("git-resource-provider-repository-unverified",!0,!0);const n=r.candidateQa&&W(r.candidateAuthority,{mergeSha:r.expectedPullRequestHeadSha,graphRevision:r.candidateQa.graphRevision,originHeadSha:r.expectedOriginHeadSha,executionHeadSha:r.expectedStoryHeadSha});if(!n||n!==r.candidateQa)return o("epic-merge-candidate-authority-required",!1,!1);const s=await L(u,i.nameWithOwner,r.prNumber),d=await C(t,r.executionBranch),l=await A(t,r.originBranch);if(!s||s.headRefName!==r.executionBranch||s.baseRefName!==r.originBranch||s.headRefOid!==r.expectedPullRequestHeadSha||d&&d!==r.expectedPullRequestHeadSha||!await v(t,r.expectedOriginHeadSha,r.expectedPullRequestHeadSha)||!await v(t,r.expectedStoryHeadSha,r.expectedPullRequestHeadSha))return o("epic-terminal-pr-revalidation-drift",!0,!0);if(s.state==="OPEN"&&l!==r.expectedOriginHeadSha)return o("epic-terminal-pr-origin-raced",!0,!0);let f;if(s.state==="OPEN"){const w=await Ee(u,i.nameWithOwner,r.originBranch,r.expectedPullRequestHeadSha,r.expectedOriginHeadSha,r.nowIso);if(!w.ok)return o(w.reasonCode,!0,!0);if(f=w.receipt,await A(t,r.originBranch)!==r.expectedOriginHeadSha)return o("epic-terminal-pr-origin-raced",!0,!0);await u("pr","merge",String(r.prNumber),"--repo",`https://github.com/${i.nameWithOwner}`,"--merge","--match-head-commit",r.expectedPullRequestHeadSha)}else{if(s.state!=="MERGED")return o("epic-terminal-pr-not-open-or-merged",!1,!0);f={schemaVersion:1,provider:"github",mode:"recovered_exact_tree",source:"physical_recovery",prHeadSha:r.expectedPullRequestHeadSha,originHeadSha:r.expectedOriginHeadSha,checkedAt:r.nowIso,publicSafe:!0}}const h=await L(u,i.nameWithOwner,r.prNumber),b=h?.mergeCommit?.oid;if(h?.state!=="MERGED"||!b)return o("epic-terminal-pr-merge-unverified",!0,!0);const g=await A(t,r.originBranch);if(await P(t,r.originBranch,g),!await v(t,r.expectedStoryHeadSha,g)||!await v(t,r.expectedPullRequestHeadSha,g)||!await v(t,b,g))return o("epic-terminal-pr-merge-ancestry-unproven",!0,!0);const[p,x]=await Promise.all([t("rev-parse",`${r.expectedPullRequestHeadSha}^{tree}`),t("rev-parse",`${b}^{tree}`)]);return p!==r.candidateQa.treeSha||x!==p?o("epic-terminal-pr-merged-tree-mismatch",!1,!0):{ok:!0,mergeSha:r.expectedPullRequestHeadSha,pushedSha:b,providerGuard:f}}catch(i){return o("epic-terminal-pr-merge-denied",!0,te(i))}},async verifyTerminalReceipt(r,i,n){try{if(i.physicalStatus!=="pushed"||!i.pushedSha||!i.mergeSha)return o("epic-terminal-physical-revalidation-required",!0,!1);const s=await D(e,c,!0),d=s.epics?.[r.epicId];if(!d||S(d.execution_branch)!==r.executionBranch||S(d.origin_branch)!==r.originBranch||S(d.graph_revision)!==r.graphRevision)return o("epic-terminal-state-revalidation-drift",!0,!1);const l=await t("rev-parse",`refs/heads/${r.executionBranch}`);if(l!==r.expectedExecutionHeadSha||i.executionHeadSha!==l)return o("epic-terminal-execution-revalidation-drift",!0,!1);const f=_(d.origin_binding)?d.origin_binding:null;if(!f||f.originBranch!==r.originBranch||f.executionBranch!==r.executionBranch||!await j(t,f.originHeadSha)||!await v(t,f.originHeadSha,l))return o("epic-origin-binding-revalidation-drift",!0,!1);const h=await X(e,s,d,r.epicId,r.executionBranch);if(re(d.branch_lease)||!ee(h,r.storyCheckpointIds))return o("epic-checkpoint-set-revalidation-drift",!0,!1);const b=_(d.aggregate_qa_receipt)?d.aggregate_qa_receipt:null;if(!he(b,l,r.graphRevision,n)||b?.receiptRef!==r.localGates.aggregateQaReceipt.receiptRef)return o("epic-terminal-aggregate-qa-revalidation-drift",!0,!1);const g=await A(t,r.originBranch);if(await P(t,r.originBranch,g),!await v(t,l,g)||!await v(t,i.mergeSha,g)||!await v(t,i.pushedSha,g))return o("epic-terminal-physical-ancestry-unproven",!0,!1);const p={originHeadSha:i.baseSha,executionHeadSha:l,mergeSha:i.mergeSha,graphRevision:r.graphRevision};let x=m.get(i.mergeSha),w=W(x,p);if(!w){const H=await oe(e,p,n);x=H.candidateAuthority,w=H.candidateQa,m.set(i.mergeSha,x)}if(i.candidateQa&&(i.candidateQa.originHeadSha!==w.originHeadSha||i.candidateQa.executionHeadSha!==w.executionHeadSha||i.candidateQa.graphRevision!==w.graphRevision||i.candidateQa.treeSha!==w.treeSha))return o("epic-merge-candidate-receipt-drift",!0,!1);let R=i.providerGuard;if(i.strategy==="single_pr"){if(!i.prNumber)return o("epic-terminal-pr-merge-unverified",!0,!1);if(await t("rev-parse",`${i.pushedSha}^{tree}`)!==w.treeSha)return o("epic-terminal-pr-merged-tree-mismatch",!1,!1);if(R&&!Ie(R,i))return o("epic-terminal-pr-base-guard-unverifiable",!1,!1);R??={schemaVersion:1,provider:"github",mode:"recovered_exact_tree",source:"physical_recovery",prHeadSha:i.mergeSha,originHeadSha:i.baseSha,checkedAt:n,publicSafe:!0}}else if(R)return o("epic-terminal-pr-base-guard-unverifiable",!1,!1);return{ok:!0,candidateQa:w,candidateAuthority:x,...R?{providerGuard:R}:{}}}catch{return o("epic-terminal-physical-revalidation-failed",!0,!1)}},async applyReconciliation(r,i,n){if(i.action==="preserve")return o("git-reconciliation-preserve-not-actionable",!1,!1);if(n)return{ok:!0,outcome:"dry_run"};if(i.action==="no_op")return{ok:!0,outcome:"already_absent"};if(i.action==="delete_remote_branch")return Be(r,i,t,u);try{const s=await F({candidate:r,decision:i,git:t,gh:u});if(!s.ok)return o(s.reasonCode,!0,!1);if(s.alreadyAbsent)return{ok:!0,outcome:"already_absent"};if(i.action==="delete_local_branch")await t("update-ref","-d",`refs/heads/${r.branchName}`,r.headSha);else if(i.action==="close_pull_request"){if(!r.prNumber)return o("git-reconciliation-pr-number-missing",!1,!1);const d=await I(t,u);if(!d)return o("git-resource-provider-repository-unverified",!0,!0);const l=`https://github.com/${d.nameWithOwner}`,f=`neocortex-reconciliation:${O("sha256").update(r.resourceId).digest("hex")}`,h=JSON.parse(await u("pr","view",String(r.prNumber),"--repo",l,"--json","comments"));if(!_(h)||!Array.isArray(h.comments)||h.comments.length>1e3||h.comments.some(p=>!_(p)||typeof p.body!="string"))return o("git-reconciliation-pr-comments-unverified",!0,!0);h.comments.some(p=>String(p.body).includes(f))||await u("pr","comment",String(r.prNumber),"--repo",l,"--body",`Neocortex reconciliation verified managed ownership and integrated content; requesting closure. [${f}]`);const b=await F({candidate:r,decision:i,git:t,gh:u});if(!b.ok)return o(b.reasonCode,!0,!1);if(b.alreadyAbsent)return{ok:!0,outcome:"already_absent"};await u("pr","close",String(r.prNumber),"--repo",l);const g=await F({candidate:r,decision:i,git:t,gh:u});if(!g.ok)return o(g.reasonCode,!0,!1);if(!g.alreadyAbsent)return o("git-reconciliation-pr-close-unverified",!0,!0)}return{ok:!0,outcome:"applied"}}catch{return o("git-reconciliation-effect-failed",!0,!1)}},async persist(r){try{let i;return await J(e,n=>{const s=z(n,r.terminalReceipt.epicId),d=Array.isArray(s.git_reconciliation_receipts)?s.git_reconciliation_receipts.filter(_):[],l=new Map(d.map(b=>[b.idempotencyKey,b]));for(const b of r.reconciliationReceipts)K(l.get(b.idempotencyKey))||l.set(b.idempotencyKey,b);const f=_(s.terminal_git_receipt)?s.terminal_git_receipt:null,h=Se(f,r.terminalReceipt);if(h.kind==="blocked"){i=h.reasonCode;return}s.terminal_git_receipt=h.receipt,s.git_reconciliation_receipts=[...l.values()],s.physicalMergeStatus=r.terminalReceipt.physicalStatus==="pushed"?"merged":r.terminalReceipt.physicalStatus,s.pr_number=r.terminalReceipt.prNumber??s.pr_number??null,s.git_reconciliation_debt=me(s.git_reconciliation_debt,r.preservedDecisions)}),i?o(i,!1,!1):{ok:!0}}catch{return o("epic-terminal-state-persist-failed",!0,!1)}},async persistReconciliation(r){try{return await J(e,i=>{const n=z(i,r.epicId),s=Array.isArray(n.git_reconciliation_receipts)?n.git_reconciliation_receipts.filter(_):[],d=new Map(s.map(l=>[l.idempotencyKey,l]));for(const l of r.reconciliationReceipts)K(d.get(l.idempotencyKey))||d.set(l.idempotencyKey,l);n.git_reconciliation_receipts=[...d.values()],n.git_reconciliation_debt=me(n.git_reconciliation_debt,r.preservedDecisions)}),{ok:!0}}catch{return o("git-reconciliation-state-persist-failed",!0,!1)}}}}async function I(e,a){try{const c=await e("remote","get-url","origin"),t=c.match(/^https:\/\/github\.com\/([A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+?)(?:\.git)?\/?$/)?.[1]??c.match(/^git@github\.com:([A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+?)(?:\.git)?$/)?.[1];if(!t)return null;const u=JSON.parse(await a("repo","view",`https://github.com/${t}`,"--json","id,nameWithOwner,url"));return u.nameWithOwner!==t||u.url!==`https://github.com/${t}`||typeof u.id!="string"||!/^[A-Za-z0-9_=-]{1,256}$/.test(u.id)?null:{id:u.id,nameWithOwner:t}}catch{return null}}async function Be(e,a,c,t){try{if(e.kind!=="remote_branch"||a.resourceId!==e.resourceId||!e.baseBranch||e.baseBranch===e.branchName||!E(e.branchName)||!E(e.baseBranch)||!/^[a-f0-9]{40}$/.test(e.headSha)||!/^[a-f0-9]{40}$/.test(e.integrationHeadSha))return o("git-reconciliation-candidate-no-longer-safe",!1,!1);const u=await I(c,t);if(!u)return o("git-resource-remote-delete-cas-unavailable",!1,!1);if(!await C(c,e.branchName))return{ok:!0,outcome:"already_absent"};const r=await F({candidate:e,decision:a,git:c,gh:t});if(!r.ok)return o(r.reasonCode,!0,!1);if((await I(c,t))?.id!==u.id)return o("git-resource-provider-repository-raced",!0,!1);const n=O("sha256").update(["managed-remote-delete-v1",u.id,e.branchName,e.headSha,e.baseBranch,e.integrationHeadSha].join("\0")).digest("hex"),s=(f,h,b)=>`{name:${JSON.stringify(`refs/heads/${f}`)},beforeOid:${JSON.stringify(h)},afterOid:${JSON.stringify(b)},force:false}`,d=`mutation{updateRefs(input:{repositoryId:${JSON.stringify(u.id)},clientMutationId:${JSON.stringify(n)},refUpdates:[`+s(e.branchName,e.headSha,"0".repeat(40))+","+s(e.baseBranch,e.integrationHeadSha,e.integrationHeadSha)+"]}){clientMutationId}}",l=JSON.parse(await t("api","graphql","--hostname","github.com","-f",`query=${d}`));return l.errors||l.data?.updateRefs?.clientMutationId!==n?o("git-resource-remote-delete-cas-unverified",!0,!0):await C(c,e.branchName)?o("git-resource-remote-delete-recreated-or-unverified",!0,!1):{ok:!0,outcome:"applied"}}catch{return o("git-resource-remote-delete-cas-rejected",!0,!0)}}async function F(e){const{candidate:a,decision:c,git:t,gh:u}=e;if(!c.managed||!a.providerVerified||a.current||a.origin||a.protected||a.lifecycle==="active"||a.hasExclusiveCommits||a.worktreeActive)return{ok:!1,reasonCode:"git-reconciliation-candidate-no-longer-safe"};const m=await t("symbolic-ref","--quiet","--short","HEAD").catch(()=>""),r=await fe(t);if(m===a.branchName||r.has(a.branchName))return{ok:!1,reasonCode:"git-resource-worktree-active"};if(a.kind==="local_branch"){const d=await le(t,`refs/heads/${a.branchName}`);return d?d!==a.headSha?{ok:!1,reasonCode:"git-resource-expected-oid-raced"}:!a.baseBranch||await A(t,a.baseBranch)!==a.integrationHeadSha||!await v(t,d,a.integrationHeadSha)?{ok:!1,reasonCode:"git-resource-integration-unproven"}:{ok:!0}:{ok:!0,alreadyAbsent:!0}}if(a.kind==="remote_branch"){const d=await C(t,a.branchName);if(!d)return{ok:!0};if(d!==a.headSha)return{ok:!1,reasonCode:"git-resource-expected-oid-raced"};const l=await I(t,u);return l?await U(u,l.nameWithOwner,a.branchName)!=="unprotected"?{ok:!1,reasonCode:"git-resource-protected"}:!a.baseBranch||await A(t,a.baseBranch)!==a.integrationHeadSha?{ok:!1,reasonCode:"git-resource-integration-unproven"}:(await t("fetch","--quiet","--no-tags","origin",`refs/heads/${a.branchName}`),await t("rev-parse","FETCH_HEAD")!==a.headSha||!await v(t,a.headSha,a.integrationHeadSha)?{ok:!1,reasonCode:"git-resource-integration-unproven"}:{ok:!0}):{ok:!1,reasonCode:"git-resource-provider-repository-unverified"}}if(!a.prNumber||!a.baseBranch)return{ok:!1,reasonCode:"git-reconciliation-pr-proof-missing"};const i=await I(t,u);if(!i)return{ok:!1,reasonCode:"git-resource-provider-repository-unverified"};const n=await L(u,i.nameWithOwner,a.prNumber);if(!n||n.headRefName!==a.branchName||n.baseRefName!==a.baseBranch||n.headRefOid!==a.headSha)return{ok:!1,reasonCode:"git-resource-expected-oid-raced"};if(n.state==="CLOSED"||n.state==="MERGED")return{ok:!0,alreadyAbsent:!0};if(await U(u,i.nameWithOwner,a.branchName)!=="unprotected")return{ok:!1,reasonCode:"git-resource-protected"};const s=await C(t,a.branchName);return!n||n.state!=="OPEN"||n.headRefName!==a.branchName||n.baseRefName!==a.baseBranch||n.headRefOid!==a.headSha||s!==a.headSha?{ok:!1,reasonCode:"git-resource-expected-oid-raced"}:(await t("fetch","--quiet","--no-tags","origin",`refs/heads/${a.branchName}`),await t("rev-parse","FETCH_HEAD")!==a.headSha||await A(t,a.baseBranch)!==a.integrationHeadSha||!await v(t,a.headSha,a.integrationHeadSha)?{ok:!1,reasonCode:"git-resource-integration-unproven"}:{ok:!0})}async function se(e){const a=[],c=await fe(e.git),t=_e(e.state),u=await I(e.git,e.gh),m=g=>{const p=g.split(`
|
|
2
|
+
`).filter(Boolean).map(w=>w.split(/\s+/));if(p.length>Z)throw new Error("git-reconciliation-ref-inventory-limit");const x=new Map;for(const[w,R]of p){const H=R?.replace(/^refs\/heads\//,"");if(!H||!E(H)||!/^[a-f0-9]{40,64}$/.test(w??"")||x.has(H))throw new Error("git-reconciliation-ref-inventory-invalid");x.set(H,w)}return x},r=m(await e.git("for-each-ref",`--count=${Z+1}`,"--format=%(objectname) %(refname)","refs/heads")),i=m(await e.git("ls-remote","--heads","origin")),n=new Set([...t.claims.keys()].filter(E)),s=u?await $e(e.gh,u.nameWithOwner,n):[],d=new Map([[e.directive.originBranch,e.originHeadSha]]),l=async g=>{if(d.has(g))return d.get(g);const p=i.get(g);return p?(await P(e.git,g,p),d.set(g,p),p):""},f=new Map,h=async(g,p)=>{const x=`${g}:${p}`;return f.has(x)||f.set(x,!!(g&&p)&&await v(e.git,g,p)),f.get(x)},b=new Set([...r.keys(),...i.keys(),...t.claims.keys()]);if(b.size>Z)throw new Error("git-reconciliation-ref-inventory-limit");for(const g of[...b].sort()){if(!E(g))continue;const p=t.claims.get(g)??[],x=new Set(p.map(y=>`${y.epicId}:${y.origin}`)),w=x.size===1?p[0].origin:e.directive.originBranch,R=await l(w),H=p.length===0||p.some(y=>y.active),G=r.get(g),B=i.get(g);B&&await P(e.git,g,B);const M=u&&B?await U(e.gh,u.nameWithOwner,g):"unknown",T=async y=>{if(x.size!==1)return[];const $=new Set;for(const q of p)for(const N of q.proofs)N.head===y&&(N.mergeSha&&(!N.pushedSha||!await h(y,N.mergeSha)||!await h(N.mergeSha,N.pushedSha)||!await h(N.pushedSha,R))||N.evidence.forEach(V=>$.add(V)));return[...$]},k={schemaVersion:1,branchName:g,baseBranch:w,integrationHeadSha:R,lifecycle:H?"active":"integrated",current:g===e.currentBranch,origin:t.origins.has(g)||g===e.directive.originBranch,worktreeActive:c.has(g),contentEquivalent:!1,publicSafe:!0};for(const[y,$]of[["local_branch",G],["remote_branch",B]]){if(!$)continue;const q=`${y}:${O("sha256").update(g).digest("hex")}:${$}`,N=await h($,R);a.push({...k,resourceId:q,kind:y,headSha:$,ownershipEvidence:await T($),protected:!!B&&M!=="unprotected",providerVerified:y==="local_branch"?!0:!!u&&M!=="unknown",...y==="remote_branch"&&u?{remoteDeletionCas:"github-update-refs-v1"}:{},commitsReachable:N,hasExclusiveCommits:!N,previouslyReconciled:e.receiptIds.has(q)})}for(const y of s.filter($=>$.headRefName===g)){const $=y.baseRefName===w&&y.sameRepository,q=await T(y.headRefOid);$&&q.includes("managed_metadata")&&q.push("pr_head_base_match");const N=`pr:${y.number}:${y.headRefOid}:${O("sha256").update(y.baseRefName).digest("hex")}`,V=await h(y.headRefOid,R);a.push({...k,resourceId:N,kind:"pull_request",prNumber:y.number,headSha:y.headRefOid,lifecycle:y.state!=="OPEN"?"already_absent":H?"active":"superseded",ownershipEvidence:q,providerVerified:$,protected:M!=="unprotected",commitsReachable:V,hasExclusiveCommits:!V,previouslyReconciled:e.receiptIds.has(N)})}}return Ne(e.epic,a),a}function Ne(e,a){const c=new Set((Array.isArray(e.git_reconciliation_receipts)?e.git_reconciliation_receipts:[]).filter(_).map(m=>S(m.idempotencyKey))),t=new Set((Array.isArray(e.git_reconciliation_debt)?e.git_reconciliation_debt:[]).filter(_).map(m=>S(m.resourceId))),u=new Set(a.filter(m=>m.kind==="pull_request"&&ie(m).action==="close_pull_request").map(m=>m.branchName));for(const m of a){const r=ie(m);r.action==="preserve"?t.add(m.resourceId):(c.add(`reconciliation:v2:${O("sha256").update([m.resourceId,r.action].join("\0")).digest("hex")}`),m.kind!=="pull_request"&&u.has(m.branchName)&&t.add(m.resourceId))}if(c.size>1024)throw new Error("git-reconciliation-receipt-capacity");if(t.size>1024)throw new Error("git-reconciliation-debt-capacity")}function de(e){const a=new Set(["git-reconciliation-state-inventory-limit","git-reconciliation-ref-inventory-limit","git-reconciliation-ref-inventory-invalid","git-reconciliation-pr-inventory-limit","git-reconciliation-pr-inventory-invalid","git-reconciliation-receipt-capacity","git-reconciliation-debt-capacity"]);return e instanceof Error&&a.has(e.message)?e.message:void 0}async function $e(e,a,c){const t=a.split("/")[0];if(!t||!/^[A-Za-z0-9_.-]+$/.test(t))throw new Error("git-reconciliation-pr-inventory-invalid");const u=[],m=new Set,r=100,i=Math.floor(ne/r)+1;for(const n of[...c].sort()){if(!E(n))throw new Error("git-reconciliation-pr-inventory-invalid");const s=encodeURIComponent(`${t}:${n}`);for(let d=1;d<=i;d+=1){const l=`repos/${a}/pulls?state=all&head=${s}&per_page=${r}&page=${d}`,f=JSON.parse(await e("api",l,"--hostname","github.com"));if(!Array.isArray(f)||f.length>r)throw new Error("git-reconciliation-pr-inventory-limit");for(const h of f){if(!_(h)||!Number.isSafeInteger(h.number)||Number(h.number)<=0||m.has(Number(h.number))||!_(h.head)||!_(h.base)||S(h.head.ref)!==n||!E(S(h.head.ref))||!E(S(h.base.ref))||!/^[a-f0-9]{40,64}$/.test(S(h.head.sha))||!["open","closed"].includes(S(h.state)))throw new Error("git-reconciliation-pr-inventory-invalid");if(m.add(Number(h.number)),u.push({number:Number(h.number),state:S(h.state).toUpperCase(),headRefName:S(h.head.ref),headRefOid:S(h.head.sha),baseRefName:S(h.base.ref),sameRepository:_(h.head.repo)&&_(h.base.repo)&&h.head.repo.full_name===a&&h.base.repo.full_name===a}),u.length>ne)throw new Error("git-reconciliation-pr-inventory-limit")}if(f.length<r)break}}return u}function K(e){return _(e)&&(e.outcome==="applied"||e.outcome==="already_absent")}async function Ae(e){try{const a=(await e.git("log",e.originHeadSha,"--format=%H","--fixed-strings",`--grep=Neocortex-Idempotency: ${e.directive.idempotencyKey}`)).split(`
|
|
3
|
+
`).filter(s=>/^[a-f0-9]{40,64}$/i.test(s));if(a.length!==1)return null;const c=a[0],t=(await e.git("rev-list","--parents","-n","1",c)).split(/\s+/),u=await e.git("show","-s","--format=%B",c),[m]=u.split(`
|
|
4
|
+
`),r=`merge(${e.directive.epicId}): integrate ${e.directive.executionBranch} into ${e.directive.originBranch}`;if(t.length!==3||t[2]!==e.executionHeadSha||m!==r)return null;const i=new Set(u.split(`
|
|
5
|
+
`).map(s=>s.trim()));for(const s of[`Neocortex-Epic: ${e.directive.epicId}`,`Neocortex-Origin-Branch: ${e.directive.originBranch}`,`Neocortex-Execution-Branch: ${e.directive.executionBranch}`,`Neocortex-Execution-Head: ${e.executionHeadSha}`,`Neocortex-Origin-Base: ${t[1]}`,`Neocortex-Graph-Revision: ${e.directive.graphRevision}`,`Neocortex-Aggregate-QA: ${e.directive.localGates.aggregateQaReceipt.receiptRef}`,`Neocortex-Idempotency: ${e.directive.idempotencyKey}`])if(!i.has(s))return null;const n=await e.git("show","-s","--format=%cI",c);return{schemaVersion:1,epicId:e.directive.epicId,executionBranch:e.directive.executionBranch,originBranch:e.directive.originBranch,baseSha:t[1],executionHeadSha:e.executionHeadSha,mergeSha:c,pushedSha:c,strategy:"direct",physicalStatus:"pushed",localGateReceiptRefs:e.directive.localGates.receiptRefs,idempotencyKey:e.directive.idempotencyKey,attemptStartedAt:n,completedAt:n,retryEligible:!1,reasonCodes:["epic-terminal-recovered-physical-push"],publicSafe:!0}}catch{return null}}async function U(e,a,c){try{if(!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(a))return"unknown";const t=await e("api",`repos/${a}/branches/${encodeURIComponent(c)}`,"--hostname","github.com","--jq",".protected");return t==="false"?"unprotected":t==="true"?"protected":"unknown"}catch{return"unknown"}}async function L(e,a,c){try{const t=JSON.parse(await e("pr","view",String(c),"--repo",`https://github.com/${a}`,"--json","state,headRefName,headRefOid,baseRefName,mergeCommit"));return[t.state,t.headRefName,t.headRefOid,t.baseRefName].every(u=>typeof u=="string")?t:null}catch{return null}}async function Ee(e,a,c,t,u,m){if(!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(a)||!E(c)||!/^[a-f0-9]{40,64}$/i.test(t)||!/^[a-f0-9]{40,64}$/i.test(u)||Number.isNaN(Date.parse(m)))return{ok:!1,reasonCode:"epic-terminal-pr-base-guard-unverifiable"};let r;try{const d=JSON.parse(await e("api",`repos/${a}/rules/branches/${encodeURIComponent(c)}`,"--hostname","github.com","--paginate","--slurp")),l=Array.isArray(d)&&d.every(Array.isArray)?d.flat():d;if(!Array.isArray(l)||l.length>256||l.some(f=>!_(f)))return{ok:!1,reasonCode:"epic-terminal-pr-base-guard-unverifiable"};r=l}catch{return{ok:!1,reasonCode:"epic-terminal-pr-base-guard-unverifiable"}}if(r.some(d=>d.type==="merge_queue"))return{ok:!1,reasonCode:"epic-terminal-pr-merge-queue-local-gate-required"};const i=r.some(d=>d.type==="required_status_checks"&&_(d.parameters)&&d.parameters.strict_required_status_checks_policy===!0);let n=!1;try{const d=JSON.parse(await e("api",`repos/${a}/branches/${encodeURIComponent(c)}/protection/required_status_checks`,"--hostname","github.com"));if(!_(d)||typeof d.strict!="boolean")return{ok:!1,reasonCode:"epic-terminal-pr-base-guard-unverifiable"};n=d.strict}catch(d){if(!qe(d))return{ok:!1,reasonCode:"epic-terminal-pr-base-guard-unverifiable"}}return!i&&!n?{ok:!1,reasonCode:"epic-terminal-pr-strict-base-guard-required"}:{ok:!0,receipt:{schemaVersion:1,provider:"github",mode:"strict_up_to_date_expected_head",source:i&&n?"branch_protection_and_ruleset":i?"ruleset":"branch_protection",prHeadSha:t,originHeadSha:u,checkedAt:m,publicSafe:!0}}}function qe(e){return e instanceof Error&&/(?:HTTP\s*)?404\b|not found/i.test(e.message)}function Ie(e,a){const c=e.mode==="recovered_exact_tree"?e.source==="physical_recovery":e.mode==="strict_up_to_date_expected_head"&&["branch_protection","ruleset","branch_protection_and_ruleset"].includes(e.source);return e.schemaVersion===1&&e.provider==="github"&&c&&e.publicSafe===!0&&!Number.isNaN(Date.parse(e.checkedAt))&&e.prHeadSha===a.mergeSha&&e.originHeadSha===a.baseSha}async function ue(e,a,c){return(await ke(e,[...a],{cwd:c,encoding:"utf8",maxBuffer:16777216})).stdout.trim()}async function D(e,a,c=!1){const t=JSON.parse(await ae(a,"utf8"));return xe(t,await He(e,{required:c}))}function he(e,a,c,t){if(!e||e.schemaVersion!==1||e.evidenceVersion!=="p205-execution-v2"||e.producer!=="local-yolo-tdd"||e.contract!=="P205-CI-02"||e.command!==Q||e.verdict!=="passed"||e.executionHeadSha!==a||e.graphRevision!==c||!/^[a-f0-9]{64}$/i.test(e.manifestDigest??"")||!/^[a-f0-9]{64}$/i.test(e.resultDigest??"")||e.gateIds.length===0)return!1;const u=`local-yolo-tdd:${O("sha256").update(["epic-aggregate-p205-execution-v2",Q,a,c,e.manifestDigest,e.gateIds.join(","),e.resultDigest].join("\0")).digest("hex")}`;if(e.receiptRef!==u)return!1;const m=Date.parse(e.checkedAt),r=Date.parse(t);return Number.isFinite(m)&&Number.isFinite(r)&&r-m<=1800*1e3&&m-r<=300*1e3}async function A(e,a){const c=await C(e,a);if(!c)throw new Error("origin-ref-missing");return c}async function C(e,a){if(!E(a))throw new Error("branch-ref-invalid");const t=(await e("ls-remote","--heads","origin",`refs/heads/${a}`)).split(`
|
|
6
|
+
`).filter(Boolean).map(u=>u.split(/\s+/));if(t.length!==0){if(t.length!==1||t[0].length!==2||t[0][1]!==`refs/heads/${a}`||!/^[a-f0-9]{40,64}$/i.test(t[0][0]??""))throw new Error("remote-ref-query-invalid");return t[0][0]}}async function P(e,a,c){if(await e("fetch","--quiet","--no-tags","origin",`refs/heads/${a}`),await e("rev-parse","FETCH_HEAD")!==c)throw new Error("remote-head-raced")}async function le(e,a){try{return await e("rev-parse","--verify",a)}catch{return}}async function v(e,a,c){try{return await e("merge-base","--is-ancestor",a,c),!0}catch{return!1}}async function j(e,a){if(!/^[a-f0-9]{40,64}$/i.test(a))return!1;try{return await e("cat-file","-t",a)==="commit"}catch{return!1}}async function fe(e){const a=new Set,c=await e("worktree","list","--porcelain");for(const t of c.split(`
|
|
7
|
+
`))t.startsWith("branch refs/heads/")&&a.add(t.slice(18));return a}function o(e,a,c){return{ok:!1,reasonCode:e,retryEligible:a,externalPolicy:c}}function S(e){return typeof e=="string"?e.trim():""}function E(e){return e.length>0&&e.length<=240&&!e.startsWith("-")&&!e.startsWith("/")&&!e.endsWith("/")&&!e.endsWith(".lock")&&!e.includes("..")&&!e.includes("\\")&&!e.includes("@{")&&!e.includes("//")&&/^[A-Za-z0-9._/-]+$/.test(e)}function _(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function Oe(e,a){return e instanceof Error&&"code"in e&&e.code===a}function Y(e){return(Array.isArray(e.stories)?e.stories:Array.isArray(e.story_ids)?e.story_ids:[]).filter(c=>typeof c=="string")}async function X(e,a,c,t,u){const m=Y(c),r=await Promise.all(m.map(i=>ce(e,a.stories?.[i]?.checkpoint,i,t,u).then(n=>n&&ge(a.stories?.[i]?.checkpoint))));return m.filter((i,n)=>r[n])}function ge(e){const a=_(e)?e:void 0,c=a&&_(a.qaReceipt)?a.qaReceipt:void 0;return!!(c&&c.evidenceVersion==="p205-execution-v2"&&c.producer==="local-yolo-tdd"&&c.contract==="P205-CI-02"&&c.command===Q&&/^[a-f0-9]{64}$/i.test(S(c.manifestDigest))&&/^[a-f0-9]{64}$/i.test(S(c.resultDigest))&&Array.isArray(c.gateIds)&&c.gateIds.length>0)}function ee(e,a){return e.length===a.length&&[...e].sort().every((c,t)=>c===[...a].sort()[t])}function re(e){return _(e)&&typeof e.expiresAt=="string"&&e.expiresAt>new Date().toISOString()}async function pe(e){const a=new Set([".neocortex/yoloop.lock",".neocortex/yoloop-state.json",".neocortex/continuity/continuity.sqlite",".neocortex/continuity/continuity.sqlite-shm",".neocortex/continuity/continuity.sqlite-wal"]);return(await e("status","--porcelain","--untracked-files=all")).split(`
|
|
8
|
+
`).filter(Boolean).some(c=>!c.startsWith("?? ")||!a.has(c.slice(3)))}function te(e){const a=e instanceof Error?e.message:"";return/protected branch|branch protection|required pull request|GH006|GH013|ruleset|review required/i.test(a)}function me(e,a){const c=Array.isArray(e)?e.filter(_):[],t=new Map(c.map(u=>[S(u.resourceId),u]));for(const u of a)t.set(u.resourceId,u);return[...t.values()]}export{ze as createCliEpicTerminalGitAdapter};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{existsSync as q,mkdirSync as B,readFileSync as H,renameSync as K,writeFileSync as W,copyFileSync as V}from"node:fs";import{join as E}from"node:path";import{randomBytes as Z}from"node:crypto";import{readGitLifecycleRuntimeSync as U,verifyAcceptedGitLifecycleStoryCheckpointSync as Y}from"../git/git-lifecycle-store.js";class b extends Error{reasonCode;constructor(t,o){super(t),this.reasonCode=o,this.name="StateRepairHardStopError"}}const Q="P189-GAP-STATE-SHAPE",h={reconciled:"epic_aggregate_reconciled",totalRecounted:"epic_total_stories_recounted",completedRecounted:"epic_completed_stories_recounted",statusPromotedDone:"epic_status_promoted_done",completedAtSet:"epic_completed_at_set",stateNotObject:"epic_aggregate_state_not_object",epicsNotObject:"epic_aggregate_epics_not_object",storiesNotObject:"epic_aggregate_stories_not_object",scopeProjected:"epic_aggregate_scope_projected",epicNotObject:"epic_aggregate_epic_not_object",epicIdConflict:"epic_aggregate_epic_id_conflict",storyListNotArray:"epic_aggregate_story_list_not_array",storyRefInvalid:"epic_aggregate_story_ref_invalid",storyRefDuplicate:"epic_aggregate_story_ref_duplicate",storyMissing:"epic_aggregate_story_missing",storyNotObject:"epic_aggregate_story_not_object",storyIdConflict:"epic_aggregate_story_id_conflict",storyEpicConflict:"epic_aggregate_story_epic_conflict",terminalIncompleteConflict:"epic_aggregate_terminal_incomplete_conflict"},X=new Set(["yolo","implement","diagnose","research","write-spec","create-tasks","refactor-suggest","property-tests","validate-tests","qa-gate","update-project-memory","commit-push","sync-main","create-pr","review-pr","merge-pr","review"]),ee=new Set(["yoloop","auto-yolo","epic-runner","story-runner","loop-yolo","auto-epic"]);function l(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function x(){return new Date().toISOString()}function te(){return x().replace(/[:.]/g,"-")}function k(e){return Array.isArray(e)?e.filter(t=>typeof t=="string"&&t.length>0):[]}function $(e){const[t]=e.trim().split(/\s+/,1);return t?t.replace(/^@?\*/,"").replace(/^@/,"").toLowerCase():null}function oe(e){const[t]=e.trim().split(/\s+/,1);if(!t)return!1;const o=t.replace(/^@/,"");return/^[A-Z]?\d+(?:\.\d+)+$/i.test(o)||/(?:^|\/)[A-Z]?\d+(?:\.\d+)+\.story\.md$/i.test(o)}function O(e){const t=e.match(/(?:^|[/@\s])([A-Z]?\d+(?:\.\d+)+)\.story\.md\b/i);return t?.[1]?t[1]:e.match(/(?:^|\s|@)([A-Z]?\d+(?:\.\d+)+)(?=$|\s)/i)?.[1]??null}function P(e,t){if(typeof e!="string")return!1;const o=t??$(e);return(o==="yolo"||o==="yolo-inline")&&T(e).length>0&&O(e)===null}function T(e){const t=e.trim(),o=t.search(/\s/);return o===-1?"":t.slice(o).trim()}function re(e){const t=T(e).split(/\s+/).find(o=>o.length>0&&!o.startsWith("-"));return t?t.replace(/^@/,"").replace(/^['"]|['"]$/g,""):null}function D(e,t){const r=e.replace(/^@/,"").replace(/^epic-/i,"").replace(/\.md$/i,""),s=[r,r.toUpperCase(),/^p\d+$/i.test(r)?`P${r.replace(/^p/i,"")}`:null,/^\d+$/.test(r)?`P${r}`:null].filter(a=>typeof a=="string"&&a.length>0);for(const a of s)if(l(t[a]))return a;return null}function se(e,t){const o=e.match(/(?:^|[/@\s])epic-(P?\d+)\.md\b/i);return o?.[1]?D(o[1],t):null}function L(e,t,o){if(typeof t!="string"||t.trim().length===0||!l(e.epics)||!l(e.stories))return;const r=t.trim(),s=o??$(r);if(s!==null&&X.has(s)||oe(r)){const u=O(r),p=u?e.stories[u]:void 0;if(l(p)&&typeof p.epic_id=="string"&&l(e.epics[p.epic_id]))return p.epic_id;const n=u?.split(".")[0];if(n&&l(e.epics[n]))return n}if(s!==null&&ee.has(s)){const u=se(r,e.epics);if(u)return u;const p=re(r);if(p){const n=D(p,e.epics);if(n)return n}}}const R="pr_merged_issue_closed_reconciled",ne="invalid_workflow_issue_removed";function w(e){return typeof e=="string"?e.trim().toLowerCase().replace(/[_\s]+/g,"-"):""}const ie=new Set(["done","completed","merged"]),ce=new Set(["done","completed","merged"]);function M(e){return ie.has(w(e))}function G(e){return ce.has(w(e))}function S(e,t){throw new b(`${t} (${Q} if this shape is intentionally new).`,e)}function ae(e){return JSON.parse(JSON.stringify(e))}function pe(e,t){const o=t.scope??e.scope;if(!l(o))return;const r=typeof o.kind=="string"?o.kind:null,s=typeof o.omittedStoryCount=="number"?o.omittedStoryCount:0,a=typeof o.omittedEpicCount=="number"?o.omittedEpicCount:0;(r==="projected"||s>0||a>0)&&S(h.scopeProjected,"[Neocortex] state.json aggregate reconciliation stopped: projected or partial state coverage cannot prove epic aggregates.")}function le(e,t){const o=[],r=new Set;for(const s of t)(typeof s!="string"||s.length===0)&&S(h.storyRefInvalid,`[Neocortex] state.json aggregate reconciliation stopped: epic "${y(e)}" has a non-string or empty story reference.`),r.has(s)&&S(h.storyRefDuplicate,`[Neocortex] state.json aggregate reconciliation stopped: epic "${y(e)}" lists story "${y(s)}" more than once.`),r.add(s),o.push(s);return o}function de(e,t,o){const r=o[t];return r===void 0&&S(h.storyMissing,`[Neocortex] state.json aggregate reconciliation stopped: epic "${y(e)}" references missing story "${y(t)}".`),l(r)||S(h.storyNotObject,`[Neocortex] state.json aggregate reconciliation stopped: story "${y(t)}" is not an object.`),r.id!==t&&S(h.storyIdConflict,`[Neocortex] state.json aggregate reconciliation stopped: story key "${y(t)}" conflicts with its id field.`),r.epic_id!==e&&S(h.storyEpicConflict,`[Neocortex] state.json aggregate reconciliation stopped: story "${y(t)}" does not belong to epic "${y(e)}".`),r}function C(e,t){e.includes(t)||e.push(t)}function ue(e,t={}){l(e)||S(h.stateNotObject,"[Neocortex] state.json aggregate reconciliation stopped: top-level state must be an object."),l(e.epics)||S(h.epicsNotObject,"[Neocortex] state.json aggregate reconciliation stopped: `epics` must be an object before counts are authoritative."),l(e.stories)||S(h.storiesNotObject,"[Neocortex] state.json aggregate reconciliation stopped: `stories` must be an object before counts are authoritative."),pe(e,t);const o=ae(e),r=o.epics,s=o.stories,a=[],u=[],p=new Set(t.effectiveDoneStoryIds??[]);for(const[n,c]of Object.entries(r)){if(t.targetEpicId&&n!==t.targetEpicId)continue;l(c)||S(h.epicNotObject,`[Neocortex] state.json aggregate reconciliation stopped: epic "${y(n)}" is not an object.`),c.id!==n&&S(h.epicIdConflict,`[Neocortex] state.json aggregate reconciliation stopped: epic key "${y(n)}" conflicts with its id field.`),Array.isArray(c.stories)||S(h.storyListNotArray,`[Neocortex] state.json aggregate reconciliation stopped: epic "${y(n)}" has non-array stories.`);const g=le(n,c.stories),i=g.length,d=g.filter(j=>{const _=de(n,j,s);return M(_.status)||p.has(j)}).length,f=i>0&&d===i;if(G(c.status)&&!f&&i>0&&S(h.terminalIncompleteConflict,`[Neocortex] state.json aggregate reconciliation stopped: terminal epic "${y(n)}" has incomplete listed stories.`),t.preserveGitLifecycleAggregates&&(c.branch_mode==="epic"||typeof c.execution_branch=="string"))continue;const m=[],v={total_stories:c.total_stories,completed_stories:c.completed_stories,status:c.status,completed_at:c.completed_at};if(c.total_stories!==i&&(c.total_stories=i,C(m,h.totalRecounted)),c.completed_stories!==d&&(c.completed_stories=d,C(m,h.completedRecounted)),!G(c.status)&&f&&(c.status="done",C(m,h.statusPromotedDone),(c.completed_at===void 0||c.completed_at===null)&&(c.completed_at=t.nowIso??x(),C(m,h.completedAtSet))),m.length>0){C(m,h.reconciled);for(const j of m)C(a,j);u.push({epicId:n,reasonCodes:m,previous:v,next:{total_stories:i,completed_stories:d,status:c.status,completed_at:c.completed_at}})}}return{mutated:JSON.stringify(o)!==JSON.stringify(e),state:o,reasonCodes:a,changes:u}}function A(e,t){const o=e[t];return l(o)?o:null}function _e(e){const t=A(e,"issueCloseAudit")??A(e,"issue_close_audit");if(t){const s=w(t.event).replace(/-/g,"_"),a=t.publicSafe===!0||t.public_safe===!0,u=t.rawLogStorage==="forbidden"||t.raw_log_storage==="forbidden";if(s==="story_issue_closed"&&a&&u)return!0}const o=A(e,"completionEvidence")??A(e,"completion_evidence"),r=w(o?.issueState??o?.issue_state??o?.state);return e.issue_closed===!0||w(e.issue_state??e.issueState??e.workflow_issue_state)==="closed"||r==="closed"}function z(e){const t=A(e,"completionEvidence")??A(e,"completion_evidence"),o=A(e,"scopedCiEvidence")??A(e,"scoped_ci_evidence"),r=[e.physicalMergeStatus,e.pr_state,e.prState,e.pull_request_state,e.pullRequestState,t?.prState,t?.pr_state,t?.pullRequestState,o?.prState].map(w);return e.merged===!0||r.includes("merged")||w(o?.finalState)==="can-merge"&&e.merged===!0}function fe(e){const t=A(e,"completionEvidence")??A(e,"completion_evidence"),o=[e.pr_state,e.prState,e.pull_request_state,e.pullRequestState,t?.prState,t?.pr_state,t?.pullRequestState].map(w),r=z(e),s=["denied-external-gate","denied-local-or-repo","not-attempted"].includes(w(e.physicalMergeStatus)),a=o.includes("closed")||o.includes("closed-unmerged");return r&&(e.merged===!1||s||a)||e.merged===!0&&s}function ge(e,t,o){const r=`state-completion-reconciliation:${y(t)}`,s=e.filter(l).map(n=>({...n})),a={id:r,status:"open",reason:"stale_state_completion_evidence_observed",reason_codes:[R],category:"state/completion-reconciliation",story_id:y(t),public_safe:!0,raw_log_storage:"forbidden",created_at:o,last_seen_at:o,occurrences:1},u=s.findIndex(n=>n.id===r);if(u<0)return[...s,a];const p=s[u];return s[u]={...p,...a,created_at:typeof p.created_at=="string"?p.created_at:o,occurrences:Math.max(1,typeof p.occurrences=="number"?p.occurrences:1)+1},s}function me(e,t,o,r){const s=l(e.stories)?e.stories:null;if(!s)return;const a=[],u=x();for(const[p,n]of Object.entries(s)){if(!l(n))continue;const c=typeof n.epic_id=="string"?n.epic_id:p.split(".")[0];if(r&&c!==r)continue;const g=w(n.status);if(fe(n))throw new b(`[Neocortex] state.json repair stopped: story "${p}" has conflicting PR merge completion evidence. Resolve .neocortex/state.json manually before invoking Neocortex.`,"conflicting_completion_evidence");if(g!=="pr-reviewed"||!z(n)||!_e(n))continue;const i=typeof n.status=="string"?n.status:g,d=k(n.steps_completed);n.status="done",n.last_step="step-c-12-merge-pr",n.steps_completed=Array.from(new Set([...d,"step-c-12-merge-pr"])),n.merged=!0,n.physicalMergeStatus="merged",n.completion_reconciled_at=u,t.push(`reconciled completion evidence for story ${p}`),o.push(R),a.push({schemaVersion:1,publicSafe:!0,rawLogStorage:"forbidden",storyId:y(p),previousStatus:i,nextStatus:"done",reasonCodes:[R],reconciledAt:u}),e.state_repair_debt=ge(Array.isArray(e.state_repair_debt)?e.state_repair_debt:[],p,u)}a.length>0&&(e.state_completion_reconciliations=[...Array.isArray(e.state_completion_reconciliations)?e.state_completion_reconciliations.filter(l):[],...a])}function ye(e,t,o){const r=l(e.stories)?e.stories:null;if(r)for(const[s,a]of Object.entries(r))!l(a)||!Object.prototype.hasOwnProperty.call(a,"workflow_issue")||typeof a.workflow_issue=="number"&&Number.isFinite(a.workflow_issue)||(delete a.workflow_issue,t.push(`removed invalid workflow_issue from story ${y(s)}`),o.push(ne))}function he(e,t,o){const r=o[e];if(!l(r))return;const s=r.epic_id;if(typeof s=="string"&&s!==t)throw new b(`[Neocortex] state.json repair stopped: story "${e}" belongs to epic "${s}" but legacy planning maps it to "${t}". Resolve the conflicting IDs in .neocortex/state.json, then rerun the command.`,"conflicting_story_epic_id")}function Se(e,t={}){if(Array.isArray(e.stories))throw new b("[Neocortex] state.json repair stopped: `stories` is an Array. Convert it to an object keyed by story ID or run `*migrate-state --preview` for guidance.","stories_array_ambiguous");if(l(e.epics)){for(const[i,d]of Object.entries(e.epics))if(l(d)&&d.stories!==void 0&&!Array.isArray(d.stories))throw new b(`[Neocortex] state.json repair stopped: epic "${i}" has non-array \`stories\`. Repair this ambiguous shape manually before invoking Neocortex.`,"epic_stories_not_array")}if(e.schema_version!==void 0&&e.schema_version!==1)throw new b(`[Neocortex] state.json repair stopped: schema_version=${String(e.schema_version)} is not a safe automatic downgrade target. Run \`*migrate-state --preview\` or repair the schema version manually.`,"unsupported_schema_version");if(P(t.args,t.canonicalCommandId)){for(const i of["epics","stories"]){const d=e[i];if(d!==void 0){if(!l(d))throw new b("[Neocortex] inline materialization stopped: invalid state collection.","inline_state_collection_invalid");for(const[f,m]of Object.entries(d)){if(!l(m))throw new b("[Neocortex] inline materialization stopped: invalid state record.","inline_state_record_invalid");if(m.id!==void 0&&m.id!==f)throw new b("[Neocortex] inline materialization stopped: conflicting state identity.","inline_state_identity_conflict")}}}return{mutated:!1,state:e,reasonCodes:[],changes:[]}}let o=JSON.parse(JSON.stringify(e));const r=[],s=[];let a=[];l(o.epics)||(o.epics={},r.push("initialized epics{}"),s.push("initialized_epics_object")),l(o.stories)||(o.stories={},r.push("initialized stories{}"),s.push("initialized_stories_object"));const u=o.epics,p=o.stories,n=L(o,t.args,t.canonicalCommandId),c=l(o.planning)?o.planning:null;if(c&&l(c.current)){const i=c.current,d=typeof i.epic_id=="string"?i.epic_id:null;if(!d)throw new b("[Neocortex] state.json repair stopped: legacy planning.current has no epic_id. Add the missing epic_id or run `*migrate-state --preview`.","planning_current_missing_epic_id");const f=Array.isArray(i.stories)?i.stories:[],m=f.map(_=>l(_)&&typeof _.id=="string"?_.id:typeof _=="string"?_:null).filter(_=>!!_);for(const _ of m)he(_,d,p);l(u[d])||(u[d]={id:d,title:typeof i.title=="string"?i.title:`Epic ${d}`,status:i.status==="done"?"done":"in-progress",stories:m,created_at:typeof i.created_at=="string"?i.created_at:x(),completed_at:typeof i.completed_at=="string"?i.completed_at:null,completed_stories:typeof i.completed_stories=="number"?i.completed_stories:0,total_stories:typeof i.total_stories=="number"?i.total_stories:m.length,source:"client-auto-repair-P156.05"},r.push(`migrated planning.current to epics["${d}"]`),s.push("legacy_planning_current_migrated"));for(const _ of f)!l(_)||typeof _.id!="string"||l(p[_.id])||(p[_.id]={..._,id:_.id,epic_id:d,title:typeof _.title=="string"?_.title:_.id,status:typeof _.status=="string"?_.status:"backlog",depends_on:k(_.depends_on),created_at:typeof _.created_at=="string"?_.created_at:x()},r.push(`migrated embedded story ${_.id}`),s.push("legacy_planning_story_migrated"));const v=typeof i.status=="string"?i.status.trim().toLowerCase().replace(/[_\s]+/g,"-"):"",I=new Set(["epic_id","title","status","created_at","updated_at","completed_at","completed_stories","total_stories","stories"]),j=Object.keys(i).filter(_=>!I.has(_));v==="generated"&&j.length===0?(delete c.current,r.push("removed safe legacy planning.current generated container"),s.push("legacy_planning_current_generated_removed")):j.length>0&&(r.push(`preserved planning.current unknown keys: ${j.sort().join(", ")}`),s.push("unknown_planning_current_keys_preserved"))}if(c&&Array.isArray(c.history)){let i=!0;const d=new Set(["epic_id","title","status","created_at","completed_at","completed_stories","total_stories","stories"]);for(const f of c.history){if(!l(f)){i=!1;continue}Object.keys(f).filter(I=>!d.has(I)).length>0&&(i=!1);const v=typeof f.epic_id=="string"?f.epic_id:null;if(!v){i=!1;continue}l(u[v])||(u[v]={id:v,title:typeof f.title=="string"?f.title:`Epic ${v}`,status:"done",stories:k(f.stories),created_at:typeof f.created_at=="string"?f.created_at:x(),completed_at:typeof f.completed_at=="string"?f.completed_at:x(),completed_stories:typeof f.completed_stories=="number"?f.completed_stories:0,total_stories:typeof f.total_stories=="number"?f.total_stories:0,source:"client-auto-repair-P156.05"},r.push(`migrated planning.history to epics["${v}"]`),s.push("legacy_planning_history_migrated"))}i?(delete c.history,r.push("removed safe legacy planning.history container"),s.push("legacy_planning_history_removed")):(r.push("preserved planning.history because one or more entries were not safely removable"),s.push("legacy_planning_history_preserved"))}if(c&&Object.keys(c).length===0&&(delete o.planning,r.push("removed empty planning container"),s.push("empty_planning_removed")),o.schema_version!==1&&(o.schema_version=1,r.push("set schema_version=1"),s.push("schema_version_added")),ye(o,r,s),me(o,r,s,n),typeof t.args=="string"){const i=ue(o,{nowIso:t.nowIso,targetEpicId:n,effectiveDoneStoryIds:t.effectiveDoneStoryIds,preserveGitLifecycleAggregates:!0});if(i.mutated){o=i.state,a=i.changes;for(const d of i.reasonCodes)s.push(d);for(const d of i.changes)r.push(`reconciled aggregate for epic ${y(d.epicId)}`)}}const g=JSON.stringify(o)!==JSON.stringify(e);if(g){const i=t.nowIso??x(),d=Array.from(new Set(s));a.length>0&&we(o,a,i),o.last_updated=i,o.state_migration_repairs=[...Array.isArray(o.state_migration_repairs)?o.state_migration_repairs:[],{schemaVersion:1,action:"client_auto_repair",reasonCodes:d,repairedAt:i}],o.state_repair_debt=Ae(Array.isArray(o.state_repair_debt)?o.state_repair_debt:[],d,i)}return{mutated:g,state:o,reasonCodes:Array.from(new Set(s)),changes:r}}function F(e){return typeof e=="number"&&Number.isFinite(e)?e:null}function J(e){const t=w(e);return t.length>0?N(t).slice(0,80):null}function be(e,t){return{schemaVersion:1,publicSafe:!0,epicId:y(e.epicId),reasonCodes:e.reasonCodes.map(N).filter(Boolean),previous:{total_stories:F(e.previous.total_stories),completed_stories:F(e.previous.completed_stories),status:J(e.previous.status),completed_at_present:typeof e.previous.completed_at=="string"&&e.previous.completed_at.length>0},next:{total_stories:e.next.total_stories,completed_stories:e.next.completed_stories,status:J(e.next.status),completed_at_present:typeof e.next.completed_at=="string"&&e.next.completed_at.length>0},repairedAt:t}}function ve(e,t,o){let r=e.filter(l).map(s=>({...s}));for(const s of t){const a=y(s.epicId),u=Array.from(new Set(s.reasonCodes.map(N).filter(Boolean))).sort(),p=`state-aggregate-reconciliation:${a}`,n={id:p,status:"open",reason:"epic_aggregate_reconciled",reason_codes:u,category:"state/epic-aggregate-reconciliation",epic_id:a,created_at:o,last_seen_at:o,occurrences:1},c=r.findIndex(i=>i.id===p);if(c<0){r=[...r,n];continue}const g=r[c];r[c]={...g,...n,created_at:typeof g.created_at=="string"?g.created_at:o,occurrences:Math.max(1,typeof g.occurrences=="number"?g.occurrences:1)+1}}return r}function we(e,t,o){e.state_aggregate_reconciliations=[...Array.isArray(e.state_aggregate_reconciliations)?e.state_aggregate_reconciliations.filter(l):[],...t.map(r=>be(r,o))],e.state_repair_debt=ve(Array.isArray(e.state_repair_debt)?e.state_repair_debt:[],t,o)}function Ae(e,t,o){const r=Array.from(new Set(t.map(N).filter(Boolean))).sort(),s=`state-drift-repair:${r.join("+")||"unknown"}`,a=e.filter(l).map(c=>({...c})),u={id:s,status:"open",reason:"state_json_drift_repaired",reason_codes:r,category:"state/drift-repair",created_at:o,last_seen_at:o,occurrences:1},p=a.findIndex(c=>c.id===s);if(p<0)return[...a,u];const n=a[p];return a[p]={...n,...u,created_at:typeof n.created_at=="string"?n.created_at:o,occurrences:Math.max(1,typeof n.occurrences=="number"?n.occurrences:1)+1},a}function N(e){return e.replace(/\b(sk|ghp|gho|github_pat|xoxb|AKIA)[A-Za-z0-9_-]{8,}\b/g,"[redacted]").replace(/(password|passwd|secret|token|api[_-]?key)\s*[:=]\s*[^,\s)]+/gi,"$1=[redacted]").replace(/https?:\/\/[^\s)]+/gi,"[url-redacted]").replace(/[^a-z0-9_+-]+/gi,"_").replace(/^_+|_+$/g,"").slice(0,120)}function y(e){return e.replace(/[^a-z0-9_.:-]+/gi,"_").replace(/^_+|_+$/g,"").slice(0,120)||"unknown"}function Ne(e,t={}){const o=E(e,".neocortex"),r=E(o,"state.json");if(!q(r))return{repaired:!1,reasonCodes:[],changes:[]};let s;try{s=JSON.parse(H(r,"utf8"))}catch(d){const f=d instanceof Error?d.message:String(d);throw new b(`[Neocortex] state.json repair stopped: invalid JSON in .neocortex/state.json (${f}). No files were overwritten. Fix the JSON or restore a backup, then rerun.`,"invalid_json")}if(!l(s))throw new b("[Neocortex] state.json repair stopped: top-level state must be a JSON object. Restore a valid .neocortex/state.json before invoking Neocortex.","state_not_object");const a=L(s,t.args,t.canonicalCommandId),u=P(t.args,t.canonicalCommandId)?[]:je(e,s,a),p=Se(s,{...t,effectiveDoneStoryIds:u});if(!p.mutated)return{repaired:!1,reasonCodes:[],changes:[]};B(o,{recursive:!0});const n=te(),c=E(o,`state.json.backup-${n}`),g=E(o,`state.json.tmp.${Z(6).toString("hex")}`);V(r,c),W(g,`${JSON.stringify(p.state,null,2)}
|
|
2
|
-
`,"utf8"),
|
|
3
|
-
`),{repaired:!0,reasonCodes:
|
|
1
|
+
import{existsSync as F,mkdirSync as J,readFileSync as q,renameSync as B,writeFileSync as H,copyFileSync as W}from"node:fs";import{join as x}from"node:path";import{randomBytes as V}from"node:crypto";import{planStateMigration as Z,StateMigrationHardStopError as U}from"../../../shared/dist/state/index.js";import{applyStateMigration as K,previewStateMigration as Y,StateMigrationApplyError as Q}from"../migrations/state-json.js";import{readGitLifecycleRuntimeSync as X,verifyAcceptedGitLifecycleStoryCheckpointSync as ee}from"../git/git-lifecycle-store.js";class S extends Error{reasonCode;constructor(t,o){super(t),this.reasonCode=o,this.name="StateRepairHardStopError"}}const te="P189-GAP-STATE-SHAPE",f={reconciled:"epic_aggregate_reconciled",totalRecounted:"epic_total_stories_recounted",completedRecounted:"epic_completed_stories_recounted",statusPromotedDone:"epic_status_promoted_done",completedAtSet:"epic_completed_at_set",stateNotObject:"epic_aggregate_state_not_object",epicsNotObject:"epic_aggregate_epics_not_object",storiesNotObject:"epic_aggregate_stories_not_object",scopeProjected:"epic_aggregate_scope_projected",epicNotObject:"epic_aggregate_epic_not_object",epicIdConflict:"epic_aggregate_epic_id_conflict",storyListNotArray:"epic_aggregate_story_list_not_array",storyRefInvalid:"epic_aggregate_story_ref_invalid",storyRefDuplicate:"epic_aggregate_story_ref_duplicate",storyMissing:"epic_aggregate_story_missing",storyNotObject:"epic_aggregate_story_not_object",storyIdConflict:"epic_aggregate_story_id_conflict",storyEpicConflict:"epic_aggregate_story_epic_conflict",terminalIncompleteConflict:"epic_aggregate_terminal_incomplete_conflict"},oe=new Set(["yolo","implement","diagnose","research","write-spec","create-tasks","refactor-suggest","property-tests","validate-tests","qa-gate","update-project-memory","commit-push","sync-main","create-pr","review-pr","merge-pr","review"]),re=new Set(["yoloop","auto-yolo","epic-runner","story-runner","loop-yolo","auto-epic"]);function d(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function j(){return new Date().toISOString()}function ne(){return j().replace(/[:.]/g,"-")}function ie(e){return Array.isArray(e)?e.filter(t=>typeof t=="string"&&t.length>0):[]}function k(e){const[t]=e.trim().split(/\s+/,1);return t?t.replace(/^@?\*/,"").replace(/^@/,"").toLowerCase():null}function se(e){const[t]=e.trim().split(/\s+/,1);if(!t)return!1;const o=t.replace(/^@/,"");return/^[A-Z]?\d+(?:\.\d+)+$/i.test(o)||/(?:^|\/)[A-Z]?\d+(?:\.\d+)+\.story\.md$/i.test(o)}function R(e){const t=e.match(/(?:^|[/@\s])([A-Z]?\d+(?:\.\d+)+)\.story\.md\b/i);return t?.[1]?t[1]:e.match(/(?:^|\s|@)([A-Z]?\d+(?:\.\d+)+)(?=$|\s)/i)?.[1]??null}function I(e,t){if(typeof e!="string")return!1;const o=t??k(e);return(o==="yolo"||o==="yolo-inline")&&$(e).length>0&&R(e)===null}function $(e){const t=e.trim(),o=t.search(/\s/);return o===-1?"":t.slice(o).trim()}function ce(e){const t=$(e).split(/\s+/).find(o=>o.length>0&&!o.startsWith("-"));return t?t.replace(/^@/,"").replace(/^['"]|['"]$/g,""):null}function O(e,t){const r=e.replace(/^@/,"").replace(/^epic-/i,"").replace(/\.md$/i,""),s=[r,r.toUpperCase(),/^p\d+$/i.test(r)?`P${r.replace(/^p/i,"")}`:null,/^\d+$/.test(r)?`P${r}`:null].filter(c=>typeof c=="string"&&c.length>0);for(const c of s)if(d(t[c]))return c;return null}function ae(e,t){const o=e.match(/(?:^|[/@\s])epic-(P?\d+)\.md\b/i);return o?.[1]?O(o[1],t):null}function P(e,t,o){if(typeof t!="string"||t.trim().length===0||!d(e.epics)||!d(e.stories))return;const r=t.trim(),s=o??k(r);if(s!==null&&oe.has(s)||se(r)){const l=R(r),a=l?e.stories[l]:void 0;if(d(a)&&typeof a.epic_id=="string"&&d(e.epics[a.epic_id]))return a.epic_id;const n=l?.split(".")[0];if(n&&d(e.epics[n]))return n}if(s!==null&&re.has(s)){const l=ae(r,e.epics);if(l)return l;const a=ce(r);if(a){const n=O(a,e.epics);if(n)return n}}}const E="pr_merged_issue_closed_reconciled",pe="invalid_workflow_issue_removed";function b(e){return typeof e=="string"?e.trim().toLowerCase().replace(/[_\s]+/g,"-"):""}const le=new Set(["done","completed","merged"]),de=new Set(["done","completed","merged"]);function T(e){return le.has(b(e))}function M(e){return de.has(b(e))}function h(e,t){throw new S(`${t} (${te} if this shape is intentionally new).`,e)}function ue(e){return JSON.parse(JSON.stringify(e))}function fe(e,t){const o=t.scope??e.scope;if(!d(o))return;const r=typeof o.kind=="string"?o.kind:null,s=typeof o.omittedStoryCount=="number"?o.omittedStoryCount:0,c=typeof o.omittedEpicCount=="number"?o.omittedEpicCount:0;(r==="projected"||s>0||c>0)&&h(f.scopeProjected,"[Neocortex] state.json aggregate reconciliation stopped: projected or partial state coverage cannot prove epic aggregates.")}function ge(e,t){const o=[],r=new Set;for(const s of t)(typeof s!="string"||s.length===0)&&h(f.storyRefInvalid,`[Neocortex] state.json aggregate reconciliation stopped: epic "${u(e)}" has a non-string or empty story reference.`),r.has(s)&&h(f.storyRefDuplicate,`[Neocortex] state.json aggregate reconciliation stopped: epic "${u(e)}" lists story "${u(s)}" more than once.`),r.add(s),o.push(s);return o}function _e(e,t,o){const r=o[t];return r===void 0&&h(f.storyMissing,`[Neocortex] state.json aggregate reconciliation stopped: epic "${u(e)}" references missing story "${u(t)}".`),d(r)||h(f.storyNotObject,`[Neocortex] state.json aggregate reconciliation stopped: story "${u(t)}" is not an object.`),r.id!==t&&h(f.storyIdConflict,`[Neocortex] state.json aggregate reconciliation stopped: story key "${u(t)}" conflicts with its id field.`),r.epic_id!==e&&h(f.storyEpicConflict,`[Neocortex] state.json aggregate reconciliation stopped: story "${u(t)}" does not belong to epic "${u(e)}".`),r}function v(e,t){e.includes(t)||e.push(t)}function me(e,t={}){d(e)||h(f.stateNotObject,"[Neocortex] state.json aggregate reconciliation stopped: top-level state must be an object."),d(e.epics)||h(f.epicsNotObject,"[Neocortex] state.json aggregate reconciliation stopped: `epics` must be an object before counts are authoritative."),d(e.stories)||h(f.storiesNotObject,"[Neocortex] state.json aggregate reconciliation stopped: `stories` must be an object before counts are authoritative."),fe(e,t);const o=ue(e),r=o.epics,s=o.stories,c=[],l=[],a=new Set(t.effectiveDoneStoryIds??[]);for(const[n,i]of Object.entries(r)){if(t.targetEpicId&&n!==t.targetEpicId)continue;d(i)||h(f.epicNotObject,`[Neocortex] state.json aggregate reconciliation stopped: epic "${u(n)}" is not an object.`),i.id!==n&&h(f.epicIdConflict,`[Neocortex] state.json aggregate reconciliation stopped: epic key "${u(n)}" conflicts with its id field.`),Array.isArray(i.stories)||h(f.storyListNotArray,`[Neocortex] state.json aggregate reconciliation stopped: epic "${u(n)}" has non-array stories.`);const p=ge(n,i.stories),g=p.length,m=p.filter(A=>{const z=_e(n,A,s);return T(z.status)||a.has(A)}).length,_=g>0&&m===g;if(M(i.status)&&!_&&g>0&&h(f.terminalIncompleteConflict,`[Neocortex] state.json aggregate reconciliation stopped: terminal epic "${u(n)}" has incomplete listed stories.`),t.preserveGitLifecycleAggregates&&(i.branch_mode==="epic"||typeof i.execution_branch=="string"))continue;const y=[],N={total_stories:i.total_stories,completed_stories:i.completed_stories,status:i.status,completed_at:i.completed_at};if(i.total_stories!==g&&(i.total_stories=g,v(y,f.totalRecounted)),i.completed_stories!==m&&(i.completed_stories=m,v(y,f.completedRecounted)),!M(i.status)&&_&&(i.status="done",v(y,f.statusPromotedDone),(i.completed_at===void 0||i.completed_at===null)&&(i.completed_at=t.nowIso??j(),v(y,f.completedAtSet))),y.length>0){v(y,f.reconciled);for(const A of y)v(c,A);l.push({epicId:n,reasonCodes:y,previous:N,next:{total_stories:g,completed_stories:m,status:i.status,completed_at:i.completed_at}})}}return{mutated:JSON.stringify(o)!==JSON.stringify(e),state:o,reasonCodes:c,changes:l}}function w(e,t){const o=e[t];return d(o)?o:null}function ye(e){const t=w(e,"issueCloseAudit")??w(e,"issue_close_audit");if(t){const s=b(t.event).replace(/-/g,"_"),c=t.publicSafe===!0||t.public_safe===!0,l=t.rawLogStorage==="forbidden"||t.raw_log_storage==="forbidden";if(s==="story_issue_closed"&&c&&l)return!0}const o=w(e,"completionEvidence")??w(e,"completion_evidence"),r=b(o?.issueState??o?.issue_state??o?.state);return e.issue_closed===!0||b(e.issue_state??e.issueState??e.workflow_issue_state)==="closed"||r==="closed"}function D(e){const t=w(e,"completionEvidence")??w(e,"completion_evidence"),o=w(e,"scopedCiEvidence")??w(e,"scoped_ci_evidence"),r=[e.physicalMergeStatus,e.pr_state,e.prState,e.pull_request_state,e.pullRequestState,t?.prState,t?.pr_state,t?.pullRequestState,o?.prState].map(b);return e.merged===!0||r.includes("merged")||b(o?.finalState)==="can-merge"&&e.merged===!0}function he(e){const t=w(e,"completionEvidence")??w(e,"completion_evidence"),o=[e.pr_state,e.prState,e.pull_request_state,e.pullRequestState,t?.prState,t?.pr_state,t?.pullRequestState].map(b),r=D(e),s=["denied-external-gate","denied-local-or-repo","not-attempted"].includes(b(e.physicalMergeStatus)),c=o.includes("closed")||o.includes("closed-unmerged");return r&&(e.merged===!1||s||c)||e.merged===!0&&s}function Se(e,t,o){const r=`state-completion-reconciliation:${u(t)}`,s=e.filter(d).map(n=>({...n})),c={id:r,status:"open",reason:"stale_state_completion_evidence_observed",reason_codes:[E],category:"state/completion-reconciliation",story_id:u(t),public_safe:!0,raw_log_storage:"forbidden",created_at:o,last_seen_at:o,occurrences:1},l=s.findIndex(n=>n.id===r);if(l<0)return[...s,c];const a=s[l];return s[l]={...a,...c,created_at:typeof a.created_at=="string"?a.created_at:o,occurrences:Math.max(1,typeof a.occurrences=="number"?a.occurrences:1)+1},s}function be(e,t,o,r){const s=d(e.stories)?e.stories:null;if(!s)return;const c=[],l=j();for(const[a,n]of Object.entries(s)){if(!d(n))continue;const i=typeof n.epic_id=="string"?n.epic_id:a.split(".")[0];if(r&&i!==r)continue;const p=b(n.status);if(he(n))throw new S(`[Neocortex] state.json repair stopped: story "${a}" has conflicting PR merge completion evidence. Resolve .neocortex/state.json manually before invoking Neocortex.`,"conflicting_completion_evidence");if(p!=="pr-reviewed"||!D(n)||!ye(n))continue;const g=typeof n.status=="string"?n.status:p,m=ie(n.steps_completed);n.status="done",n.last_step="step-c-12-merge-pr",n.steps_completed=Array.from(new Set([...m,"step-c-12-merge-pr"])),n.merged=!0,n.physicalMergeStatus="merged",n.completion_reconciled_at=l,t.push(`reconciled completion evidence for story ${a}`),o.push(E),c.push({schemaVersion:1,publicSafe:!0,rawLogStorage:"forbidden",storyId:u(a),previousStatus:g,nextStatus:"done",reasonCodes:[E],reconciledAt:l}),e.state_repair_debt=Se(Array.isArray(e.state_repair_debt)?e.state_repair_debt:[],a,l)}c.length>0&&(e.state_completion_reconciliations=[...Array.isArray(e.state_completion_reconciliations)?e.state_completion_reconciliations.filter(d):[],...c])}function we(e,t,o){const r=d(e.stories)?e.stories:null;if(r)for(const[s,c]of Object.entries(r))!d(c)||!Object.prototype.hasOwnProperty.call(c,"workflow_issue")||typeof c.workflow_issue=="number"&&Number.isFinite(c.workflow_issue)||(delete c.workflow_issue,t.push(`removed invalid workflow_issue from story ${u(s)}`),o.push(pe))}function ve(e,t={}){if(Array.isArray(e.stories))throw new S("[Neocortex] state.json repair stopped: `stories` is an Array. Convert it to an object keyed by story ID or run `*migrate-state --preview` for guidance.","stories_array_ambiguous");if(d(e.epics)){for(const[i,p]of Object.entries(e.epics))if(d(p)&&p.stories!==void 0&&!Array.isArray(p.stories))throw new S(`[Neocortex] state.json repair stopped: epic "${i}" has non-array \`stories\`. Repair this ambiguous shape manually before invoking Neocortex.`,"epic_stories_not_array")}if(e.schema_version!==void 0&&e.schema_version!==0&&e.schema_version!==1)throw new S(`[Neocortex] state.json repair stopped: schema_version=${String(e.schema_version)} is not a safe automatic downgrade target. Run \`*migrate-state --preview\` or repair the schema version manually.`,"unsupported_schema_version");if(I(t.args,t.canonicalCommandId)){for(const i of["epics","stories"]){const p=e[i];if(p!==void 0){if(!d(p))throw new S("[Neocortex] inline materialization stopped: invalid state collection.","inline_state_collection_invalid");for(const[g,m]of Object.entries(p)){if(!d(m))throw new S("[Neocortex] inline materialization stopped: invalid state record.","inline_state_record_invalid");if(m.id!==void 0&&m.id!==g)throw new S("[Neocortex] inline materialization stopped: conflicting state identity.","inline_state_identity_conflict")}}}return{mutated:!1,state:e,reasonCodes:[],changes:[]}}let o;try{o=Z(e)}catch(i){throw i instanceof U?new S(`[Neocortex] state.json repair stopped by shared migration planner: ${i.message}`,i.reasonCode):i}let r=JSON.parse(JSON.stringify(o.kind==="migrate"?o.plan.targetState:e));const s=o.kind==="migrate"?o.plan.steps.map(i=>`shared migration ${i.kind}: ${i.target}`):[],c=o.kind==="migrate"?["state_migration_shared_plan"]:[];let l=[];const a=P(r,t.args,t.canonicalCommandId);if(we(r,s,c),be(r,s,c,a),typeof t.args=="string"){const i=me(r,{nowIso:t.nowIso,targetEpicId:a,effectiveDoneStoryIds:t.effectiveDoneStoryIds,preserveGitLifecycleAggregates:!0});if(i.mutated){r=i.state,l=i.changes;for(const p of i.reasonCodes)c.push(p);for(const p of i.changes)s.push(`reconciled aggregate for epic ${u(p.epicId)}`)}}const n=JSON.stringify(r)!==JSON.stringify(e);if(n){const i=t.nowIso??j(),p=Array.from(new Set(c));l.length>0&&je(r,l,i),r.last_updated=i,r.state_migration_repairs=[...Array.isArray(r.state_migration_repairs)?r.state_migration_repairs:[],{schemaVersion:1,action:"client_auto_repair",reasonCodes:p,repairedAt:i}],r.state_repair_debt=Ce(Array.isArray(r.state_repair_debt)?r.state_repair_debt:[],p,i)}return{mutated:n,state:r,reasonCodes:Array.from(new Set(c)),changes:s}}function G(e){return typeof e=="number"&&Number.isFinite(e)?e:null}function L(e){const t=b(e);return t.length>0?C(t).slice(0,80):null}function Ae(e,t){return{schemaVersion:1,publicSafe:!0,epicId:u(e.epicId),reasonCodes:e.reasonCodes.map(C).filter(Boolean),previous:{total_stories:G(e.previous.total_stories),completed_stories:G(e.previous.completed_stories),status:L(e.previous.status),completed_at_present:typeof e.previous.completed_at=="string"&&e.previous.completed_at.length>0},next:{total_stories:e.next.total_stories,completed_stories:e.next.completed_stories,status:L(e.next.status),completed_at_present:typeof e.next.completed_at=="string"&&e.next.completed_at.length>0},repairedAt:t}}function xe(e,t,o){let r=e.filter(d).map(s=>({...s}));for(const s of t){const c=u(s.epicId),l=Array.from(new Set(s.reasonCodes.map(C).filter(Boolean))).sort(),a=`state-aggregate-reconciliation:${c}`,n={id:a,status:"open",reason:"epic_aggregate_reconciled",reason_codes:l,category:"state/epic-aggregate-reconciliation",epic_id:c,created_at:o,last_seen_at:o,occurrences:1},i=r.findIndex(g=>g.id===a);if(i<0){r=[...r,n];continue}const p=r[i];r[i]={...p,...n,created_at:typeof p.created_at=="string"?p.created_at:o,occurrences:Math.max(1,typeof p.occurrences=="number"?p.occurrences:1)+1}}return r}function je(e,t,o){e.state_aggregate_reconciliations=[...Array.isArray(e.state_aggregate_reconciliations)?e.state_aggregate_reconciliations.filter(d):[],...t.map(r=>Ae(r,o))],e.state_repair_debt=xe(Array.isArray(e.state_repair_debt)?e.state_repair_debt:[],t,o)}function Ce(e,t,o){const r=Array.from(new Set(t.map(C).filter(Boolean))).sort(),s=`state-drift-repair:${r.join("+")||"unknown"}`,c=e.filter(d).map(i=>({...i})),l={id:s,status:"open",reason:"state_json_drift_repaired",reason_codes:r,category:"state/drift-repair",created_at:o,last_seen_at:o,occurrences:1},a=c.findIndex(i=>i.id===s);if(a<0)return[...c,l];const n=c[a];return c[a]={...n,...l,created_at:typeof n.created_at=="string"?n.created_at:o,occurrences:Math.max(1,typeof n.occurrences=="number"?n.occurrences:1)+1},c}function C(e){return e.replace(/\b(sk|ghp|gho|github_pat|xoxb|AKIA)[A-Za-z0-9_-]{8,}\b/g,"[redacted]").replace(/(password|passwd|secret|token|api[_-]?key)\s*[:=]\s*[^,\s)]+/gi,"$1=[redacted]").replace(/https?:\/\/[^\s)]+/gi,"[url-redacted]").replace(/[^a-z0-9_+-]+/gi,"_").replace(/^_+|_+$/g,"").slice(0,120)}function u(e){return e.replace(/[^a-z0-9_.:-]+/gi,"_").replace(/^_+|_+$/g,"").slice(0,120)||"unknown"}function Te(e,t={}){const o=x(e,".neocortex"),r=x(o,"state.json");if(!F(r))return{repaired:!1,reasonCodes:[],changes:[]};let s;if(!I(t.args,t.canonicalCommandId))try{const _=Y(e);_&&(s=K(e,_))}catch(_){const y=_ instanceof Q?_.reasonCode:"state_migration_apply_failed";throw new S(`[Neocortex] state.json migration stopped before invoke: ${_ instanceof Error?_.message:String(_)}. No unverified state was used.`,y)}let c;try{c=JSON.parse(q(r,"utf8"))}catch(_){const y=_ instanceof Error?_.message:String(_);throw new S(`[Neocortex] state.json repair stopped: invalid JSON in .neocortex/state.json (${y}). No files were overwritten. Fix the JSON or restore a backup, then rerun.`,"invalid_json")}if(!d(c))throw new S("[Neocortex] state.json repair stopped: top-level state must be a JSON object. Restore a valid .neocortex/state.json before invoking Neocortex.","state_not_object");const l=P(c,t.args,t.canonicalCommandId),a=I(t.args,t.canonicalCommandId)?[]:Ie(e,c,l),n=ve(c,{...t,effectiveDoneStoryIds:a});if(!n.mutated)return s?{repaired:!0,reasonCodes:["state_migration_transaction_applied"],backupPath:s.backupHash?`.neocortex/state.json.migration-backup-${s.operationId}`:void 0,changes:[`shared state migration ${s.outcome}`]}:{repaired:!1,reasonCodes:[],changes:[]};J(o,{recursive:!0});const i=ne(),p=x(o,`state.json.backup-${i}`),g=x(o,`state.json.tmp.${V(6).toString("hex")}`);W(r,p),H(g,`${JSON.stringify(n.state,null,2)}
|
|
2
|
+
`,"utf8"),B(g,r);const m=`.neocortex/state.json.backup-${i}`;return process.stderr.write(`[Neocortex P156.05] state.json auto-repaired before invoke; backup=${m}; reasonCodes=${n.reasonCodes.join(",")}; changes=${n.changes.join("; ")}
|
|
3
|
+
`),{repaired:!0,reasonCodes:n.reasonCodes,backupPath:m,changes:n.changes}}function Ie(e,t,o){const r=X(e),s=d(t.epics)?t.epics:{},c=d(t.stories)?t.stories:{},l=[];for(const[a,n]of Object.entries(r.stories)){if(!d(n)||!T(n.status)||!n.checkpoint)continue;const i=d(c[a])?c[a]:null,p=typeof i?.epic_id=="string"?i.epic_id:a.split(".")[0];if(!p||o&&p!==o)continue;const g=d(s[p])?s[p]:null,m=d(r.epics[p])?r.epics[p]:null,_=m?.branch_mode??g?.branch_mode,y=m?.execution_branch??g?.execution_branch;if(_!=="epic"||typeof y!="string")continue;ee(e,n.checkpoint,a,p,y).verdict==="verified"&&l.push(a)}return l.sort()}export{f as EPIC_AGGREGATE_REASON_CODES,te as P189_STATE_SHAPE_GAP_CODE,S as StateRepairHardStopError,I as isInlineYoloMaterialization,T as isP189DoneLikeStoryStatus,Te as maybeRepairStateJsonBeforeInvoke,me as reconcileEpicAggregatesInState};
|
|
@@ -93,9 +93,8 @@ export declare function persistYoloopFromResponse(metadata: Record<string, unkno
|
|
|
93
93
|
* on crash mid-write. Mirrors `YoloopClientStateStore.writeState()`
|
|
94
94
|
* pattern.
|
|
95
95
|
*
|
|
96
|
-
* Migration filesystem failures
|
|
97
|
-
*
|
|
98
|
-
* invoke boundary can fail closed.
|
|
96
|
+
* Migration directives and filesystem failures return `blocking: true`; the
|
|
97
|
+
* invoke boundary cannot continue after an unverified state write or rollback.
|
|
99
98
|
*/
|
|
100
99
|
export interface StateWritebackResult {
|
|
101
100
|
readonly applied: boolean;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import{YoloopClientStateStore as
|
|
2
|
-
`);return}}async function
|
|
3
|
-
`)}}function
|
|
4
|
-
`),{applied:!0,blocking:!1,reasonCode:"inline-yolo-materialized"}}catch(
|
|
5
|
-
`),{applied:!1,blocking:!0,reasonCode:"inline-yolo-persistence-failed"}}}async function
|
|
6
|
-
`),{applied
|
|
7
|
-
`),{applied:!
|
|
8
|
-
`),{applied:!1,blocking:!
|
|
1
|
+
import{YoloopClientStateStore as j}from"./yoloop-client-state-store.js";import{isYoloopInvocation as F}from"./target-normalization.js";import{applyStateMigration as T,StateMigrationApplyError as M}from"../migrations/state-json.js";function N(e){return F(e)}const V=/^\s*\*?(yoloop|auto-yolo|epic-runner|story-runner|loop-yolo|auto-epic)\b/i;function Z(e){if(typeof e.args!="string"||e.args.length===0)return;const r=e.args.match(V);if(!r)return;const i=r[0];if(e.args.slice(i.length).trim()!=="")return;let a=!1;try{const o=require("node:fs"),d=require("node:path").join(e.projectRoot,"docs","epics");a=o.statSync(d).isDirectory()}catch(o){const c=o instanceof Error?o.message:String(o);e.logger?.warn?.(`[Neocortex] Zero-config *yoloop hook (P121.05): could not stat docs/epics/: ${c}. Proceeding with raw args.`);return}a&&(e.args=`${i} @docs/epics/`,e.logger?.info?.("[Neocortex] Zero-config *yoloop (P121.05): auto-resolved empty args to @docs/epics/"))}async function B(e,r,i){if(N(e))try{return await new j(r).readAll(i)}catch(t){process.stderr.write(`[yoloop] Failed to read local state: ${t.message}. Continuing without state.
|
|
2
|
+
`);return}}async function L(e,r,i){if(!e)return;const t=e.yoloopStateUpdate;if(!(!t||!Array.isArray(t.operations)||t.operations.length===0))try{await new j(r).applyUpdate(t,i)}catch(a){const o=a.message??String(a);process.stderr.write(`[yoloop] Failed to persist state: ${o}. Your next *yoloop may repeat or skip an operation. Run *yoloop again to retry.
|
|
3
|
+
`)}}function n(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function z(e,r){return Array.isArray(e)&&e.length<=r&&e.every(i=>typeof i=="string"&&i.length>0&&i.length<=160&&/^[A-Za-z0-9_.:+-]+$/.test(i))}function R(e){return n(e.scope)?e.scope.kind==="projected"||typeof e.scope.omittedStoryCount=="number"&&e.scope.omittedStoryCount>0||typeof e.scope.omittedEpicCount=="number"&&e.scope.omittedEpicCount>0:!1}function H(e){const r={...e};return delete r.scope,delete r.stateSummary,delete r.graphRetrieval,delete r.runnerBootstrap,delete r.projectEvidence,delete r.continuity,delete r.implementCheckpoint,r}function D(e){return typeof e=="string"&&["done","completed","merged"].includes(e.trim().toLowerCase())}async function G(e,r){const i=e.syntheticStory;if(!n(i))return{applied:!1,blocking:!0,reasonCode:"inline-yolo-mutation-missing"};const t=i.storyId,a=i.storyPath,o=i.deterministicKey,c=i.storyContent;if(typeof t!="string"||!/^P\d+(?:\.\d+){2}$/.test(t)||typeof a!="string"||a!==`docs/stories/${t}.story.md`||typeof o!="string"||!/^[a-f0-9]{16}$/.test(o)||typeof c!="string"||c.length===0||c.length>131072||i.publicSafe!==!0||i.sanitized!==!0)return{applied:!1,blocking:!0,reasonCode:"inline-yolo-mutation-invalid"};const d=await import("node:fs/promises"),u=await import("node:path"),O=await import("node:crypto"),b=u.join(r,".neocortex"),w=u.join(b,"state.json"),h=u.join(r,...a.split("/"));let I;try{I=await d.readFile(w,"utf8")}catch{return{applied:!1,blocking:!0,reasonCode:"inline-yolo-state-missing"}}let p;try{p=JSON.parse(I)}catch{return{applied:!1,blocking:!0,reasonCode:"inline-yolo-state-corrupt"}}if(!n(p)||!n(p.stories)||!n(p.epics)||R(p))return{applied:!1,blocking:!0,reasonCode:"inline-yolo-state-not-full"};const s=p.stories[t];if(s!==void 0&&(!n(s)||s.epic_id!=="P174"||s.story_file!==a||!n(s.inlineYolo)||s.inlineYolo.deterministicKey!==o))return{applied:!1,blocking:!0,reasonCode:"inline-yolo-story-conflict"};const g=p.epics.P174;if(g!==void 0&&(!n(g)||g.id!==void 0&&g.id!=="P174"||!Array.isArray(g.stories)))return{applied:!1,blocking:!0,reasonCode:"inline-yolo-epic-conflict"};const k=n(g)?g:{},f=Array.isArray(k.stories)?k.stories:[];if(f.some(l=>typeof l!="string")||new Set(f).size!==f.length)return{applied:!1,blocking:!0,reasonCode:"inline-yolo-epic-story-list-invalid"};for(const l of f){const m=p.stories[l];if(!n(m)||m.id!==l||m.epic_id!=="P174")return{applied:!1,blocking:!0,reasonCode:"inline-yolo-epic-ownership-conflict"}}try{const l=await d.readFile(h,"utf8"),m=l.includes(`id: ${t}`)&&l.includes(`synthetic_key: ${o}`);if(l!==c&&!m)return{applied:!1,blocking:!0,reasonCode:"inline-yolo-story-file-conflict"}}catch(l){if(l.code!=="ENOENT")return{applied:!1,blocking:!0,reasonCode:"inline-yolo-story-file-unreadable"}}const y=f.includes(t)?[...f]:[...f,t],S={...p.stories,[t]:{...n(s)?s:{},id:t,epic_id:"P174",title:"Synthetic inline USER STORY for yolo",status:n(s)&&typeof s.status=="string"?s.status:"backlog",steps_completed:n(s)&&Array.isArray(s.steps_completed)?s.steps_completed:[],last_step:n(s)?s.last_step??null:null,branch_name:n(s)?s.branch_name??null:null,story_file:a,source:"inline-yolo-materialization",inlineYolo:{...n(s)&&n(s.inlineYolo)?s.inlineYolo:{},schemaVersion:1,deterministicKey:o,publicSafe:!0,sanitized:!0,materializedBeforeExecution:!0,ephemeralExecutionForbidden:!0}}},C=y.filter(l=>n(S[l])&&D(S[l].status)).length,P=n(p.inlineYolo)?p.inlineYolo:{},Y=n(P.materializations)?P.materializations:{},x=H({...p,stories:S,epics:{...p.epics,P174:{...k,id:"P174",status:C===y.length&&y.length>0?"done":"in-progress",stories:y,total_stories:y.length,completed_stories:C,...C===y.length&&y.length>0?{}:{completed_at:null}}},inlineYolo:{...P,schemaVersion:1,materializations:{...Y,[t]:{schemaVersion:1,storyId:t,storyPath:a,deterministicKey:o,publicSafe:!0,sanitized:!0,materializedBeforeExecution:!0,ephemeralExecutionForbidden:!0,pipelineTrigger:`*yolo @${a}`,reasonCodes:Array.isArray(i.reasonCodes)?i.reasonCodes:[]}}},last_updated:new Date().toISOString()}),_=`${Date.now()}-${O.randomBytes(6).toString("hex")}`,A=u.join(b,`state.json.tmp.${_}`),$=`${h}.tmp.${_}`,E=u.join(b,`state.json.backup-${new Date().toISOString().replace(/[:.]/g,"-")}`);let v=!1;try{await d.mkdir(b,{recursive:!0}),await d.mkdir(u.dirname(h),{recursive:!0});try{await d.access(h)}catch{await d.writeFile($,c,{encoding:"utf8",flag:"wx"}),await d.rename($,h),v=!0}return await d.copyFile(w,E),await d.writeFile(A,JSON.stringify(x,null,2),{encoding:"utf8",flag:"wx"}),await d.rename(A,w),process.stderr.write(`[Neocortex] inline yolo story materialized with bounded full-state merge; story=${t}; backup=${u.basename(E)}.
|
|
4
|
+
`),{applied:!0,blocking:!1,reasonCode:"inline-yolo-materialized"}}catch(l){await Promise.allSettled([d.unlink(A),d.unlink($)]),v&&await d.unlink(h).catch(()=>{});const m=l.message??String(l);return process.stderr.write(`[Neocortex] inline yolo materialization failed: ${m}.
|
|
5
|
+
`),{applied:!1,blocking:!0,reasonCode:"inline-yolo-persistence-failed"}}}async function J(e,r){if(!e||e.stateMutated!==!0)return{applied:!1,blocking:!1,reasonCode:"state-writeback-not-requested"};if(e.stateMutationStrategy==="merge-inline-yolo-v1"||e.stateMutationReason==="inline-yolo-materialized-synthetic-story")return G(e,r);if(e.stateMutationStrategy==="state-migration-v1"||e.stateMigrationPlan!==void 0){const t=e.stateMigrationPlan,a=new Set(["schemaVersion","operationId","sourceSchemaVersion","targetSchemaVersion","sourceCanonicalHash","targetCanonicalHash","steps","migratedEpicIds","migratedStoryIds","preservedUnknownFieldCount","reasonCodes"]);if(!n(t)||Object.keys(t).some(o=>!a.has(o))||t.schemaVersion!=="StateMigrationPlan/1.0"||typeof t.operationId!="string"||!/^smig_[a-f0-9]{32}$/.test(t.operationId)||typeof t.sourceCanonicalHash!="string"||!/^[a-f0-9]{64}$/.test(t.sourceCanonicalHash)||typeof t.targetCanonicalHash!="string"||!/^[a-f0-9]{64}$/.test(t.targetCanonicalHash)||![0,1,"absent"].includes(t.sourceSchemaVersion)||t.targetSchemaVersion!==1||!Array.isArray(t.steps)||t.steps.length>8192||t.steps.some(o=>!n(o)||Object.keys(o).some(c=>!["kind","source","target"].includes(c))||!["initialize-collection","merge-epic","merge-story","preserve-legacy","remove-legacy","set-schema-version"].includes(String(o.kind))||typeof o.target!="string"||o.target.length>512||!/^[A-Za-z0-9_.:[\]-]+$/.test(o.target)||o.source!==void 0&&(typeof o.source!="string"||o.source.length>512||!/^[A-Za-z0-9_.:[\]-]+$/.test(o.source)))||!z(t.migratedEpicIds,4096)||!z(t.migratedStoryIds,4096)||!z(t.reasonCodes,32)||!Number.isSafeInteger(t.preservedUnknownFieldCount)||t.preservedUnknownFieldCount<0)return{applied:!1,blocking:!0,reasonCode:"state-migration-plan-missing-or-invalid"};try{const o=T(r,t),c=`.neocortex/state.json.migration-backup-${o.operationId}`;return process.stderr.write(`[P220-EV-STATE-MIGRATION] outcome=${o.outcome}; operation=${o.operationId}; backup=${o.backupHash?c:"none"}.
|
|
6
|
+
`),{applied:o.outcome==="applied",blocking:!1,reasonCode:o.outcome==="applied"?"state-migration-applied":"state-migration-replay-verified"}}catch(o){const c=o instanceof M?o.reasonCode:"state-migration-apply-failed";return process.stderr.write(`[P220-EV-STATE-MIGRATION] blocked=${c}.
|
|
7
|
+
`),{applied:!1,blocking:!0,reasonCode:c}}}const i=e.migratedState;return!i||typeof i!="object"||Array.isArray(i)?{applied:!1,blocking:!0,reasonCode:"state-writeback-payload-missing"}:R(i)?(process.stderr.write(`[Neocortex] Refused projected/partial state replacement; canonical state was not modified.
|
|
8
|
+
`),{applied:!1,blocking:!0,reasonCode:"state-writeback-partial-rejected"}):(process.stderr.write(`[P220-EV-STATE-MIGRATION] blocked=state-migration-plan-missing-or-invalid; canonical state was not modified.
|
|
9
|
+
`),{applied:!1,blocking:!0,reasonCode:"state-migration-plan-missing-or-invalid"})}async function Q(e){if(!e.featureFlag)return{applied:!1,added:Object.freeze([]),skipped:Object.freeze([]),storiesAdded:0,reason:"flag off"};try{return await(await import("./discovery-hook.js")).runDiscoveryHook(e)}catch(r){const i=r instanceof Error?r.message:String(r);return e.logger?.warn?.(`[neocortex] discovery hook (lazy load) failed: ${i}. Proceeding without enrichment.`),{applied:!1,added:Object.freeze([]),skipped:Object.freeze([]),storiesAdded:0,reason:`lazy load error: ${i}`}}}async function W(e){if(!e.featureFlag)return{applied:!1,reason:"flag_off"};try{return await(await import("../graph-retrieval/pre-command-hook.js")).runGraphRetrievalHook(e)}catch(r){const i=r instanceof Error?r.message:String(r);return e.logger?.warn?.(`[neocortex] graph retrieval hook (lazy load) failed: ${i}. Proceeding without enrichment.`),{applied:!1,reason:"hook_error",warning:i}}}export{N as isYoloopTrigger,Z as maybeAutoResolveYoloopArgs,Q as maybeRunDiscoveryHook,W as maybeRunGraphRetrievalHook,J as persistMigratedStateFromResponse,L as persistYoloopFromResponse,B as readYoloopForRequest};
|
|
@@ -81,6 +81,78 @@ export interface YoloopPersistedSession {
|
|
|
81
81
|
/** P96.05 AC5: storyIds skipped due to `*yolo` timeout. */
|
|
82
82
|
readonly skippedStories?: readonly string[];
|
|
83
83
|
}
|
|
84
|
+
/** P220.11 additive run-control wire. Kept structural for standalone clients. */
|
|
85
|
+
export type YoloopRunStatus = 'created' | 'running' | 'pausing' | 'paused' | 'resuming' | 'cancelling' | 'success' | 'partial' | 'blocked' | 'failure' | 'exhausted' | 'cancelled';
|
|
86
|
+
export type YoloopOperationalBudgetDimension = 'deadline' | 'wall' | 'turns' | 'tokens' | 'cost' | 'toolCalls' | 'attempts' | 'feedback' | 'depth' | 'concurrency' | 'risk';
|
|
87
|
+
export type YoloopOperationalBudgetValues = Readonly<Record<YoloopOperationalBudgetDimension, number>>;
|
|
88
|
+
export interface YoloopPersistedOperationalBudgetV1 {
|
|
89
|
+
readonly schemaVersion: 'YoloopOperationalBudget/1.0';
|
|
90
|
+
readonly startedAtMs: number;
|
|
91
|
+
readonly limits: YoloopOperationalBudgetValues;
|
|
92
|
+
}
|
|
93
|
+
export interface YoloopPersistedProgressSnapshotV1 {
|
|
94
|
+
readonly schemaVersion: 'ProgressSnapshot/1.0';
|
|
95
|
+
readonly runId: string;
|
|
96
|
+
readonly snapshotId: string;
|
|
97
|
+
readonly sequence: number;
|
|
98
|
+
readonly revision: number;
|
|
99
|
+
readonly operationId: string;
|
|
100
|
+
readonly boundary: 'budget' | 'retry' | 'pause' | 'resume' | 'cancel' | 'terminal';
|
|
101
|
+
readonly phase: 'before' | 'after';
|
|
102
|
+
readonly status: YoloopRunStatus;
|
|
103
|
+
readonly occurredAt: string;
|
|
104
|
+
readonly parentSnapshotHash: string | null;
|
|
105
|
+
readonly snapshotHash: string;
|
|
106
|
+
readonly budget: {
|
|
107
|
+
readonly schemaVersion: 'YoloopOperationalBudgetSummary/1.0';
|
|
108
|
+
readonly limits: YoloopOperationalBudgetValues;
|
|
109
|
+
readonly usage: YoloopOperationalBudgetValues;
|
|
110
|
+
readonly exhaustedDimension: YoloopOperationalBudgetDimension | null;
|
|
111
|
+
};
|
|
112
|
+
readonly reasonCodes: readonly string[];
|
|
113
|
+
readonly publicSafe: true;
|
|
114
|
+
}
|
|
115
|
+
export interface YoloopPersistedRunEventV1 {
|
|
116
|
+
readonly schemaVersion: 'YoloopRunEvent/1.0';
|
|
117
|
+
readonly runId: string;
|
|
118
|
+
readonly operationId: string;
|
|
119
|
+
readonly eventId: string;
|
|
120
|
+
readonly kind: 'transition' | 'budget' | 'retry' | 'intent' | 'conflict' | 'terminal';
|
|
121
|
+
readonly sequence: number;
|
|
122
|
+
readonly revision: number;
|
|
123
|
+
readonly occurredAt: string;
|
|
124
|
+
readonly fromStatus: YoloopRunStatus;
|
|
125
|
+
readonly toStatus: YoloopRunStatus;
|
|
126
|
+
readonly reasonCodes: readonly string[];
|
|
127
|
+
readonly budgetDimension: YoloopOperationalBudgetDimension | null;
|
|
128
|
+
readonly parentEventHash: string | null;
|
|
129
|
+
readonly eventHash: string;
|
|
130
|
+
readonly publicSafe: true;
|
|
131
|
+
}
|
|
132
|
+
export interface YoloopRunTransitionReceiptV1 {
|
|
133
|
+
readonly schemaVersion: 'YoloopRunTransitionReceipt/1.0';
|
|
134
|
+
readonly runId: string;
|
|
135
|
+
readonly operationId: string;
|
|
136
|
+
readonly idempotencyKey: string;
|
|
137
|
+
readonly payloadHash: string;
|
|
138
|
+
readonly revision: number;
|
|
139
|
+
readonly snapshotHash: string | null;
|
|
140
|
+
readonly status: YoloopRunStatus;
|
|
141
|
+
}
|
|
142
|
+
export interface YoloopRunControlV1 {
|
|
143
|
+
readonly schemaVersion: 'YoloopRunControl/1.0';
|
|
144
|
+
readonly runId: string;
|
|
145
|
+
readonly revision: number;
|
|
146
|
+
readonly status: YoloopRunStatus;
|
|
147
|
+
readonly startedAt: string;
|
|
148
|
+
readonly deadlineAt: string;
|
|
149
|
+
readonly operationalBudget: YoloopPersistedOperationalBudgetV1;
|
|
150
|
+
readonly usage: YoloopOperationalBudgetValues;
|
|
151
|
+
readonly events: readonly YoloopPersistedRunEventV1[];
|
|
152
|
+
readonly progressSnapshots: readonly YoloopPersistedProgressSnapshotV1[];
|
|
153
|
+
readonly latestSnapshotHash: string | null;
|
|
154
|
+
readonly receipts: readonly YoloopRunTransitionReceiptV1[];
|
|
155
|
+
}
|
|
84
156
|
/**
|
|
85
157
|
* Forward-declared opaque reference to `YoloopRecoveryAttempt` (canonical lives
|
|
86
158
|
* in `@neocortex/shared/yoloop/recovery-types`). Structural mirror to avoid
|
|
@@ -100,7 +172,7 @@ export interface YoloopRecoveryAttemptRef {
|
|
|
100
172
|
* Mirrors server-internal `YoloopRuntimeState` minus lock-related fields.
|
|
101
173
|
*/
|
|
102
174
|
export interface YoloopPersistedCounters {
|
|
103
|
-
readonly version: '1.0' | '1.1';
|
|
175
|
+
readonly version: '1.0' | '1.1' | '1.2';
|
|
104
176
|
readonly lastResetAt: string;
|
|
105
177
|
readonly storiesRunToday: number;
|
|
106
178
|
readonly iterationsToday: number;
|
|
@@ -111,6 +183,8 @@ export interface YoloopPersistedCounters {
|
|
|
111
183
|
readonly totalRunsAllTime: number;
|
|
112
184
|
readonly totalStoriesAllTime: number;
|
|
113
185
|
readonly currentSession: YoloopPersistedSession | null;
|
|
186
|
+
/** P220.11: absent for readable v1.0/v1.1 state. */
|
|
187
|
+
readonly runControl?: YoloopRunControlV1;
|
|
114
188
|
/** P205.08 semantic continuation policy. New state writes v2; absence is legacy v1. */
|
|
115
189
|
readonly continuationPolicy?: 'p137-resume-first-v1' | 'p205-finite-resume-v2';
|
|
116
190
|
/** Opaque, public-safe frozen session/certificate state owned by the root. */
|
|
@@ -144,6 +218,9 @@ export type YoloopStateOperation = {
|
|
|
144
218
|
readonly lock: YoloopPersistedLock;
|
|
145
219
|
} | {
|
|
146
220
|
readonly type: 'release_lock';
|
|
221
|
+
readonly expectedRunId?: string;
|
|
222
|
+
readonly expectedSessionId?: string;
|
|
223
|
+
readonly expectedLeaseId?: string;
|
|
147
224
|
} | {
|
|
148
225
|
readonly type: 'write_state';
|
|
149
226
|
readonly state: YoloopPersistedCounters;
|
|
@@ -169,6 +246,15 @@ export type YoloopStateOperation = {
|
|
|
169
246
|
readonly type: 'switch_continuation_policy';
|
|
170
247
|
readonly policy: 'p137-resume-first-v1' | 'p205-finite-resume-v2';
|
|
171
248
|
readonly session: Readonly<Record<string, unknown>>;
|
|
249
|
+
} | {
|
|
250
|
+
readonly type: 'transition_run';
|
|
251
|
+
readonly runId: string;
|
|
252
|
+
readonly operationId: string;
|
|
253
|
+
readonly idempotencyKey: string;
|
|
254
|
+
readonly expectedRevision: number;
|
|
255
|
+
readonly expectedSnapshotHash: string | null;
|
|
256
|
+
readonly payloadHash: string;
|
|
257
|
+
readonly next: YoloopRunControlV1;
|
|
172
258
|
} | {
|
|
173
259
|
readonly type: 'recovery_attempted';
|
|
174
260
|
readonly attempt: YoloopRecoveryAttemptRef;
|