@ornexus/neocortex 4.60.23 → 4.60.25
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 +23 -23
- package/install.js +67 -10
- package/install.ps1 +39 -8
- package/install.sh +18 -6
- package/package.json +5 -2
- package/packages/client/dist/adapters/adapter-registry.d.ts +2 -2
- package/packages/client/dist/adapters/adapter-registry.js +1 -1
- package/packages/client/dist/adapters/grok-adapter.d.ts +11 -0
- package/packages/client/dist/adapters/grok-adapter.js +2 -0
- package/packages/client/dist/adapters/index.d.ts +1 -0
- package/packages/client/dist/adapters/index.js +1 -1
- package/packages/client/dist/adapters/platform-detector.d.ts +1 -1
- package/packages/client/dist/adapters/platform-detector.js +1 -1
- package/packages/client/dist/agent/refresh-stubs.js +2 -2
- package/packages/client/dist/cli.js +21 -2
- package/packages/client/dist/commands/invoke.d.ts +20 -21
- package/packages/client/dist/commands/invoke.js +59 -55
- package/packages/client/dist/commands/story-checkpoint.d.ts +15 -0
- package/packages/client/dist/commands/story-checkpoint.js +1 -0
- package/packages/client/dist/config.js +1 -1
- package/packages/client/dist/generated/command-registry.generated.d.ts +18 -52
- package/packages/client/dist/generated/command-registry.generated.js +1 -1
- package/packages/client/dist/git/direct-yolo-checkpoint.d.ts +67 -0
- package/packages/client/dist/git/direct-yolo-checkpoint.js +3 -0
- package/packages/client/dist/git/git-lifecycle-step-ids.d.ts +2 -0
- package/packages/client/dist/git/git-lifecycle-step-ids.js +1 -0
- package/packages/client/dist/git/git-lifecycle-store.d.ts +198 -0
- package/packages/client/dist/git/git-lifecycle-store.js +4 -0
- package/packages/client/dist/git/secure-filesystem-boundary.d.ts +26 -0
- package/packages/client/dist/git/secure-filesystem-boundary.js +123 -0
- package/packages/client/dist/graph-retrieval/shared-graph-retrieval-contract.js +1 -1
- package/packages/client/dist/index.d.ts +2 -0
- package/packages/client/dist/index.js +1 -1
- package/packages/client/dist/memory/shared-project-memory-types.d.ts +29 -2
- package/packages/client/dist/memory/shared-project-memory-types.js +2 -2
- package/packages/client/dist/policy/subagent-approval-policy.js +1 -1
- package/packages/client/dist/runner/headless/continuous-story-workspace.d.ts +131 -0
- package/packages/client/dist/runner/headless/continuous-story-workspace.js +58 -0
- package/packages/client/dist/runner/headless/epic-git-reconciliation-inventory.d.ts +23 -0
- package/packages/client/dist/runner/headless/epic-git-reconciliation-inventory.js +1 -0
- package/packages/client/dist/runner/headless/epic-merge-candidate-qa.d.ts +15 -0
- package/packages/client/dist/runner/headless/epic-merge-candidate-qa.js +28 -0
- package/packages/client/dist/runner/headless/epic-terminal-git-cli-adapter.d.ts +12 -0
- package/packages/client/dist/runner/headless/epic-terminal-git-cli-adapter.js +8 -0
- package/packages/client/dist/runner/headless/epic-terminal-git-runner.d.ts +134 -0
- package/packages/client/dist/runner/headless/epic-terminal-git-runner.js +2 -0
- package/packages/client/dist/runner/headless/prepared-story-bundle.js +1 -1
- package/packages/client/dist/runner/headless/run-yolo.js +1 -1
- package/packages/client/dist/runner/headless/run-yoloop.d.ts +7 -2
- package/packages/client/dist/runner/headless/run-yoloop.js +8 -1
- package/packages/client/dist/runner/headless/types.d.ts +1 -1
- package/packages/client/dist/runner/headless/types.js +2 -2
- package/packages/client/dist/runner-cli.js +0 -0
- package/packages/client/dist/state/project-state-snapshot.js +1 -1
- package/packages/client/dist/state/state-json-repair.d.ts +9 -0
- package/packages/client/dist/state/state-json-repair.js +3 -3
- package/packages/client/dist/tier/tier-aware-client.js +1 -1
- package/packages/client/dist/yoloop/invoke-hooks.d.ts +13 -10
- package/packages/client/dist/yoloop/invoke-hooks.js +8 -5
- package/packages/client/dist/yoloop/shared-discover-epics.js +1 -1
- package/packages/shared/dist/generated/command-registry.generated.js +18 -66
- package/packages/shared/dist/generated/invocation-schema.generated.js +5 -25
- package/packages/shared/dist/invocation/index.d.ts +1 -1
- package/packages/shared/dist/yoloop/epic-terminal-git.js +227 -0
- package/packages/shared/dist/yoloop/planning-graph.js +119 -0
- package/packages/shared/dist/yoloop/public-story-write-set.js +27 -0
- package/packages/shared/dist/yoloop/wave-decision-receipt.js +19 -0
- package/targets-stubs/antigravity/gemini.md +1 -1
- package/targets-stubs/antigravity/neocortex.md +18 -0
- 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 +21 -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 +20 -1
- package/targets-stubs/cursor/agent.md +20 -2
- package/targets-stubs/gemini-cli/agent.md +2 -2
- package/targets-stubs/gemini-cli/gemini.md +18 -0
- package/targets-stubs/grok/README.md +13 -0
- package/targets-stubs/grok/SKILL.md +50 -0
- package/targets-stubs/grok/config-mcp.toml +22 -0
- package/targets-stubs/grok/install-grok.sh +31 -0
- package/targets-stubs/kimi/neocortex.md +18 -0
- package/targets-stubs/openclaw/SKILL.md +18 -0
- package/targets-stubs/opencode/neocortex-root.md +20 -1
- package/targets-stubs/vscode/neocortex.agent.md +20 -2
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import{execFile as st,spawn as Me}from"node:child_process";import{createHash as G,randomBytes as Ke}from"node:crypto";import{lstat as le,readFile as B,realpath as Y,stat as dt}from"node:fs/promises";import{basename as $,dirname as C,isAbsolute as Fe,join as D,relative as ut,resolve as ht}from"node:path";import{promisify as ft}from"node:util";import{admitPreparedStoryBundle as pt}from"./prepared-story-bundle.js";import{acquireGitLifecyclePhysicalIntegrationLock as lt,assertGitLifecyclePrivateDirectory as W,assertGitLifecyclePrivateFile as F,applyGitLifecycleRuntimeOverlay as re,computeImmutableStoryQaEvidenceDigest as yt,computeP205StoryQaEvidenceDigest as wt,ensureGitLifecyclePrivateChildDirectory as be,ensureRuntimeEpic as ye,ensureRuntimeStory as mt,gitLifecycleRuntimeDirectory as gt,mutateGitLifecycleRuntime as X,P205_COMPLETE_COMMAND as St,readGitLifecycleRuntime as V,releaseGitLifecyclePhysicalIntegrationLock as It,removeGitLifecyclePrivateDirectory as oe,verifyAcceptedGitLifecycleStoryCheckpoint as ke,verifyGitLifecyclePhysicalIntegrationLock as xe}from"../../git/git-lifecycle-store.js";import{anchoredAtomicWriteFile as vt,anchoredCreateDirectory as Ee,anchoredDirectoryIdentity as Rt,anchoredExecuteGit as Ae,anchoredEnsureDirectory as bt,anchoredRemoveFile as kt}from"../../git/secure-filesystem-boundary.js";import{GIT_LIFECYCLE_COMMIT_PUSH_STEP_ID as Ve,GIT_LIFECYCLE_REVIEW_PR_STEP_ID as we}from"../../git/git-lifecycle-step-ids.js";const je=ft(st),xt=String.raw`
|
|
2
|
+
const fs = require('node:fs');
|
|
3
|
+
const cp = require('node:child_process');
|
|
4
|
+
const [expectedParentDev, expectedParentIno, name, expectedChildDev, expectedChildIno, encoded, handshake] = process.argv.slice(1);
|
|
5
|
+
const fail = (code) => { process.stderr.write(code); process.exit(73); };
|
|
6
|
+
const safeName = typeof name === 'string' && name.length > 0 && name.length <= 255
|
|
7
|
+
&& !name.includes('/') && !name.includes('\\') && !name.includes('\0') && name !== '.' && name !== '..';
|
|
8
|
+
if (!safeName) fail('continuous-child-name-invalid');
|
|
9
|
+
const parent = fs.statSync('.', { bigint: true });
|
|
10
|
+
if (!parent.isDirectory() || String(parent.dev) !== expectedParentDev || String(parent.ino) !== expectedParentIno) {
|
|
11
|
+
fail('continuous-child-parent-anchor-mismatch');
|
|
12
|
+
}
|
|
13
|
+
const childEntry = fs.lstatSync(name, { bigint: true });
|
|
14
|
+
if (!childEntry.isDirectory() || childEntry.isSymbolicLink()
|
|
15
|
+
|| String(childEntry.dev) !== expectedChildDev || String(childEntry.ino) !== expectedChildIno) {
|
|
16
|
+
fail('continuous-child-anchor-mismatch');
|
|
17
|
+
}
|
|
18
|
+
process.chdir(name);
|
|
19
|
+
const handed = fs.statSync('.', { bigint: true });
|
|
20
|
+
if (!handed.isDirectory() || String(handed.dev) !== expectedChildDev || String(handed.ino) !== expectedChildIno) {
|
|
21
|
+
fail('continuous-child-handoff-mismatch');
|
|
22
|
+
}
|
|
23
|
+
if (handshake === '1') {
|
|
24
|
+
process.stderr.write('continuous-child-handoff-ready\n');
|
|
25
|
+
fs.readFileSync(0);
|
|
26
|
+
}
|
|
27
|
+
let invocation;
|
|
28
|
+
try { invocation = JSON.parse(Buffer.from(encoded, 'base64url').toString('utf8')); } catch { fail('continuous-child-invocation-invalid'); }
|
|
29
|
+
if (!invocation || typeof invocation.command !== 'string' || invocation.command.length === 0 || invocation.command.length > 4096
|
|
30
|
+
|| invocation.command.includes('\0') || !Array.isArray(invocation.args) || invocation.args.length > 512
|
|
31
|
+
|| invocation.args.some((arg) => typeof arg !== 'string' || arg.length > 32768 || arg.includes('\0'))) {
|
|
32
|
+
fail('continuous-child-invocation-invalid');
|
|
33
|
+
}
|
|
34
|
+
process.umask(0o077);
|
|
35
|
+
const childEnv = { ...process.env };
|
|
36
|
+
for (const key of ['GIT_DIR', 'GIT_WORK_TREE', 'GIT_INDEX_FILE', 'GIT_OBJECT_DIRECTORY',
|
|
37
|
+
'GIT_ALTERNATE_OBJECT_DIRECTORIES', 'GIT_COMMON_DIR', 'GIT_NAMESPACE', 'GIT_CEILING_DIRECTORIES']) delete childEnv[key];
|
|
38
|
+
const runtime = cp.spawn(invocation.command, invocation.args, { cwd: '.', env: childEnv, shell: false, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
39
|
+
runtime.stdout.pipe(process.stdout);
|
|
40
|
+
runtime.stderr.pipe(process.stderr);
|
|
41
|
+
const forward = (signal) => { try { runtime.kill(signal); } catch {} };
|
|
42
|
+
process.on('SIGTERM', () => forward('SIGTERM'));
|
|
43
|
+
process.on('SIGINT', () => forward('SIGINT'));
|
|
44
|
+
runtime.on('error', (error) => fail('continuous-child-command-error:' + String(error && error.code || 'UNKNOWN')));
|
|
45
|
+
runtime.on('close', (code, signal) => {
|
|
46
|
+
if (signal) process.kill(process.pid, signal);
|
|
47
|
+
else process.exit(Number.isInteger(code) ? code : 1);
|
|
48
|
+
});
|
|
49
|
+
`;function ur(e,r={}){return async(t,o,n={})=>Nt(e,t,o,n,r)}function hr(e={}){let r=Promise.resolve();return{async prepare(t){if((e.platform??process.platform)==="win32")return f("continuous-sandbox-platform-unsupported");if(!/^[a-f0-9]{64}$/.test(t.dispatchId))return f("continuous-sandbox-dispatch-invalid");if(!ce(t.targetRef))return f("continuous-sandbox-target-invalid");if(!Jt(t.executionBranch))return f("continuous-execution-branch-invalid");if(!/^[a-f0-9]{64}$/.test(t.graphRevision))return f("continuous-graph-revision-invalid");const o=Ht(t.authorizedWriteSet);if(o.length===0||o.length!==t.authorizedWriteSet.length)return f("continuous-authorized-write-set-invalid");const n=Te(o),a=r;let c=()=>{};r=new Promise(s=>{c=s}),await a;let i,d;try{const s=await At(t.projectRoot),g=await P(s,["rev-parse","HEAD"]),S=await Ie(s,t.epicId,t.storyId),v=S.revision,A=await gt(s),w=await be(A,"leases"),j=D(w,t.dispatchId);try{await Ee(w,t.dispatchId),await W(j,w)}catch(m){if(!q(m,"EEXIST"))throw m;await W(j,w);const l=await ue(j,w);if(l!==void 0&&Qt(l.pid))return f("continuous-sandbox-lease-active");if(!l||!await oe(j,w,async E=>ot(await ue(E,w),l)))return f("continuous-sandbox-lease-owner-replaced");await Ee(w,t.dispatchId),await W(j,w)}i=j,d=Object.freeze({pid:process.pid,createdAt:new Date().toISOString(),nonce:Ke(16).toString("hex")}),await te(j,"owner.json",Buffer.from(JSON.stringify(d)),e.beforeSandboxWriteCommit),await F(D(j,"owner.json"),j);const R=await be(A,"sandboxes"),p=D(R,t.dispatchId.slice(0,20)),I=await Ge(p,R);let b,u;const k=I?.preparedAt??new Date().toISOString(),x=I?.workspaceOwnerNonce??Ke(16).toString("hex");if(I){if(I.projectRoot!==s||I.storyId!==t.storyId||I.targetRef!==t.targetRef||I.dispatchId!==t.dispatchId||I.epicId!==t.epicId||I.executionBranch!==t.executionBranch||I.graphRevision!==t.graphRevision||I.claimRevision!==n||I.graphAuthorityRevision!==v||I.graphAuthorityEpoch!==S.epoch||I.graphAuthorityStoryStatus!==S.storyStatus||!rt(I.authorizedWriteSet,o)||(b=I.contextSnapshots.map(m=>({path:m.path,...m.contentBase64!==void 0?{content:Buffer.from(m.contentBase64,"base64")}:{}})),u=await Rt(p),u.dev!==I.workspaceIdentity.dev||u.ino!==I.workspaceIdentity.ino))return f("continuous-sandbox-recovery-mismatch")}else await oe(p,R),u=await Ee(R,$(p)),await W(p,R),await ne(R,$(p),u,["init","--quiet"]),await Dt(s,p),await jt(s,R,$(p),u),await ne(R,$(p),u,["remote","add","neocortex-root",s]),await ne(R,$(p),u,["fetch","--quiet","--no-tags","neocortex-root",g]),await ne(R,$(p),u,["checkout","--quiet","--detach","FETCH_HEAD"]),b=await Et(s,p,t.epicId,t.targetRef,e.beforeSandboxWriteCommit),await Zt(p,R,{schemaVersion:1,projectRoot:s,storyId:t.storyId,targetRef:t.targetRef,epicId:t.epicId,executionBranch:t.executionBranch,graphRevision:t.graphRevision,graphAuthorityRevision:v,graphAuthorityEpoch:S.epoch,graphAuthorityStoryStatus:S.storyStatus,authorizedWriteSet:o,claimRevision:n,dispatchId:t.dispatchId,baseSha:g,preparedAt:k,workspaceOwnerNonce:x,workspaceIdentity:u,contextSnapshots:b.map(m=>({path:m.path,...m.content!==void 0?{contentBase64:m.content.toString("base64")}:{}}))},e.beforeSandboxWriteCommit);return i=void 0,{ok:!0,value:{projectRoot:s,workspacePath:p,workspaceStrategy:"branchless_sandbox",storyId:t.storyId,targetRef:t.targetRef,epicId:t.epicId,executionBranch:t.executionBranch,graphRevision:t.graphRevision,graphAuthorityRevision:v,graphAuthorityEpoch:S.epoch,graphAuthorityStoryStatus:S.storyStatus,authorizedWriteSet:o,claimRevision:n,baseSha:I?.baseSha??g,dispatchId:t.dispatchId,preparedAt:k,leasePath:j,leaseOwnerNonce:d.nonce,workspaceOwnerNonce:x,workspaceIdentity:u,contextSnapshots:b}}}catch{if(i&&d){const s=i,g=d;await oe(s,C(s),async S=>ot(await ue(S,C(s)),g)).catch(()=>{})}return f("continuous-sandbox-prepare-failed")}finally{c()}},async integrate(t,o){const n=await ue(t.leasePath,C(t.leasePath)).catch(()=>{}),a=await Ge(t.workspacePath,C(t.workspacePath)).catch(()=>{});if(!n||n.nonce!==t.leaseOwnerNonce||a?.workspaceOwnerNonce!==t.workspaceOwnerNonce||a.dispatchId!==t.dispatchId||a.storyId!==t.storyId)return f("continuous-sandbox-owner-mismatch");const c=Xe(o?.nowIso??new Date().toISOString()),i=await lt(t.projectRoot,{kind:"continuous",storyId:t.storyId,sessionId:t.dispatchId,idempotencyKey:t.dispatchId,expiresAt:new Date(Date.parse(c)+1800*1e3).toISOString(),persistent:!1,createdAt:c});if(!i.ok)return f(i.reasonCode==="git-lifecycle-integration-backpressure"?"continuous-integration-backpressure":"continuous-integration-lease-failed");let d=[],s=!1,g=!1;try{const S=(await ie(t,["status","--porcelain"])).trimEnd(),v=await De(t,["rev-parse","HEAD"]);if(await ee(t.projectRoot,["symbolic-ref","--quiet","--short","HEAD"])!==t.executionBranch)return f("continuous-execution-branch-mismatch");const w=C(C(t.leasePath)),j=await be(w,"integrated"),R=D(j,t.dispatchId),p=await Ut(R,j);if(p?.childHeadSha===v&&p.bundle.storyId===t.storyId&&p.bundle.epicId===t.epicId&&p.bundle.executionBranch===t.executionBranch&&p.bundle.graphRevision===t.graphRevision&&p.bundle.claimRevision===t.claimRevision&&rt(p.bundle.authorizedWriteSet,t.authorizedWriteSet)&&p.checkpoint.idempotencyKey===t.dispatchId&&p.checkpoint.bundleId===p.bundle.bundleId&&p.checkpoint.bundleDigest===p.bundle.bundleDigest&&p.checkpoint.graphRevision===t.graphRevision&&p.checkpoint.claimRevision===t.claimRevision&&await ae(t.projectRoot,["merge-base","--is-ancestor",p.rootHeadSha,"HEAD"])&&await Yt(t,p.checkpoint))return{ok:!0,value:{headSha:p.rootHeadSha,alreadyIntegrated:!0,bundle:p.bundle,checkpoint:p.checkpoint}};if(v!==t.baseSha)return f("continuous-sandbox-child-git-authority-denied");if(S.length===0)return f("continuous-bundle-empty");const I=S.split(`
|
|
50
|
+
`).some(y=>y.length>0&&y[0]!==" "&&y[0]!=="?");I||await De(t,["add","-A"]);const b=L(await ie(t,["diff","--cached","--name-only","-z",t.baseSha]));if(b.length===0||b.some(y=>!ce(y)))return f("continuous-integration-write-set-invalid");if(!await Ct(t,["merge-base","--is-ancestor",t.baseSha,v]))return f("continuous-bundle-base-not-ancestor");if(!await ae(t.projectRoot,["merge-base","--is-ancestor",t.baseSha,"HEAD"]))return f("continuous-root-base-not-ancestor");const u=o?.nowIso??new Date().toISOString(),k=Ft(o?.receiptRefs,t.dispatchId),x=await ie(t,["diff","--cached","--binary",t.baseSha]),m={schemaVersion:1,storyId:t.storyId,epicId:t.epicId,executionBranch:t.executionBranch,baseSha:t.baseSha,bundleDigest:G("sha256").update(x).digest("hex"),graphRevision:t.graphRevision,claimRevision:t.claimRevision,authorizedWriteSet:t.authorizedWriteSet,contextDigests:Object.fromEntries(t.contextSnapshots.map(y=>[y.path,G("sha256").update(y.content??Buffer.alloc(0)).digest("hex")])),validationRefs:k,createdAt:t.preparedAt,expiresAt:new Date(Date.parse(t.preparedAt)+1440*60*1e3).toISOString(),branchless:!0},l=Object.freeze({...m,bundleId:Te(m)}),E=await er(t,l);if(E){const y=await Xt(t,l,E);if(y)return await We(R,v,E.headSha,l,y,e.beforeSandboxWriteCommit),{ok:!0,value:{headSha:E.headSha,alreadyIntegrated:!0,bundle:l,checkpoint:y}}}if(!E&&await Kt(t.projectRoot,t.epicId)!==t.graphAuthorityRevision)return f("continuous-graph-revision-stale");if(E){const y=await Ye({lease:t,bundle:l,baseSha:E.baseSha,headSha:E.headSha,receiptRefs:k,nowIso:u,qaRunner:e.storyQaRunner});if(!y.ok)return y;await e.beforeCheckpointPersist?.({projectRoot:t.projectRoot,storyId:t.storyId,headSha:E.headSha});const O=await Ue({lease:t,physicalLock:i,checkpointAuthority:E.checkpointAuthority,bundle:l,baseSha:E.baseSha,headSha:E.headSha,receiptRefs:[...k,`bundle:${l.bundleId}`,y.value.qaReceipt.receiptRef,y.value.reviewReceipt.receiptRef],qaReceipt:y.value.qaReceipt,reviewReceipt:y.value.reviewReceipt,recordedAt:u,checkpointNow:e.checkpointNow,insideCheckpointPreCommit:e.insideCheckpointPreCommit,afterCheckpointPersist:e.afterCheckpointPersist});return O.ok?(await We(R,v,E.headSha,l,O.value,e.beforeSandboxWriteCommit),{ok:!0,value:{headSha:E.headSha,alreadyIntegrated:!0,bundle:l,checkpoint:O.value}}):O}if(I)return f("continuous-sandbox-child-git-authority-denied");if(L(await z(t.projectRoot,["diff","--cached","--name-only","-z"])).length>0)return f("continuous-integration-root-index-dirty");const J=new Set(t.contextSnapshots.map(y=>y.path));if(b.filter(y=>!t.authorizedWriteSet.some(O=>Se(O,y))).length>0)return f("continuous-bundle-write-set-unauthorized");const M=b.filter(y=>!J.has(y)),Re=new Set(L(await z(t.projectRoot,["diff","--name-only","-z",`${t.baseSha}..HEAD`])));if(M.some(y=>Re.has(y)))return f("continuous-bundle-intervening-conflict");const he=new Set([...L(await z(t.projectRoot,["diff","--name-only","-z"])),...L(await z(t.projectRoot,["ls-files","--others","--exclude-standard","-z"]))]);if(M.some(y=>he.has(y)))return f("continuous-integration-root-conflict");d=await $t(t.projectRoot,[...new Set([...b,".neocortex/state.json"])]);const H=await zt(t,u);if(!H.ok)return H;const K=pt({bundle:l,payload:Buffer.from(x),expected:{epicId:t.epicId,storyId:t.storyId,executionBranch:t.executionBranch,baseSha:t.baseSha,graphRevision:t.graphRevision,claimRevision:t.claimRevision,authorizedWriteSet:t.authorizedWriteSet,nowIso:u},writerLease:H.value});if(K.length>0)return await Ne(t.projectRoot,d),f(`continuous-bundle-admission-${K[0]}`);const fe=await P(t.projectRoot,["rev-parse","HEAD"]);let Q;await e.beforeRootCommit?.({projectRoot:t.projectRoot,storyId:t.storyId,headSha:fe});try{await X(t.projectRoot,y=>{const O=h(ye(y,t.epicId).branch_lease);if(O?.ownerStoryId!==t.storyId||O.sessionId!==t.dispatchId||O.idempotencyKey!==t.dispatchId||Date.parse(String(O.expiresAt))<=Date.now())throw new Error("continuous-checkpoint-writer-lease-lost")},{preCommit:async y=>{const O=await Ie(t.projectRoot,t.epicId,t.storyId,y);if(O.revision!==t.graphAuthorityRevision||O.epoch!==t.graphAuthorityEpoch||O.storyStatus!==t.graphAuthorityStoryStatus)throw new Error("continuous-graph-revision-stale");if(!await xe(i.path,i.owner))throw new Error("continuous-integration-lock-raced");const nt=await ee(t.projectRoot,["symbolic-ref","--quiet","--short","HEAD"]),it=await P(t.projectRoot,["rev-parse","HEAD"]);if(nt!==t.executionBranch||it!==fe)throw new Error("continuous-integration-head-raced");const at=L(await z(t.projectRoot,["diff","--cached","--name-only","-z"])),ct=new Set([...L(await z(t.projectRoot,["diff","--name-only","-z"])),...L(await z(t.projectRoot,["ls-files","--others","--exclude-standard","-z"]))]);if(at.length>0)throw new Error("continuous-integration-root-index-dirty");if(M.some(_=>ct.has(_)))throw new Error("continuous-integration-root-conflict");if(g=!0,M.length>0){const _=await ie(t,["diff","--cached","--binary",t.baseSha,"--",...M]);if(_.length>0&&!await _t(t.projectRoot,["apply","--index","--3way","--whitespace=nowarn"],_))throw new Error("continuous-integration-conflict")}for(const _ of t.contextSnapshots){if(!b.includes(_.path))continue;const qe=await Ce(D(t.projectRoot,_.path)),He=await Ot(t,_.path),Je=_.path===".neocortex/state.json"?qt(_.content,qe,Gt(_.content,He,t.storyId)):Wt(_.content,qe,He);if(!Je.ok)throw new Error(_.path===".neocortex/state.json"?"continuous-state-merge-conflict":"continuous-context-merge-conflict");await te(t.projectRoot,_.path,Je.value),await P(t.projectRoot,["add","-A","--",_.path])}if(b.includes(".neocortex/state.json")&&await P(t.projectRoot,["add","-A","--",".neocortex/state.json"]),await ae(t.projectRoot,["diff","--cached","--quiet"]))throw new Error("continuous-integration-empty-after-admission");Q=await Ie(t.projectRoot,t.epicId,t.storyId,y),await P(t.projectRoot,["commit","-m",`feat(yoloop): integrate ${t.storyId}`,"-m",`Neocortex-Story: ${t.storyId}`,"-m",`Neocortex-Epic: ${t.epicId}`,"-m",`Neocortex-Execution-Branch: ${t.executionBranch}`,"-m",`Neocortex-Prepared-Base: ${t.baseSha}`,"-m",`Neocortex-Dispatch: ${t.dispatchId}`,"-m",`Neocortex-Bundle: ${l.bundleId}`,"-m",`Neocortex-Bundle-Digest: ${l.bundleDigest}`,"-m",`Neocortex-Graph-Revision: ${t.graphRevision}`,"-m",`Neocortex-Claim-Revision: ${t.claimRevision}`,"-m",`Neocortex-Checkpoint-Authority: ${Q.revision}`,"-m",`Neocortex-Checkpoint-Epoch: ${Q.epoch??"null"}`,"-m",`Neocortex-Checkpoint-Story-Status: ${Q.storyStatus??"null"}`]),s=!0}})}catch(y){g&&!s&&await Ne(t.projectRoot,d);const O=y instanceof Error&&/^continuous-/.test(y.message)?y.message:"continuous-integration-failed";return f(O)}const Z=await P(t.projectRoot,["rev-parse","HEAD"]);if(!Q)return f("continuous-checkpoint-authority-missing");await e.afterRootCommit?.({projectRoot:t.projectRoot,storyId:t.storyId,headSha:Z});const U=await Ye({lease:t,bundle:l,baseSha:fe,headSha:Z,receiptRefs:k,nowIso:u,qaRunner:e.storyQaRunner});if(!U.ok)return U;await e.beforeCheckpointPersist?.({projectRoot:t.projectRoot,storyId:t.storyId,headSha:Z});const pe=await Ue({lease:t,physicalLock:i,checkpointAuthority:Q,bundle:l,baseSha:fe,headSha:Z,receiptRefs:[...k,`bundle:${l.bundleId}`,U.value.qaReceipt.receiptRef,U.value.reviewReceipt.receiptRef],qaReceipt:U.value.qaReceipt,reviewReceipt:U.value.reviewReceipt,recordedAt:u,checkpointNow:e.checkpointNow,insideCheckpointPreCommit:e.insideCheckpointPreCommit,afterCheckpointPersist:e.afterCheckpointPersist});return pe.ok?(await We(R,v,Z,l,pe.value,e.beforeSandboxWriteCommit),{ok:!0,value:{headSha:Z,alreadyIntegrated:!1,bundle:l,checkpoint:pe.value}}):pe}catch{return g&&!s&&d.length>0&&await Ne(t.projectRoot,d),f("continuous-integration-failed")}finally{await Tt(t).catch(()=>{}),await It(i.path,i.owner).catch(()=>{})}},async release(t,o){o||await oe(t.workspacePath,C(t.workspacePath),async n=>{const a=await Ge(n,C(t.workspacePath)).catch(()=>{});return a?.workspaceOwnerNonce===t.workspaceOwnerNonce&&a.dispatchId===t.dispatchId&&a.storyId===t.storyId}),await oe(t.leasePath,C(t.leasePath),async n=>(await ue(n,C(t.leasePath)).catch(()=>{}))?.nonce===t.leaseOwnerNonce)}}}async function Et(e,r,t,o,n){const a=[...new Set([".neocortex/state.json",o,`docs/epics/epic-${Vt(t)}.md`])],c=[];for(const i of a){if(!ce(i))continue;const d=await Ce(D(e,i));c.push({path:i,...d!==void 0?{content:d}:{}}),d!==void 0&&await te(r,i,d,n)}return c}async function At(e){const r=await P(e,["rev-parse","--show-toplevel"]),[t,o]=await Promise.all([Y(r),Y(e)]);if(t!==o)throw new Error("project root mismatch");return t}async function jt(e,r,t,o){for(const n of["user.name","user.email"]){const a=await ee(e,["config","--get",n]);a&&await ne(r,t,o,["config",n,a])}}async function Dt(e,r){const t=await V(e);await X(r,o=>{for(const[n,a]of Object.entries(t.epics))o.epics[n]={...structuredClone(a),branch_lease:null};for(const[n,a]of Object.entries(t.stories))o.stories[n]=structuredClone(a)})}async function ne(e,r,t,o){return(await Ae(e,r,t,o)).toString("utf8").trim()}async function ie(e,r){return(await Ae(C(e.workspacePath),$(e.workspacePath),e.workspaceIdentity,r)).toString("utf8")}async function De(e,r){return(await ie(e,r)).trim()}async function Ct(e,r){try{return await De(e,r),!0}catch{return!1}}async function Ot(e,r){try{return await Ae(C(e.workspacePath),$(e.workspacePath),e.workspaceIdentity,["show",`:${r}`])}catch{return}}async function Nt(e,r,t,o,n){const a=Date.now(),c=A=>({exitCode:null,timedOut:!1,durationMs:Date.now()-a,stdout:"",stderr:"",stdoutBytes:0,stderrBytes:0,...A});if(o.signal?.aborted)return c({signal:"SIGTERM",cancelled:!0});if(!r||r.length>4096||r.includes("\0")||t.length>512||t.some(A=>A.length>32768||A.includes("\0")))return c({exitCode:73,stderr:"continuous-child-invocation-invalid",stderrBytes:35});const i=C(e.workspacePath),d=await le(i,{bigint:!0}),s=await dt(i,{bigint:!0});if(!d.isDirectory()||d.isSymbolicLink()||!s.isDirectory()||d.dev!==s.dev||d.ino!==s.ino)return c({exitCode:73,stderr:"continuous-child-parent-invalid",stderrBytes:31});await n.beforeHandoff?.();const g=Buffer.from(JSON.stringify({command:r,args:[...t]})).toString("base64url"),S=o.maxOutputBytes??64e3,v=Math.max(1,Math.min(o.timeoutMs??600*1e3,1800*1e3));return await new Promise(A=>{const w=Me(process.execPath,["-e",xt,String(s.dev),String(s.ino),$(e.workspacePath),e.workspaceIdentity.dev,e.workspaceIdentity.ino,g,n.afterHandoff?"1":"0"],{cwd:i,env:o.env,stdio:["pipe","pipe","pipe"]});let j="",R="",p=0,I=0,b=!1,u=!1,k=!1,x=!n.afterHandoff;const m=(N,J)=>{b||(b=!0,clearTimeout(E),o.signal?.removeEventListener("abort",l),A({exitCode:N,...J?{signal:J}:{},timedOut:u,...k?{cancelled:!0}:{},durationMs:Date.now()-a,stdout:j,stderr:R,stdoutBytes:p,stderrBytes:I}))},l=()=>{k=!0,w.kill("SIGTERM")};o.signal?.addEventListener("abort",l,{once:!0});const E=setTimeout(()=>{u=!0,w.kill("SIGTERM")},v);w.stdout.on("data",N=>{p+=N.length,p+I>S?w.kill("SIGTERM"):j+=N.toString("utf8")}),w.stderr.on("data",N=>{I+=N.length,p+I>S?w.kill("SIGTERM"):R+=N.toString("utf8"),!x&&R.includes(`continuous-child-handoff-ready
|
|
51
|
+
`)&&(x=!0,Promise.resolve(n.afterHandoff?.()).then(()=>w.stdin.end()).catch(()=>w.kill("SIGTERM")))}),w.once("error",()=>m(null)),w.once("close",(N,J)=>m(N,J)),n.afterHandoff||w.stdin.end()})}async function P(e,r){return(await z(e,r)).trim()}async function z(e,r){const t=await je("git",r,{cwd:e,encoding:"utf8",maxBuffer:16777216});return String(t.stdout)}async function ee(e,r){try{return await P(e,r)}catch(t){if(q(t,"ENOENT"))return;throw t}}async function ae(e,r){try{return await P(e,r),!0}catch{return!1}}async function _t(e,r,t){return await new Promise(o=>{const n=Me("git",[...r],{cwd:e,stdio:["pipe","ignore","ignore"]});n.once("error",()=>o(!1)),n.once("close",a=>o(a===0)),n.stdin.end(t)})}async function fr(e,r,t){try{const o=await je("git",["show",`${r}:${t}`],{cwd:e,encoding:"buffer",maxBuffer:16777216});return Buffer.from(o.stdout)}catch(o){if(q(o,"ENOENT"))return;throw o}}async function Ce(e){try{return await B(e)}catch(r){if(q(r,"ENOENT"))return;throw r}}async function te(e,r,t,o){const n=se(r);if(!ce(n))throw new Error("continuous-sandbox-write-path-invalid");const a=await Y(e);if(a!==ht(e))throw new Error("continuous-sandbox-write-root-symlink");const c=C(n)==="."?"":C(n),i=await Bt(a,c),d=await Qe(i),s=D(i,$(n));if(await me(s,a,!0),t===void 0){await o?.({boundaryRoot:a,relativePath:n,destinationPath:s}),await Ze(i,a,d),await me(s,a,!0),await kt(i,$(n));return}await o?.({boundaryRoot:a,relativePath:n,destinationPath:s}),await Ze(i,a,d),await me(s,a,!0),await vt(i,$(n),t),await me(s,a,!1)}async function Bt(e,r){let t=e;for(const o of r.split("/").filter(Boolean)){t=D(t,o),await bt(C(t),$(t));const n=await le(t);if(!n.isDirectory()||n.isSymbolicLink()||await Y(t)!==t)throw new Error("continuous-sandbox-write-component-unsafe");Oe(e,t)}return t}async function me(e,r,t){Oe(r,e);try{const o=await le(e);if(o.isSymbolicLink()||!o.isFile())throw new Error("continuous-sandbox-write-destination-unsafe");if(await Y(e)!==e)throw new Error("continuous-sandbox-write-destination-symlink")}catch(o){if(t&&q(o,"ENOENT"))return;throw o}}function Oe(e,r){const t=ut(e,r);if(t===""||t.startsWith("..")||Fe(t))throw new Error("continuous-sandbox-write-containment-invalid")}async function Qe(e){const r=await le(e,{bigint:!0});if(!r.isDirectory()||r.isSymbolicLink())throw new Error("continuous-sandbox-write-parent-unsafe");return{dev:r.dev,ino:r.ino}}async function Pt(e,r,t){try{if(e!==r&&Oe(r,e),await Y(e)!==e)return!1;const o=await Qe(e);return o.dev===t.dev&&o.ino===t.ino}catch{return!1}}async function Ze(e,r,t){if(!await Pt(e,r,t))throw new Error("continuous-sandbox-write-parent-raced")}async function $t(e,r){return await Promise.all(r.map(async t=>{const o=await Ce(D(e,t));return{path:t,...o!==void 0?{content:o}:{}}}))}async function Ne(e,r){const t=r.map(o=>o.path);t.length>0&&await ae(e,["reset","--quiet","HEAD","--",...t]);for(const o of r)await te(e,o.path,o.content)}async function zt(e,r){try{const t=D(e.projectRoot,".neocortex/state.json"),o=T(JSON.parse((await B(t)).toString("utf8"))),n=h(h(o.epics)?.[e.epicId]),a=h(n?.branch_lease),c=Object.freeze({schemaVersion:1,ownerStoryId:e.storyId,sessionId:e.dispatchId,idempotencyKey:e.dispatchId,acquiredAt:r,expiresAt:new Date(Date.parse(r)+600*1e3).toISOString()});let i=!1;return await X(e.projectRoot,d=>{const s=ye(d,e.epicId),g=h(s.branch_lease)??a;if(g&&typeof g.expiresAt=="string"&&g.expiresAt>r&&(g.ownerStoryId!==e.storyId||g.idempotencyKey!==e.dispatchId)){i=!0;return}s.branch_mode="epic",s.execution_branch=e.executionBranch,s.branch_lease=c}),i?f("continuous-epic-checkout-writer-active"):{ok:!0,value:c}}catch{return f("continuous-writer-lease-persist-failed")}}async function Tt(e){await X(e.projectRoot,r=>{const t=ye(r,e.epicId),o=h(t.branch_lease);o?.ownerStoryId===e.storyId&&o.idempotencyKey===e.dispatchId&&(t.branch_lease=null)})}async function Ue(e){try{const r=D(e.lease.projectRoot,".neocortex/state.json"),t=Object.freeze({schemaVersion:1,storyId:e.lease.storyId,epicId:e.lease.epicId,branch:e.lease.executionBranch,baseSha:e.baseSha,headSha:e.headSha,commits:Object.freeze([e.headSha]),idempotencyKey:e.lease.dispatchId,dispatchId:e.lease.dispatchId,bundleId:e.bundle.bundleId,bundleDigest:e.bundle.bundleDigest,graphRevision:e.lease.graphRevision,claimRevision:e.lease.claimRevision,receiptRefs:Object.freeze([...new Set(e.receiptRefs)]),qaVerdict:"passed",reviewVerdict:"approved",qaReceipt:e.qaReceipt,reviewReceipt:e.reviewReceipt,recordedAt:e.recordedAt}),o=T(JSON.parse((await B(r)).toString("utf8"))),n=h(h(o.stories)?.[e.lease.storyId]);let a="persisted",c,i,d,s,g=!1,S;const v=async(u,k,x)=>{const m=await ee(e.lease.projectRoot,["symbolic-ref","--quiet","--short","HEAD"]),l=await P(e.lease.projectRoot,["rev-parse","HEAD"]);if(m!==e.lease.executionBranch||l!==e.headSha)throw new Error("continuous-checkpoint-head-raced");if(u.graphRevision!==e.lease.graphRevision||u.claimRevision!==e.lease.claimRevision||u.bundleId!==e.bundle.bundleId||u.bundleDigest!==e.bundle.bundleDigest)throw new Error("continuous-checkpoint-graph-raced");const E=x??T(JSON.parse((await B(r)).toString("utf8"))),N=re(E,k??await V(e.lease.projectRoot)),J=h(h(N.epics)?.[e.lease.epicId]),ve=h(h(N.stories)?.[e.lease.storyId]),M=Array.isArray(ve?.depends_on)?ve.depends_on.filter(H=>typeof H=="string"):[],Re=h(N.stories);if(J?.execution_branch!==e.lease.executionBranch||M.some(H=>{const K=h(Re?.[H]);return!K||!["done","completed","merged"].includes(String(K.status))}))throw new Error("continuous-checkpoint-dependency-raced");const he=L(await z(e.lease.projectRoot,["diff-tree","--no-commit-id","--name-only","-r","-z",e.headSha]));if(he.length===0||he.some(H=>!e.lease.authorizedWriteSet.some(K=>Se(K,H))))throw new Error("continuous-checkpoint-write-set-raced");if(!await ke(e.lease.projectRoot,u,e.lease.storyId,e.lease.epicId,e.lease.executionBranch,void 0,e.lease.authorizedWriteSet))throw new Error("continuous-checkpoint-receipt-revalidation-failed")},A=await V(e.lease.projectRoot,{required:!0}),w=h(h(A.epics[e.lease.epicId])?.branch_lease);if(w?.ownerStoryId!==e.lease.storyId||w.idempotencyKey!==e.lease.dispatchId||w.sessionId!==e.lease.dispatchId||typeof w.expiresAt!="string"||Date.parse(w.expiresAt)<=(e.checkpointNow?.("before-mutation")??Date.now()))return f("continuous-checkpoint-writer-lease-lost");if(!await xe(e.physicalLock.path,e.physicalLock.owner))return f("continuous-checkpoint-integration-lock-raced");const j=await B(r),R=G("sha256").update(j).digest("hex");await v(t,A);const p=$e(T(JSON.parse(j.toString("utf8"))),A,e.lease.epicId,e.lease.storyId);if(p.revision!==e.checkpointAuthority.revision||p.epoch!==e.checkpointAuthority.epoch||p.storyStatus!==e.checkpointAuthority.storyStatus)return f("continuous-graph-revision-stale");const I=await X(e.lease.projectRoot,u=>{const k=ye(u,e.lease.epicId),x=h(k.branch_lease);i=x===void 0?void 0:structuredClone(x),d=structuredClone(u),g=u.stories[e.lease.storyId]!==void 0,s=structuredClone(u.stories[e.lease.storyId]??{}),S=structuredClone(k.branch_lease);const m=mt(u,e.lease.storyId),l=h(m.checkpoint)??h(n?.checkpoint);if(l?.idempotencyKey===e.lease.dispatchId){l.baseSha!==e.baseSha||l.headSha!==e.headSha||l.bundleId!==e.bundle.bundleId||l.bundleDigest!==e.bundle.bundleDigest||l.graphRevision!==e.lease.graphRevision||l.claimRevision!==e.lease.claimRevision?a="conflict":(a="reused",c=l,k.branch_lease=null);return}if(x?.ownerStoryId!==e.lease.storyId||x.idempotencyKey!==e.lease.dispatchId||x.sessionId!==e.lease.dispatchId){a="lease-lost";return}m.checkpoint=t,m.status="done",m.last_step=we;const E=Array.isArray(m.steps_completed)?m.steps_completed.filter(N=>typeof N=="string"):[];m.steps_completed=[...new Set([...E,Ve,we])],k.branch_lease=null},{expectedRevision:A.revision,preCommit:async u=>{if(i?.ownerStoryId!==e.lease.storyId||i.idempotencyKey!==e.lease.dispatchId||i.sessionId!==e.lease.dispatchId||typeof i.expiresAt!="string"||Date.parse(i.expiresAt)<=(e.checkpointNow?.("pre-commit")??Date.now()))throw new Error("continuous-checkpoint-writer-lease-lost");if(await e.insideCheckpointPreCommit?.({projectRoot:e.lease.projectRoot,storyId:e.lease.storyId,headSha:e.headSha}),!await xe(e.physicalLock.path,e.physicalLock.owner))throw new Error("continuous-checkpoint-integration-lock-raced");await v(c??t,u);const k=await B(r);if(G("sha256").update(k).digest("hex")!==R)throw new Error("continuous-graph-revision-stale");if(!d)throw new Error("continuous-checkpoint-authority-missing");const x=T(JSON.parse(k.toString("utf8"))),m=$e(x,d,e.lease.epicId,e.lease.storyId);if(m.revision!==e.checkpointAuthority.revision||m.epoch!==e.checkpointAuthority.epoch||m.storyStatus!==e.checkpointAuthority.storyStatus)throw new Error("continuous-graph-revision-stale");if(a==="persisted"&&!ze(x,u,e.lease,t))throw new Error("continuous-checkpoint-projection-raced")}}),b=a;if(b==="conflict")return f("continuous-checkpoint-idempotency-conflict");if(b==="lease-lost")return f("continuous-checkpoint-writer-lease-lost");if(b==="reused"&&c)try{return await v(c),{ok:!0,value:c}}catch{return f("continuous-checkpoint-reused-evidence-invalid")}try{await e.afterCheckpointPersist?.({projectRoot:e.lease.projectRoot,storyId:e.lease.storyId,headSha:e.headSha});const u=await V(e.lease.projectRoot,{required:!0});if(u.revision!==I.document.revision)throw new Error("continuous-checkpoint-post-persist-runtime-raced");await v(t,u);const k=await B(r);if(G("sha256").update(k).digest("hex")!==R)throw new Error("continuous-checkpoint-post-persist-authority-raced");const x=T(JSON.parse(k.toString("utf8")));if(!ze(x,u,e.lease,t))throw new Error("continuous-checkpoint-post-persist-projection-raced")}catch{const u=await B(r).catch(()=>{}),k=u===void 0||G("sha256").update(u).digest("hex")!==R;return await X(e.lease.projectRoot,x=>{const m=h(x.epics[e.lease.epicId]),l=h(x.stories[e.lease.storyId]),E=u===void 0?void 0:T(JSON.parse(u.toString("utf8")));if(!m||!l||!E||!ze(E,x,e.lease,t))throw new Error("continuous-checkpoint-cleanup-cas-conflict");if(k){g?Mt(l,s??{}):delete x.stories[e.lease.storyId],S===void 0?delete m.branch_lease:m.branch_lease=structuredClone(S);return}l.checkpoint=null,l.status="blocked",m.branch_lease=null},{expectedRevision:I.document.revision}).catch(()=>{}),f("continuous-checkpoint-post-persist-raced")}return{ok:!0,value:t}}catch(r){const t=r instanceof Error&&/^continuous-/.test(r.message)?r.message:"continuous-checkpoint-persist-failed";return f(t)}}async function Ye(e){try{if(await P(e.lease.projectRoot,["rev-parse","HEAD"])!==e.headSha)return f("continuous-story-evidence-head-raced");const r=await B(D(e.lease.projectRoot,"config","release-gates.v2.json"),"utf8"),t=JSON.parse(r),o=Array.isArray(t.requiredCompleteClasses)?t.requiredCompleteClasses.filter(b=>typeof b=="string"&&/^[a-z0-9-]+$/.test(b)):[];if(t.contract!=="P205-CI-02"||o.length===0)return f("continuous-story-qa-manifest-invalid");const n=String((await je(process.execPath,["scripts/test-root.mjs","--gate","complete"],{cwd:e.lease.projectRoot,encoding:"utf8",maxBuffer:32*1024*1024})).stdout);if(await e.qaRunner?.({projectRoot:e.lease.projectRoot,storyId:e.lease.storyId,headSha:e.headSha}),await P(e.lease.projectRoot,["rev-parse","HEAD"])!==e.headSha)return f("continuous-story-qa-head-raced");const a=Xe(e.nowIso),c=await P(e.lease.projectRoot,["rev-list","--count",`${e.baseSha}..${e.headSha}`]),i=await z(e.lease.projectRoot,["show","--no-patch","--format=%H%n%P%n%B",e.headSha]),d=L(await z(e.lease.projectRoot,["diff","--name-only","-z",`${e.baseSha}..${e.headSha}`])),s=yt({storyId:e.lease.storyId,baseSha:e.baseSha,headSha:e.headSha,bundleDigest:e.bundle.bundleDigest,graphRevision:e.lease.graphRevision,commitBody:i,changedPaths:d}),g=G("sha256").update(r).digest("hex"),S=G("sha256").update(n).digest("hex"),v=wt({gitEvidenceDigest:s,headSha:e.headSha,graphRevision:e.lease.graphRevision,manifestDigest:g,gateIds:o,resultDigest:S}),A=`story-qa:${v}`,w=Object.freeze({schemaVersion:1,kind:"story_qa_evidence",producer:"local-yolo-tdd",evidenceVersion:"p205-execution-v2",storyId:e.lease.storyId,epicId:e.lease.epicId,baseSha:e.baseSha,headSha:e.headSha,bundleDigest:e.bundle.bundleDigest,graphRevision:e.lease.graphRevision,contract:"P205-CI-02",command:St,manifestDigest:g,gateIds:Object.freeze(o),resultDigest:S,sourceEvidenceDigest:v,verdict:"passed",checkedAt:a,receiptRef:A}),j=[`Neocortex-Story: ${e.lease.storyId}`,`Neocortex-Epic: ${e.lease.epicId}`,`Neocortex-Bundle-Digest: ${e.bundle.bundleDigest}`,`Neocortex-Graph-Revision: ${e.lease.graphRevision}`];if(c!=="1"||!j.every(b=>i.includes(b))||d.length===0||d.some(b=>!e.lease.authorizedWriteSet.some(u=>Se(u,b))))return f("continuous-story-review-evidence-rejected");const R=G("sha256").update(["root-commit-range-review-v1",e.lease.storyId,e.baseSha,e.headSha,e.bundle.bundleDigest,e.lease.graphRevision,A,G("sha256").update(i).digest("hex"),[...d].sort().join(",")].join("\0")).digest("hex"),p=`story-review:${R}`,I=Object.freeze({schemaVersion:1,kind:"story_review_evidence",producer:"root-commit-range-review",storyId:e.lease.storyId,epicId:e.lease.epicId,baseSha:e.baseSha,headSha:e.headSha,bundleDigest:e.bundle.bundleDigest,graphRevision:e.lease.graphRevision,sourceEvidenceDigest:R,qaReceiptRef:A,verdict:"approved",reviewedAt:a,receiptRef:p});return{ok:!0,value:{qaReceipt:w,reviewReceipt:I}}}catch{return f("continuous-story-evidence-production-failed")}}function Xe(e){return Number.isFinite(Date.parse(e))?new Date(Date.parse(e)).toISOString():new Date().toISOString()}function Gt(e,r,t){if(r!==void 0)try{const o=e===void 0?{}:T(JSON.parse(e.toString("utf8"))),n=T(JSON.parse(r.toString("utf8"))),a=structuredClone(o),c=h(h(o.stories)?.[t]),i=h(h(n.stories)?.[t]),d=Lt(a,"stories"),s=structuredClone(c??{});for(const g of["status","last_step","steps_completed","last_updated","blocked_reason","blocked_at","implementation_evidence","validation_evidence","qa_gate"])i&&Object.prototype.hasOwnProperty.call(i,g)&&(s[g]=structuredClone(i[g]));return d[t]=s,Buffer.from(`${JSON.stringify(a,null,2)}
|
|
52
|
+
`)}catch{return Buffer.from("{invalid-child-state")}}function Lt(e,r){const t=h(e[r]);if(t)return t;const o={};return e[r]=o,o}function T(e){if(!e||typeof e!="object"||Array.isArray(e))throw new Error("record expected");return e}function h(e){return e&&typeof e=="object"&&!Array.isArray(e)?e:void 0}function Wt(e,r,t){return Pe(t,e)||Pe(r,t)?{ok:!0,value:r}:Pe(r,e)?{ok:!0,value:t}:f("continuous-context-merge-conflict")}function qt(e,r,t){try{const o=et(_e(e),_e(r),_e(t));return o.ok?{ok:!0,value:o.value===void 0?void 0:Buffer.from(`${JSON.stringify(o.value,null,2)}
|
|
53
|
+
`)}:o}catch{return f("continuous-state-merge-invalid")}}function _e(e){return e===void 0?void 0:JSON.parse(e.toString("utf8"))}function et(e,r,t){if(ge(t,e)||ge(r,t))return{ok:!0,value:r};if(ge(r,e))return{ok:!0,value:t};if(Be(e)&&Be(r)&&Be(t)){const o={};for(const n of[...new Set([...Object.keys(e),...Object.keys(r),...Object.keys(t)])].sort()){const a=et(e[n],r[n],t[n]);if(!a.ok)return a;a.value!==void 0&&(o[n]=a.value)}return{ok:!0,value:o}}if(Array.isArray(e)&&Array.isArray(r)&&Array.isArray(t)&&tt(e,r)&&tt(e,t)){const o=[...e,...r.slice(e.length),...t.slice(e.length)],n=new Set;return{ok:!0,value:o.filter(a=>{const c=JSON.stringify(a);return n.has(c)?!1:(n.add(c),!0)})}}return f("continuous-state-merge-conflict")}function Be(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function tt(e,r){return e.length<=r.length&&e.every((t,o)=>ge(t,r[o]))}function ge(e,r){return JSON.stringify(e)===JSON.stringify(r)}function Pe(e,r){return e===void 0||r===void 0?e===r:e.equals(r)}function L(e){return e.split("\0").filter(Boolean)}function ce(e){const r=se(e);return r.length>0&&r.length<=1024&&!Fe(r)&&!/^[A-Za-z]:\//.test(r)&&!r.split("/").includes("..")&&!r.includes("\0")&&!r.startsWith("/")}function se(e){return e.replaceAll("\\","/").replace(/^\.\//,"").replace(/\/{2,}/g,"/")}function Ht(e){const r=e.map(se).filter(ce);return[...new Set(r)].sort()}function Se(e,r){const t=se(e),o=se(r);return t===o||t.endsWith("/")&&o.startsWith(t)}function Jt(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 rt(e,r){return e.length===r.length&&e.every((t,o)=>t===r[o])}async function Ie(e,r,t,o){const n=T(JSON.parse(await B(D(e,".neocortex/state.json"),"utf8")));return $e(n,o??await V(e),r,t)}function $e(e,r,t,o){const n=re(e,r),a=h(n.epics),c=h(n.stories),i=h(a?.[t])??{},d=(Array.isArray(i.stories)?i.stories:Array.isArray(i.story_ids)?i.story_ids:[]).filter(v=>typeof v=="string"),s=d.length>0?d:Object.entries(c??{}).filter(([v,A])=>h(A)?.epic_id===t||v.startsWith(`${t}.`)).map(([v])=>v).sort(),g=["status","depends_on","files_to_modify","contracts","contract_refs","shared_surfaces","steps_completed","last_step","branch_name","physicalMergeStatus","merged","checkpoint","planning","attempts","retry_count","rollback_receipt","cancelled_at"],S=Object.fromEntries(s.map(v=>{const A=h(c?.[v])??{};return[v,Object.fromEntries(g.filter(w=>Object.prototype.hasOwnProperty.call(A,w)).map(w=>[w,A[w]]))]}));return{revision:Te({epicId:t,epic:{stories:s,status:i.status??null,branch_mode:i.branch_mode??null,execution_branch:i.execution_branch??null,graph_revision:i.graph_revision??null,depends_on:i.depends_on??null,planning:i.planning??null,execution_plan:i.execution_plan??null,waves:i.waves??null,current_wave:i.current_wave??null,replan:i.replan??null,replan_revision:i.replan_revision??null,dependency_graph:i.dependency_graph??null,scheduler:i.scheduler??null,attempts:i.attempts??null,retry_count:i.retry_count??null},stories:S}),epoch:typeof i.graph_revision=="string"?i.graph_revision:null,storyStatus:typeof h(c?.[o])?.status=="string"?String(h(c?.[o])?.status):null}}function ze(e,r,t,o){const n=re(e,r),a=h(h(n.epics)?.[t.epicId]),c=h(h(n.stories)?.[t.storyId]),i=Array.isArray(c?.steps_completed)?c.steps_completed.filter(d=>typeof d=="string"):[];return!!(a&&c&&a.branch_lease===null&&c.status==="done"&&c.last_step===we&&i.includes(Ve)&&i.includes(we)&&de(c.checkpoint)===de(o))}function Mt(e,r){for(const t of Object.keys(e))delete e[t];Object.assign(e,structuredClone(r))}async function Kt(e,r){return(await Ie(e,r,"")).revision}function Te(e){return G("sha256").update(de(e)).digest("hex")}function de(e){return e===null||typeof e!="object"?JSON.stringify(e):Array.isArray(e)?`[${e.map(de).join(",")}]`:`{${Object.entries(e).sort(([r],[t])=>r.localeCompare(t)).map(([r,t])=>`${JSON.stringify(r)}:${de(t)}`).join(",")}}`}function Ft(e,r){const t=(e??[]).filter(o=>/^[A-Za-z0-9._/:@#-]{1,200}$/.test(o)&&!o.includes(".."));return Object.freeze([...new Set(t.length>0?t:[`dispatch:${r}`])].sort())}function Vt(e){return e.replace(/[^A-Za-z0-9._-]/g,"-").slice(0,80)}function f(e){return{ok:!1,reasonCode:e}}function q(e,r){return e instanceof Error&&"code"in e&&e.code===r}async function ue(e,r){try{await W(e,r);const t=D(e,"owner.json");await F(t,e);const o=JSON.parse(await B(t,"utf8"));if(!Number.isSafeInteger(o.pid)||Number(o.pid)<=0||typeof o.createdAt!="string"||!Number.isFinite(Date.parse(o.createdAt))||typeof o.nonce!="string"||!/^[a-f0-9]{32}$/.test(o.nonce))throw new Error("continuous-sandbox-lease-owner-invalid");return{pid:Number(o.pid),createdAt:o.createdAt,nonce:o.nonce}}catch(t){if(q(t,"ENOENT"))return;throw t}}function ot(e,r){return e?.pid===r.pid&&e.createdAt===r.createdAt&&e.nonce===r.nonce}function Qt(e){try{return process.kill(e,0),!0}catch(r){return q(r,"EPERM")}}async function Ge(e,r){try{await W(e,r);const t=D(e,".git");await W(t,e);const o=D(t,"neocortex-sandbox.json");await F(o,t);const n=JSON.parse(await B(o,"utf8"));if(n.schemaVersion!==1||typeof n.workspaceOwnerNonce!="string"||!/^[a-f0-9]{32}$/.test(n.workspaceOwnerNonce)||!/^[a-f0-9]{64}$/.test(n.graphAuthorityRevision)||!(n.graphAuthorityEpoch===null||/^[a-f0-9]{64}$/.test(n.graphAuthorityEpoch))||!(n.graphAuthorityStoryStatus===null||typeof n.graphAuthorityStoryStatus=="string"&&n.graphAuthorityStoryStatus.length<=80)||typeof n.workspaceIdentity?.dev!="string"||!/^[0-9]+$/.test(n.workspaceIdentity.dev)||typeof n.workspaceIdentity?.ino!="string"||!/^[0-9]+$/.test(n.workspaceIdentity.ino))throw new Error("continuous-sandbox-metadata-invalid");return n}catch(t){if(q(t,"ENOENT"))return;throw t}}async function Zt(e,r,t,o){await W(e,r);const n=D(e,".git");await W(n,e);const a=D(n,"neocortex-sandbox.json");await te(e,".git/neocortex-sandbox.json",Buffer.from(`${JSON.stringify(t,null,2)}
|
|
54
|
+
`),o),await F(a,n)}async function Ut(e,r){try{await F(e,r);const t=JSON.parse(await B(e,"utf8"));return typeof t.childHeadSha=="string"&&typeof t.rootHeadSha=="string"&&/^[a-f0-9]{40,64}$/.test(t.childHeadSha)&&/^[a-f0-9]{40,64}$/.test(t.rootHeadSha)&&t.bundle?.schemaVersion===1&&t.checkpoint?.schemaVersion===1?t:void 0}catch(t){if(q(t,"ENOENT"))return;throw t}}async function Yt(e,r){try{const t=T(JSON.parse(await B(D(e.projectRoot,".neocortex/state.json"),"utf8"))),o=re(t,await V(e.projectRoot)),n=h(h(o.stories)?.[e.storyId]),a=h(n?.checkpoint);return a?.idempotencyKey===r.idempotencyKey&&a.headSha===r.headSha&&a.bundleDigest===r.bundleDigest&&h(h(o.epics)?.[e.epicId])?.branch_lease===null&&await ke(e.projectRoot,a,e.storyId,e.epicId,e.executionBranch,void 0,e.authorizedWriteSet)}catch{return!1}}async function Xt(e,r,t){try{const o=T(JSON.parse(await B(D(e.projectRoot,".neocortex/state.json"),"utf8"))),n=re(o,await V(e.projectRoot)),a=h(h(n.stories)?.[e.storyId]),c=h(a?.checkpoint);return c?.schemaVersion!==1||c.storyId!==e.storyId||c.epicId!==e.epicId||c.branch!==e.executionBranch||c.baseSha!==t.baseSha||c.headSha!==t.headSha||c.idempotencyKey!==e.dispatchId||c.dispatchId!==e.dispatchId||c.bundleId!==r.bundleId||c.bundleDigest!==r.bundleDigest||c.graphRevision!==e.graphRevision||c.claimRevision!==e.claimRevision||h(h(n.epics)?.[e.epicId])?.branch_lease!==null||!await ke(e.projectRoot,c,e.storyId,e.epicId,e.executionBranch,void 0,e.authorizedWriteSet)?void 0:c}catch{return}}async function er(e,r){const t=(await ee(e.projectRoot,["log","--format=%H","--fixed-strings",`--grep=Neocortex-Dispatch: ${e.dispatchId}`]))?.split(`
|
|
55
|
+
`).filter(S=>/^[a-f0-9]{40,64}$/.test(S))??[];if(t.length!==1)return;const o=t[0];if(!await ae(e.projectRoot,["merge-base","--is-ancestor",o,"HEAD"]))return;const n=await P(e.projectRoot,["show","-s","--format=%B",o]),a=new Set(n.split(`
|
|
56
|
+
`).map(S=>S.trim()));for(const S of[`Neocortex-Story: ${e.storyId}`,`Neocortex-Epic: ${e.epicId}`,`Neocortex-Execution-Branch: ${e.executionBranch}`,`Neocortex-Prepared-Base: ${e.baseSha}`,`Neocortex-Dispatch: ${e.dispatchId}`,`Neocortex-Bundle: ${r.bundleId}`,`Neocortex-Bundle-Digest: ${r.bundleDigest}`,`Neocortex-Graph-Revision: ${e.graphRevision}`,`Neocortex-Claim-Revision: ${e.claimRevision}`])if(!a.has(S))return;const c=Le(n,"Neocortex-Checkpoint-Authority"),i=Le(n,"Neocortex-Checkpoint-Epoch"),d=Le(n,"Neocortex-Checkpoint-Story-Status");if(!c||!/^[a-f0-9]{64}$/.test(c)||!i||!(i==="null"||/^[a-f0-9]{64}$/.test(i))||!d||d.length>80)return;const s=await ee(e.projectRoot,["rev-parse",`${o}^`]);if(!s||!/^[a-f0-9]{40,64}$/.test(s))return;const g=L(await z(e.projectRoot,["diff-tree","--no-commit-id","--name-only","-r","-z",o]));if(!(g.length===0||g.some(S=>!r.authorizedWriteSet.some(v=>Se(v,S)))))return{baseSha:s,headSha:o,checkpointAuthority:{revision:c,epoch:i==="null"?null:i,storyStatus:d==="null"?null:d}}}function Le(e,r){const t=`${r}: `,o=e.split(`
|
|
57
|
+
`).map(n=>n.trim()).filter(n=>n.startsWith(t));return o.length===1?o[0].slice(t.length):void 0}async function We(e,r,t,o,n,a){const c=C(e);await W(c,C(c));try{await F(e,c)}catch(i){if(!q(i,"ENOENT"))throw i}await te(c,$(e),Buffer.from(`${JSON.stringify({childHeadSha:r,rootHeadSha:t,bundle:o,checkpoint:n})}
|
|
58
|
+
`),a),await F(e,c)}export{ur as createAnchoredContinuousStoryCommandRunner,hr as createBranchlessContinuousStoryWorkspaceManager};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** Bounded ownership inventory. Names alone never establish managed ownership. */
|
|
2
|
+
import type { GitOwnershipEvidence } from '../../../../shared/dist/yoloop/epic-terminal-git.js';
|
|
3
|
+
export interface GitResourceOwnership {
|
|
4
|
+
readonly epicId: string;
|
|
5
|
+
readonly branch: string;
|
|
6
|
+
readonly origin: string;
|
|
7
|
+
readonly active: boolean;
|
|
8
|
+
readonly proofs: readonly {
|
|
9
|
+
readonly head: string;
|
|
10
|
+
readonly evidence: readonly GitOwnershipEvidence[];
|
|
11
|
+
readonly mergeSha?: string;
|
|
12
|
+
readonly pushedSha?: string;
|
|
13
|
+
}[];
|
|
14
|
+
readonly prNumbers: readonly number[];
|
|
15
|
+
}
|
|
16
|
+
export declare const GIT_INVENTORY_REF_LIMIT = 2048;
|
|
17
|
+
export declare function collectGitResourceOwnership(state: {
|
|
18
|
+
readonly epics?: Record<string, Record<string, unknown>>;
|
|
19
|
+
readonly stories?: Record<string, Record<string, unknown>>;
|
|
20
|
+
}): {
|
|
21
|
+
readonly claims: ReadonlyMap<string, readonly GitResourceOwnership[]>;
|
|
22
|
+
readonly origins: ReadonlySet<string>;
|
|
23
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const I=2048;function S(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&&h(n.executionHeadSha)&&h(n.mergeSha)&&h(n.pushedSha),p=m(r.branch_lease)||(r.status===void 0?!a:!g(r.status));_({epicId:t,branch:i,origin:o,active:p,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 p=n?.branch===a&&h(n.headSha);_({epicId:i,branch:a,origin:c(o.origin_branch),active:!g(r.status)||m(r.branch_lease),proofs:p?[{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 h(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{I as GIT_INVENTORY_REF_LIMIT,S as collectGitResourceOwnership};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { EpicMergeCandidateQaReceipt } from '../../../../shared/dist/yoloop/epic-terminal-git.js';
|
|
2
|
+
export interface EpicMergeCandidateBinding {
|
|
3
|
+
readonly originHeadSha: string;
|
|
4
|
+
readonly executionHeadSha: string;
|
|
5
|
+
readonly mergeSha: string;
|
|
6
|
+
readonly graphRevision: string;
|
|
7
|
+
}
|
|
8
|
+
export interface EpicMergeCandidateQaAuthority {
|
|
9
|
+
readonly __epicMergeCandidateOpaqueAuthority: never;
|
|
10
|
+
}
|
|
11
|
+
export declare function verifiedEpicMergeCandidateReceipt(authority: EpicMergeCandidateQaAuthority | undefined, binding: EpicMergeCandidateBinding): EpicMergeCandidateQaReceipt | undefined;
|
|
12
|
+
export declare function executeEpicMergeCandidateQa(projectRoot: string, binding: EpicMergeCandidateBinding, nowIso?: string): Promise<{
|
|
13
|
+
readonly candidateQa: EpicMergeCandidateQaReceipt;
|
|
14
|
+
readonly candidateAuthority: EpicMergeCandidateQaAuthority;
|
|
15
|
+
}>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import{execFile as D}from"node:child_process";import{createHash as x,randomBytes as H}from"node:crypto";import{join as j}from"node:path";import{promisify as A}from"node:util";import{assertGitLifecyclePrivateDirectory as O,ensureGitLifecyclePrivateChildDirectory as L,executeP205TransientAuthority as C,gitLifecycleRuntimeDirectory as P,P205_COMPLETE_COMMAND as R,p205TransientAuthorityMatchesAggregate as q,projectP205TransientExecutionEvidence as I,removeGitLifecyclePrivateDirectory as M}from"../../git/git-lifecycle-store.js";import{anchoredCreateDirectory as N,anchoredDirectoryIdentity as y,anchoredExecuteGit as T,anchoredExecuteNode as _,anchoredExecuteNpm as $,secureFilesystemBoundaryAvailable as z}from"../../git/secure-filesystem-boundary.js";const G=A(D),v=new WeakMap;function U(c,e){const s=c&&v.get(c);if(!s||!q(s.p205,e.mergeSha,e.graphRevision))return;const h=Date.now()-s.acceptedAt;if(h<0||h>1800*1e3)return;const r=s.receipt;return r.originHeadSha===e.originHeadSha&&r.executionHeadSha===e.executionHeadSha&&r.mergeSha===e.mergeSha&&r.graphRevision===e.graphRevision?r:void 0}async function Z(c,e,s=new Date().toISOString()){if(!z())throw new Error("epic-merge-candidate-platform-unsupported");if(![e.originHeadSha,e.executionHeadSha,e.mergeSha].every(a=>/^[a-f0-9]{40,64}$/.test(a))||!/^[a-f0-9]{64}$/.test(e.graphRevision))throw new Error("epic-merge-candidate-binding-invalid");const h=async(...a)=>String((await G("git",a,{cwd:c,encoding:"utf8"})).stdout).trim(),r=(await h("rev-list","--parents","-n","1",e.mergeSha)).split(/\s+/);if(r.length!==3||r[1]!==e.originHeadSha||r[2]!==e.executionHeadSha)throw new Error("epic-merge-candidate-parents-invalid");const f=await h("rev-parse",`${e.mergeSha}^{tree}`),S=await P(c),n=await L(S,"merge-candidates"),p=`${e.mergeSha.slice(0,16)}-${H(8).toString("hex")}`,o=await N(n,p),u=j(n,p),t=async(...a)=>(await T(n,p,o,a)).toString("utf8").trim();let w;try{if(await t("init","--quiet","--template="),await t("fetch","--quiet","--no-tags",c,e.mergeSha),await t("checkout","--quiet","--detach","FETCH_HEAD"),await t("rev-parse","HEAD")!==e.mergeSha||await t("rev-parse","HEAD^{tree}")!==f)throw new Error("epic-merge-candidate-checkout-invalid");const a=(await t("ls-files","--stage","-z","--","package.json","*/package.json","package-lock.json","*/package-lock.json","npm-shrinkwrap.json","*/npm-shrinkwrap.json",".npmrc","*/.npmrc")).split("\0").filter(Boolean);if(a.length>1024||a.some(l=>!/^100(?:644|755) [a-f0-9]{40,64} 0\t/.test(l)))throw new Error("epic-merge-candidate-dependency-entry-unsafe");const m=(await t("ls-files","-z","--","package-lock.json","*/package-lock.json","npm-shrinkwrap.json","*/npm-shrinkwrap.json")).split("\0").filter(Boolean);if(m.length>32)throw new Error("epic-merge-candidate-dependency-limit");const d=JSON.parse((await _(n,p,o,["-e",`
|
|
2
|
+
const fs=require('node:fs'); const path=require('node:path'); const locks=JSON.parse(process.argv[1]);
|
|
3
|
+
// npm-shrinkwrap wins over package-lock in the same installation root.
|
|
4
|
+
// Validate every selected root, not only the repository's top-level lock.
|
|
5
|
+
const selected=new Map();
|
|
6
|
+
for(const file of locks){
|
|
7
|
+
const dir=path.dirname(file);
|
|
8
|
+
if(!selected.has(dir)||path.basename(file)==='npm-shrinkwrap.json') selected.set(dir,file);
|
|
9
|
+
}
|
|
10
|
+
const cwd=fs.realpathSync('.');
|
|
11
|
+
const contained=p=>p===cwd||p.startsWith(cwd+path.sep);
|
|
12
|
+
const workspacePaths=new Set(); let unsafeLinks=false; let linkCount=0;
|
|
13
|
+
for(const [dir,file] of selected){
|
|
14
|
+
const lock=JSON.parse(fs.readFileSync(file,'utf8'));
|
|
15
|
+
for(const entry of Object.values(lock?.packages??{})){
|
|
16
|
+
if(entry?.link!==true) continue;
|
|
17
|
+
const p=entry.resolved; linkCount++;
|
|
18
|
+
if(linkCount>128||typeof p!=='string'||path.isAbsolute(p)){unsafeLinks=true;continue;}
|
|
19
|
+
const target=path.resolve(dir,p);
|
|
20
|
+
if(!contained(target)){unsafeLinks=true;continue;}
|
|
21
|
+
try{if(!contained(fs.realpathSync(target))){unsafeLinks=true;continue;}}
|
|
22
|
+
catch{unsafeLinks=true;continue;}
|
|
23
|
+
workspacePaths.add(path.relative(cwd,target)||'.');
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
process.stdout.write(JSON.stringify({unsafeLinks,roots:[...selected.keys()]
|
|
27
|
+
.filter(p=>!workspacePaths.has(p)).sort((a,b)=>a.length-b.length)}));
|
|
28
|
+
`,JSON.stringify(m)])).toString("utf8"));if(d.unsafeLinks)throw new Error("epic-merge-candidate-dependency-link-unsafe");if(d.roots.length>16)throw new Error("epic-merge-candidate-dependency-limit");for(const l of d.roots){if(l!=="."&&!/^[A-Za-z0-9_.\/-]+$/.test(l)||l.split("/").includes(".."))throw new Error("epic-merge-candidate-dependency-path-invalid");await $(n,p,o,["--prefix",l,"ci","--include=dev","--no-audit","--no-fund"])}if(await t("status","--porcelain","--untracked-files=normal"))throw new Error("epic-merge-candidate-dirty");if(w=await C(u,{headSha:e.mergeSha,graphRevision:e.graphRevision,nowIso:s,anchoredWorkspace:{parent:n,name:p,identity:o}}),await t("status","--porcelain","--untracked-files=normal")||await t("rev-parse","HEAD")!==e.mergeSha||await t("rev-parse","HEAD^{tree}")!==f)throw new Error("epic-merge-candidate-raced")}finally{await O(u,n);const a=await y(u);if(a.dev!==o.dev||a.ino!==o.ino)throw new Error("epic-merge-candidate-cleanup-owner-raced");if(!await M(u,n,async m=>{const d=await y(m);return d.dev===o.dev&&d.ino===o.ino}))throw new Error("epic-merge-candidate-cleanup-unverified")}const i=I(w);if(!i||i.executionHeadSha!==e.mergeSha)throw new Error("epic-merge-candidate-authority-missing");const E=`merge-qa:${x("sha256").update(["epic-merge-candidate-qa-v1",e.originHeadSha,e.executionHeadSha,e.mergeSha,f,e.graphRevision,i.manifestDigest,i.resultDigest,i.gateIds.join(",")].join("\0")).digest("hex")}`,g=Object.freeze({schemaVersion:1,kind:"epic_merge_candidate_qa",contract:"P205-CI-02",command:R,...e,treeSha:f,manifestDigest:i.manifestDigest,resultDigest:i.resultDigest,gateIds:Object.freeze([...i.gateIds]),checkedAt:i.executedAt,receiptRef:E}),k=Object.freeze(Object.create(null));return v.set(k,{receipt:g,p205:w,acceptedAt:Date.now()}),{candidateQa:g,candidateAuthority:k}}export{Z as executeEpicMergeCandidateQa,U as verifiedEpicMergeCandidateReceipt};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { EpicTerminalGitAdapter } from './epic-terminal-git-runner.js';
|
|
2
|
+
export interface CliEpicTerminalGitAdapterOptions {
|
|
3
|
+
/** Test observer after canonical P205; cannot replace the command or result. */
|
|
4
|
+
readonly aggregateQaRunner?: (input: {
|
|
5
|
+
readonly projectRoot: string;
|
|
6
|
+
readonly gateIds: readonly string[];
|
|
7
|
+
}) => Promise<unknown>;
|
|
8
|
+
/** Command seams keep provider-destructive tests inside temporary fixtures. */
|
|
9
|
+
readonly gitRunner?: (...args: readonly string[]) => Promise<string>;
|
|
10
|
+
readonly providerRunner?: (...args: readonly string[]) => Promise<string>;
|
|
11
|
+
}
|
|
12
|
+
export declare function createCliEpicTerminalGitAdapter(projectRoot: string, options?: CliEpicTerminalGitAdapterOptions): EpicTerminalGitAdapter;
|
|
@@ -0,0 +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(x=>x.split(/\s+/));if(m.length>L)throw new Error("git-reconciliation-ref-inventory-limit");const w=new Map;for(const[x,b]of m){const H=b?.replace(/^refs\/heads\//,"");if(!H||!A(H)||!/^[a-f0-9]{40,64}$/.test(x??"")||w.has(H))throw new Error("git-reconciliation-ref-inventory-invalid");w.set(H,x)}return w},e=g(await r.git("for-each-ref",`--count=${L+1}`,"--format=%(objectname) %(refname)","refs/heads")),i=g(await r.git("ls-remote","--heads","origin")),n=d?await Be(r.gh,d.nameWithOwner):[],s=new Map([[r.directive.originBranch,r.originHeadSha]]),u=async l=>{if(s.has(l))return s.get(l);const m=i.get(l);return m?(await P(r.git,l,m),s.set(l,m),m):""},h=new Map,f=async(l,m)=>{const w=`${l}:${m}`;return h.has(w)||h.set(w,!!(l&&m)&&await R(r.git,l,m)),h.get(w)},p=new Set([...e.keys(),...i.keys(),...t.claims.keys(),...n.map(l=>l.headRefName)]);if(p.size>L)throw new Error("git-reconciliation-ref-inventory-limit");for(const l of[...p].sort()){if(!A(l))continue;const m=t.claims.get(l)??[],w=new Set(m.map(y=>`${y.epicId}:${y.origin}`)),x=w.size===1?m[0].origin:r.directive.originBranch,b=await u(x),H=m.length===0||m.some(y=>y.active),$=e.get(l),q=i.get(l);q&&await P(r.git,l,q);const B=d&&q?await Z(r.gh,d.nameWithOwner,l):"unknown",M=async y=>{if(w.size!==1)return[];const v=new Set;for(const E of m)for(const k of E.proofs)k.head===y&&(k.mergeSha&&(!k.pushedSha||!await f(y,k.mergeSha)||!await f(k.mergeSha,k.pushedSha)||!await f(k.pushedSha,b))||k.evidence.forEach(V=>v.add(V)));return[...v]},G={schemaVersion:1,branchName:l,baseBranch:x,integrationHeadSha:b,lifecycle:H?"active":"integrated",current:l===r.currentBranch,origin:t.origins.has(l)||l===r.directive.originBranch,worktreeActive:c.has(l),contentEquivalent:!1,publicSafe:!0};for(const[y,v]of[["local_branch",$],["remote_branch",q]]){if(!v)continue;const E=`${y}:${I("sha256").update(l).digest("hex")}:${v}`,k=await f(v,b);a.push({...G,resourceId:E,kind:y,headSha:v,ownershipEvidence:await M(v),protected:!!q&&B!=="unprotected",providerVerified:y==="local_branch"?!0:!!d&&B!=="unknown",...y==="remote_branch"&&d?{remoteDeletionCas:"github-update-refs-v1"}:{},commitsReachable:k,hasExclusiveCommits:!k,previouslyReconciled:r.receiptIds.has(E)})}for(const y of n.filter(v=>v.headRefName===l)){const v=y.baseRefName===x&&y.sameRepository,E=await M(y.headRefOid);v&&E.includes("managed_metadata")&&E.push("pr_head_base_match");const k=`pr:${y.number}:${y.headRefOid}:${I("sha256").update(y.baseRefName).digest("hex")}`,V=await f(y.headRefOid,b);a.push({...G,resourceId:k,kind:"pull_request",prNumber:y.number,headSha:y.headRefOid,lifecycle:y.state!=="OPEN"?"already_absent":H?"active":"superseded",ownershipEvidence:E,providerVerified:v,protected:B!=="unprotected",commitsReachable:V,hasExclusiveCommits:!V,previouslyReconciled:r.receiptIds.has(k)})}}return ke(r.epic,a),a}function ke(r,a){const c=new Set((Array.isArray(r.git_reconciliation_receipts)?r.git_reconciliation_receipts:[]).filter(S).map(g=>_(g.idempotencyKey))),t=new Set((Array.isArray(r.git_reconciliation_debt)?r.git_reconciliation_debt:[]).filter(S).map(g=>_(g.resourceId))),d=new Set(a.filter(g=>g.kind==="pull_request"&&ae(g).action==="close_pull_request").map(g=>g.branchName));for(const g of a){const e=ae(g);e.action==="preserve"?t.add(g.resourceId):(c.add(`reconciliation:v2:${I("sha256").update([g.resourceId,e.action].join("\0")).digest("hex")}`),g.kind!=="pull_request"&&d.has(g.branchName)&&t.add(g.resourceId))}if(c.size>1024)throw new Error("git-reconciliation-receipt-capacity");if(t.size>1024)throw new Error("git-reconciliation-debt-capacity")}function ce(r){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 r instanceof Error&&a.has(r.message)?r.message:void 0}async function Be(r,a){const c=[],t=new Set;for(let d=1;d<=11;d+=1){const g=JSON.parse(await r("api",`repos/${a}/pulls?state=all&per_page=100&page=${d}`,"--hostname","github.com"));if(!Array.isArray(g)||g.length>100||d===11&&g.length>0)throw new Error("git-reconciliation-pr-inventory-limit");for(const e of g){if(!S(e)||!Number.isSafeInteger(e.number)||Number(e.number)<=0||t.has(Number(e.number))||!S(e.head)||!S(e.base)||!A(_(e.head.ref))||!A(_(e.base.ref))||!/^[a-f0-9]{40,64}$/.test(_(e.head.sha))||!["open","closed"].includes(_(e.state)))throw new Error("git-reconciliation-pr-inventory-invalid");t.add(Number(e.number)),c.push({number:Number(e.number),state:_(e.state).toUpperCase(),headRefName:_(e.head.ref),headRefOid:_(e.head.sha),baseRefName:_(e.base.ref),sameRepository:S(e.head.repo)&&S(e.base.repo)&&e.head.repo.full_name===a&&e.base.repo.full_name===a})}if(g.length<100)return c}throw new Error("git-reconciliation-pr-inventory-limit")}function z(r){return S(r)&&(r.outcome==="applied"||r.outcome==="already_absent")}async function Ne(r){try{const a=(await r.git("log",r.originHeadSha,"--format=%H","--fixed-strings",`--grep=Neocortex-Idempotency: ${r.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 r.git("rev-list","--parents","-n","1",c)).split(/\s+/),d=await r.git("show","-s","--format=%B",c),[g]=d.split(`
|
|
4
|
+
`),e=`merge(${r.directive.epicId}): integrate ${r.directive.executionBranch} into ${r.directive.originBranch}`;if(t.length!==3||t[2]!==r.executionHeadSha||g!==e)return null;const i=new Set(d.split(`
|
|
5
|
+
`).map(s=>s.trim()));for(const s of[`Neocortex-Epic: ${r.directive.epicId}`,`Neocortex-Origin-Branch: ${r.directive.originBranch}`,`Neocortex-Execution-Branch: ${r.directive.executionBranch}`,`Neocortex-Execution-Head: ${r.executionHeadSha}`,`Neocortex-Origin-Base: ${t[1]}`,`Neocortex-Graph-Revision: ${r.directive.graphRevision}`,`Neocortex-Aggregate-QA: ${r.directive.localGates.aggregateQaReceipt.receiptRef}`,`Neocortex-Idempotency: ${r.directive.idempotencyKey}`])if(!i.has(s))return null;const n=await r.git("show","-s","--format=%cI",c);return{schemaVersion:1,epicId:r.directive.epicId,executionBranch:r.directive.executionBranch,originBranch:r.directive.originBranch,baseSha:t[1],executionHeadSha:r.executionHeadSha,mergeSha:c,pushedSha:c,strategy:"direct",physicalStatus:"pushed",localGateReceiptRefs:r.directive.localGates.receiptRefs,idempotencyKey:r.directive.idempotencyKey,attemptStartedAt:n,completedAt:n,retryEligible:!1,reasonCodes:["epic-terminal-recovered-physical-push"],publicSafe:!0}}catch{return null}}async function Z(r,a,c){try{if(!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(a))return"unknown";const t=await r("api",`repos/${a}/branches/${encodeURIComponent(c)}`,"--hostname","github.com","--jq",".protected");return t==="false"?"unprotected":t==="true"?"protected":"unknown"}catch{return"unknown"}}async function K(r,a,c){try{const t=JSON.parse(await r("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(d=>typeof d=="string")?t:null}catch{return null}}async function $e(r,a,c,t,d,g){if(!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(a)||!A(c)||!/^[a-f0-9]{40,64}$/i.test(t)||!/^[a-f0-9]{40,64}$/i.test(d)||Number.isNaN(Date.parse(g)))return{ok:!1,reasonCode:"epic-terminal-pr-base-guard-unverifiable"};let e;try{const u=JSON.parse(await r("api",`repos/${a}/rules/branches/${encodeURIComponent(c)}`,"--hostname","github.com","--paginate","--slurp")),h=Array.isArray(u)&&u.every(Array.isArray)?u.flat():u;if(!Array.isArray(h)||h.length>256||h.some(f=>!S(f)))return{ok:!1,reasonCode:"epic-terminal-pr-base-guard-unverifiable"};e=h}catch{return{ok:!1,reasonCode:"epic-terminal-pr-base-guard-unverifiable"}}if(e.some(u=>u.type==="merge_queue"))return{ok:!1,reasonCode:"epic-terminal-pr-merge-queue-local-gate-required"};const i=e.some(u=>u.type==="required_status_checks"&&S(u.parameters)&&u.parameters.strict_required_status_checks_policy===!0);let n=!1;try{const u=JSON.parse(await r("api",`repos/${a}/branches/${encodeURIComponent(c)}/protection/required_status_checks`,"--hostname","github.com"));if(!S(u)||typeof u.strict!="boolean")return{ok:!1,reasonCode:"epic-terminal-pr-base-guard-unverifiable"};n=u.strict}catch(u){if(!Ae(u))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:d,checkedAt:g,publicSafe:!0}}}function Ae(r){return r instanceof Error&&/(?:HTTP\s*)?404\b|not found/i.test(r.message)}function qe(r,a){const c=r.mode==="recovered_exact_tree"?r.source==="physical_recovery":r.mode==="strict_up_to_date_expected_head"&&["branch_protection","ruleset","branch_protection_and_ruleset"].includes(r.source);return r.schemaVersion===1&&r.provider==="github"&&c&&r.publicSafe===!0&&!Number.isNaN(Date.parse(r.checkedAt))&&r.prHeadSha===a.mergeSha&&r.originHeadSha===a.baseSha}async function se(r,a,c){return(await Re(r,[...a],{cwd:c,encoding:"utf8",maxBuffer:16777216})).stdout.trim()}async function D(r,a,c=!1){const t=JSON.parse(await te(a,"utf8"));return Se(t,await ve(r,{required:c}))}function de(r,a,c,t){if(!r||r.schemaVersion!==1||r.evidenceVersion!=="p205-execution-v2"||r.producer!=="local-yolo-tdd"||r.contract!=="P205-CI-02"||r.command!==Q||r.verdict!=="passed"||r.executionHeadSha!==a||r.graphRevision!==c||!/^[a-f0-9]{64}$/i.test(r.manifestDigest??"")||!/^[a-f0-9]{64}$/i.test(r.resultDigest??"")||r.gateIds.length===0)return!1;const d=`local-yolo-tdd:${I("sha256").update(["epic-aggregate-p205-execution-v2",Q,a,c,r.manifestDigest,r.gateIds.join(","),r.resultDigest].join("\0")).digest("hex")}`;if(r.receiptRef!==d)return!1;const g=Date.parse(r.checkedAt),e=Date.parse(t);return Number.isFinite(g)&&Number.isFinite(e)&&e-g<=1800*1e3&&g-e<=300*1e3}async function N(r,a){const c=await C(r,a);if(!c)throw new Error("origin-ref-missing");return c}async function C(r,a){if(!A(a))throw new Error("branch-ref-invalid");const t=(await r("ls-remote","--heads","origin",`refs/heads/${a}`)).split(`
|
|
6
|
+
`).filter(Boolean).map(d=>d.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(r,a,c){if(await r("fetch","--quiet","--no-tags","origin",`refs/heads/${a}`),await r("rev-parse","FETCH_HEAD")!==c)throw new Error("remote-head-raced")}async function ue(r,a){try{return await r("rev-parse","--verify",a)}catch{return}}async function R(r,a,c){try{return await r("merge-base","--is-ancestor",a,c),!0}catch{return!1}}async function U(r,a){if(!/^[a-f0-9]{40,64}$/i.test(a))return!1;try{return await r("cat-file","-t",a)==="commit"}catch{return!1}}async function he(r){const a=new Set,c=await r("worktree","list","--porcelain");for(const t of c.split(`
|
|
7
|
+
`))t.startsWith("branch refs/heads/")&&a.add(t.slice(18));return a}function o(r,a,c){return{ok:!1,reasonCode:r,retryEligible:a,externalPolicy:c}}function _(r){return typeof r=="string"?r.trim():""}function A(r){return r.length>0&&r.length<=240&&!r.startsWith("-")&&!r.startsWith("/")&&!r.endsWith("/")&&!r.endsWith(".lock")&&!r.includes("..")&&!r.includes("\\")&&!r.includes("@{")&&!r.includes("//")&&/^[A-Za-z0-9._/-]+$/.test(r)}function S(r){return typeof r=="object"&&r!==null&&!Array.isArray(r)}function Ee(r,a){return r instanceof Error&&"code"in r&&r.code===a}function j(r){return(Array.isArray(r.stories)?r.stories:Array.isArray(r.story_ids)?r.story_ids:[]).filter(c=>typeof c=="string")}async function Y(r,a,c,t,d){const g=j(c),e=await Promise.all(g.map(i=>ne(r,a.stories?.[i]?.checkpoint,i,t,d).then(n=>n&&le(a.stories?.[i]?.checkpoint))));return g.filter((i,n)=>e[n])}function le(r){const a=S(r)?r:void 0,c=a&&S(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(_(c.manifestDigest))&&/^[a-f0-9]{64}$/i.test(_(c.resultDigest))&&Array.isArray(c.gateIds)&&c.gateIds.length>0)}function X(r,a){return r.length===a.length&&[...r].sort().every((c,t)=>c===[...a].sort()[t])}function ee(r){return S(r)&&typeof r.expiresAt=="string"&&r.expiresAt>new Date().toISOString()}async function fe(r){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 r("status","--porcelain","--untracked-files=all")).split(`
|
|
8
|
+
`).filter(Boolean).some(c=>!c.startsWith("?? ")||!a.has(c.slice(3)))}function re(r){const a=r instanceof Error?r.message:"";return/protected branch|branch protection|required pull request|GH006|GH013|ruleset|review required/i.test(a)}function ge(r,a){const c=Array.isArray(r)?r.filter(S):[],t=new Map(c.map(d=>[_(d.resourceId),d]));for(const d of a)t.set(d.resourceId,d);return[...t.values()]}export{We as createCliEpicTerminalGitAdapter};
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { type EpicGitReconciliationDirective, type EpicTerminalGitDirective, type EpicTerminalGitReceipt, type GitReconciliationCandidate, type GitReconciliationDecision, type GitReconciliationReceipt } from '../../../../shared/dist/yoloop/epic-terminal-git.js';
|
|
2
|
+
import { type P205TransientExecutionAuthority } from '../../git/git-lifecycle-store.js';
|
|
3
|
+
import { type EpicMergeCandidateQaAuthority } from './epic-merge-candidate-qa.js';
|
|
4
|
+
import type { EpicMergeCandidateQaReceipt } from '../../../../shared/dist/yoloop/epic-terminal-git.js';
|
|
5
|
+
import type { EpicPullRequestMergeGuardReceipt } from '../../../../shared/dist/yoloop/epic-terminal-git.js';
|
|
6
|
+
export interface EpicTerminalGitSnapshot {
|
|
7
|
+
readonly executionHeadSha: string;
|
|
8
|
+
readonly originHeadSha: string;
|
|
9
|
+
readonly graphRevision?: string;
|
|
10
|
+
readonly writerLeaseActive: boolean;
|
|
11
|
+
readonly acceptedCheckpointIds: readonly string[];
|
|
12
|
+
readonly existingReceipt?: EpicTerminalGitReceipt | null;
|
|
13
|
+
readonly aggregateQaReceipt?: import('../../../../shared/dist/yoloop/epic-terminal-git.js').EpicAggregateQaReceipt | null;
|
|
14
|
+
readonly originBinding?: import('../../../../shared/dist/yoloop/epic-terminal-git.js').EpicOriginBindingEvidence | null;
|
|
15
|
+
readonly nowIso?: string;
|
|
16
|
+
readonly reconciliationCandidates: readonly GitReconciliationCandidate[];
|
|
17
|
+
}
|
|
18
|
+
export type EpicTerminalEffectResult<T extends object = object> = ({
|
|
19
|
+
readonly ok: true;
|
|
20
|
+
} & T) | {
|
|
21
|
+
readonly ok: false;
|
|
22
|
+
readonly reasonCode: string;
|
|
23
|
+
readonly retryEligible: boolean;
|
|
24
|
+
readonly externalPolicy: boolean;
|
|
25
|
+
};
|
|
26
|
+
export interface EpicTerminalGitAdapter {
|
|
27
|
+
/** Produce missing physical origin/aggregate-QA evidence before server projection. */
|
|
28
|
+
prepareLifecycleEvidence?(input: {
|
|
29
|
+
readonly epicId: string;
|
|
30
|
+
readonly nowIso: string;
|
|
31
|
+
}): Promise<EpicTerminalEffectResult<{
|
|
32
|
+
readonly originBindingCaptured: boolean;
|
|
33
|
+
readonly aggregateQaRecorded: boolean;
|
|
34
|
+
readonly p205Authority?: P205TransientExecutionAuthority;
|
|
35
|
+
}>>;
|
|
36
|
+
inspect(directive: EpicTerminalGitDirective): Promise<EpicTerminalEffectResult<{
|
|
37
|
+
readonly snapshot: EpicTerminalGitSnapshot;
|
|
38
|
+
}>>;
|
|
39
|
+
prepareMergeObject(input: {
|
|
40
|
+
readonly originBranch: string;
|
|
41
|
+
readonly executionBranch: string;
|
|
42
|
+
readonly originHeadSha: string;
|
|
43
|
+
readonly executionHeadSha: string;
|
|
44
|
+
readonly message: string;
|
|
45
|
+
readonly graphRevision: string;
|
|
46
|
+
readonly nowIso: string;
|
|
47
|
+
}): Promise<EpicTerminalEffectResult<{
|
|
48
|
+
readonly mergeSha: string;
|
|
49
|
+
readonly candidateQa?: EpicMergeCandidateQaReceipt;
|
|
50
|
+
readonly candidateAuthority?: EpicMergeCandidateQaAuthority;
|
|
51
|
+
}>>;
|
|
52
|
+
pushMergeObject(input: {
|
|
53
|
+
readonly epicId: string;
|
|
54
|
+
readonly originBranch: string;
|
|
55
|
+
readonly executionBranch: string;
|
|
56
|
+
readonly executionHeadSha: string;
|
|
57
|
+
readonly expectedOriginSha: string;
|
|
58
|
+
readonly mergeSha: string;
|
|
59
|
+
readonly graphRevision: string;
|
|
60
|
+
readonly candidateAuthority: EpicMergeCandidateQaAuthority;
|
|
61
|
+
readonly storyCheckpointIds: readonly string[];
|
|
62
|
+
readonly aggregateQaReceipt: EpicTerminalGitDirective['localGates']['aggregateQaReceipt'];
|
|
63
|
+
readonly nowIso: string;
|
|
64
|
+
}): Promise<EpicTerminalEffectResult<{
|
|
65
|
+
readonly pushedSha: string;
|
|
66
|
+
}>>;
|
|
67
|
+
ensureSingleEpicPullRequest(input: {
|
|
68
|
+
readonly epicId: string;
|
|
69
|
+
readonly originBranch: string;
|
|
70
|
+
readonly executionBranch: string;
|
|
71
|
+
readonly expectedStoryHeadSha: string;
|
|
72
|
+
readonly expectedPullRequestHeadSha: string;
|
|
73
|
+
readonly candidateQa: EpicMergeCandidateQaReceipt;
|
|
74
|
+
readonly candidateAuthority: EpicMergeCandidateQaAuthority;
|
|
75
|
+
readonly idempotencyKey: string;
|
|
76
|
+
}): Promise<EpicTerminalEffectResult<{
|
|
77
|
+
readonly prNumber: number;
|
|
78
|
+
}>>;
|
|
79
|
+
mergeEpicPullRequest(input: {
|
|
80
|
+
readonly prNumber: number;
|
|
81
|
+
readonly originBranch: string;
|
|
82
|
+
readonly executionBranch: string;
|
|
83
|
+
readonly expectedStoryHeadSha: string;
|
|
84
|
+
readonly expectedPullRequestHeadSha: string;
|
|
85
|
+
readonly expectedOriginHeadSha: string;
|
|
86
|
+
readonly candidateQa?: EpicMergeCandidateQaReceipt;
|
|
87
|
+
readonly candidateAuthority?: EpicMergeCandidateQaAuthority;
|
|
88
|
+
readonly nowIso: string;
|
|
89
|
+
}): Promise<EpicTerminalEffectResult<{
|
|
90
|
+
readonly mergeSha: string;
|
|
91
|
+
readonly pushedSha: string;
|
|
92
|
+
readonly providerGuard: EpicPullRequestMergeGuardReceipt;
|
|
93
|
+
}>>;
|
|
94
|
+
/** Fresh physical proof after direct push or provider merge, before persistence/cleanup. */
|
|
95
|
+
verifyTerminalReceipt(directive: EpicTerminalGitDirective, receipt: EpicTerminalGitReceipt, nowIso: string): Promise<EpicTerminalEffectResult<{
|
|
96
|
+
readonly candidateQa?: EpicMergeCandidateQaReceipt;
|
|
97
|
+
readonly candidateAuthority?: EpicMergeCandidateQaAuthority;
|
|
98
|
+
readonly providerGuard?: EpicPullRequestMergeGuardReceipt;
|
|
99
|
+
}>>;
|
|
100
|
+
applyReconciliation(candidate: GitReconciliationCandidate, decision: GitReconciliationDecision, dryRun: boolean): Promise<EpicTerminalEffectResult<{
|
|
101
|
+
readonly outcome: 'applied' | 'already_absent' | 'dry_run';
|
|
102
|
+
}>>;
|
|
103
|
+
inspectReconciliation(directive: EpicGitReconciliationDirective): Promise<EpicTerminalEffectResult<{
|
|
104
|
+
readonly candidates: readonly GitReconciliationCandidate[];
|
|
105
|
+
}>>;
|
|
106
|
+
persistReconciliation(input: {
|
|
107
|
+
readonly epicId: string;
|
|
108
|
+
readonly reconciliationReceipts: readonly GitReconciliationReceipt[];
|
|
109
|
+
readonly preservedDecisions: readonly GitReconciliationDecision[];
|
|
110
|
+
}): Promise<EpicTerminalEffectResult>;
|
|
111
|
+
persist(input: {
|
|
112
|
+
readonly terminalReceipt: EpicTerminalGitReceipt;
|
|
113
|
+
readonly reconciliationReceipts: readonly GitReconciliationReceipt[];
|
|
114
|
+
readonly preservedDecisions: readonly GitReconciliationDecision[];
|
|
115
|
+
}): Promise<EpicTerminalEffectResult>;
|
|
116
|
+
}
|
|
117
|
+
export interface EpicTerminalGitExecutionResult {
|
|
118
|
+
readonly ok: boolean;
|
|
119
|
+
readonly receipt: EpicTerminalGitReceipt;
|
|
120
|
+
readonly reconciliationReceipts: readonly GitReconciliationReceipt[];
|
|
121
|
+
readonly preservedDecisions: readonly GitReconciliationDecision[];
|
|
122
|
+
readonly reasonCodes: readonly string[];
|
|
123
|
+
readonly publicSafe: true;
|
|
124
|
+
}
|
|
125
|
+
export interface EpicGitReconciliationExecutionResult {
|
|
126
|
+
readonly ok: boolean;
|
|
127
|
+
readonly reconciliationReceipts: readonly GitReconciliationReceipt[];
|
|
128
|
+
readonly preservedDecisions: readonly GitReconciliationDecision[];
|
|
129
|
+
readonly reasonCodes: readonly string[];
|
|
130
|
+
readonly publicSafe: true;
|
|
131
|
+
}
|
|
132
|
+
/** Run the same fail-closed reconciler at start/resume or explicit audit. */
|
|
133
|
+
export declare function executeEpicGitReconciliation(directive: EpicGitReconciliationDirective, adapter: EpicTerminalGitAdapter, now: () => string): Promise<EpicGitReconciliationExecutionResult>;
|
|
134
|
+
export declare function executeEpicTerminalGit(directive: EpicTerminalGitDirective, adapter: EpicTerminalGitAdapter, now: () => string, preparedAuthority?: P205TransientExecutionAuthority): Promise<EpicTerminalGitExecutionResult>;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{createHash as G}from"node:crypto";import{EPIC_TERMINAL_GIT_REASON_CODES as x,classifyGitReconciliationCandidate as w,validateEpicTerminalGitDirective as N}from"../../../../shared/dist/yoloop/epic-terminal-git.js";import{p205TransientAuthorityMatchesAggregate as q}from"../../git/git-lifecycle-store.js";import{verifiedEpicMergeCandidateReceipt as _}from"./epic-merge-candidate-qa.js";async function O(e,o,t){const c=await o.inspectReconciliation(e);if(!c.ok)return{ok:!1,reconciliationReceipts:[],preservedDecisions:[],reasonCodes:[c.reasonCode],publicSafe:!0};const a=await E({idempotencyKey:e.idempotencyKey,enabled:!0,dryRun:e.dryRun},c.candidates,o,t),l=await o.persistReconciliation({epicId:e.epicId,reconciliationReceipts:a.receipts,preservedDecisions:a.preserved}),h=R([...a.reasonCodes,...l.ok?[]:[l.reasonCode]]);return{ok:a.ok&&l.ok,reconciliationReceipts:a.receipts,preservedDecisions:a.preserved,reasonCodes:h,publicSafe:!0}}async function T(e,o,t,c){const a=t();if(!o.prepareLifecycleEvidence)return m(e,{ok:!1,reasonCode:"epic-aggregate-qa-producer-required",retryEligible:!1,externalPolicy:!1},a);const l=c?{ok:!0,originBindingCaptured:!1,aggregateQaRecorded:!0,p205Authority:c}:await o.prepareLifecycleEvidence({epicId:e.epicId,nowIso:a});if(!l.ok)return m(e,l,a);if(!l.p205Authority)return m(e,{ok:!1,reasonCode:"epic-aggregate-qa-authority-required",retryEligible:!1,externalPolicy:!1},a);const h=await o.inspect(e);if(!h.ok)return m(e,h,a);const r=h.snapshot;if(!q(l.p205Authority,r.executionHeadSha,e.graphRevision))return m(e,{ok:!1,reasonCode:"epic-aggregate-qa-authority-binding-mismatch",retryEligible:!1,externalPolicy:!1},a);const p=N(e,{...r,nowIso:a});if(!p.ready)return y(!1,f(e,r,a,{strategy:e.strategy==="single_pr"?"single_pr":"direct",physicalStatus:"blocked_local_or_repo",retryEligible:p.reasonCodes.some(n=>n.endsWith("mismatch")||n.endsWith("active")),reasonCodes:p.reasonCodes}),[],[],p.reasonCodes);if(p.reasonCodes.includes(x.ALREADY_COMPLETE)&&r.existingReceipt){const n=await o.verifyTerminalReceipt(e,r.existingReceipt,a);if(!n.ok)return y(!1,r.existingReceipt,[],[],[n.reasonCode]);const k=_(n.candidateAuthority,{mergeSha:r.existingReceipt.mergeSha,originHeadSha:r.existingReceipt.baseSha,executionHeadSha:r.existingReceipt.executionHeadSha,graphRevision:e.graphRevision});if(!k)return y(!1,r.existingReceipt,[],[],["epic-merge-candidate-authority-required"]);const B={...r.existingReceipt,candidateQa:k,...n.providerGuard?{providerGuard:n.providerGuard}:{}},S=await E({idempotencyKey:e.idempotencyKey,enabled:e.reconcile.enabled,dryRun:e.reconcile.dryRun},r.reconciliationCandidates,o,t),b=await o.persist({terminalReceipt:B,reconciliationReceipts:S.receipts,preservedDecisions:S.preserved}),A=R([...p.reasonCodes,...S.reasonCodes,...b.ok?[]:[b.reasonCode]]);return y(b.ok&&S.ok,B,S.receipts,S.preserved,A)}let i;const s=await o.prepareMergeObject({originBranch:e.originBranch,executionBranch:e.executionBranch,originHeadSha:r.originHeadSha,executionHeadSha:r.executionHeadSha,graphRevision:e.graphRevision,nowIso:a,message:[`merge(${e.epicId}): integrate ${e.executionBranch} into ${e.originBranch}`,"",`Neocortex-Epic: ${e.epicId}`,`Neocortex-Origin-Branch: ${e.originBranch}`,`Neocortex-Execution-Branch: ${e.executionBranch}`,`Neocortex-Execution-Head: ${r.executionHeadSha}`,`Neocortex-Origin-Base: ${r.originHeadSha}`,`Neocortex-Graph-Revision: ${e.graphRevision}`,`Neocortex-Aggregate-QA: ${e.localGates.aggregateQaReceipt.receiptRef}`,`Neocortex-Idempotency: ${e.idempotencyKey}`].join(`
|
|
2
|
+
`)});if(!s.ok)return i=f(e,r,a,{strategy:"direct",physicalStatus:"blocked_local_or_repo",retryEligible:s.retryEligible,reasonCodes:[s.reasonCode]}),await o.persist({terminalReceipt:i,reconciliationReceipts:[],preservedDecisions:[]}),y(!1,i,[],[],i.reasonCodes);const d=_(s.candidateAuthority,{originHeadSha:r.originHeadSha,executionHeadSha:r.executionHeadSha,mergeSha:s.mergeSha,graphRevision:e.graphRevision});if(!d)return m(e,{ok:!1,reasonCode:"epic-merge-candidate-authority-required",retryEligible:!1,externalPolicy:!1},a);if(e.strategy!=="single_pr"){const n=await o.pushMergeObject({epicId:e.epicId,originBranch:e.originBranch,executionBranch:e.executionBranch,executionHeadSha:r.executionHeadSha,expectedOriginSha:r.originHeadSha,mergeSha:s.mergeSha,graphRevision:e.graphRevision,candidateAuthority:s.candidateAuthority,storyCheckpointIds:e.storyCheckpointIds,aggregateQaReceipt:e.localGates.aggregateQaReceipt,nowIso:t()});n.ok?i=f(e,r,a,{strategy:"direct",mergeSha:s.mergeSha,pushedSha:n.pushedSha,candidateQa:d,physicalStatus:"pushed",retryEligible:!1,reasonCodes:[x.READY],completedAt:t()}):e.strategy==="direct_or_single_pr"&&n.externalPolicy?i=await I(e,r,o,a,t,d,s.candidateAuthority):i=f(e,r,a,{strategy:"direct",mergeSha:s.mergeSha,physicalStatus:n.externalPolicy?"denied_external_gate":"blocked_local_or_repo",retryEligible:n.retryEligible,reasonCodes:[n.reasonCode,...n.retryEligible?[x.RETRY_ELIGIBLE]:[]]})}else i=await I(e,r,o,a,t,d,s.candidateAuthority);const u=i.physicalStatus==="pushed"?await o.verifyTerminalReceipt(e,i,t()):null;if(u&&!u.ok){const n=f(e,r,a,{...i,physicalStatus:"blocked_local_or_repo",retryEligible:u.retryEligible,reasonCodes:[u.reasonCode]});return await o.persist({terminalReceipt:n,reconciliationReceipts:[],preservedDecisions:[]}),y(!1,n,[],[],n.reasonCodes)}if(u?.ok){const n=_(u.candidateAuthority,{mergeSha:i.mergeSha,originHeadSha:i.baseSha,executionHeadSha:i.executionHeadSha,graphRevision:e.graphRevision});if(!n)return y(!1,i,[],[],["epic-merge-candidate-authority-required"]);i={...i,candidateQa:n,...u.providerGuard?{providerGuard:u.providerGuard}:{}}}const g=i.physicalStatus==="pushed"?await E({idempotencyKey:e.idempotencyKey,enabled:e.reconcile.enabled,dryRun:e.reconcile.dryRun},r.reconciliationCandidates,o,t):{ok:!0,receipts:[],preserved:[],reasonCodes:[]},C=await o.persist({terminalReceipt:i,reconciliationReceipts:g.receipts,preservedDecisions:g.preserved}),H=R([...i.reasonCodes,...g.reasonCodes,...C.ok?[]:[C.reasonCode]]);return y(i.physicalStatus==="pushed"&&g.ok&&C.ok,i,g.receipts,g.preserved,H)}async function I(e,o,t,c,a,l,h){const r=await t.ensureSingleEpicPullRequest({epicId:e.epicId,originBranch:e.originBranch,executionBranch:e.executionBranch,expectedStoryHeadSha:o.executionHeadSha,expectedPullRequestHeadSha:l.mergeSha,candidateQa:l,candidateAuthority:h,idempotencyKey:e.idempotencyKey});if(!r.ok)return f(e,o,c,{strategy:"single_pr",physicalStatus:r.externalPolicy?"denied_external_gate":"blocked_local_or_repo",retryEligible:r.retryEligible,reasonCodes:[r.reasonCode]});const p=await t.mergeEpicPullRequest({prNumber:r.prNumber,originBranch:e.originBranch,executionBranch:e.executionBranch,expectedStoryHeadSha:o.executionHeadSha,expectedPullRequestHeadSha:l.mergeSha,expectedOriginHeadSha:o.originHeadSha,candidateQa:l,candidateAuthority:h,nowIso:a()});return p.ok?f(e,o,c,{strategy:"single_pr",prNumber:r.prNumber,mergeSha:p.mergeSha,pushedSha:p.pushedSha,candidateQa:l,providerGuard:p.providerGuard,physicalStatus:"pushed",retryEligible:!1,reasonCodes:[x.READY],completedAt:a()}):f(e,o,c,{strategy:"single_pr",prNumber:r.prNumber,physicalStatus:p.externalPolicy?"denied_external_gate":"blocked_local_or_repo",retryEligible:p.retryEligible,reasonCodes:[p.reasonCode,...p.retryEligible?[x.RETRY_ELIGIBLE]:[]]})}async function E(e,o,t,c){if(!e.enabled)return{ok:!0,receipts:[],preserved:[],reasonCodes:[]};const a=[],l=[],h=[];let r=!0;const p=new Set,i={pull_request:0,local_branch:1,remote_branch:2};for(const s of[...o].sort((d,u)=>i[d.kind]-i[u.kind]||d.resourceId.localeCompare(u.resourceId))){const d=w(s);if(h.push(...d.reasonCodes),d.action==="preserve"){l.push(d);continue}if(s.kind!=="pull_request"&&p.has(s.branchName)){l.push({...d,action:"preserve",reasonCodes:["git-resource-related-pr-close-pending"]}),h.push("git-resource-related-pr-close-pending");continue}const u=`reconciliation:v2:${G("sha256").update([s.resourceId,d.action].join("\0")).digest("hex")}`,g=await t.applyReconciliation(s,d,e.dryRun);if(!g.ok){r=!1,s.kind==="pull_request"&&p.add(s.branchName),h.push(g.reasonCode),a.push({schemaVersion:1,resourceId:s.resourceId,action:d.action,outcome:"failed",idempotencyKey:u,reasonCodes:[g.reasonCode],recordedAt:c(),publicSafe:!0});continue}a.push({schemaVersion:1,resourceId:s.resourceId,action:d.action,outcome:g.outcome,idempotencyKey:u,reasonCodes:d.reasonCodes,recordedAt:c(),publicSafe:!0})}return{ok:r,receipts:a,preserved:l,reasonCodes:R(h)}}function f(e,o,t,c){return Object.freeze({schemaVersion:1,epicId:e.epicId,executionBranch:e.executionBranch,originBranch:e.originBranch,baseSha:o.originHeadSha,executionHeadSha:o.executionHeadSha,localGateReceiptRefs:e.localGates.receiptRefs,idempotencyKey:e.idempotencyKey,attemptStartedAt:t,publicSafe:!0,...c})}function m(e,o,t){const c="0".repeat(40),a={schemaVersion:1,epicId:e.epicId,executionBranch:e.executionBranch,originBranch:e.originBranch,baseSha:c,executionHeadSha:e.expectedExecutionHeadSha,strategy:e.strategy==="single_pr"?"single_pr":"direct",physicalStatus:o.externalPolicy?"denied_external_gate":"blocked_local_or_repo",localGateReceiptRefs:e.localGates.receiptRefs,idempotencyKey:e.idempotencyKey,attemptStartedAt:t,retryEligible:o.retryEligible,reasonCodes:[o.reasonCode],publicSafe:!0};return y(!1,a,[],[],a.reasonCodes)}function y(e,o,t,c,a){return{ok:e,receipt:o,reconciliationReceipts:t,preservedDecisions:c,reasonCodes:R(a),publicSafe:!0}}function R(e){return[...new Set(e)]}export{O as executeEpicGitReconciliation,T as executeEpicTerminalGit};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createHash as u}from"node:crypto";import{validateYoloopPreparationBundle as
|
|
1
|
+
import{createHash as u}from"node:crypto";import{validateYoloopPreparationBundle as h}from"../../../../shared/dist/yoloop/wave-decision-receipt.js";function f(e){const r=[],s=Array.isArray(e.phases)?e.phases.filter(a):[],i=a(e.budgets)?e.budgets:{};(e.version!==2||!c(e.graphRevision))&&r.push("phased-plan-invalid"),i.maxWriteConcurrency!==1&&r.push("writer-concurrency-invalid");for(const t of s)(!Number.isInteger(t.maxConcurrency)||Number(t.maxConcurrency)<1)&&r.push("phase-concurrency-invalid"),(t.mutatesEpicCheckout===!0||t.mutatesEpicWorktree===!0)&&(t.maxConcurrency!==1||!["epic-writer-lease","commit-range"].includes(String(t.isolation)))&&r.push("mutating-phase-not-serialized");["read-only-analysis","isolated-build-test","patch-preparation","single-writer-integration","story-checkpoint-review"].some(t=>!s.some(n=>n.kind===t))&&r.push("phase-missing");const o=Array.isArray(e.decisionReceiptIds)?e.decisionReceiptIds:[];(o.length===0||o.some(t=>!c(t)))&&r.push("decision-receipt-invalid");const d=Math.max(1,...s.filter(t=>t.mutatesEpicCheckout!==!0&&t.mutatesEpicWorktree!==!0).map(t=>Number(t.maxConcurrency)||1));return{ok:r.length===0,preparationConcurrency:d,childExecutionConcurrency:1,reasonCodes:[...new Set(r)].sort()}}function g(e){const r=[...h(e.bundle,e.expected)];return e.bundle.claimRevision!==e.expected.claimRevision&&r.push("bundle-claims-stale"),l(e.bundle.authorizedWriteSet,e.expected.authorizedWriteSet??[])||r.push("bundle-write-set-stale"),u("sha256").update(e.payload).digest("hex")!==e.bundle.bundleDigest&&r.push("bundle-payload-digest-mismatch"),(!e.writerLease||e.writerLease.ownerStoryId!==e.bundle.storyId||e.writerLease.expiresAt<=e.expected.nowIso)&&r.push("bundle-writer-lease-missing"),[...new Set(r)].sort()}function a(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function c(e){return typeof e=="string"&&/^[a-f0-9]{40,64}$/.test(e)}function l(e,r){return e.length===r.length&&e.every((s,i)=>s===r[i])}export{g as admitPreparedStoryBundle,f as validatePhasedYoloopPlan};
|