@ornexus/neocortex 4.60.72 → 4.60.74
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/release-source.json +1 -1
- package/dist/sbom.cdx.json +1217 -1211
- package/install.ps1 +1 -1
- package/install.sh +1 -1
- package/package.json +2 -2
- package/packages/client/dist/commands/invoke.js +66 -66
- package/packages/client/dist/continuity/sqlite-store.d.ts +22 -1
- package/packages/client/dist/continuity/sqlite-store.js +60 -60
- package/packages/client/dist/git/epic-operational-state.d.ts +198 -0
- package/packages/client/dist/git/epic-operational-state.js +2 -0
- package/packages/client/dist/git/git-lifecycle-store.d.ts +32 -1
- package/packages/client/dist/git/git-lifecycle-store.js +9 -8
- package/packages/client/dist/migrations/epic-lifecycle-backfill.d.ts +97 -0
- package/packages/client/dist/migrations/epic-lifecycle-backfill.js +3 -0
- package/packages/client/dist/migrations/state-json.d.ts +22 -0
- package/packages/client/dist/migrations/state-json.js +4 -4
- package/packages/client/dist/runner/headless/epic-merge-candidate-qa.d.ts +1 -1
- package/packages/client/dist/runner/headless/epic-merge-candidate-qa.js +2 -2
- package/packages/client/dist/runner/headless/epic-terminal-git-cli-adapter.d.ts +2 -0
- package/packages/client/dist/runner/headless/epic-terminal-git-cli-adapter.js +10 -8
- package/packages/client/dist/runner/headless/epic-terminal-git-runner.d.ts +18 -2
- package/packages/client/dist/runner/headless/epic-terminal-git-runner.js +2 -2
- package/packages/client/dist/runner/headless/run-yolo.js +1 -1
- package/packages/client/dist/runner/headless/run-yoloop.d.ts +1 -0
- package/packages/client/dist/runner/headless/run-yoloop.js +2 -2
- package/packages/client/dist/runner/headless/types.js +2 -2
- package/packages/client/dist/state/project-state-snapshot.d.ts +8 -0
- package/packages/client/dist/state/project-state-snapshot.js +1 -1
- package/packages/client/dist/yolo/spec-envelope-collector.js +3 -3
- package/packages/client/dist/yoloop/shared-yoloop-types.d.ts +65 -1
- package/packages/client/dist/yoloop/yoloop-client-state-store.d.ts +8 -0
- package/packages/client/dist/yoloop/yoloop-client-state-store.js +1 -1
- package/packages/shared/dist/yolo/change-evidence.js +1 -1
- package/packages/shared/dist/yolo/spec-envelope.js +3 -6
- package/packages/shared/dist/yoloop/epic-terminal-git.js +144 -2
- package/targets-stubs/antigravity/gemini.md +1 -1
- package/targets-stubs/antigravity/skill/SKILL.md +1 -1
- package/targets-stubs/claude-code/neocortex-root.agent.yaml +1 -1
- package/targets-stubs/claude-code/neocortex-root.md +2 -2
- package/targets-stubs/claude-code/neocortex.agent.yaml +1 -1
- package/targets-stubs/claude-code/neocortex.md +2 -2
- package/targets-stubs/codex/AGENTS.md +1 -1
- package/targets-stubs/cursor/agent.md +2 -2
- package/targets-stubs/gemini-cli/agent.md +2 -2
- package/targets-stubs/opencode/neocortex-root.md +1 -1
- package/targets-stubs/vscode/neocortex.agent.md +2 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{createHash as
|
|
2
|
-
`;function
|
|
3
|
-
`,s=()=>{const c=`${
|
|
4
|
-
`)}function
|
|
1
|
+
import{createHash as z,randomUUID as U}from"node:crypto";import{existsSync as m,linkSync as J,lstatSync as I,mkdirSync as G,readFileSync as f,realpathSync as K,statSync as L,unlinkSync as T}from"node:fs";import{dirname as x,join as p,resolve as X}from"node:path";import{assertStateMigrationPlanV1 as Y,assertStateMigrationReceiptV1 as Q,planStateMigration as M,stateMigrationDigest as W,STATE_MIGRATION_RECEIPT_SCHEMA as Z,StateMigrationHardStopError as R}from"../../../shared/dist/state/index.js";import{atomicReplaceUtf8 as H,durableCreateUtf8 as N,fsyncDirectoryBestEffort as O,recoverAtomicReplaceUtf8 as P}from"../fs/atomic-state-replace.js";const E=p(".neocortex","state.json"),$=p(".neocortex","state-migration.json"),tt=p(".neocortex","state-migration.lock"),V=4*1024*1024;class a extends Error{reasonCode;receipt;constructor(t,r=t,e){super(r),this.reasonCode=t,this.receipt=e,this.name="StateMigrationApplyError"}}const _=o=>z("sha256").update(o).digest("hex"),et=o=>`${JSON.stringify(o,null,2)}
|
|
2
|
+
`;function b(o){let t;try{t=K(X(o))}catch{throw new a("state_migration_root_invalid")}if(!L(t).isDirectory())throw new a("state_migration_root_invalid");for(const r of[".neocortex",E,$]){const e=p(t,r);if(!m(e))continue;const s=I(e);if(s.isSymbolicLink()||r!==".neocortex"&&!s.isFile()||r===".neocortex"&&!s.isDirectory())throw new a("state_migration_symlink_or_type_rejected")}return t}function j(o){try{const t=JSON.parse(o);if(t&&typeof t=="object"&&!Array.isArray(t))return t}catch{}throw new a("state_migration_state_corrupt")}function v(o){const t=p(o,E);if(!m(t))throw new a("state_migration_state_missing");const r=L(t).size;if(r>V)throw new a("state_migration_input_too_large");const e=f(t,"utf8"),s=Buffer.byteLength(e,"utf8");if(s!==r||s>V)throw new a("state_migration_source_changed");return{state:j(e),raw:e,digest:_(e),bytes:s}}function ct(o){const t=v(b(o));let r;try{r=M(t.state)}catch(l){throw l instanceof R?new a(l.reasonCode,l.message):l}const e=r.kind==="migrate"?r.plan.targetState:t.state,s=(l,g)=>{const i=l[g];return i&&typeof i=="object"&&!Array.isArray(i)?Object.keys(i).length:0},c=r.kind==="migrate"?(({targetState:l,...g})=>g)(r.plan):null;return Object.freeze({schemaVersion:"StateMigrationAuthority/1.0",sourceByteDigest:t.digest,sourceCanonicalHash:r.kind==="migrate"?r.plan.sourceCanonicalHash:r.sourceCanonicalHash,sourceByteLength:t.bytes,before:{epics:s(t.state,"epics"),stories:s(t.state,"stories")},after:{epics:s(e,"epics"),stories:s(e,"stories")},outcome:r.kind,plan:c,publicSafe:!0})}function lt(o){const t=v(b(o));return Object.freeze({state:t.state,raw:t.raw,byteDigest:t.digest,byteLength:t.bytes})}function gt(o){if(!/^smig_[a-f0-9]{32}$/.test(o.operationId)||!/^[a-f0-9]{64}$/.test(o.expectedCurrentDigest)||!/^[a-f0-9]{64}$/.test(o.expectedBackupDigest))throw new a("state_migration_rollback_binding_invalid");const t=b(o.projectRoot),r=q(t);try{const e=p(t,E),s=p(t,".neocortex",`state.json.migration-backup-${o.operationId}`);if(!m(s))throw new a("state_migration_backup_missing");const c=I(s);if(!c.isFile()||c.isSymbolicLink())throw new a("state_migration_backup_conflict");const l=f(e,"utf8"),g=f(s,"utf8");if(_(l)!==o.expectedCurrentDigest)throw new a("state_migration_rollback_postimage_conflict");if(_(g)!==o.expectedBackupDigest)throw new a("state_migration_backup_conflict");if(H(e,g),_(f(e))!==o.expectedBackupDigest)throw new a("state_migration_rollback_failed");return{restoredDigest:o.expectedBackupDigest,backupPreserved:!0}}finally{r()}}function q(o){const t=p(o,tt);G(x(t),{recursive:!0,mode:448});const r=U(),e=`${process.pid}:${r}
|
|
3
|
+
`,s=()=>{const c=`${t}.${process.pid}.${U()}.owner`;N(c,e);try{J(c,t)}finally{try{T(c)}catch{}O(x(t))}};try{s()}catch(c){if(c.code!=="EEXIST")throw new a("state_migration_lock_failed");const l=I(t);if(l.isSymbolicLink()||!l.isFile())throw new a("state_migration_lock_corrupt");const g=f(t,"utf8"),i=g.match(/^([1-9][0-9]*):([a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12})\n$/i);if(!i||g.length>64)throw new a("state_migration_lock_corrupt");let h=!0;try{process.kill(Number(i[1]),0)}catch(n){h=n.code!=="ESRCH"}if(h)throw new a("state_migration_locked");try{T(t),s()}catch{throw new a("state_migration_locked")}}return()=>{try{if(f(t,"utf8")!==e)return;T(t),O(x(t))}catch{}}}function rt(o){const t=p(o,$);if(P(t),!m(t))return null;let r;try{r=JSON.parse(f(t,"utf8"))}catch{throw new a("state_migration_manifest_corrupt")}if(!r||typeof r!="object"||Array.isArray(r))throw new a("state_migration_manifest_corrupt");const e=r;if(e.schemaVersion!==1||typeof e.operationId!="string"||!/^smig_[a-f0-9]{32}$/.test(e.operationId)||!["prepared","applied","rolled-back"].includes(String(e.phase))||typeof e.sourceCanonicalHash!="string"||!/^[a-f0-9]{64}$/.test(e.sourceCanonicalHash)||typeof e.sourceByteDigest!="string"||!/^[a-f0-9]{64}$/.test(e.sourceByteDigest)||typeof e.targetCanonicalHash!="string"||!/^[a-f0-9]{64}$/.test(e.targetCanonicalHash)||typeof e.targetByteDigest!="string"||!/^[a-f0-9]{64}$/.test(e.targetByteDigest)||!Number.isSafeInteger(e.migratedEpicCount)||(e.migratedEpicCount??-1)<0||!Number.isSafeInteger(e.migratedStoryCount)||(e.migratedStoryCount??-1)<0||!Number.isSafeInteger(e.preservedUnknownFieldCount)||(e.preservedUnknownFieldCount??-1)<0||typeof e.backupPath!="string"||e.backupPath!==p(".neocortex",`state.json.migration-backup-${e.operationId}`))throw new a("state_migration_manifest_corrupt");return e}function D(o,t){H(p(o,$),`${JSON.stringify(t,null,2)}
|
|
4
|
+
`)}function S(o,t,r,e,s,c,l,g,i,h,n,u){const w={schemaVersion:Z,operationId:t,outcome:o,sourceBytesHash:r,targetBytesHash:e,sourceCanonicalHash:s,targetCanonicalHash:c,backupHash:l,rollbackStatus:g,migratedEpicCount:i,migratedStoryCount:h,preservedUnknownFieldCount:n,reasonCodes:u,publicSafe:!0};return Q(w),w}function pt(o){const t=b(o);let r;try{r=M(v(t).state)}catch(e){throw e instanceof R?new a(e.reasonCode,e.message):e}return r.kind==="migrate"?r.plan:null}function ut(o,t,r={}){const e=b(o),s=q(e);try{const c=p(e,E);try{P(c)}catch{throw new a("state_migration_atomic_recovery_failed")}if(!m(c))throw new a("state_migration_state_missing");const l=v(e).raw;if(t?.sourceByteDigest&&_(l)!==t.sourceByteDigest)throw new a("state_migration_expected_bytes_conflict");let g;try{g=M(j(l))}catch(d){throw d instanceof R?new a(d.reasonCode,d.message):d}const i=rt(e),h=_(l);if(g.kind==="unchanged"){if(t&&g.sourceCanonicalHash!==t.targetCanonicalHash)throw new a("state_migration_expected_plan_conflict");if(i&&i.targetCanonicalHash===g.sourceCanonicalHash){if(i.targetByteDigest!==h)throw new a("state_migration_state_drift");if(i.phase==="prepared"&&D(e,{...i,phase:"applied"}),i.phase==="rolled-back")throw new a("state_migration_state_drift");return S("already-applied",i.operationId,i.sourceByteDigest,i.targetByteDigest,i.sourceCanonicalHash,i.targetCanonicalHash,i.sourceByteDigest,"not-required",i.migratedEpicCount,i.migratedStoryCount,i.preservedUnknownFieldCount,["migration_replay_verified"])}if(i)throw new a("state_migration_state_drift");return S("already-applied",t?.operationId??"smig_00000000000000000000000000000000",h,h,g.sourceCanonicalHash,g.sourceCanonicalHash,null,"not-required",0,0,0,["canonical_state_unchanged"])}const n=g.plan;if(Y(n),t&&(t.operationId!==n.operationId||t.sourceCanonicalHash!==n.sourceCanonicalHash||t.targetCanonicalHash!==n.targetCanonicalHash))throw new a("state_migration_expected_plan_conflict");if(i&&i.operationId!==n.operationId)throw new a("state_migration_operation_conflict");const u=h,w=et(n.targetState),y=_(w),F=p(".neocortex",`state.json.migration-backup-${n.operationId}`),k=p(e,F);if(m(k)){const d=I(k);if(d.isSymbolicLink()||!d.isFile()||_(f(k))!==u)throw new a("state_migration_backup_conflict")}else N(k,l);const A={schemaVersion:1,operationId:n.operationId,phase:"prepared",sourceCanonicalHash:n.sourceCanonicalHash,sourceByteDigest:u,targetCanonicalHash:n.targetCanonicalHash,targetByteDigest:y,migratedEpicCount:n.migratedEpicIds.length,migratedStoryCount:n.migratedStoryIds.length,preservedUnknownFieldCount:n.preservedUnknownFieldCount,backupPath:F};if(D(e,A),r.afterPrepared?.(c),_(f(c))!==u)throw new a("state_migration_source_cas_conflict");try{H(c,w),r.afterReplace?.(c);const d=f(c,"utf8"),B=j(d);if(_(d)!==y||W(B)!==n.targetCanonicalHash)throw new Error("readback-digest-mismatch");D(e,{...A,phase:"applied"})}catch(d){try{const C=f(k,"utf8");if(_(C)!==u)throw new Error("rollback-backup-digest-mismatch");if(H(c,C),_(f(c))!==u)throw new Error("rollback-digest-mismatch");D(e,{...A,phase:"rolled-back"})}catch{const C=S("blocked",n.operationId,u,y,n.sourceCanonicalHash,n.targetCanonicalHash,u,"unverified",n.migratedEpicIds.length,n.migratedStoryIds.length,n.preservedUnknownFieldCount,["apply_failed","rollback_unverified"]);throw new a("state_migration_rollback_failed",String(d),C)}const B=S("blocked",n.operationId,u,y,n.sourceCanonicalHash,n.targetCanonicalHash,u,"restored",n.migratedEpicIds.length,n.migratedStoryIds.length,n.preservedUnknownFieldCount,["apply_failed","rollback_verified"]);throw new a("state_migration_apply_rolled_back",String(d),B)}return S("applied",n.operationId,u,y,n.sourceCanonicalHash,n.targetCanonicalHash,u,"not-required",n.migratedEpicIds.length,n.migratedStoryIds.length,n.preservedUnknownFieldCount,["lock_acquired","source_cas_verified","backup_verified","atomic_replace_verified"])}finally{s()}}export{V as STATE_MIGRATION_INPUT_LIMIT_BYTES,a as StateMigrationApplyError,ut as applyStateMigration,ct as collectStateMigrationAuthority,pt as previewStateMigration,lt as readStateMigrationSourceSnapshot,gt as restoreStateMigrationPreimage};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type EpicMergeCandidateQaReceipt } from '../../../../shared/dist/yoloop/epic-terminal-git.js';
|
|
2
2
|
export interface EpicMergeCandidateBinding {
|
|
3
3
|
readonly originHeadSha: string;
|
|
4
4
|
readonly executionHeadSha: string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{execFile as D}from"node:child_process";import{createHash as x,randomBytes as
|
|
1
|
+
import{execFile as D}from"node:child_process";import{createHash as x,randomBytes as A}from"node:crypto";import{join as H}from"node:path";import{promisify as j}from"node:util";import{P205_RELEASE_GATE_BUDGETS as O}from"../../../../shared/dist/yoloop/epic-terminal-git.js";import{assertGitLifecyclePrivateDirectory as L,ensureGitLifecyclePrivateChildDirectory as P,executeP205TransientAuthority as C,gitLifecycleRuntimeDirectory as R,P205_COMPLETE_COMMAND as _,p205TransientAuthorityMatchesAggregate as M,projectP205TransientExecutionEvidence as T,removeGitLifecyclePrivateDirectory as q}from"../../git/git-lifecycle-store.js";import{anchoredCreateDirectory as I,anchoredDirectoryIdentity as y,anchoredExecuteGit as N,anchoredExecuteNode as G,anchoredExecuteNpm as $,secureFilesystemBoundaryAvailable as z}from"../../git/secure-filesystem-boundary.js";const B=j(D),S=new WeakMap;function K(c,e){const s=c&&S.get(c);if(!s||!M(s.p205,e.mergeSha,e.graphRevision))return;const h=Date.now()-s.acceptedAt;if(h<0||h>O.aggregateValidityMs)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 X(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 B("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}`),v=await R(c),n=await P(v,"merge-candidates"),p=`${e.mergeSha.slice(0,16)}-${A(8).toString("hex")}`,o=await I(n,p),u=H(n,p),t=async(...a)=>(await N(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 G(n,p,o,["-e",`
|
|
2
2
|
const fs=require('node:fs'); const path=require('node:path'); const locks=JSON.parse(process.argv[1]);
|
|
3
3
|
// npm-shrinkwrap wins over package-lock in the same installation root.
|
|
4
4
|
// Validate every selected root, not only the repository's top-level lock.
|
|
@@ -25,4 +25,4 @@ import{execFile as D}from"node:child_process";import{createHash as x,randomBytes
|
|
|
25
25
|
}
|
|
26
26
|
process.stdout.write(JSON.stringify({unsafeLinks,roots:[...selected.keys()]
|
|
27
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
|
|
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 L(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 q(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=T(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:_,...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 S.set(k,{receipt:g,p205:w,acceptedAt:Date.now()}),{candidateQa:g,candidateAuthority:k}}export{X as executeEpicMergeCandidateQa,K as verifiedEpicMergeCandidateReceipt};
|
|
@@ -8,5 +8,7 @@ export interface CliEpicTerminalGitAdapterOptions {
|
|
|
8
8
|
/** Command seams keep provider-destructive tests inside temporary fixtures. */
|
|
9
9
|
readonly gitRunner?: (...args: readonly string[]) => Promise<string>;
|
|
10
10
|
readonly providerRunner?: (...args: readonly string[]) => Promise<string>;
|
|
11
|
+
/** Deterministic physical freshness clock for tests; production uses the local clock. */
|
|
12
|
+
readonly now?: () => string;
|
|
11
13
|
}
|
|
12
14
|
export declare function createCliEpicTerminalGitAdapter(projectRoot: string, options?: CliEpicTerminalGitAdapterOptions): EpicTerminalGitAdapter;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import{execFile as ye}from"node:child_process";import{createHash as O}from"node:crypto";import{readFile as ae}from"node:fs/promises";import{join as we}from"node:path";import{promisify as be}from"node:util";import{classifyGitReconciliationCandidate as ie,reconcileTerminalReceipt as Se}from"../../../../shared/dist/yoloop/epic-terminal-git.js";import{collectGitResourceOwnership as _e,GIT_INVENTORY_PR_LIMIT as ne,GIT_INVENTORY_REF_LIMIT as Z}from"./epic-git-reconciliation-inventory.js";import{executeEpicMergeCandidateQa as oe,verifiedEpicMergeCandidateReceipt as W}from"./epic-merge-candidate-qa.js";import{applyGitLifecycleRuntimeOverlay as xe,ensureRuntimeEpic as z,executeP205TransientAuthority as ve,P205_COMPLETE_COMMAND as Q,projectP205TransientExecutionEvidence as Re,verifyAcceptedGitLifecycleStoryCheckpoint as ce,mutateGitLifecycleRuntime as J,readGitLifecycleRuntime as He}from"../../git/git-lifecycle-store.js";const ke=be(ye);function ze(e,a={}){const c=we(e,".neocortex","state.json"),t=a.gitRunner??((...r)=>ue("git",r,e)),u=a.providerRunner??((...r)=>ue("gh",r,e)),m=new Map;return{async prepareLifecycleEvidence(r){try{try{await ae(c,"utf8")}catch(k){if(Oe(k,"ENOENT"))return{ok:!0,originBindingCaptured:!1,aggregateQaRecorded:!1};throw k}const n=(await D(e,c)).epics?.[r.epicId];if(!n)return o("epic-state-missing",!1,!1);if(S(n.branch_mode)!=="epic")return{ok:!0,originBindingCaptured:!1,aggregateQaRecorded:!1};const s=S(n.execution_branch),d=S(n.origin_branch);if(!s||!E(s))return o("epic-execution-branch-missing",!1,!1);let l=await t("symbolic-ref","--quiet","--short","HEAD").catch(()=>""),f=await t("rev-parse","HEAD"),h=_(n.origin_binding)?n.origin_binding:null,b=!1;if(h){if(h.executionBranch!==s||h.originBranch!==d||l!==s&&l!==h.originBranch||!await j(t,h.originHeadSha)||!await v(t,h.originHeadSha,f))return o("epic-origin-binding-physical-drift",!1,!1)}else{if(!l||l===s||!E(l))return o("epic-origin-binding-capture-window-missed",!1,!1);if(d&&d!==l)return o("epic-origin-binding-recorded-origin-drift",!1,!1);h={schemaVersion:1,originBranch:l,originHeadSha:f,executionBranch:s,capturedFromCurrentCheckout:!0,capturedAt:r.nowIso},await J(e,k=>{const y=z(k,r.epicId);if(y.origin_binding&&JSON.stringify(y.origin_binding)!==JSON.stringify(h))throw new Error("origin-binding-cas-conflict");y.origin_branch=l,y.origin_binding=h}),b=!0}if(l!==s){const k=await le(t,`refs/heads/${s}`);try{if(k){if(!await v(t,h.originHeadSha,k))return o("epic-execution-branch-origin-drift",!1,!1);await t("switch",s)}else await t("switch","-c",s,h.originHeadSha)}catch{const y=await t("status","--porcelain","--untracked-files=normal").catch(()=>"");return o(y?"epic-execution-branch-switch-blocked-by-user-changes":"epic-execution-branch-switch-failed",!0,!1)}if(l=await t("symbolic-ref","--quiet","--short","HEAD"),f=await t("rev-parse","HEAD"),l!==s)return o("epic-execution-branch-switch-unverified",!0,!1)}const g=await D(e,c,!0),p=g.epics?.[r.epicId];if(!p)return o("epic-state-missing",!1,!1);const x=Y(p),w=await Promise.all(x.map(k=>ce(e,g.stories?.[k]?.checkpoint,k,r.epicId,s).then(y=>y&&ge(g.stories?.[k]?.checkpoint))));if(!(x.length>0&&w.every(Boolean)))return{ok:!0,originBindingCaptured:b,aggregateQaRecorded:!1};if(l!==s)return o("epic-aggregate-qa-execution-checkout-required",!0,!1);const H=S(p.graph_revision);if(!/^[a-f0-9]{64}$/i.test(H))return o("epic-aggregate-qa-graph-required",!1,!1);if(await pe(t))return o("epic-aggregate-qa-checkout-dirty",!0,!1);const G=await ve(e,{headSha:f,graphRevision:H,nowIso:r.nowIso}),B=Re(G);if(!B)return o("epic-aggregate-qa-authority-invalid",!1,!1);if(await a.aggregateQaRunner?.({projectRoot:e,gateIds:B.gateIds}),await t("rev-parse","HEAD")!==f||await pe(t))return o("epic-aggregate-qa-head-raced",!0,!1);const M=`local-yolo-tdd:${O("sha256").update(["epic-aggregate-p205-execution-v2",Q,f,H,B.manifestDigest,B.gateIds.join(","),B.resultDigest].join("\0")).digest("hex")}`,T={schemaVersion:1,evidenceVersion:"p205-execution-v2",producer:"local-yolo-tdd",contract:"P205-CI-02",command:Q,verdict:"passed",executionHeadSha:f,graphRevision:H,manifestDigest:B.manifestDigest,resultDigest:B.resultDigest,checkedAt:B.executedAt,receiptRef:M,gateIds:B.gateIds};return await J(e,k=>{const y=z(k,r.epicId);y.aggregate_qa_receipt=T}),{ok:!0,originBindingCaptured:b,aggregateQaRecorded:!0,p205Authority:G}}catch{return o("epic-lifecycle-evidence-production-failed",!0,!1)}},async inspect(r){try{const i=await D(e,c,!0),n=i.epics?.[r.epicId];if(!n)return o("epic-state-missing",!1,!1);if(S(n.execution_branch)!==r.executionBranch||S(n.origin_branch)!==r.originBranch)return o("epic-branch-boundary-drift",!1,!1);const s=await t("rev-parse",`refs/heads/${r.executionBranch}`),d=await A(t,r.originBranch);await P(t,r.originBranch,d);const l=await X(e,i,n,r.epicId,r.executionBranch);let f=_(n.terminal_git_receipt)?n.terminal_git_receipt:null;f?.physicalStatus==="pushed"&&(f.pushedSha&&await v(t,f.executionHeadSha,f.pushedSha)&&await v(t,f.pushedSha,d)||(f=null)),f||(f=await Ae({directive:r,executionHeadSha:s,originHeadSha:d,git:t}));const h=new Set((Array.isArray(n.git_reconciliation_receipts)?n.git_reconciliation_receipts:[]).filter(_).filter(K).map(R=>S(R.resourceId)).filter(Boolean)),b=await t("symbolic-ref","--quiet","--short","HEAD").catch(()=>""),g=r.reconcile.enabled?await se({state:i,epic:n,directive:r,currentBranch:b,originHeadSha:d,receiptIds:h,git:t,gh:u}):[],p=_(n.origin_binding)?n.origin_binding:null,x=p&&p.originBranch===r.originBranch&&p.executionBranch===r.executionBranch&&await j(t,p.originHeadSha)&&await v(t,p.originHeadSha,s)?p:null;return{ok:!0,snapshot:{executionHeadSha:s,originHeadSha:d,...S(n.graph_revision)?{graphRevision:S(n.graph_revision)}:{},writerLeaseActive:re(n.branch_lease),acceptedCheckpointIds:l,existingReceipt:f,aggregateQaReceipt:_(n.aggregate_qa_receipt)?n.aggregate_qa_receipt:null,originBinding:x,reconciliationCandidates:g}}}catch(i){return o(de(i)??"epic-terminal-inspection-failed",!0,!1)}},async prepareMergeObject(r){let i;try{const n=await t("merge-tree","--write-tree",r.originHeadSha,r.executionHeadSha);i=await t("commit-tree",n,"-p",r.originHeadSha,"-p",r.executionHeadSha,"-m",r.message)}catch{return o("epic-terminal-merge-conflict",!1,!1)}try{const n=await oe(e,{mergeSha:i,originHeadSha:r.originHeadSha,executionHeadSha:r.executionHeadSha,graphRevision:r.graphRevision},r.nowIso);return m.set(i,n.candidateAuthority),{ok:!0,mergeSha:i,...n}}catch{return o("epic-merge-candidate-qa-failed",!0,!1)}},async inspectReconciliation(r){try{const i=await D(e,c,!0),n=i.epics?.[r.epicId];if(!n)return o("epic-state-missing",!1,!1);if(S(n.execution_branch)!==r.executionBranch||S(n.origin_branch)!==r.originBranch)return o("epic-branch-boundary-drift",!1,!1);const s=await A(t,r.originBranch);await P(t,r.originBranch,s);const d=new Set((Array.isArray(n.git_reconciliation_receipts)?n.git_reconciliation_receipts:[]).filter(_).filter(K).map(h=>S(h.resourceId)).filter(Boolean)),l=await t("symbolic-ref","--quiet","--short","HEAD").catch(()=>"");return{ok:!0,candidates:await se({state:i,epic:n,directive:r,currentBranch:l,originHeadSha:s,receiptIds:d,git:t,gh:u})}}catch(i){return o(de(i)??"git-reconciliation-inspection-failed",!0,!1)}},async pushMergeObject(r){try{if(!W(r.candidateAuthority,{mergeSha:r.mergeSha,originHeadSha:r.expectedOriginSha,executionHeadSha:r.executionHeadSha,graphRevision:r.graphRevision}))return o("epic-merge-candidate-authority-required",!1,!1);const n=await D(e,c,!0),s=n.epics?.[r.epicId];if(!s||s.execution_branch!==r.executionBranch||s.origin_branch!==r.originBranch||s.graph_revision!==r.graphRevision||re(s.branch_lease)||await t("rev-parse",`refs/heads/${r.executionBranch}`)!==r.executionHeadSha)return o("epic-merge-candidate-authority-raced",!0,!1);const d=await X(e,n,s,r.epicId,r.executionBranch);if(!ee(d,r.storyCheckpointIds)||!ee(Y(s),r.storyCheckpointIds))return o("epic-checkpoint-set-revalidation-drift",!0,!1);const l=_(s.aggregate_qa_receipt)?s.aggregate_qa_receipt:null;if(!he(l,r.executionHeadSha,r.graphRevision,r.nowIso)||l.receiptRef!==r.aggregateQaReceipt.receiptRef)return o("epic-terminal-aggregate-qa-revalidation-drift",!0,!1);if(await A(t,r.originBranch)!==r.expectedOriginSha)return o("epic-origin-head-raced",!0,!1);await t("push","--porcelain","origin",`${r.mergeSha}:refs/heads/${r.originBranch}`);const h=await A(t,r.originBranch);return h!==r.mergeSha?o("epic-terminal-push-unverified",!0,!1):{ok:!0,pushedSha:h}}catch(i){return te(i)?o("epic-terminal-remote-policy-denied",!0,!0):o("epic-terminal-push-failed",!0,!1)}},async ensureSingleEpicPullRequest(r){try{const i=W(r.candidateAuthority,{mergeSha:r.expectedPullRequestHeadSha,originHeadSha:r.candidateQa.originHeadSha,executionHeadSha:r.expectedStoryHeadSha,graphRevision:r.candidateQa.graphRevision});if(!i||i!==r.candidateQa||!await v(t,r.candidateQa.originHeadSha,r.expectedPullRequestHeadSha)||!await v(t,r.expectedStoryHeadSha,r.expectedPullRequestHeadSha))return o("epic-merge-candidate-authority-required",!1,!1);const n=await I(t,u);if(!n)return o("git-resource-provider-repository-unverified",!0,!0);const s=`https://github.com/${n.nameWithOwner}`,d=await C(t,r.executionBranch);if(d&&d!==r.expectedPullRequestHeadSha){if(await P(t,r.executionBranch,d),!await v(t,d,r.expectedPullRequestHeadSha))return o("epic-terminal-execution-remote-head-drift",!0,!0);await t("push","--porcelain","origin",`${r.expectedPullRequestHeadSha}:refs/heads/${r.executionBranch}`)}if(d||await t("push","--porcelain","origin",`${r.expectedPullRequestHeadSha}:refs/heads/${r.executionBranch}`),await A(t,r.executionBranch)!==r.expectedPullRequestHeadSha)return o("epic-terminal-execution-publish-unverified",!0,!0);const l=await u("pr","list","--repo",s,"--state","all","--head",r.executionBranch,"--base",r.originBranch,"--json","number,state,headRefName,headRefOid,baseRefName,mergeCommit","--limit","100"),f=JSON.parse(l),h=f.filter(w=>w.headRefName===r.executionBranch&&w.baseRefName===r.originBranch&&w.headRefOid===r.expectedPullRequestHeadSha&&(w.state==="OPEN"||w.state==="MERGED"));if(h.length>1)return o("epic-terminal-duplicate-prs",!1,!0);if(typeof h[0]?.number=="number")return{ok:!0,prNumber:h[0].number};if(f.some(w=>w.state==="OPEN"))return o("epic-terminal-stale-pr",!1,!0);const g=(await u("pr","create","--repo",s,"--head",r.executionBranch,"--base",r.originBranch,"--title",`[${r.epicId}] Terminal epic integration`,"--body",`Autonomous terminal integration receipt: ${r.idempotencyKey}`)).match(/\/(\d+)(?:\s*)$/);if(!g)return o("epic-terminal-pr-number-unverified",!0,!0);const p=Number(g[1]),x=await L(u,n.nameWithOwner,p);return!x||x.state!=="OPEN"||x.headRefName!==r.executionBranch||x.baseRefName!==r.originBranch||x.headRefOid!==r.expectedPullRequestHeadSha?o("epic-terminal-pr-create-unverified",!0,!0):{ok:!0,prNumber:p}}catch(i){return o("epic-terminal-pr-create-denied",!0,te(i))}},async mergeEpicPullRequest(r){try{const i=await I(t,u);if(!i)return o("git-resource-provider-repository-unverified",!0,!0);const n=r.candidateQa&&W(r.candidateAuthority,{mergeSha:r.expectedPullRequestHeadSha,graphRevision:r.candidateQa.graphRevision,originHeadSha:r.expectedOriginHeadSha,executionHeadSha:r.expectedStoryHeadSha});if(!n||n!==r.candidateQa)return o("epic-merge-candidate-authority-required",!1,!1);const s=await L(u,i.nameWithOwner,r.prNumber),d=await C(t,r.executionBranch),l=await A(t,r.originBranch);if(!s||s.headRefName!==r.executionBranch||s.baseRefName!==r.originBranch||s.headRefOid!==r.expectedPullRequestHeadSha||d&&d!==r.expectedPullRequestHeadSha||!await v(t,r.expectedOriginHeadSha,r.expectedPullRequestHeadSha)||!await v(t,r.expectedStoryHeadSha,r.expectedPullRequestHeadSha))return o("epic-terminal-pr-revalidation-drift",!0,!0);if(s.state==="OPEN"&&l!==r.expectedOriginHeadSha)return o("epic-terminal-pr-origin-raced",!0,!0);let f;if(s.state==="OPEN"){const w=await Ee(u,i.nameWithOwner,r.originBranch,r.expectedPullRequestHeadSha,r.expectedOriginHeadSha,r.nowIso);if(!w.ok)return o(w.reasonCode,!0,!0);if(f=w.receipt,await A(t,r.originBranch)!==r.expectedOriginHeadSha)return o("epic-terminal-pr-origin-raced",!0,!0);await u("pr","merge",String(r.prNumber),"--repo",`https://github.com/${i.nameWithOwner}`,"--merge","--match-head-commit",r.expectedPullRequestHeadSha)}else{if(s.state!=="MERGED")return o("epic-terminal-pr-not-open-or-merged",!1,!0);f={schemaVersion:1,provider:"github",mode:"recovered_exact_tree",source:"physical_recovery",prHeadSha:r.expectedPullRequestHeadSha,originHeadSha:r.expectedOriginHeadSha,checkedAt:r.nowIso,publicSafe:!0}}const h=await L(u,i.nameWithOwner,r.prNumber),b=h?.mergeCommit?.oid;if(h?.state!=="MERGED"||!b)return o("epic-terminal-pr-merge-unverified",!0,!0);const g=await A(t,r.originBranch);if(await P(t,r.originBranch,g),!await v(t,r.expectedStoryHeadSha,g)||!await v(t,r.expectedPullRequestHeadSha,g)||!await v(t,b,g))return o("epic-terminal-pr-merge-ancestry-unproven",!0,!0);const[p,x]=await Promise.all([t("rev-parse",`${r.expectedPullRequestHeadSha}^{tree}`),t("rev-parse",`${b}^{tree}`)]);return p!==r.candidateQa.treeSha||x!==p?o("epic-terminal-pr-merged-tree-mismatch",!1,!0):{ok:!0,mergeSha:r.expectedPullRequestHeadSha,pushedSha:b,providerGuard:f}}catch(i){return o("epic-terminal-pr-merge-denied",!0,te(i))}},async verifyTerminalReceipt(r,i,n){try{if(i.physicalStatus!=="pushed"||!i.pushedSha||!i.mergeSha)return o("epic-terminal-physical-revalidation-required",!0,!1);const s=await D(e,c,!0),d=s.epics?.[r.epicId];if(!d||S(d.execution_branch)!==r.executionBranch||S(d.origin_branch)!==r.originBranch||S(d.graph_revision)!==r.graphRevision)return o("epic-terminal-state-revalidation-drift",!0,!1);const l=await t("rev-parse",`refs/heads/${r.executionBranch}`);if(l!==r.expectedExecutionHeadSha||i.executionHeadSha!==l)return o("epic-terminal-execution-revalidation-drift",!0,!1);const f=_(d.origin_binding)?d.origin_binding:null;if(!f||f.originBranch!==r.originBranch||f.executionBranch!==r.executionBranch||!await j(t,f.originHeadSha)||!await v(t,f.originHeadSha,l))return o("epic-origin-binding-revalidation-drift",!0,!1);const h=await X(e,s,d,r.epicId,r.executionBranch);if(re(d.branch_lease)||!ee(h,r.storyCheckpointIds))return o("epic-checkpoint-set-revalidation-drift",!0,!1);const b=_(d.aggregate_qa_receipt)?d.aggregate_qa_receipt:null;if(!he(b,l,r.graphRevision,n)||b?.receiptRef!==r.localGates.aggregateQaReceipt.receiptRef)return o("epic-terminal-aggregate-qa-revalidation-drift",!0,!1);const g=await A(t,r.originBranch);if(await P(t,r.originBranch,g),!await v(t,l,g)||!await v(t,i.mergeSha,g)||!await v(t,i.pushedSha,g))return o("epic-terminal-physical-ancestry-unproven",!0,!1);const p={originHeadSha:i.baseSha,executionHeadSha:l,mergeSha:i.mergeSha,graphRevision:r.graphRevision};let x=m.get(i.mergeSha),w=W(x,p);if(!w){const H=await oe(e,p,n);x=H.candidateAuthority,w=H.candidateQa,m.set(i.mergeSha,x)}if(i.candidateQa&&(i.candidateQa.originHeadSha!==w.originHeadSha||i.candidateQa.executionHeadSha!==w.executionHeadSha||i.candidateQa.graphRevision!==w.graphRevision||i.candidateQa.treeSha!==w.treeSha))return o("epic-merge-candidate-receipt-drift",!0,!1);let R=i.providerGuard;if(i.strategy==="single_pr"){if(!i.prNumber)return o("epic-terminal-pr-merge-unverified",!0,!1);if(await t("rev-parse",`${i.pushedSha}^{tree}`)!==w.treeSha)return o("epic-terminal-pr-merged-tree-mismatch",!1,!1);if(R&&!Ie(R,i))return o("epic-terminal-pr-base-guard-unverifiable",!1,!1);R??={schemaVersion:1,provider:"github",mode:"recovered_exact_tree",source:"physical_recovery",prHeadSha:i.mergeSha,originHeadSha:i.baseSha,checkedAt:n,publicSafe:!0}}else if(R)return o("epic-terminal-pr-base-guard-unverifiable",!1,!1);return{ok:!0,candidateQa:w,candidateAuthority:x,...R?{providerGuard:R}:{}}}catch{return o("epic-terminal-physical-revalidation-failed",!0,!1)}},async applyReconciliation(r,i,n){if(i.action==="preserve")return o("git-reconciliation-preserve-not-actionable",!1,!1);if(n)return{ok:!0,outcome:"dry_run"};if(i.action==="no_op")return{ok:!0,outcome:"already_absent"};if(i.action==="delete_remote_branch")return Be(r,i,t,u);try{const s=await F({candidate:r,decision:i,git:t,gh:u});if(!s.ok)return o(s.reasonCode,!0,!1);if(s.alreadyAbsent)return{ok:!0,outcome:"already_absent"};if(i.action==="delete_local_branch")await t("update-ref","-d",`refs/heads/${r.branchName}`,r.headSha);else if(i.action==="close_pull_request"){if(!r.prNumber)return o("git-reconciliation-pr-number-missing",!1,!1);const d=await I(t,u);if(!d)return o("git-resource-provider-repository-unverified",!0,!0);const l=`https://github.com/${d.nameWithOwner}`,f=`neocortex-reconciliation:${O("sha256").update(r.resourceId).digest("hex")}`,h=JSON.parse(await u("pr","view",String(r.prNumber),"--repo",l,"--json","comments"));if(!_(h)||!Array.isArray(h.comments)||h.comments.length>1e3||h.comments.some(p=>!_(p)||typeof p.body!="string"))return o("git-reconciliation-pr-comments-unverified",!0,!0);h.comments.some(p=>String(p.body).includes(f))||await u("pr","comment",String(r.prNumber),"--repo",l,"--body",`Neocortex reconciliation verified managed ownership and integrated content; requesting closure. [${f}]`);const b=await F({candidate:r,decision:i,git:t,gh:u});if(!b.ok)return o(b.reasonCode,!0,!1);if(b.alreadyAbsent)return{ok:!0,outcome:"already_absent"};await u("pr","close",String(r.prNumber),"--repo",l);const g=await F({candidate:r,decision:i,git:t,gh:u});if(!g.ok)return o(g.reasonCode,!0,!1);if(!g.alreadyAbsent)return o("git-reconciliation-pr-close-unverified",!0,!0)}return{ok:!0,outcome:"applied"}}catch{return o("git-reconciliation-effect-failed",!0,!1)}},async persist(r){try{let i;return await J(e,n=>{const s=z(n,r.terminalReceipt.epicId),d=Array.isArray(s.git_reconciliation_receipts)?s.git_reconciliation_receipts.filter(_):[],l=new Map(d.map(b=>[b.idempotencyKey,b]));for(const b of r.reconciliationReceipts)K(l.get(b.idempotencyKey))||l.set(b.idempotencyKey,b);const f=_(s.terminal_git_receipt)?s.terminal_git_receipt:null,h=Se(f,r.terminalReceipt);if(h.kind==="blocked"){i=h.reasonCode;return}s.terminal_git_receipt=h.receipt,s.git_reconciliation_receipts=[...l.values()],s.physicalMergeStatus=r.terminalReceipt.physicalStatus==="pushed"?"merged":r.terminalReceipt.physicalStatus,s.pr_number=r.terminalReceipt.prNumber??s.pr_number??null,s.git_reconciliation_debt=me(s.git_reconciliation_debt,r.preservedDecisions)}),i?o(i,!1,!1):{ok:!0}}catch{return o("epic-terminal-state-persist-failed",!0,!1)}},async persistReconciliation(r){try{return await J(e,i=>{const n=z(i,r.epicId),s=Array.isArray(n.git_reconciliation_receipts)?n.git_reconciliation_receipts.filter(_):[],d=new Map(s.map(l=>[l.idempotencyKey,l]));for(const l of r.reconciliationReceipts)K(d.get(l.idempotencyKey))||d.set(l.idempotencyKey,l);n.git_reconciliation_receipts=[...d.values()],n.git_reconciliation_debt=me(n.git_reconciliation_debt,r.preservedDecisions)}),{ok:!0}}catch{return o("git-reconciliation-state-persist-failed",!0,!1)}}}}async function I(e,a){try{const c=await e("remote","get-url","origin"),t=c.match(/^https:\/\/github\.com\/([A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+?)(?:\.git)?\/?$/)?.[1]??c.match(/^git@github\.com:([A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+?)(?:\.git)?$/)?.[1];if(!t)return null;const u=JSON.parse(await a("repo","view",`https://github.com/${t}`,"--json","id,nameWithOwner,url"));return u.nameWithOwner!==t||u.url!==`https://github.com/${t}`||typeof u.id!="string"||!/^[A-Za-z0-9_=-]{1,256}$/.test(u.id)?null:{id:u.id,nameWithOwner:t}}catch{return null}}async function Be(e,a,c,t){try{if(e.kind!=="remote_branch"||a.resourceId!==e.resourceId||!e.baseBranch||e.baseBranch===e.branchName||!E(e.branchName)||!E(e.baseBranch)||!/^[a-f0-9]{40}$/.test(e.headSha)||!/^[a-f0-9]{40}$/.test(e.integrationHeadSha))return o("git-reconciliation-candidate-no-longer-safe",!1,!1);const u=await I(c,t);if(!u)return o("git-resource-remote-delete-cas-unavailable",!1,!1);if(!await C(c,e.branchName))return{ok:!0,outcome:"already_absent"};const r=await F({candidate:e,decision:a,git:c,gh:t});if(!r.ok)return o(r.reasonCode,!0,!1);if((await I(c,t))?.id!==u.id)return o("git-resource-provider-repository-raced",!0,!1);const n=O("sha256").update(["managed-remote-delete-v1",u.id,e.branchName,e.headSha,e.baseBranch,e.integrationHeadSha].join("\0")).digest("hex"),s=(f,h,b)=>`{name:${JSON.stringify(`refs/heads/${f}`)},beforeOid:${JSON.stringify(h)},afterOid:${JSON.stringify(b)},force:false}`,d=`mutation{updateRefs(input:{repositoryId:${JSON.stringify(u.id)},clientMutationId:${JSON.stringify(n)},refUpdates:[`+s(e.branchName,e.headSha,"0".repeat(40))+","+s(e.baseBranch,e.integrationHeadSha,e.integrationHeadSha)+"]}){clientMutationId}}",l=JSON.parse(await t("api","graphql","--hostname","github.com","-f",`query=${d}`));return l.errors||l.data?.updateRefs?.clientMutationId!==n?o("git-resource-remote-delete-cas-unverified",!0,!0):await C(c,e.branchName)?o("git-resource-remote-delete-recreated-or-unverified",!0,!1):{ok:!0,outcome:"applied"}}catch{return o("git-resource-remote-delete-cas-rejected",!0,!0)}}async function F(e){const{candidate:a,decision:c,git:t,gh:u}=e;if(!c.managed||!a.providerVerified||a.current||a.origin||a.protected||a.lifecycle==="active"||a.hasExclusiveCommits||a.worktreeActive)return{ok:!1,reasonCode:"git-reconciliation-candidate-no-longer-safe"};const m=await t("symbolic-ref","--quiet","--short","HEAD").catch(()=>""),r=await fe(t);if(m===a.branchName||r.has(a.branchName))return{ok:!1,reasonCode:"git-resource-worktree-active"};if(a.kind==="local_branch"){const d=await le(t,`refs/heads/${a.branchName}`);return d?d!==a.headSha?{ok:!1,reasonCode:"git-resource-expected-oid-raced"}:!a.baseBranch||await A(t,a.baseBranch)!==a.integrationHeadSha||!await v(t,d,a.integrationHeadSha)?{ok:!1,reasonCode:"git-resource-integration-unproven"}:{ok:!0}:{ok:!0,alreadyAbsent:!0}}if(a.kind==="remote_branch"){const d=await C(t,a.branchName);if(!d)return{ok:!0};if(d!==a.headSha)return{ok:!1,reasonCode:"git-resource-expected-oid-raced"};const l=await I(t,u);return l?await U(u,l.nameWithOwner,a.branchName)!=="unprotected"?{ok:!1,reasonCode:"git-resource-protected"}:!a.baseBranch||await A(t,a.baseBranch)!==a.integrationHeadSha?{ok:!1,reasonCode:"git-resource-integration-unproven"}:(await t("fetch","--quiet","--no-tags","origin",`refs/heads/${a.branchName}`),await t("rev-parse","FETCH_HEAD")!==a.headSha||!await v(t,a.headSha,a.integrationHeadSha)?{ok:!1,reasonCode:"git-resource-integration-unproven"}:{ok:!0}):{ok:!1,reasonCode:"git-resource-provider-repository-unverified"}}if(!a.prNumber||!a.baseBranch)return{ok:!1,reasonCode:"git-reconciliation-pr-proof-missing"};const i=await I(t,u);if(!i)return{ok:!1,reasonCode:"git-resource-provider-repository-unverified"};const n=await L(u,i.nameWithOwner,a.prNumber);if(!n||n.headRefName!==a.branchName||n.baseRefName!==a.baseBranch||n.headRefOid!==a.headSha)return{ok:!1,reasonCode:"git-resource-expected-oid-raced"};if(n.state==="CLOSED"||n.state==="MERGED")return{ok:!0,alreadyAbsent:!0};if(await U(u,i.nameWithOwner,a.branchName)!=="unprotected")return{ok:!1,reasonCode:"git-resource-protected"};const s=await C(t,a.branchName);return!n||n.state!=="OPEN"||n.headRefName!==a.branchName||n.baseRefName!==a.baseBranch||n.headRefOid!==a.headSha||s!==a.headSha?{ok:!1,reasonCode:"git-resource-expected-oid-raced"}:(await t("fetch","--quiet","--no-tags","origin",`refs/heads/${a.branchName}`),await t("rev-parse","FETCH_HEAD")!==a.headSha||await A(t,a.baseBranch)!==a.integrationHeadSha||!await v(t,a.headSha,a.integrationHeadSha)?{ok:!1,reasonCode:"git-resource-integration-unproven"}:{ok:!0})}async function se(e){const a=[],c=await fe(e.git),t=_e(e.state),u=await I(e.git,e.gh),m=g=>{const p=g.split(`
|
|
2
|
-
`).filter(Boolean).map(w=>w.split(/\s+/));if(
|
|
3
|
-
`).filter(
|
|
4
|
-
`),r=`merge(${e.directive.epicId}): integrate ${e.directive.executionBranch} into ${e.directive.originBranch}`;if(
|
|
5
|
-
`).map(
|
|
6
|
-
`).filter(Boolean).
|
|
7
|
-
`))
|
|
8
|
-
`).filter(Boolean).
|
|
1
|
+
import{execFile as ve}from"node:child_process";import{createHash as q}from"node:crypto";import{readFile as oe}from"node:fs/promises";import{join as He}from"node:path";import{promisify as Be}from"node:util";import{P205_RELEASE_GATE_BUDGETS as ce,classifyGitReconciliationCandidate as se,epicHeadReconciliationInputDigest as ke,epicHeadReconciliationPathsDigest as de,epicHeadReconciliationRangeDigest as Ie,reconcileTerminalReceipt as Ne,validateEpicHeadReconciliationReceipt as he}from"../../../../shared/dist/yoloop/epic-terminal-git.js";import{collectGitResourceOwnership as $e,GIT_INVENTORY_PR_LIMIT as le,GIT_INVENTORY_REF_LIMIT as re}from"./epic-git-reconciliation-inventory.js";import{executeEpicMergeCandidateQa as ge,verifiedEpicMergeCandidateReceipt as L}from"./epic-merge-candidate-qa.js";import{applyGitLifecycleRuntimeOverlay as Ae,ensureRuntimeEpic as K,executeP205TransientAuthority as Ee,gitLifecycleStoryWriteSet as fe,P205_COMPLETE_COMMAND as z,projectP205TransientExecutionEvidence as Ce,revalidateEpicHeadReconciliationAuthority as Oe,verifyAcceptedGitLifecycleStoryCheckpoint as ue,mutateGitLifecycleRuntime as Z,readGitLifecycleRuntime as qe}from"../../git/git-lifecycle-store.js";const Pe=Be(ve);function rr(e,t={}){const i=He(e,".neocortex","state.json"),a=t.gitRunner??((...r)=>ye("git",r,e)),d=t.providerRunner??((...r)=>ye("gh",r,e)),u=new Map;return{async prepareLifecycleEvidence(r){try{try{await oe(i,"utf8")}catch(H){if(Je(H,"ENOENT"))return{ok:!0,originBindingCaptured:!1,aggregateQaRecorded:!1};throw H}const o=(await P(e,i)).epics?.[r.epicId];if(!o)return s("epic-state-missing",!1,!1);if(b(o.branch_mode)!=="epic")return{ok:!0,originBindingCaptured:!1,aggregateQaRecorded:!1};const c=b(o.execution_branch),h=b(o.origin_branch);if(!c||!A(c))return s("epic-execution-branch-missing",!1,!1);let g=await a("symbolic-ref","--quiet","--short","HEAD").catch(()=>""),f=await a("rev-parse","HEAD"),l=y(o.origin_binding)?o.origin_binding:null,S=!1;if(l){if(l.executionBranch!==c||l.originBranch!==h||g!==c&&g!==l.originBranch||!await T(a,l.originHeadSha)||!await v(a,l.originHeadSha,f))return s("epic-origin-binding-physical-drift",!1,!1)}else{if(!g||g===c||!A(g))return s("epic-origin-binding-capture-window-missed",!1,!1);if(h&&h!==g)return s("epic-origin-binding-recorded-origin-drift",!1,!1);l={schemaVersion:1,originBranch:g,originHeadSha:f,executionBranch:c,capturedFromCurrentCheckout:!0,capturedAt:r.nowIso},await Z(e,H=>{const R=K(H,r.epicId);if(R.origin_binding&&JSON.stringify(R.origin_binding)!==JSON.stringify(l))throw new Error("origin-binding-cas-conflict");R.origin_branch=g,R.origin_binding=l}),S=!0}if(g!==c){const H=await Se(a,`refs/heads/${c}`);try{if(H){if(!await v(a,l.originHeadSha,H))return s("epic-execution-branch-origin-drift",!1,!1);await a("switch",c)}else await a("switch","-c",c,l.originHeadSha)}catch{const R=await a("status","--porcelain","--untracked-files=normal").catch(()=>"");return s(R?"epic-execution-branch-switch-blocked-by-user-changes":"epic-execution-branch-switch-failed",!0,!1)}if(g=await a("symbolic-ref","--quiet","--short","HEAD"),f=await a("rev-parse","HEAD"),g!==c)return s("epic-execution-branch-switch-unverified",!0,!1)}const p=await P(e,i,!0),m=p.epics?.[r.epicId];if(!m)return s("epic-state-missing",!1,!1);const x=G(m),w=await Promise.all(x.map(H=>ue(e,p.stories?.[H]?.checkpoint,H,r.epicId,c).then(R=>R&&xe(p.stories?.[H]?.checkpoint))));if(!(x.length>0&&w.every(Boolean)))return{ok:!0,originBindingCaptured:S,aggregateQaRecorded:!1};if(g!==c)return s("epic-aggregate-qa-execution-checkout-required",!0,!1);const B=b(m.graph_revision);if(!/^[a-f0-9]{64}$/i.test(B))return s("epic-aggregate-qa-graph-required",!1,!1);if(await _e(a))return s("epic-aggregate-qa-checkout-dirty",!0,!1);const W=y(m.aggregate_qa_receipt)?m.aggregate_qa_receipt:null,E=y(m.head_reconciliation_receipt)?m.head_reconciliation_receipt:null;if(E&&X(W,f,B,r.nowIso)){const H=await Oe(e,E,W,r.nowIso);return{ok:!0,originBindingCaptured:S,aggregateQaRecorded:!1,headReconciliationRecorded:!0,terminalAuthority:H.authority}}const O=await Ee(e,{headSha:f,graphRevision:B,nowIso:r.nowIso}),N=Ce(O);if(!N)return s("epic-aggregate-qa-authority-invalid",!1,!1);if(await t.aggregateQaRunner?.({projectRoot:e,gateIds:N.gateIds}),await a("rev-parse","HEAD")!==f||await _e(a))return s("epic-aggregate-qa-head-raced",!0,!1);const J=`local-yolo-tdd:${q("sha256").update(["epic-aggregate-p205-execution-v2",z,f,B,N.manifestDigest,N.gateIds.join(","),N.resultDigest].join("\0")).digest("hex")}`,_={schemaVersion:1,evidenceVersion:"p205-execution-v2",producer:"local-yolo-tdd",contract:"P205-CI-02",command:z,verdict:"passed",executionHeadSha:f,graphRevision:B,manifestDigest:N.manifestDigest,resultDigest:N.resultDigest,checkedAt:N.executedAt,receiptRef:J,gateIds:N.gateIds},I=await Ve({git:a,state:p,epic:m,epicId:r.epicId,executionBranch:c,originBranch:l.originBranch,originHeadSha:l.originHeadSha,executionHeadSha:f,graphRevision:B,aggregateQaReceipt:_,nowIso:N.executedAt});return await Z(e,H=>{const R=K(H,r.epicId);R.aggregate_qa_receipt=_,R.head_reconciliation_receipt=I}),{ok:!0,originBindingCaptured:S,aggregateQaRecorded:!0,headReconciliationRecorded:!0,terminalAuthority:O,p205Authority:O}}catch{return s("epic-lifecycle-evidence-production-failed",!0,!1)}},async inspect(r){try{const n=await P(e,i,!0),o=n.epics?.[r.epicId];if(!o)return s("epic-state-missing",!1,!1);if(b(o.execution_branch)!==r.executionBranch||b(o.origin_branch)!==r.originBranch)return s("epic-branch-boundary-drift",!1,!1);const c=await a("rev-parse",`refs/heads/${r.executionBranch}`),h=await $(a,r.originBranch);await Q(a,r.originBranch,h);const g=await V(e,n,o,r.epicId,r.executionBranch);let f=y(o.terminal_git_receipt)?o.terminal_git_receipt:null;f?.physicalStatus==="pushed"&&(f.pushedSha&&await v(a,f.executionHeadSha,f.pushedSha)&&await v(a,f.pushedSha,h)||(f=null)),f||(f=await Ge({directive:r,executionHeadSha:c,originHeadSha:h,git:a}));const l=new Set((Array.isArray(o.git_reconciliation_receipts)?o.git_reconciliation_receipts:[]).filter(y).filter(j).map(E=>b(E.resourceId)).filter(Boolean)),S=await a("symbolic-ref","--quiet","--short","HEAD").catch(()=>""),p=r.reconcile.enabled?await pe({state:n,epic:o,directive:r,currentBranch:S,originHeadSha:h,receiptIds:l,git:a,gh:d}):[],m=y(o.origin_binding)?o.origin_binding:null,x=m&&m.originBranch===r.originBranch&&m.executionBranch===r.executionBranch&&await T(a,m.originHeadSha)&&await v(a,m.originHeadSha,c)?m:null,w=y(o.aggregate_qa_receipt)?o.aggregate_qa_receipt:null,k=t.now?.()??new Date().toISOString(),B=w&&x&&await te({projectRoot:e,git:a,state:n,epic:o,epicId:r.epicId,executionBranch:r.executionBranch,originBranch:r.originBranch,executionHeadSha:c,graphRevision:r.graphRevision,aggregateQaReceipt:w,nowIso:k})?o.head_reconciliation_receipt:null;return{ok:!0,snapshot:{executionHeadSha:c,originHeadSha:h,...b(o.graph_revision)?{graphRevision:b(o.graph_revision)}:{},writerLeaseActive:ee(o.branch_lease),acceptedCheckpointIds:g,existingReceipt:f,aggregateQaReceipt:w,headReconciliationReceipt:B,originBinding:x,reconciliationCandidates:p}}}catch(n){return s(me(n)??"epic-terminal-inspection-failed",!0,!1)}},async prepareMergeObject(r){let n;try{const o=await a("merge-tree","--write-tree",r.originHeadSha,r.executionHeadSha);n=await a("commit-tree",o,"-p",r.originHeadSha,"-p",r.executionHeadSha,"-m",r.message)}catch{return s("epic-terminal-merge-conflict",!1,!1)}try{const o=await ge(e,{mergeSha:n,originHeadSha:r.originHeadSha,executionHeadSha:r.executionHeadSha,graphRevision:r.graphRevision},r.nowIso);return u.set(n,o.candidateAuthority),{ok:!0,mergeSha:n,...o}}catch{return s("epic-merge-candidate-qa-failed",!0,!1)}},async inspectReconciliation(r){try{const n=await P(e,i,!0),o=n.epics?.[r.epicId];if(!o)return s("epic-state-missing",!1,!1);if(b(o.execution_branch)!==r.executionBranch||b(o.origin_branch)!==r.originBranch)return s("epic-branch-boundary-drift",!1,!1);const c=await $(a,r.originBranch);await Q(a,r.originBranch,c);const h=new Set((Array.isArray(o.git_reconciliation_receipts)?o.git_reconciliation_receipts:[]).filter(y).filter(j).map(l=>b(l.resourceId)).filter(Boolean)),g=await a("symbolic-ref","--quiet","--short","HEAD").catch(()=>"");return{ok:!0,candidates:await pe({state:n,epic:o,directive:r,currentBranch:g,originHeadSha:c,receiptIds:h,git:a,gh:d})}}catch(n){return s(me(n)??"git-reconciliation-inspection-failed",!0,!1)}},async pushMergeObject(r){try{if(!L(r.candidateAuthority,{mergeSha:r.mergeSha,originHeadSha:r.expectedOriginSha,executionHeadSha:r.executionHeadSha,graphRevision:r.graphRevision}))return s("epic-merge-candidate-authority-required",!1,!1);const o=await P(e,i,!0),c=o.epics?.[r.epicId];if(!c||c.execution_branch!==r.executionBranch||c.origin_branch!==r.originBranch||c.graph_revision!==r.graphRevision||ee(c.branch_lease)||await a("rev-parse",`refs/heads/${r.executionBranch}`)!==r.executionHeadSha)return s("epic-merge-candidate-authority-raced",!0,!1);const h=await V(e,o,c,r.epicId,r.executionBranch);if(!ie(h,r.storyCheckpointIds)||!ie(G(c),r.storyCheckpointIds))return s("epic-checkpoint-set-revalidation-drift",!0,!1);const g=y(c.aggregate_qa_receipt)?c.aggregate_qa_receipt:null;if(!X(g,r.executionHeadSha,r.graphRevision,r.nowIso)||g.receiptRef!==r.aggregateQaReceipt.receiptRef)return s("epic-terminal-aggregate-qa-revalidation-drift",!0,!1);if(!await te({projectRoot:e,git:a,state:o,epic:c,epicId:r.epicId,executionBranch:r.executionBranch,originBranch:r.originBranch,executionHeadSha:r.executionHeadSha,graphRevision:r.graphRevision,aggregateQaReceipt:g,nowIso:r.nowIso}))return s("epic-head-reconciliation-revalidation-drift",!0,!1);if(await $(a,r.originBranch)!==r.expectedOriginSha)return s("epic-origin-head-raced",!0,!1);await a("push","--porcelain","origin",`${r.mergeSha}:refs/heads/${r.originBranch}`);const l=await $(a,r.originBranch);return l!==r.mergeSha?s("epic-terminal-push-unverified",!0,!1):{ok:!0,pushedSha:l}}catch(n){return ne(n)?s("epic-terminal-remote-policy-denied",!0,!0):s("epic-terminal-push-failed",!0,!1)}},async ensureSingleEpicPullRequest(r){try{const n=L(r.candidateAuthority,{mergeSha:r.expectedPullRequestHeadSha,originHeadSha:r.candidateQa.originHeadSha,executionHeadSha:r.expectedStoryHeadSha,graphRevision:r.candidateQa.graphRevision});if(!n||n!==r.candidateQa||!await v(a,r.candidateQa.originHeadSha,r.expectedPullRequestHeadSha)||!await v(a,r.expectedStoryHeadSha,r.expectedPullRequestHeadSha))return s("epic-merge-candidate-authority-required",!1,!1);const o=await C(a,d);if(!o)return s("git-resource-provider-repository-unverified",!0,!0);if(r.terminalBinding&&!await we({projectRoot:e,statePath:i,git:a,epicId:r.epicId,originBranch:r.originBranch,executionBranch:r.executionBranch,executionHeadSha:r.expectedStoryHeadSha,graphRevision:r.terminalBinding.graphRevision,storyCheckpointIds:r.terminalBinding.storyCheckpointIds,aggregateQaReceipt:r.terminalBinding.aggregateQaReceipt,nowIso:r.terminalBinding.nowIso}))return s("epic-head-reconciliation-revalidation-drift",!0,!1);const c=`https://github.com/${o.nameWithOwner}`,h=await D(a,r.executionBranch);if(h&&h!==r.expectedPullRequestHeadSha){if(await Q(a,r.executionBranch,h),!await v(a,h,r.expectedPullRequestHeadSha))return s("epic-terminal-execution-remote-head-drift",!0,!0);await a("push","--porcelain","origin",`${r.expectedPullRequestHeadSha}:refs/heads/${r.executionBranch}`)}if(h||await a("push","--porcelain","origin",`${r.expectedPullRequestHeadSha}:refs/heads/${r.executionBranch}`),await $(a,r.executionBranch)!==r.expectedPullRequestHeadSha)return s("epic-terminal-execution-publish-unverified",!0,!0);const g=await d("pr","list","--repo",c,"--state","all","--head",r.executionBranch,"--base",r.originBranch,"--json","number,state,headRefName,headRefOid,baseRefName,mergeCommit","--limit","100"),f=JSON.parse(g),l=f.filter(w=>w.headRefName===r.executionBranch&&w.baseRefName===r.originBranch&&w.headRefOid===r.expectedPullRequestHeadSha&&(w.state==="OPEN"||w.state==="MERGED"));if(l.length>1)return s("epic-terminal-duplicate-prs",!1,!0);if(typeof l[0]?.number=="number")return{ok:!0,prNumber:l[0].number};if(f.some(w=>w.state==="OPEN"))return s("epic-terminal-stale-pr",!1,!0);const p=(await d("pr","create","--repo",c,"--head",r.executionBranch,"--base",r.originBranch,"--title",`[${r.epicId}] Terminal epic integration`,"--body",`Autonomous terminal integration receipt: ${r.idempotencyKey}`)).match(/\/(\d+)(?:\s*)$/);if(!p)return s("epic-terminal-pr-number-unverified",!0,!0);const m=Number(p[1]),x=await Y(d,o.nameWithOwner,m);return!x||x.state!=="OPEN"||x.headRefName!==r.executionBranch||x.baseRefName!==r.originBranch||x.headRefOid!==r.expectedPullRequestHeadSha?s("epic-terminal-pr-create-unverified",!0,!0):{ok:!0,prNumber:m}}catch(n){return s("epic-terminal-pr-create-denied",!0,ne(n))}},async mergeEpicPullRequest(r){try{const n=await C(a,d);if(!n)return s("git-resource-provider-repository-unverified",!0,!0);const o=r.candidateQa&&L(r.candidateAuthority,{mergeSha:r.expectedPullRequestHeadSha,graphRevision:r.candidateQa.graphRevision,originHeadSha:r.expectedOriginHeadSha,executionHeadSha:r.expectedStoryHeadSha});if(!o||o!==r.candidateQa)return s("epic-merge-candidate-authority-required",!1,!1);const c=await Y(d,n.nameWithOwner,r.prNumber),h=await D(a,r.executionBranch),g=await $(a,r.originBranch);if(!c||c.headRefName!==r.executionBranch||c.baseRefName!==r.originBranch||c.headRefOid!==r.expectedPullRequestHeadSha||h&&h!==r.expectedPullRequestHeadSha||!await v(a,r.expectedOriginHeadSha,r.expectedPullRequestHeadSha)||!await v(a,r.expectedStoryHeadSha,r.expectedPullRequestHeadSha))return s("epic-terminal-pr-revalidation-drift",!0,!0);if(c.state==="OPEN"&&g!==r.expectedOriginHeadSha)return s("epic-terminal-pr-origin-raced",!0,!0);let f;if(c.state==="OPEN"){const w=await We(d,n.nameWithOwner,r.originBranch,r.expectedPullRequestHeadSha,r.expectedOriginHeadSha,r.nowIso);if(!w.ok)return s(w.reasonCode,!0,!0);if(f=w.receipt,r.terminalBinding&&(!r.epicId||!await we({projectRoot:e,statePath:i,git:a,epicId:r.epicId,originBranch:r.originBranch,executionBranch:r.executionBranch,executionHeadSha:r.expectedStoryHeadSha,graphRevision:r.terminalBinding.graphRevision,storyCheckpointIds:r.terminalBinding.storyCheckpointIds,aggregateQaReceipt:r.terminalBinding.aggregateQaReceipt,nowIso:r.nowIso})))return s("epic-head-reconciliation-revalidation-drift",!0,!1);if(await $(a,r.originBranch)!==r.expectedOriginHeadSha)return s("epic-terminal-pr-origin-raced",!0,!0);await d("pr","merge",String(r.prNumber),"--repo",`https://github.com/${n.nameWithOwner}`,"--merge","--match-head-commit",r.expectedPullRequestHeadSha)}else{if(c.state!=="MERGED")return s("epic-terminal-pr-not-open-or-merged",!1,!0);f={schemaVersion:1,provider:"github",mode:"recovered_exact_tree",source:"physical_recovery",prHeadSha:r.expectedPullRequestHeadSha,originHeadSha:r.expectedOriginHeadSha,checkedAt:r.nowIso,publicSafe:!0}}const l=await Y(d,n.nameWithOwner,r.prNumber),S=l?.mergeCommit?.oid;if(l?.state!=="MERGED"||!S)return s("epic-terminal-pr-merge-unverified",!0,!0);const p=await $(a,r.originBranch);if(await Q(a,r.originBranch,p),!await v(a,r.expectedStoryHeadSha,p)||!await v(a,r.expectedPullRequestHeadSha,p)||!await v(a,S,p))return s("epic-terminal-pr-merge-ancestry-unproven",!0,!0);const[m,x]=await Promise.all([a("rev-parse",`${r.expectedPullRequestHeadSha}^{tree}`),a("rev-parse",`${S}^{tree}`)]);return m!==r.candidateQa.treeSha||x!==m?s("epic-terminal-pr-merged-tree-mismatch",!1,!0):{ok:!0,mergeSha:r.expectedPullRequestHeadSha,pushedSha:S,providerGuard:f}}catch(n){return s("epic-terminal-pr-merge-denied",!0,ne(n))}},async verifyTerminalReceipt(r,n,o){try{if(n.physicalStatus!=="pushed"||!n.pushedSha||!n.mergeSha)return s("epic-terminal-physical-revalidation-required",!0,!1);const c=await P(e,i,!0),h=c.epics?.[r.epicId];if(!h||b(h.execution_branch)!==r.executionBranch||b(h.origin_branch)!==r.originBranch||b(h.graph_revision)!==r.graphRevision)return s("epic-terminal-state-revalidation-drift",!0,!1);const g=await a("rev-parse",`refs/heads/${r.executionBranch}`);if(g!==r.expectedExecutionHeadSha||n.executionHeadSha!==g)return s("epic-terminal-execution-revalidation-drift",!0,!1);const f=y(h.origin_binding)?h.origin_binding:null;if(!f||f.originBranch!==r.originBranch||f.executionBranch!==r.executionBranch||!await T(a,f.originHeadSha)||!await v(a,f.originHeadSha,g))return s("epic-origin-binding-revalidation-drift",!0,!1);const l=await V(e,c,h,r.epicId,r.executionBranch);if(ee(h.branch_lease)||!ie(l,r.storyCheckpointIds))return s("epic-checkpoint-set-revalidation-drift",!0,!1);const S=y(h.aggregate_qa_receipt)?h.aggregate_qa_receipt:null;if(!X(S,g,r.graphRevision,o)||S?.receiptRef!==r.localGates.aggregateQaReceipt.receiptRef)return s("epic-terminal-aggregate-qa-revalidation-drift",!0,!1);const p=await $(a,r.originBranch);if(await Q(a,r.originBranch,p),!await v(a,g,p)||!await v(a,n.mergeSha,p)||!await v(a,n.pushedSha,p))return s("epic-terminal-physical-ancestry-unproven",!0,!1);const m={originHeadSha:n.baseSha,executionHeadSha:g,mergeSha:n.mergeSha,graphRevision:r.graphRevision};let x=u.get(n.mergeSha),w=L(x,m);if(!w){const B=await ge(e,m,o);x=B.candidateAuthority,w=B.candidateQa,u.set(n.mergeSha,x)}if(n.candidateQa&&(n.candidateQa.originHeadSha!==w.originHeadSha||n.candidateQa.executionHeadSha!==w.executionHeadSha||n.candidateQa.graphRevision!==w.graphRevision||n.candidateQa.treeSha!==w.treeSha))return s("epic-merge-candidate-receipt-drift",!0,!1);let k=n.providerGuard;if(n.strategy==="single_pr"){if(!n.prNumber)return s("epic-terminal-pr-merge-unverified",!0,!1);if(await a("rev-parse",`${n.pushedSha}^{tree}`)!==w.treeSha)return s("epic-terminal-pr-merged-tree-mismatch",!1,!1);if(k&&!Te(k,n))return s("epic-terminal-pr-base-guard-unverifiable",!1,!1);k??={schemaVersion:1,provider:"github",mode:"recovered_exact_tree",source:"physical_recovery",prHeadSha:n.mergeSha,originHeadSha:n.baseSha,checkedAt:o,publicSafe:!0}}else if(k)return s("epic-terminal-pr-base-guard-unverifiable",!1,!1);return{ok:!0,candidateQa:w,candidateAuthority:x,...k?{providerGuard:k}:{}}}catch{return s("epic-terminal-physical-revalidation-failed",!0,!1)}},async applyReconciliation(r,n,o){if(n.action==="preserve")return s("git-reconciliation-preserve-not-actionable",!1,!1);if(o)return{ok:!0,outcome:"dry_run"};if(n.action==="no_op")return{ok:!0,outcome:"already_absent"};if(n.action==="delete_remote_branch")return De(r,n,a,d);try{const c=await U({candidate:r,decision:n,git:a,gh:d});if(!c.ok)return s(c.reasonCode,!0,!1);if(c.alreadyAbsent)return{ok:!0,outcome:"already_absent"};if(n.action==="delete_local_branch")await a("update-ref","-d",`refs/heads/${r.branchName}`,r.headSha);else if(n.action==="close_pull_request"){if(!r.prNumber)return s("git-reconciliation-pr-number-missing",!1,!1);const h=await C(a,d);if(!h)return s("git-resource-provider-repository-unverified",!0,!0);const g=`https://github.com/${h.nameWithOwner}`,f=`neocortex-reconciliation:${q("sha256").update(r.resourceId).digest("hex")}`,l=JSON.parse(await d("pr","view",String(r.prNumber),"--repo",g,"--json","comments"));if(!y(l)||!Array.isArray(l.comments)||l.comments.length>1e3||l.comments.some(m=>!y(m)||typeof m.body!="string"))return s("git-reconciliation-pr-comments-unverified",!0,!0);l.comments.some(m=>String(m.body).includes(f))||await d("pr","comment",String(r.prNumber),"--repo",g,"--body",`Neocortex reconciliation verified managed ownership and integrated content; requesting closure. [${f}]`);const S=await U({candidate:r,decision:n,git:a,gh:d});if(!S.ok)return s(S.reasonCode,!0,!1);if(S.alreadyAbsent)return{ok:!0,outcome:"already_absent"};await d("pr","close",String(r.prNumber),"--repo",g);const p=await U({candidate:r,decision:n,git:a,gh:d});if(!p.ok)return s(p.reasonCode,!0,!1);if(!p.alreadyAbsent)return s("git-reconciliation-pr-close-unverified",!0,!0)}return{ok:!0,outcome:"applied"}}catch{return s("git-reconciliation-effect-failed",!0,!1)}},async persist(r){try{let n;return await Z(e,o=>{const c=K(o,r.terminalReceipt.epicId),h=Array.isArray(c.git_reconciliation_receipts)?c.git_reconciliation_receipts.filter(y):[],g=new Map(h.map(S=>[S.idempotencyKey,S]));for(const S of r.reconciliationReceipts)j(g.get(S.idempotencyKey))||g.set(S.idempotencyKey,S);const f=y(c.terminal_git_receipt)?c.terminal_git_receipt:null,l=Ne(f,r.terminalReceipt);if(l.kind==="blocked"){n=l.reasonCode;return}c.terminal_git_receipt=l.receipt,c.git_reconciliation_receipts=[...g.values()],c.physicalMergeStatus=r.terminalReceipt.physicalStatus==="pushed"?"merged":r.terminalReceipt.physicalStatus,c.pr_number=r.terminalReceipt.prNumber??c.pr_number??null,c.git_reconciliation_debt=Re(c.git_reconciliation_debt,r.preservedDecisions)}),n?s(n,!1,!1):{ok:!0}}catch{return s("epic-terminal-state-persist-failed",!0,!1)}},async persistReconciliation(r){try{return await Z(e,n=>{const o=K(n,r.epicId),c=Array.isArray(o.git_reconciliation_receipts)?o.git_reconciliation_receipts.filter(y):[],h=new Map(c.map(g=>[g.idempotencyKey,g]));for(const g of r.reconciliationReceipts)j(h.get(g.idempotencyKey))||h.set(g.idempotencyKey,g);o.git_reconciliation_receipts=[...h.values()],o.git_reconciliation_debt=Re(o.git_reconciliation_debt,r.preservedDecisions)}),{ok:!0}}catch{return s("git-reconciliation-state-persist-failed",!0,!1)}}}}async function C(e,t){try{const i=await e("remote","get-url","origin"),a=i.match(/^https:\/\/github\.com\/([A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+?)(?:\.git)?\/?$/)?.[1]??i.match(/^git@github\.com:([A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+?)(?:\.git)?$/)?.[1];if(!a)return null;const d=JSON.parse(await t("repo","view",`https://github.com/${a}`,"--json","id,nameWithOwner,url"));return d.nameWithOwner!==a||d.url!==`https://github.com/${a}`||typeof d.id!="string"||!/^[A-Za-z0-9_=-]{1,256}$/.test(d.id)?null:{id:d.id,nameWithOwner:a}}catch{return null}}async function De(e,t,i,a){try{if(e.kind!=="remote_branch"||t.resourceId!==e.resourceId||!e.baseBranch||e.baseBranch===e.branchName||!A(e.branchName)||!A(e.baseBranch)||!/^[a-f0-9]{40}$/.test(e.headSha)||!/^[a-f0-9]{40}$/.test(e.integrationHeadSha))return s("git-reconciliation-candidate-no-longer-safe",!1,!1);const d=await C(i,a);if(!d)return s("git-resource-remote-delete-cas-unavailable",!1,!1);if(!await D(i,e.branchName))return{ok:!0,outcome:"already_absent"};const r=await U({candidate:e,decision:t,git:i,gh:a});if(!r.ok)return s(r.reasonCode,!0,!1);if((await C(i,a))?.id!==d.id)return s("git-resource-provider-repository-raced",!0,!1);const o=q("sha256").update(["managed-remote-delete-v1",d.id,e.branchName,e.headSha,e.baseBranch,e.integrationHeadSha].join("\0")).digest("hex"),c=(f,l,S)=>`{name:${JSON.stringify(`refs/heads/${f}`)},beforeOid:${JSON.stringify(l)},afterOid:${JSON.stringify(S)},force:false}`,h=`mutation{updateRefs(input:{repositoryId:${JSON.stringify(d.id)},clientMutationId:${JSON.stringify(o)},refUpdates:[`+c(e.branchName,e.headSha,"0".repeat(40))+","+c(e.baseBranch,e.integrationHeadSha,e.integrationHeadSha)+"]}){clientMutationId}}",g=JSON.parse(await a("api","graphql","--hostname","github.com","-f",`query=${h}`));return g.errors||g.data?.updateRefs?.clientMutationId!==o?s("git-resource-remote-delete-cas-unverified",!0,!0):await D(i,e.branchName)?s("git-resource-remote-delete-recreated-or-unverified",!0,!1):{ok:!0,outcome:"applied"}}catch{return s("git-resource-remote-delete-cas-rejected",!0,!0)}}async function U(e){const{candidate:t,decision:i,git:a,gh:d}=e;if(!i.managed||!t.providerVerified||t.current||t.origin||t.protected||t.lifecycle==="active"||t.hasExclusiveCommits||t.worktreeActive)return{ok:!1,reasonCode:"git-reconciliation-candidate-no-longer-safe"};const u=await a("symbolic-ref","--quiet","--short","HEAD").catch(()=>""),r=await be(a);if(u===t.branchName||r.has(t.branchName))return{ok:!1,reasonCode:"git-resource-worktree-active"};if(t.kind==="local_branch"){const h=await Se(a,`refs/heads/${t.branchName}`);return h?h!==t.headSha?{ok:!1,reasonCode:"git-resource-expected-oid-raced"}:!t.baseBranch||await $(a,t.baseBranch)!==t.integrationHeadSha||!await v(a,h,t.integrationHeadSha)?{ok:!1,reasonCode:"git-resource-integration-unproven"}:{ok:!0}:{ok:!0,alreadyAbsent:!0}}if(t.kind==="remote_branch"){const h=await D(a,t.branchName);if(!h)return{ok:!0};if(h!==t.headSha)return{ok:!1,reasonCode:"git-resource-expected-oid-raced"};const g=await C(a,d);return g?await ae(d,g.nameWithOwner,t.branchName)!=="unprotected"?{ok:!1,reasonCode:"git-resource-protected"}:!t.baseBranch||await $(a,t.baseBranch)!==t.integrationHeadSha?{ok:!1,reasonCode:"git-resource-integration-unproven"}:(await a("fetch","--quiet","--no-tags","origin",`refs/heads/${t.branchName}`),await a("rev-parse","FETCH_HEAD")!==t.headSha||!await v(a,t.headSha,t.integrationHeadSha)?{ok:!1,reasonCode:"git-resource-integration-unproven"}:{ok:!0}):{ok:!1,reasonCode:"git-resource-provider-repository-unverified"}}if(!t.prNumber||!t.baseBranch)return{ok:!1,reasonCode:"git-reconciliation-pr-proof-missing"};const n=await C(a,d);if(!n)return{ok:!1,reasonCode:"git-resource-provider-repository-unverified"};const o=await Y(d,n.nameWithOwner,t.prNumber);if(!o||o.headRefName!==t.branchName||o.baseRefName!==t.baseBranch||o.headRefOid!==t.headSha)return{ok:!1,reasonCode:"git-resource-expected-oid-raced"};if(o.state==="CLOSED"||o.state==="MERGED")return{ok:!0,alreadyAbsent:!0};if(await ae(d,n.nameWithOwner,t.branchName)!=="unprotected")return{ok:!1,reasonCode:"git-resource-protected"};const c=await D(a,t.branchName);return!o||o.state!=="OPEN"||o.headRefName!==t.branchName||o.baseRefName!==t.baseBranch||o.headRefOid!==t.headSha||c!==t.headSha?{ok:!1,reasonCode:"git-resource-expected-oid-raced"}:(await a("fetch","--quiet","--no-tags","origin",`refs/heads/${t.branchName}`),await a("rev-parse","FETCH_HEAD")!==t.headSha||await $(a,t.baseBranch)!==t.integrationHeadSha||!await v(a,t.headSha,t.integrationHeadSha)?{ok:!1,reasonCode:"git-resource-integration-unproven"}:{ok:!0})}async function pe(e){const t=[],i=await be(e.git),a=$e(e.state),d=await C(e.git,e.gh),u=p=>{const m=p.split(`
|
|
2
|
+
`).filter(Boolean).map(w=>w.split(/\s+/));if(m.length>re)throw new Error("git-reconciliation-ref-inventory-limit");const x=new Map;for(const[w,k]of m){const B=k?.replace(/^refs\/heads\//,"");if(!B||!A(B)||!/^[a-f0-9]{40,64}$/.test(w??"")||x.has(B))throw new Error("git-reconciliation-ref-inventory-invalid");x.set(B,w)}return x},r=u(await e.git("for-each-ref",`--count=${re+1}`,"--format=%(objectname) %(refname)","refs/heads")),n=u(await e.git("ls-remote","--heads","origin")),o=new Set([...a.claims.keys()].filter(A)),c=d?await Me(e.gh,d.nameWithOwner,o):[],h=new Map([[e.directive.originBranch,e.originHeadSha]]),g=async p=>{if(h.has(p))return h.get(p);const m=n.get(p);return m?(await Q(e.git,p,m),h.set(p,m),m):""},f=new Map,l=async(p,m)=>{const x=`${p}:${m}`;return f.has(x)||f.set(x,!!(p&&m)&&await v(e.git,p,m)),f.get(x)},S=new Set([...r.keys(),...n.keys(),...a.claims.keys()]);if(S.size>re)throw new Error("git-reconciliation-ref-inventory-limit");for(const p of[...S].sort()){if(!A(p))continue;const m=a.claims.get(p)??[],x=new Set(m.map(_=>`${_.epicId}:${_.origin}`)),w=x.size===1?m[0].origin:e.directive.originBranch,k=await g(w),B=m.length===0||m.some(_=>_.active),W=r.get(p),E=n.get(p);E&&await Q(e.git,p,E);const O=d&&E?await ae(e.gh,d.nameWithOwner,p):"unknown",N=async _=>{if(x.size!==1)return[];const I=new Set;for(const H of m)for(const R of H.proofs)R.head===_&&(R.mergeSha&&(!R.pushedSha||!await l(_,R.mergeSha)||!await l(R.mergeSha,R.pushedSha)||!await l(R.pushedSha,k))||R.evidence.forEach(F=>I.add(F)));return[...I]},J={schemaVersion:1,branchName:p,baseBranch:w,integrationHeadSha:k,lifecycle:B?"active":"integrated",current:p===e.currentBranch,origin:a.origins.has(p)||p===e.directive.originBranch,worktreeActive:i.has(p),contentEquivalent:!1,publicSafe:!0};for(const[_,I]of[["local_branch",W],["remote_branch",E]]){if(!I)continue;const H=`${_}:${q("sha256").update(p).digest("hex")}:${I}`,R=await l(I,k);t.push({...J,resourceId:H,kind:_,headSha:I,ownershipEvidence:await N(I),protected:!!E&&O!=="unprotected",providerVerified:_==="local_branch"?!0:!!d&&O!=="unknown",..._==="remote_branch"&&d?{remoteDeletionCas:"github-update-refs-v1"}:{},commitsReachable:R,hasExclusiveCommits:!R,previouslyReconciled:e.receiptIds.has(H)})}for(const _ of c.filter(I=>I.headRefName===p)){const I=_.baseRefName===w&&_.sameRepository,H=await N(_.headRefOid);I&&H.includes("managed_metadata")&&H.push("pr_head_base_match");const R=`pr:${_.number}:${_.headRefOid}:${q("sha256").update(_.baseRefName).digest("hex")}`,F=await l(_.headRefOid,k);t.push({...J,resourceId:R,kind:"pull_request",prNumber:_.number,headSha:_.headRefOid,lifecycle:_.state!=="OPEN"?"already_absent":B?"active":"superseded",ownershipEvidence:H,providerVerified:I,protected:O!=="unprotected",commitsReachable:F,hasExclusiveCommits:!F,previouslyReconciled:e.receiptIds.has(R)})}}return Qe(e.epic,t),t}function Qe(e,t){const i=new Set((Array.isArray(e.git_reconciliation_receipts)?e.git_reconciliation_receipts:[]).filter(y).map(u=>b(u.idempotencyKey))),a=new Set((Array.isArray(e.git_reconciliation_debt)?e.git_reconciliation_debt:[]).filter(y).map(u=>b(u.resourceId))),d=new Set(t.filter(u=>u.kind==="pull_request"&&se(u).action==="close_pull_request").map(u=>u.branchName));for(const u of t){const r=se(u);r.action==="preserve"?a.add(u.resourceId):(i.add(`reconciliation:v2:${q("sha256").update([u.resourceId,r.action].join("\0")).digest("hex")}`),u.kind!=="pull_request"&&d.has(u.branchName)&&a.add(u.resourceId))}if(i.size>1024)throw new Error("git-reconciliation-receipt-capacity");if(a.size>1024)throw new Error("git-reconciliation-debt-capacity")}function me(e){const t=new Set(["git-reconciliation-state-inventory-limit","git-reconciliation-ref-inventory-limit","git-reconciliation-ref-inventory-invalid","git-reconciliation-pr-inventory-limit","git-reconciliation-pr-inventory-invalid","git-reconciliation-receipt-capacity","git-reconciliation-debt-capacity"]);return e instanceof Error&&t.has(e.message)?e.message:void 0}async function Me(e,t,i){const a=t.split("/")[0];if(!a||!/^[A-Za-z0-9_.-]+$/.test(a))throw new Error("git-reconciliation-pr-inventory-invalid");const d=[],u=new Set,r=100,n=Math.floor(le/r)+1;for(const o of[...i].sort()){if(!A(o))throw new Error("git-reconciliation-pr-inventory-invalid");const c=encodeURIComponent(`${a}:${o}`);for(let h=1;h<=n;h+=1){const g=`repos/${t}/pulls?state=all&head=${c}&per_page=${r}&page=${h}`,f=JSON.parse(await e("api",g,"--hostname","github.com"));if(!Array.isArray(f)||f.length>r)throw new Error("git-reconciliation-pr-inventory-limit");for(const l of f){if(!y(l)||!Number.isSafeInteger(l.number)||Number(l.number)<=0||u.has(Number(l.number))||!y(l.head)||!y(l.base)||b(l.head.ref)!==o||!A(b(l.head.ref))||!A(b(l.base.ref))||!/^[a-f0-9]{40,64}$/.test(b(l.head.sha))||!["open","closed"].includes(b(l.state)))throw new Error("git-reconciliation-pr-inventory-invalid");if(u.add(Number(l.number)),d.push({number:Number(l.number),state:b(l.state).toUpperCase(),headRefName:b(l.head.ref),headRefOid:b(l.head.sha),baseRefName:b(l.base.ref),sameRepository:y(l.head.repo)&&y(l.base.repo)&&l.head.repo.full_name===t&&l.base.repo.full_name===t}),d.length>le)throw new Error("git-reconciliation-pr-inventory-limit")}if(f.length<r)break}}return d}function j(e){return y(e)&&(e.outcome==="applied"||e.outcome==="already_absent")}async function Ge(e){try{const t=(await e.git("log",e.originHeadSha,"--format=%H","--fixed-strings",`--grep=Neocortex-Idempotency: ${e.directive.idempotencyKey}`)).split(`
|
|
3
|
+
`).filter(c=>/^[a-f0-9]{40,64}$/i.test(c));if(t.length!==1)return null;const i=t[0],a=(await e.git("rev-list","--parents","-n","1",i)).split(/\s+/),d=await e.git("show","-s","--format=%B",i),[u]=d.split(`
|
|
4
|
+
`),r=`merge(${e.directive.epicId}): integrate ${e.directive.executionBranch} into ${e.directive.originBranch}`;if(a.length!==3||a[2]!==e.executionHeadSha||u!==r)return null;const n=new Set(d.split(`
|
|
5
|
+
`).map(c=>c.trim()));for(const c of[`Neocortex-Epic: ${e.directive.epicId}`,`Neocortex-Origin-Branch: ${e.directive.originBranch}`,`Neocortex-Execution-Branch: ${e.directive.executionBranch}`,`Neocortex-Execution-Head: ${e.executionHeadSha}`,`Neocortex-Origin-Base: ${a[1]}`,`Neocortex-Graph-Revision: ${e.directive.graphRevision}`,`Neocortex-Aggregate-QA: ${e.directive.localGates.aggregateQaReceipt.receiptRef}`,`Neocortex-Idempotency: ${e.directive.idempotencyKey}`])if(!n.has(c))return null;const o=await e.git("show","-s","--format=%cI",i);return{schemaVersion:1,epicId:e.directive.epicId,executionBranch:e.directive.executionBranch,originBranch:e.directive.originBranch,baseSha:a[1],executionHeadSha:e.executionHeadSha,mergeSha:i,pushedSha:i,strategy:"direct",physicalStatus:"pushed",localGateReceiptRefs:e.directive.localGates.receiptRefs,idempotencyKey:e.directive.idempotencyKey,attemptStartedAt:o,completedAt:o,retryEligible:!1,reasonCodes:["epic-terminal-recovered-physical-push"],publicSafe:!0}}catch{return null}}async function ae(e,t,i){try{if(!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(t))return"unknown";const a=await e("api",`repos/${t}/branches/${encodeURIComponent(i)}`,"--hostname","github.com","--jq",".protected");return a==="false"?"unprotected":a==="true"?"protected":"unknown"}catch{return"unknown"}}async function Y(e,t,i){try{const a=JSON.parse(await e("pr","view",String(i),"--repo",`https://github.com/${t}`,"--json","state,headRefName,headRefOid,baseRefName,mergeCommit"));return[a.state,a.headRefName,a.headRefOid,a.baseRefName].every(d=>typeof d=="string")?a:null}catch{return null}}async function We(e,t,i,a,d,u){if(!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(t)||!A(i)||!/^[a-f0-9]{40,64}$/i.test(a)||!/^[a-f0-9]{40,64}$/i.test(d)||Number.isNaN(Date.parse(u)))return{ok:!1,reasonCode:"epic-terminal-pr-base-guard-unverifiable"};let r;try{const h=JSON.parse(await e("api",`repos/${t}/rules/branches/${encodeURIComponent(i)}`,"--hostname","github.com","--paginate","--slurp")),g=Array.isArray(h)&&h.every(Array.isArray)?h.flat():h;if(!Array.isArray(g)||g.length>256||g.some(f=>!y(f)))return{ok:!1,reasonCode:"epic-terminal-pr-base-guard-unverifiable"};r=g}catch{return{ok:!1,reasonCode:"epic-terminal-pr-base-guard-unverifiable"}}if(r.some(h=>h.type==="merge_queue"))return{ok:!1,reasonCode:"epic-terminal-pr-merge-queue-local-gate-required"};const n=r.some(h=>h.type==="required_status_checks"&&y(h.parameters)&&h.parameters.strict_required_status_checks_policy===!0);let o=!1;try{const h=JSON.parse(await e("api",`repos/${t}/branches/${encodeURIComponent(i)}/protection/required_status_checks`,"--hostname","github.com"));if(!y(h)||typeof h.strict!="boolean")return{ok:!1,reasonCode:"epic-terminal-pr-base-guard-unverifiable"};o=h.strict}catch(h){if(!ze(h))return{ok:!1,reasonCode:"epic-terminal-pr-base-guard-unverifiable"}}return!n&&!o?{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:n&&o?"branch_protection_and_ruleset":n?"ruleset":"branch_protection",prHeadSha:a,originHeadSha:d,checkedAt:u,publicSafe:!0}}}function ze(e){return e instanceof Error&&/(?:HTTP\s*)?404\b|not found/i.test(e.message)}function Te(e,t){const i=e.mode==="recovered_exact_tree"?e.source==="physical_recovery":e.mode==="strict_up_to_date_expected_head"&&["branch_protection","ruleset","branch_protection_and_ruleset"].includes(e.source);return e.schemaVersion===1&&e.provider==="github"&&i&&e.publicSafe===!0&&!Number.isNaN(Date.parse(e.checkedAt))&&e.prHeadSha===t.mergeSha&&e.originHeadSha===t.baseSha}async function ye(e,t,i){return(await Pe(e,[...t],{cwd:i,encoding:"utf8",maxBuffer:16777216})).stdout.trim()}async function P(e,t,i=!1){const a=JSON.parse(await oe(t,"utf8"));return Ae(a,await qe(e,{required:i}))}async function Ve(e){if(!await T(e.git,e.originHeadSha)||!await v(e.git,e.originHeadSha,e.executionHeadSha))throw new Error("epic-head-reconciliation-origin-ancestry-invalid");const t=G(e.epic),i=[],a=new Set;for(const h of t){const g=e.state.stories?.[h],f=g?.checkpoint;if(!y(g)||!y(f))throw new Error("epic-head-reconciliation-checkpoint-missing");const l=b(f.headSha);if(!/^[a-f0-9]{40,64}$/i.test(l))throw new Error("epic-head-reconciliation-checkpoint-head-invalid");i.push(l);for(const S of fe(h,g,f))a.add(S)}const d=i.at(-1);if(!d||!await v(e.git,d,e.executionHeadSha))throw new Error("epic-head-reconciliation-checkpoint-ancestry-invalid");const u=(await e.git("rev-list","--first-parent","--reverse",`${d}..${e.executionHeadSha}`)).split(`
|
|
6
|
+
`).filter(Boolean),r=(await e.git("diff","--name-only","-z",`${d}..${e.executionHeadSha}`)).split("\0").filter(Boolean).sort(),n=[...a].sort(),o=Object.freeze({schemaVersion:1,kind:"epic_head_reconciliation",producer:"client-p205-head-reconciliation-v1",epicId:e.epicId,executionBranch:e.executionBranch,originBranch:e.originBranch,graphRevision:e.graphRevision,originHeadSha:e.originHeadSha,lastAcceptedCheckpointHeadSha:d,executionHeadSha:e.executionHeadSha,acceptedCheckpointIds:Object.freeze([...t]),acceptedCheckpointHeads:Object.freeze(i),reconciliationCommitShas:Object.freeze(u),commitRangeDigest:Ie(d,e.executionHeadSha,u),changedPaths:Object.freeze(r),changedPathsDigest:de(r),allowedWriteSet:Object.freeze(n),allowedWriteSetDigest:de(n),aggregateReceiptRef:e.aggregateQaReceipt.receiptRef,aggregateManifestDigest:e.aggregateQaReceipt.manifestDigest??"",aggregateResultDigest:e.aggregateQaReceipt.resultDigest??"",producedAt:e.nowIso,revalidatedAt:e.nowIso,publicSafe:!0}),c=Object.freeze({...o,inputDigest:ke(o)});if(!he(c,{epicId:e.epicId,executionBranch:e.executionBranch,originBranch:e.originBranch,graphRevision:e.graphRevision,originHeadSha:e.originHeadSha,executionHeadSha:e.executionHeadSha,acceptedCheckpointIds:t,acceptedCheckpointHeads:i,aggregateQaReceipt:e.aggregateQaReceipt,nowIso:e.nowIso}))throw new Error("epic-head-reconciliation-receipt-invalid");return c}async function te(e){const t=y(e.epic.head_reconciliation_receipt)?e.epic.head_reconciliation_receipt:null,i=y(e.epic.origin_binding)?e.epic.origin_binding:null;if(!t||!i||i.originBranch!==e.originBranch||i.executionBranch!==e.executionBranch||!await T(e.git,i.originHeadSha)||!await v(e.git,i.originHeadSha,e.executionHeadSha))return!1;const a=await V(e.projectRoot,e.state,e.epic,e.epicId,e.executionBranch);if(!M(a,G(e.epic)))return!1;const d=[],u=new Set;for(const c of a){const h=e.state.stories?.[c],g=h?.checkpoint;if(!y(h)||!y(g))return!1;d.push(b(g.headSha));for(const f of fe(c,h,g))u.add(f)}const r=d.at(-1);if(!r||!await v(e.git,r,e.executionHeadSha))return!1;const n=(await e.git("rev-list","--first-parent","--reverse",`${r}..${e.executionHeadSha}`)).split(`
|
|
7
|
+
`).filter(Boolean),o=(await e.git("diff","--name-only","-z",`${r}..${e.executionHeadSha}`)).split("\0").filter(Boolean).sort();return M(n,t.reconciliationCommitShas)&&M(o,t.changedPaths)&&M([...u].sort(),t.allowedWriteSet)&&he(t,{epicId:e.epicId,executionBranch:e.executionBranch,originBranch:e.originBranch,graphRevision:e.graphRevision,originHeadSha:i.originHeadSha,executionHeadSha:e.executionHeadSha,acceptedCheckpointIds:a,acceptedCheckpointHeads:d,aggregateQaReceipt:e.aggregateQaReceipt,nowIso:e.nowIso})}async function we(e){const t=await P(e.projectRoot,e.statePath,!0),i=t.epics?.[e.epicId];if(!i||i.execution_branch!==e.executionBranch||i.origin_branch!==e.originBranch||i.graph_revision!==e.graphRevision||ee(i.branch_lease)||await e.git("rev-parse",`refs/heads/${e.executionBranch}`)!==e.executionHeadSha)return!1;const a=await V(e.projectRoot,t,i,e.epicId,e.executionBranch);if(!M(a,e.storyCheckpointIds)||!M(a,G(i)))return!1;const d=y(i.aggregate_qa_receipt)?i.aggregate_qa_receipt:null;if(!X(d,e.executionHeadSha,e.graphRevision,e.nowIso)||d.receiptRef!==e.aggregateQaReceipt.receiptRef)return!1;const u=t.stories?.[a.at(-1)??""]?.checkpoint;return(y(u)?b(u.headSha):"")===e.executionHeadSha?!0:te({projectRoot:e.projectRoot,git:e.git,state:t,epic:i,epicId:e.epicId,executionBranch:e.executionBranch,originBranch:e.originBranch,executionHeadSha:e.executionHeadSha,graphRevision:e.graphRevision,aggregateQaReceipt:d,nowIso:e.nowIso})}function X(e,t,i,a){if(!e||e.schemaVersion!==1||e.evidenceVersion!=="p205-execution-v2"||e.producer!=="local-yolo-tdd"||e.contract!=="P205-CI-02"||e.command!==z||e.verdict!=="passed"||e.executionHeadSha!==t||e.graphRevision!==i||!/^[a-f0-9]{64}$/i.test(e.manifestDigest??"")||!/^[a-f0-9]{64}$/i.test(e.resultDigest??"")||e.gateIds.length===0)return!1;const d=`local-yolo-tdd:${q("sha256").update(["epic-aggregate-p205-execution-v2",z,t,i,e.manifestDigest,e.gateIds.join(","),e.resultDigest].join("\0")).digest("hex")}`;if(e.receiptRef!==d)return!1;const u=Date.parse(e.checkedAt),r=Date.parse(a);return Number.isFinite(u)&&Number.isFinite(r)&&r-u<=ce.aggregateValidityMs&&u-r<=ce.cancellationGraceMs}function M(e,t){return e.length===t.length&&e.every((i,a)=>i===t[a])}async function $(e,t){const i=await D(e,t);if(!i)throw new Error("origin-ref-missing");return i}async function D(e,t){if(!A(t))throw new Error("branch-ref-invalid");const a=(await e("ls-remote","--heads","origin",`refs/heads/${t}`)).split(`
|
|
8
|
+
`).filter(Boolean).map(d=>d.split(/\s+/));if(a.length!==0){if(a.length!==1||a[0].length!==2||a[0][1]!==`refs/heads/${t}`||!/^[a-f0-9]{40,64}$/i.test(a[0][0]??""))throw new Error("remote-ref-query-invalid");return a[0][0]}}async function Q(e,t,i){if(await e("fetch","--quiet","--no-tags","origin",`refs/heads/${t}`),await e("rev-parse","FETCH_HEAD")!==i)throw new Error("remote-head-raced")}async function Se(e,t){try{return await e("rev-parse","--verify",t)}catch{return}}async function v(e,t,i){try{return await e("merge-base","--is-ancestor",t,i),!0}catch{return!1}}async function T(e,t){if(!/^[a-f0-9]{40,64}$/i.test(t))return!1;try{return await e("cat-file","-t",t)==="commit"}catch{return!1}}async function be(e){const t=new Set,i=await e("worktree","list","--porcelain");for(const a of i.split(`
|
|
9
|
+
`))a.startsWith("branch refs/heads/")&&t.add(a.slice(18));return t}function s(e,t,i){return{ok:!1,reasonCode:e,retryEligible:t,externalPolicy:i}}function b(e){return typeof e=="string"?e.trim():""}function A(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 y(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function Je(e,t){return e instanceof Error&&"code"in e&&e.code===t}function G(e){return(Array.isArray(e.stories)?e.stories:Array.isArray(e.story_ids)?e.story_ids:[]).filter(i=>typeof i=="string")}async function V(e,t,i,a,d){const u=G(i),r=await Promise.all(u.map(n=>ue(e,t.stories?.[n]?.checkpoint,n,a,d).then(o=>o&&xe(t.stories?.[n]?.checkpoint))));return u.filter((n,o)=>r[o])}function xe(e){const t=y(e)?e:void 0,i=t&&y(t.qaReceipt)?t.qaReceipt:void 0;return!!(i&&i.evidenceVersion==="p205-execution-v2"&&i.producer==="local-yolo-tdd"&&i.contract==="P205-CI-02"&&i.command===z&&/^[a-f0-9]{64}$/i.test(b(i.manifestDigest))&&/^[a-f0-9]{64}$/i.test(b(i.resultDigest))&&Array.isArray(i.gateIds)&&i.gateIds.length>0)}function ie(e,t){return e.length===t.length&&[...e].sort().every((i,a)=>i===[...t].sort()[a])}function ee(e){return y(e)&&typeof e.expiresAt=="string"&&e.expiresAt>new Date().toISOString()}async function _e(e){const t=new Set([".neocortex/yoloop.lock",".neocortex/yoloop-state.json",".neocortex/continuity/continuity.sqlite",".neocortex/continuity/continuity.sqlite-shm",".neocortex/continuity/continuity.sqlite-wal"]);return(await e("status","--porcelain","--untracked-files=all")).split(`
|
|
10
|
+
`).filter(Boolean).some(i=>!i.startsWith("?? ")||!t.has(i.slice(3)))}function ne(e){const t=e instanceof Error?e.message:"";return/protected branch|branch protection|required pull request|GH006|GH013|ruleset|review required/i.test(t)}function Re(e,t){const i=Array.isArray(e)?e.filter(y):[],a=new Map(i.map(d=>[b(d.resourceId),d]));for(const d of t)a.set(d.resourceId,d);return[...a.values()]}export{rr as createCliEpicTerminalGitAdapter};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
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';
|
|
2
|
+
import { type EpicTerminalReleaseAuthority, type P205TransientExecutionAuthority } from '../../git/git-lifecycle-store.js';
|
|
3
3
|
import { type EpicMergeCandidateQaAuthority } from './epic-merge-candidate-qa.js';
|
|
4
4
|
import type { EpicMergeCandidateQaReceipt } from '../../../../shared/dist/yoloop/epic-terminal-git.js';
|
|
5
5
|
import type { EpicPullRequestMergeGuardReceipt } from '../../../../shared/dist/yoloop/epic-terminal-git.js';
|
|
@@ -11,6 +11,7 @@ export interface EpicTerminalGitSnapshot {
|
|
|
11
11
|
readonly acceptedCheckpointIds: readonly string[];
|
|
12
12
|
readonly existingReceipt?: EpicTerminalGitReceipt | null;
|
|
13
13
|
readonly aggregateQaReceipt?: import('../../../../shared/dist/yoloop/epic-terminal-git.js').EpicAggregateQaReceipt | null;
|
|
14
|
+
readonly headReconciliationReceipt?: import('../../../../shared/dist/yoloop/epic-terminal-git.js').EpicHeadReconciliationReceiptV1 | null;
|
|
14
15
|
readonly originBinding?: import('../../../../shared/dist/yoloop/epic-terminal-git.js').EpicOriginBindingEvidence | null;
|
|
15
16
|
readonly nowIso?: string;
|
|
16
17
|
readonly reconciliationCandidates: readonly GitReconciliationCandidate[];
|
|
@@ -31,6 +32,9 @@ export interface EpicTerminalGitAdapter {
|
|
|
31
32
|
}): Promise<EpicTerminalEffectResult<{
|
|
32
33
|
readonly originBindingCaptured: boolean;
|
|
33
34
|
readonly aggregateQaRecorded: boolean;
|
|
35
|
+
readonly headReconciliationRecorded?: boolean;
|
|
36
|
+
readonly terminalAuthority?: EpicTerminalReleaseAuthority;
|
|
37
|
+
/** Backward-compatible alias for same-process fresh aggregate producers. */
|
|
34
38
|
readonly p205Authority?: P205TransientExecutionAuthority;
|
|
35
39
|
}>>;
|
|
36
40
|
inspect(directive: EpicTerminalGitDirective): Promise<EpicTerminalEffectResult<{
|
|
@@ -73,10 +77,17 @@ export interface EpicTerminalGitAdapter {
|
|
|
73
77
|
readonly candidateQa: EpicMergeCandidateQaReceipt;
|
|
74
78
|
readonly candidateAuthority: EpicMergeCandidateQaAuthority;
|
|
75
79
|
readonly idempotencyKey: string;
|
|
80
|
+
readonly terminalBinding?: {
|
|
81
|
+
readonly graphRevision: string;
|
|
82
|
+
readonly storyCheckpointIds: readonly string[];
|
|
83
|
+
readonly aggregateQaReceipt: EpicTerminalGitDirective['localGates']['aggregateQaReceipt'];
|
|
84
|
+
readonly nowIso: string;
|
|
85
|
+
};
|
|
76
86
|
}): Promise<EpicTerminalEffectResult<{
|
|
77
87
|
readonly prNumber: number;
|
|
78
88
|
}>>;
|
|
79
89
|
mergeEpicPullRequest(input: {
|
|
90
|
+
readonly epicId?: string;
|
|
80
91
|
readonly prNumber: number;
|
|
81
92
|
readonly originBranch: string;
|
|
82
93
|
readonly executionBranch: string;
|
|
@@ -86,6 +97,11 @@ export interface EpicTerminalGitAdapter {
|
|
|
86
97
|
readonly candidateQa?: EpicMergeCandidateQaReceipt;
|
|
87
98
|
readonly candidateAuthority?: EpicMergeCandidateQaAuthority;
|
|
88
99
|
readonly nowIso: string;
|
|
100
|
+
readonly terminalBinding?: {
|
|
101
|
+
readonly graphRevision: string;
|
|
102
|
+
readonly storyCheckpointIds: readonly string[];
|
|
103
|
+
readonly aggregateQaReceipt: EpicTerminalGitDirective['localGates']['aggregateQaReceipt'];
|
|
104
|
+
};
|
|
89
105
|
}): Promise<EpicTerminalEffectResult<{
|
|
90
106
|
readonly mergeSha: string;
|
|
91
107
|
readonly pushedSha: string;
|
|
@@ -131,4 +147,4 @@ export interface EpicGitReconciliationExecutionResult {
|
|
|
131
147
|
}
|
|
132
148
|
/** Run the same fail-closed reconciler at start/resume or explicit audit. */
|
|
133
149
|
export declare function executeEpicGitReconciliation(directive: EpicGitReconciliationDirective, adapter: EpicTerminalGitAdapter, now: () => string): Promise<EpicGitReconciliationExecutionResult>;
|
|
134
|
-
export declare function executeEpicTerminalGit(directive: EpicTerminalGitDirective, adapter: EpicTerminalGitAdapter, now: () => string, preparedAuthority?:
|
|
150
|
+
export declare function executeEpicTerminalGit(directive: EpicTerminalGitDirective, adapter: EpicTerminalGitAdapter, now: () => string, preparedAuthority?: EpicTerminalReleaseAuthority): Promise<EpicTerminalGitExecutionResult>;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{createHash as
|
|
2
|
-
`)});if(!
|
|
1
|
+
import{createHash as w}from"node:crypto";import{EPIC_TERMINAL_GIT_REASON_CODES as x,classifyGitReconciliationCandidate as N,validateEpicTerminalGitDirective as q}from"../../../../shared/dist/yoloop/epic-terminal-git.js";import{consumeEpicTerminalReleaseAuthority as P}from"../../git/git-lifecycle-store.js";import{verifiedEpicMergeCandidateReceipt as E}from"./epic-merge-candidate-qa.js";async function O(e,r,s){const c=await r.inspectReconciliation(e);if(!c.ok)return{ok:!1,reconciliationReceipts:[],preservedDecisions:[],reasonCodes:[c.reasonCode],publicSafe:!0};const o=await k({idempotencyKey:e.idempotencyKey,enabled:!0,dryRun:e.dryRun},c.candidates,r,s),l=await r.persistReconciliation({epicId:e.epicId,reconciliationReceipts:o.receipts,preservedDecisions:o.preserved}),u=C([...o.reasonCodes,...l.ok?[]:[l.reasonCode]]);return{ok:o.ok&&l.ok,reconciliationReceipts:o.receipts,preservedDecisions:o.preserved,reasonCodes:u,publicSafe:!0}}async function T(e,r,s,c){const o=s();if(!r.prepareLifecycleEvidence)return S(e,{ok:!1,reasonCode:"epic-aggregate-qa-producer-required",retryEligible:!1,externalPolicy:!1},o);const l=c?{ok:!0,originBindingCaptured:!1,aggregateQaRecorded:!0,terminalAuthority:c}:await r.prepareLifecycleEvidence({epicId:e.epicId,nowIso:o});if(!l.ok)return S(e,l,o);const u=l.terminalAuthority??l.p205Authority;if(!u)return S(e,{ok:!1,reasonCode:"epic-aggregate-qa-authority-required",retryEligible:!1,externalPolicy:!1},o);const p=await r.inspect(e);if(!p.ok)return S(e,p,o);const a=p.snapshot,h=q(e,{...a,nowIso:o});if(!h.ready)return y(!1,R(e,a,o,{strategy:e.strategy==="single_pr"?"single_pr":"direct",physicalStatus:"blocked_local_or_repo",retryEligible:h.reasonCodes.some(t=>t.endsWith("mismatch")||t.endsWith("active")),reasonCodes:h.reasonCodes}),[],[],h.reasonCodes);if(!a.aggregateQaReceipt||!P(u,{executionHeadSha:a.executionHeadSha,graphRevision:e.graphRevision,aggregateQaReceipt:a.aggregateQaReceipt,headReconciliationReceipt:a.headReconciliationReceipt}))return S(e,{ok:!1,reasonCode:"epic-aggregate-qa-authority-binding-mismatch",retryEligible:!1,externalPolicy:!1},o);if(h.reasonCodes.includes(x.ALREADY_COMPLETE)&&a.existingReceipt){const t=await r.verifyTerminalReceipt(e,a.existingReceipt,o);if(!t.ok)return y(!1,a.existingReceipt,[],[],[t.reasonCode]);const I=E(t.candidateAuthority,{mergeSha:a.existingReceipt.mergeSha,originHeadSha:a.existingReceipt.baseSha,executionHeadSha:a.existingReceipt.executionHeadSha,graphRevision:e.graphRevision});if(!I)return y(!1,a.existingReceipt,[],[],["epic-merge-candidate-authority-required"]);const B={...a.existingReceipt,candidateQa:I,...t.providerGuard?{providerGuard:t.providerGuard}:{}},f=await k({idempotencyKey:e.idempotencyKey,enabled:e.reconcile.enabled,dryRun:e.reconcile.dryRun},a.reconciliationCandidates,r,s),_=await r.persist({terminalReceipt:B,reconciliationReceipts:f.receipts,preservedDecisions:f.preserved}),A=C([...h.reasonCodes,...f.reasonCodes,..._.ok?[]:[_.reasonCode]]);return y(_.ok&&f.ok,B,f.receipts,f.preserved,A)}let i;const n=await r.prepareMergeObject({originBranch:e.originBranch,executionBranch:e.executionBranch,originHeadSha:a.originHeadSha,executionHeadSha:a.executionHeadSha,graphRevision:e.graphRevision,nowIso:o,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: ${a.executionHeadSha}`,`Neocortex-Origin-Base: ${a.originHeadSha}`,`Neocortex-Graph-Revision: ${e.graphRevision}`,`Neocortex-Aggregate-QA: ${e.localGates.aggregateQaReceipt.receiptRef}`,`Neocortex-Idempotency: ${e.idempotencyKey}`].join(`
|
|
2
|
+
`)});if(!n.ok)return i=R(e,a,o,{strategy:"direct",physicalStatus:"blocked_local_or_repo",retryEligible:n.retryEligible,reasonCodes:[n.reasonCode]}),await r.persist({terminalReceipt:i,reconciliationReceipts:[],preservedDecisions:[]}),y(!1,i,[],[],i.reasonCodes);const g=E(n.candidateAuthority,{originHeadSha:a.originHeadSha,executionHeadSha:a.executionHeadSha,mergeSha:n.mergeSha,graphRevision:e.graphRevision});if(!g)return S(e,{ok:!1,reasonCode:"epic-merge-candidate-authority-required",retryEligible:!1,externalPolicy:!1},o);if(e.strategy!=="single_pr"){const t=await r.pushMergeObject({epicId:e.epicId,originBranch:e.originBranch,executionBranch:e.executionBranch,executionHeadSha:a.executionHeadSha,expectedOriginSha:a.originHeadSha,mergeSha:n.mergeSha,graphRevision:e.graphRevision,candidateAuthority:n.candidateAuthority,storyCheckpointIds:e.storyCheckpointIds,aggregateQaReceipt:e.localGates.aggregateQaReceipt,nowIso:s()});t.ok?i=R(e,a,o,{strategy:"direct",mergeSha:n.mergeSha,pushedSha:t.pushedSha,candidateQa:g,physicalStatus:"pushed",retryEligible:!1,reasonCodes:[x.READY],completedAt:s()}):e.strategy==="direct_or_single_pr"&&t.externalPolicy?i=await H(e,a,r,o,s,g,n.candidateAuthority):i=R(e,a,o,{strategy:"direct",mergeSha:n.mergeSha,physicalStatus:t.externalPolicy?"denied_external_gate":"blocked_local_or_repo",retryEligible:t.retryEligible,reasonCodes:[t.reasonCode,...t.retryEligible?[x.RETRY_ELIGIBLE]:[]]})}else i=await H(e,a,r,o,s,g,n.candidateAuthority);const d=i.physicalStatus==="pushed"?await r.verifyTerminalReceipt(e,i,s()):null;if(d&&!d.ok){const t=R(e,a,o,{...i,physicalStatus:"blocked_local_or_repo",retryEligible:d.retryEligible,reasonCodes:[d.reasonCode]});return await r.persist({terminalReceipt:t,reconciliationReceipts:[],preservedDecisions:[]}),y(!1,t,[],[],t.reasonCodes)}if(d?.ok){const t=E(d.candidateAuthority,{mergeSha:i.mergeSha,originHeadSha:i.baseSha,executionHeadSha:i.executionHeadSha,graphRevision:e.graphRevision});if(!t)return y(!1,i,[],[],["epic-merge-candidate-authority-required"]);i={...i,candidateQa:t,...d.providerGuard?{providerGuard:d.providerGuard}:{}}}const m=i.physicalStatus==="pushed"?await k({idempotencyKey:e.idempotencyKey,enabled:e.reconcile.enabled,dryRun:e.reconcile.dryRun},a.reconciliationCandidates,r,s):{ok:!0,receipts:[],preserved:[],reasonCodes:[]},b=await r.persist({terminalReceipt:i,reconciliationReceipts:m.receipts,preservedDecisions:m.preserved}),G=C([...i.reasonCodes,...m.reasonCodes,...b.ok?[]:[b.reasonCode]]);return y(i.physicalStatus==="pushed"&&m.ok&&b.ok,i,m.receipts,m.preserved,G)}async function H(e,r,s,c,o,l,u){const p=await s.ensureSingleEpicPullRequest({epicId:e.epicId,originBranch:e.originBranch,executionBranch:e.executionBranch,expectedStoryHeadSha:r.executionHeadSha,expectedPullRequestHeadSha:l.mergeSha,candidateQa:l,candidateAuthority:u,idempotencyKey:e.idempotencyKey,terminalBinding:{graphRevision:e.graphRevision,storyCheckpointIds:e.storyCheckpointIds,aggregateQaReceipt:e.localGates.aggregateQaReceipt,nowIso:o()}});if(!p.ok)return R(e,r,c,{strategy:"single_pr",physicalStatus:p.externalPolicy?"denied_external_gate":"blocked_local_or_repo",retryEligible:p.retryEligible,reasonCodes:[p.reasonCode]});const a=await s.mergeEpicPullRequest({epicId:e.epicId,prNumber:p.prNumber,originBranch:e.originBranch,executionBranch:e.executionBranch,expectedStoryHeadSha:r.executionHeadSha,expectedPullRequestHeadSha:l.mergeSha,expectedOriginHeadSha:r.originHeadSha,candidateQa:l,candidateAuthority:u,nowIso:o(),terminalBinding:{graphRevision:e.graphRevision,storyCheckpointIds:e.storyCheckpointIds,aggregateQaReceipt:e.localGates.aggregateQaReceipt}});return a.ok?R(e,r,c,{strategy:"single_pr",prNumber:p.prNumber,mergeSha:a.mergeSha,pushedSha:a.pushedSha,candidateQa:l,providerGuard:a.providerGuard,physicalStatus:"pushed",retryEligible:!1,reasonCodes:[x.READY],completedAt:o()}):R(e,r,c,{strategy:"single_pr",prNumber:p.prNumber,physicalStatus:a.externalPolicy?"denied_external_gate":"blocked_local_or_repo",retryEligible:a.retryEligible,reasonCodes:[a.reasonCode,...a.retryEligible?[x.RETRY_ELIGIBLE]:[]]})}async function k(e,r,s,c){if(!e.enabled)return{ok:!0,receipts:[],preserved:[],reasonCodes:[]};const o=[],l=[],u=[];let p=!0;const a=new Set,h={pull_request:0,local_branch:1,remote_branch:2};for(const i of[...r].sort((n,g)=>h[n.kind]-h[g.kind]||n.resourceId.localeCompare(g.resourceId))){const n=N(i);if(u.push(...n.reasonCodes),n.action==="preserve"){l.push(n);continue}if(i.kind!=="pull_request"&&a.has(i.branchName)){l.push({...n,action:"preserve",reasonCodes:["git-resource-related-pr-close-pending"]}),u.push("git-resource-related-pr-close-pending");continue}const g=`reconciliation:v2:${w("sha256").update([i.resourceId,n.action].join("\0")).digest("hex")}`,d=await s.applyReconciliation(i,n,e.dryRun);if(!d.ok){p=!1,i.kind==="pull_request"&&a.add(i.branchName),u.push(d.reasonCode),o.push({schemaVersion:1,resourceId:i.resourceId,action:n.action,outcome:"failed",idempotencyKey:g,reasonCodes:[d.reasonCode],recordedAt:c(),publicSafe:!0});continue}o.push({schemaVersion:1,resourceId:i.resourceId,action:n.action,outcome:d.outcome,idempotencyKey:g,reasonCodes:n.reasonCodes,recordedAt:c(),publicSafe:!0})}return{ok:p,receipts:o,preserved:l,reasonCodes:C(u)}}function R(e,r,s,c){return Object.freeze({schemaVersion:1,epicId:e.epicId,executionBranch:e.executionBranch,originBranch:e.originBranch,baseSha:r.originHeadSha,executionHeadSha:r.executionHeadSha,localGateReceiptRefs:e.localGates.receiptRefs,idempotencyKey:e.idempotencyKey,attemptStartedAt:s,publicSafe:!0,...c})}function S(e,r,s){const c="0".repeat(40),o={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:r.externalPolicy?"denied_external_gate":"blocked_local_or_repo",localGateReceiptRefs:e.localGates.receiptRefs,idempotencyKey:e.idempotencyKey,attemptStartedAt:s,retryEligible:r.retryEligible,reasonCodes:[r.reasonCode],publicSafe:!0};return y(!1,o,[],[],o.reasonCodes)}function y(e,r,s,c,o){return{ok:e,receipt:r,reconciliationReceipts:s,preservedDecisions:c,reasonCodes:C(o),publicSafe:!0}}function C(e){return[...new Set(e)]}export{O as executeEpicGitReconciliation,T as executeEpicTerminalGit};
|