@coreplane/switchboard 1.262.0 → 1.262.2
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.
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "switchboard",
|
|
3
|
-
"version": "1.262.
|
|
3
|
+
"version": "1.262.2",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "switchboard",
|
|
9
|
-
"version": "1.262.
|
|
9
|
+
"version": "1.262.2",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"workspaces": [
|
|
12
12
|
"web",
|
|
@@ -21060,7 +21060,7 @@
|
|
|
21060
21060
|
},
|
|
21061
21061
|
"packages/switchboard": {
|
|
21062
21062
|
"name": "@coreplane/switchboard",
|
|
21063
|
-
"version": "1.262.
|
|
21063
|
+
"version": "1.262.2",
|
|
21064
21064
|
"license": "Apache-2.0",
|
|
21065
21065
|
"dependencies": {
|
|
21066
21066
|
"@earendil-works/pi-ai": "0.85.1",
|
package/dist/assets/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "switchboard",
|
|
3
|
-
"version": "1.262.
|
|
3
|
+
"version": "1.262.2",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "Mention it in Slack and an agent reviews the PR, ships the fix, or answers the question — on the model you choose, with its tools running where you decide.",
|
|
6
6
|
"license": "Apache-2.0",
|
package/dist/assets/source.json
CHANGED
|
@@ -514,6 +514,10 @@ export interface OriginalUnitRecovery {
|
|
|
514
514
|
/** The exact terminal value replaced by the claim, for fail-closed rollback
|
|
515
515
|
* if the Workflow cannot be admitted. */
|
|
516
516
|
previousEnding: NonNullable<CoordinatorUnit["ending"]>;
|
|
517
|
+
/** Original binding fields before an evidence-backed repair. The empty object
|
|
518
|
+
* means both were absent; retained across restart for admission rollback,
|
|
519
|
+
* then retired when the recovery records progress. */
|
|
520
|
+
previousBinding?: { publication?: ExistingPrPublicationBinding; lastPush?: string };
|
|
517
521
|
/** The separate Workflow execution checkpoint. This is transport identity,
|
|
518
522
|
* not a replacement coordinator/unit identity. */
|
|
519
523
|
workflowId: string;
|
|
@@ -813,6 +817,13 @@ export function isCoordinatorUnit(v: unknown): v is CoordinatorUnit {
|
|
|
813
817
|
r.recovery.findingsKey !== undefined)) &&
|
|
814
818
|
(r.recovery.findings === undefined ||
|
|
815
819
|
(Array.isArray(r.recovery.findings) && r.recovery.findings.every(isFindingShape))) &&
|
|
820
|
+
(r.recovery.previousBinding === undefined ||
|
|
821
|
+
(isObject(r.recovery.previousBinding) &&
|
|
822
|
+
(r.recovery.previousBinding.publication === undefined ||
|
|
823
|
+
isPublication(r.recovery.previousBinding.publication)) &&
|
|
824
|
+
(r.recovery.previousBinding.lastPush === undefined ||
|
|
825
|
+
(typeof r.recovery.previousBinding.lastPush === "string" &&
|
|
826
|
+
/^[0-9a-f]{40}$/i.test(r.recovery.previousBinding.lastPush))))) &&
|
|
816
827
|
isObject(r.recovery.previousEnding) &&
|
|
817
828
|
isText(r.recovery.previousEnding.kind) &&
|
|
818
829
|
typeof r.recovery.previousEnding.report === "string" &&
|
package/dist/cli.js
CHANGED
|
@@ -5808,7 +5808,7 @@ function isCoordinatorUnit(v) {
|
|
|
5808
5808
|
(x) => isObject(x) && isFinite(x.index) && isText(x.agent) && isText(x.outcome) && isFinite(x.at) && (x.gate === void 0 || isRoundGate(x.gate))
|
|
5809
5809
|
))
|
|
5810
5810
|
return false;
|
|
5811
|
-
if (r.recovery !== void 0 && !(isObject(r.recovery) && (r.recovery.kind === "findings" || r.recovery.kind === "review") && typeof r.recovery.round === "number" && Number.isInteger(r.recovery.round) && r.recovery.round >= 1 && typeof r.recovery.expectedHeadSha === "string" && /^[0-9a-f]{40}$/i.test(r.recovery.expectedHeadSha) && isFinite(r.recovery.remainingMs) && r.recovery.remainingMs > 0 && isFinite(r.recovery.claimedAt) && typeof r.recovery.step === "string" && STEP_NAME_PATTERN.test(r.recovery.step) && isText(r.recovery.reviewRunId) && (r.recovery.findingsRunId === void 0 || isText(r.recovery.findingsRunId)) && (r.recovery.findingsKey === void 0 || isText(r.recovery.findingsKey)) && (r.recovery.findingsRunId === void 0 && r.recovery.findingsKey === void 0 || r.recovery.kind === "review" && r.recovery.findingsRunId !== void 0 && r.recovery.findingsKey !== void 0) && (r.recovery.findings === void 0 || Array.isArray(r.recovery.findings) && r.recovery.findings.every(isFindingShape)) && isObject(r.recovery.previousEnding) && isText(r.recovery.previousEnding.kind) && typeof r.recovery.previousEnding.report === "string" && isFinite(r.recovery.previousEnding.at) && typeof r.recovery.workflowId === "string" && INSTANCE_ID_PATTERN.test(r.recovery.workflowId) && isFinite(r.recovery.deadlineAt) && isText(r.recovery.reviewKey)))
|
|
5811
|
+
if (r.recovery !== void 0 && !(isObject(r.recovery) && (r.recovery.kind === "findings" || r.recovery.kind === "review") && typeof r.recovery.round === "number" && Number.isInteger(r.recovery.round) && r.recovery.round >= 1 && typeof r.recovery.expectedHeadSha === "string" && /^[0-9a-f]{40}$/i.test(r.recovery.expectedHeadSha) && isFinite(r.recovery.remainingMs) && r.recovery.remainingMs > 0 && isFinite(r.recovery.claimedAt) && typeof r.recovery.step === "string" && STEP_NAME_PATTERN.test(r.recovery.step) && isText(r.recovery.reviewRunId) && (r.recovery.findingsRunId === void 0 || isText(r.recovery.findingsRunId)) && (r.recovery.findingsKey === void 0 || isText(r.recovery.findingsKey)) && (r.recovery.findingsRunId === void 0 && r.recovery.findingsKey === void 0 || r.recovery.kind === "review" && r.recovery.findingsRunId !== void 0 && r.recovery.findingsKey !== void 0) && (r.recovery.findings === void 0 || Array.isArray(r.recovery.findings) && r.recovery.findings.every(isFindingShape)) && (r.recovery.previousBinding === void 0 || isObject(r.recovery.previousBinding) && (r.recovery.previousBinding.publication === void 0 || isPublication(r.recovery.previousBinding.publication)) && (r.recovery.previousBinding.lastPush === void 0 || typeof r.recovery.previousBinding.lastPush === "string" && /^[0-9a-f]{40}$/i.test(r.recovery.previousBinding.lastPush))) && isObject(r.recovery.previousEnding) && isText(r.recovery.previousEnding.kind) && typeof r.recovery.previousEnding.report === "string" && isFinite(r.recovery.previousEnding.at) && typeof r.recovery.workflowId === "string" && INSTANCE_ID_PATTERN.test(r.recovery.workflowId) && isFinite(r.recovery.deadlineAt) && isText(r.recovery.reviewKey)))
|
|
5812
5812
|
return false;
|
|
5813
5813
|
if (r.recoveryReceipt !== void 0 && (!isObject(r.recoveryReceipt) || !isText(r.recoveryReceipt.reviewRunId) || typeof r.recoveryReceipt.workflowId !== "string" || !INSTANCE_ID_PATTERN.test(r.recoveryReceipt.workflowId) || !isFinite(r.recoveryReceipt.at)))
|
|
5814
5814
|
return false;
|
|
@@ -55102,7 +55102,8 @@ async function runShipBranch(deps, msg, io, ctx) {
|
|
|
55102
55102
|
() => deps.recoverOriginalUnit === void 0 ? Promise.resolve({ status: 503, body: { error: "original-unit recovery is unavailable" } }) : deps.recoverOriginalUnit(recovery, { userId: msg.userId, threadKey: msg.threadKey })
|
|
55103
55103
|
);
|
|
55104
55104
|
if (answer.status !== 200) {
|
|
55105
|
-
const
|
|
55105
|
+
const error = typeof answer.body.error === "string" ? answer.body.error : "original-unit recovery was refused";
|
|
55106
|
+
const reason2 = error === "recovery_budget_unknown" && typeof answer.body.reason === "string" ? `${error}: ${answer.body.reason}` : error;
|
|
55106
55107
|
await refuse3(
|
|
55107
55108
|
refusalOf("setup_failed", reason2),
|
|
55108
55109
|
() => card.done(shell.close({ kind: "refused", icon: "\u{1F6AB}", reason: reason2, ...closeLines(clock4(), false) }))
|
|
@@ -73649,16 +73650,36 @@ async function steerChild(body, deps) {
|
|
|
73649
73650
|
const steered = await deps.steerChild?.(body.runId, text, instance) ?? false;
|
|
73650
73651
|
return json3(200, { ok: true, outcome: steered ? "steered" : "not_live", at });
|
|
73651
73652
|
}
|
|
73652
|
-
async function
|
|
73653
|
-
if (row2.
|
|
73654
|
-
|
|
73655
|
-
const child = await deps.runs.getRun(runId3);
|
|
73656
|
-
const expectedPrefix = `${instance.id}:${row2.unit}/recovery/`;
|
|
73653
|
+
async function advanceFindingsPublication(deps, instance, row2, facts2, runId3) {
|
|
73654
|
+
if (row2.publication === void 0 || facts2.headSha === row2.publication.expectedHeadSha) return row2;
|
|
73655
|
+
const child = await deps.runs.getRun(runId3, { include: "messages" });
|
|
73657
73656
|
const owner = { instanceId: instance.id, unit: row2.unit };
|
|
73658
|
-
|
|
73657
|
+
const latestReview = [...row2.rounds].reverse().find((round2) => round2.agent === "review");
|
|
73658
|
+
const segment = row2.segments?.reduce((latest, entry) => Math.max(latest, entry.index), 1) ?? 1;
|
|
73659
|
+
const prefix = row2.recovery !== void 0 ? `${row2.unit}/recovery` : stepPrefixOf(row2.unit, segment > 1 ? { segment, renewalsSpent: 0, spendUsd: null } : void 0);
|
|
73660
|
+
const findingsPrefix = `${instance.id}:${prefix}/${latestReview?.index}/findings`;
|
|
73661
|
+
if (row2.recovery === void 0) {
|
|
73662
|
+
const tags = child.ok ? child.value.events?.filter((event) => event.type === "coordinator_tag") ?? [] : [];
|
|
73663
|
+
const tag = tags.length === 1 ? tags[0] : void 0;
|
|
73664
|
+
if (row2.ending !== void 0 || row2.idle !== void 0 || latestReview?.outcome !== "request_changes" || !child.ok || child.value.startedAt < latestReview.at || tag?.parentInstanceId !== instance.id || tag.unit !== row2.unit || tag.base !== instance.base || JSON.stringify(tag.publication) !== JSON.stringify(row2.publication))
|
|
73665
|
+
throw new PublicationBindingRefusal("publication_facts_mismatch");
|
|
73666
|
+
const listing = await deps.runs.listRuns({
|
|
73667
|
+
status: "finished",
|
|
73668
|
+
visibleTo: EVERY_RUN,
|
|
73669
|
+
threadKey: row2.threadKey ?? instance.threadKey,
|
|
73670
|
+
limit: RUN_LIST_MAX_LIMIT
|
|
73671
|
+
});
|
|
73672
|
+
if (listing.storeUnavailable || listing.nextBefore !== void 0 || listing.runs.filter(
|
|
73673
|
+
(run2) => run2.parentInstanceId === instance.id && isStepAttempt(run2.idempotencyKey, findingsPrefix)
|
|
73674
|
+
).length !== 1)
|
|
73675
|
+
throw new PublicationBindingRefusal("publication_facts_mismatch");
|
|
73676
|
+
}
|
|
73677
|
+
if (!child.ok || child.value.finished !== true || child.value.status !== "completed" || child.value.agent !== "coding" || child.value.parentInstanceId !== instance.id || child.value.userId !== instance.userId || child.value.repo?.toLowerCase() !== instance.repo.toLowerCase() || child.value.threadKey !== (row2.threadKey ?? instance.threadKey) || (row2.recovery !== void 0 ? child.value.idempotencyKey?.startsWith(`${instance.id}:${row2.unit}/recovery/`) !== true || !/\/findings(?:\/a[1-9][0-9]*)?$/.test(child.value.idempotencyKey ?? "") : !isStepAttempt(child.value.idempotencyKey, findingsPrefix)) || !fullHead(facts2.headSha) || child.value.headSha !== facts2.headSha || child.value.pushed?.some((push) => push.ref === row2.branch && push.sha === facts2.headSha) !== true || facts2.state !== "open" || facts2.sameRepoHead !== true || facts2.headBranchExists !== true || facts2.headRef !== row2.publication.headRef || facts2.baseRef !== row2.publication.baseRef || facts2.verifiedHead?.repo.toLowerCase() !== instance.repo.toLowerCase() || facts2.verifiedHead.ref !== row2.publication.publicationRef || facts2.verifiedHead.sha !== facts2.headSha || row2.publication.repo.toLowerCase() !== instance.repo.toLowerCase() || row2.publication.pr !== row2.pr?.number || row2.publication.baseRef !== instance.base || row2.publication.headRef !== row2.branch || row2.publication.publicationRef !== row2.branch || !samePublicationOwner(row2.publication.owner, owner))
|
|
73659
73678
|
throw new PublicationBindingRefusal("publication_facts_mismatch");
|
|
73660
73679
|
try {
|
|
73661
|
-
if (deps.runnerOwnership?.claim(instance.repo, row2.publication.pr, owner) !== true)
|
|
73680
|
+
if (row2.recovery !== void 0 && deps.runnerOwnership?.claim(instance.repo, row2.publication.pr, owner) !== true)
|
|
73681
|
+
throw new PublicationBindingRefusal("publication_ownership_changed");
|
|
73682
|
+
if (!samePublicationOwner(deps.runnerOwnership?.owner(instance.repo, row2.publication.pr), owner))
|
|
73662
73683
|
throw new PublicationBindingRefusal("publication_ownership_changed");
|
|
73663
73684
|
} catch (err2) {
|
|
73664
73685
|
if (err2 instanceof PublicationBindingRefusal) throw err2;
|
|
@@ -73668,7 +73689,7 @@ async function advanceRecoveryPublication(deps, instance, row2, facts2, runId3)
|
|
|
73668
73689
|
...row2,
|
|
73669
73690
|
lastPush: facts2.headSha,
|
|
73670
73691
|
publication: { ...row2.publication, expectedHeadSha: facts2.headSha },
|
|
73671
|
-
recovery: { ...row2.recovery, expectedHeadSha: facts2.headSha }
|
|
73692
|
+
...row2.recovery !== void 0 ? { recovery: { ...row2.recovery, previousBinding: void 0, expectedHeadSha: facts2.headSha } } : {}
|
|
73672
73693
|
};
|
|
73673
73694
|
let replaced;
|
|
73674
73695
|
try {
|
|
@@ -73682,6 +73703,16 @@ async function advanceRecoveryPublication(deps, instance, row2, facts2, runId3)
|
|
|
73682
73703
|
throw new PublicationBindingRefusal(
|
|
73683
73704
|
replaced?.reason === "stale" ? "publication_binding_stale" : "publication_store_unavailable"
|
|
73684
73705
|
);
|
|
73706
|
+
let stillOwned = false;
|
|
73707
|
+
try {
|
|
73708
|
+
stillOwned = samePublicationOwner(deps.runnerOwnership?.owner(instance.repo, row2.publication.pr), owner);
|
|
73709
|
+
} catch {
|
|
73710
|
+
}
|
|
73711
|
+
if (!stillOwned) {
|
|
73712
|
+
const restored = await deps.instances.compareAndReplaceUnit(updated, row2).catch(() => void 0);
|
|
73713
|
+
if (restored?.ok !== true) throw new PublicationBindingRefusal("publication_rollback_failed");
|
|
73714
|
+
throw new PublicationBindingRefusal("publication_ownership_changed");
|
|
73715
|
+
}
|
|
73685
73716
|
return updated;
|
|
73686
73717
|
}
|
|
73687
73718
|
async function bindOpenPullRequest(deps, instance, row2, pr, facts2) {
|
|
@@ -73773,11 +73804,11 @@ async function recoverOriginalUnit(body, deps, caller) {
|
|
|
73773
73804
|
if (row2.idle !== void 0 && row2.ending !== void 0)
|
|
73774
73805
|
return json3(409, { ok: false, error: "unit_lifecycle_ambiguous", at });
|
|
73775
73806
|
const originalOwner = { instanceId: instance.id, unit: row2.unit };
|
|
73776
|
-
|
|
73807
|
+
let publication = row2.publication;
|
|
73777
73808
|
const pr = row2.pr;
|
|
73778
73809
|
const base = instance.base;
|
|
73779
|
-
let expectedHead = row2.lastPush;
|
|
73780
|
-
if (pr === void 0 || base === void 0 || !fullHead(expectedHead) || publication
|
|
73810
|
+
let expectedHead = row2.lastPush ?? publication?.expectedHeadSha;
|
|
73811
|
+
if (pr === void 0 || base === void 0 || expectedHead !== void 0 && !fullHead(expectedHead) || publication !== void 0 && (publication.repo.toLowerCase() !== instance.repo.toLowerCase() || publication.pr !== pr.number || publication.headRef !== row2.branch || publication.baseRef !== base || publication.publicationRef !== row2.branch || !fullHead(publication.expectedHeadSha) || !samePublicationOwner(publication.owner, originalOwner)))
|
|
73781
73812
|
return json3(409, { ok: false, error: "recovery_binding_mismatch", at });
|
|
73782
73813
|
const existingClaim = row2.recovery;
|
|
73783
73814
|
let kind;
|
|
@@ -73808,10 +73839,14 @@ async function recoverOriginalUnit(body, deps, caller) {
|
|
|
73808
73839
|
findingsRunId,
|
|
73809
73840
|
findingsKey
|
|
73810
73841
|
} = existingClaim);
|
|
73811
|
-
if (existingClaim.expectedHeadSha !== expectedHead || publication.expectedHeadSha !== expectedHead)
|
|
73842
|
+
if (publication === void 0 || existingClaim.expectedHeadSha !== expectedHead || publication.expectedHeadSha !== expectedHead)
|
|
73812
73843
|
return json3(409, { ok: false, error: "recovery_binding_mismatch", at });
|
|
73813
73844
|
const { recovery: _recovery, ...withoutRecovery } = row2;
|
|
73814
73845
|
originalRow = { ...withoutRecovery, ending: existingClaim.previousEnding };
|
|
73846
|
+
if (existingClaim.previousBinding !== void 0) {
|
|
73847
|
+
const { publication: _publication, lastPush: _lastPush, ...prior } = originalRow;
|
|
73848
|
+
originalRow = { ...prior, ...existingClaim.previousBinding };
|
|
73849
|
+
}
|
|
73815
73850
|
} else {
|
|
73816
73851
|
if (row2.idle !== void 0 || row2.ending === void 0)
|
|
73817
73852
|
return json3(409, { ok: false, error: "unit_not_terminal", at });
|
|
@@ -73831,30 +73866,35 @@ async function recoverOriginalUnit(body, deps, caller) {
|
|
|
73831
73866
|
return json3(409, { ok: false, error: "recovery_ending_mismatch", at });
|
|
73832
73867
|
round2 = boundary.index;
|
|
73833
73868
|
const caps = instance.caps;
|
|
73834
|
-
|
|
73835
|
-
|
|
73836
|
-
if (
|
|
73869
|
+
const unknownBudget = (reason2) => json3(409, { ok: false, error: "recovery_budget_unknown", reason: reason2, at });
|
|
73870
|
+
if (caps === void 0) return unknownBudget("caps_missing");
|
|
73871
|
+
if (!Number.isSafeInteger(caps.maxRounds) || caps.maxRounds < 1 || !Number.isFinite(caps.maxMinutes) || caps.maxMinutes <= 0)
|
|
73872
|
+
return unknownBudget("caps_invalid");
|
|
73873
|
+
if (row2.startedAt === void 0) return unknownBudget("started_at_missing");
|
|
73874
|
+
if (!Number.isFinite(row2.startedAt)) return unknownBudget("started_at_invalid");
|
|
73875
|
+
if (!Number.isFinite(row2.ending.at)) return unknownBudget("ending_at_invalid");
|
|
73876
|
+
if (instance.grant?.costCapUsd !== void 0) return unknownBudget("cost_cap_spend_unknown");
|
|
73837
73877
|
if (round2 >= caps.maxRounds) return json3(409, { ok: false, error: "recovery_rounds_exhausted", at });
|
|
73838
73878
|
const latestSegmentIndex = row2.segments?.reduce((latest, candidate) => Math.max(latest, candidate.index), 1) ?? 1;
|
|
73839
73879
|
const latestSegments = row2.segments?.filter((candidate) => candidate.index === latestSegmentIndex) ?? [];
|
|
73840
|
-
if (latestSegments.length > 1) return
|
|
73880
|
+
if (latestSegments.length > 1) return unknownBudget("latest_segment_ambiguous");
|
|
73841
73881
|
const segment = latestSegments[0];
|
|
73842
73882
|
const leaseStartedAt = segment?.at ?? row2.startedAt;
|
|
73843
73883
|
const resumedLeases = Object.values(row2.wakes ?? {}).filter(
|
|
73844
73884
|
(answer) => answer.kind === "segment" && answer.index === latestSegmentIndex && answer.leaseMs !== void 0
|
|
73845
73885
|
);
|
|
73846
|
-
if (resumedLeases.length > 0) return
|
|
73886
|
+
if (resumedLeases.length > 0) return unknownBudget("resume_time_missing");
|
|
73847
73887
|
remainingMs = minutesToMs(caps.maxMinutes) - (at - leaseStartedAt);
|
|
73848
73888
|
const reviewThreadKey = row2.reviewThread?.threadKey ?? row2.threadKey ?? instance.threadKey;
|
|
73849
73889
|
const unitThreadKey = row2.threadKey ?? instance.threadKey;
|
|
73850
73890
|
const listing = await deps.runs.listRuns({
|
|
73851
|
-
status: "finished",
|
|
73891
|
+
status: publication === void 0 ? "all" : "finished",
|
|
73852
73892
|
visibleTo: EVERY_RUN,
|
|
73853
73893
|
threadKey: reviewThreadKey,
|
|
73854
73894
|
limit: RUN_LIST_MAX_LIMIT
|
|
73855
73895
|
});
|
|
73856
73896
|
const unitListing = unitThreadKey === reviewThreadKey ? listing : await deps.runs.listRuns({
|
|
73857
|
-
status: "finished",
|
|
73897
|
+
status: publication === void 0 ? "all" : "finished",
|
|
73858
73898
|
visibleTo: EVERY_RUN,
|
|
73859
73899
|
threadKey: unitThreadKey,
|
|
73860
73900
|
limit: RUN_LIST_MAX_LIMIT
|
|
@@ -73863,18 +73903,51 @@ async function recoverOriginalUnit(body, deps, caller) {
|
|
|
73863
73903
|
row2.unit,
|
|
73864
73904
|
latestSegmentIndex > 1 ? { segment: latestSegmentIndex, renewalsSpent: 0, spendUsd: null } : void 0
|
|
73865
73905
|
);
|
|
73866
|
-
|
|
73906
|
+
if (listing.storeUnavailable || unitListing.storeUnavailable || listing.nextBefore !== void 0 || unitListing.nextBefore !== void 0)
|
|
73907
|
+
return json3(409, { ok: false, error: "recovery_evidence_incomplete", at });
|
|
73867
73908
|
const reviewKeyPrefix = `${instance.id}:${segmentPrefix}/${round2}/review`;
|
|
73868
73909
|
const candidates2 = listing.runs.filter((run2) => {
|
|
73869
|
-
if (!run2.finished || run2.agent !== "review" || run2.parentInstanceId !== instance.id || run2.idempotencyKey
|
|
73910
|
+
if (!run2.finished || run2.agent !== "review" || run2.parentInstanceId !== instance.id || !isStepAttempt(run2.idempotencyKey, reviewKeyPrefix) || run2.userId !== instance.userId || run2.repo?.toLowerCase() !== instance.repo.toLowerCase() || run2.threadKey !== reviewThreadKey)
|
|
73870
73911
|
return false;
|
|
73871
|
-
if (run2.reviewHead !==
|
|
73912
|
+
if (!fullHead(run2.reviewHead) || publication !== void 0 && run2.reviewHead !== publication.expectedHeadSha)
|
|
73913
|
+
return false;
|
|
73914
|
+
const reviewedHead2 = run2.reviewHead;
|
|
73872
73915
|
if (kind === "review") return run2.verdict === void 0 && run2.reviewPost === void 0;
|
|
73873
|
-
return run2.verdict?.verdict === "request_changes" && run2.reviewHead ===
|
|
73916
|
+
return run2.verdict?.verdict === "request_changes" && run2.reviewHead === reviewedHead2 && run2.reviewPost?.posted === true && run2.reviewPost.head === reviewedHead2 && run2.reviewPost.verdict === "request_changes" && run2.reviewPost.target.repo.toLowerCase() === instance.repo.toLowerCase() && run2.reviewPost.target.number === pr.number;
|
|
73874
73917
|
});
|
|
73875
73918
|
if (candidates2.length !== 1) return json3(409, { ok: false, error: "recovery_review_evidence_ambiguous", at });
|
|
73876
|
-
|
|
73877
|
-
|
|
73919
|
+
const review = candidates2[0];
|
|
73920
|
+
const reviewedHead = review.reviewHead;
|
|
73921
|
+
if (publication === void 0) {
|
|
73922
|
+
if (listing.runs.filter(
|
|
73923
|
+
(run2) => run2.parentInstanceId === instance.id && isStepAttempt(run2.idempotencyKey, reviewKeyPrefix)
|
|
73924
|
+
).length !== 1)
|
|
73925
|
+
return json3(409, { ok: false, error: "recovery_review_evidence_ambiguous", at });
|
|
73926
|
+
if ([...listing.runs, ...unitListing.runs].some(
|
|
73927
|
+
(run2) => !run2.finished && (run2.agent === "coding" || run2.agent === "review")
|
|
73928
|
+
))
|
|
73929
|
+
return json3(409, { ok: false, error: "recovery_child_active", at });
|
|
73930
|
+
const codingKey = `${instance.id}:${segmentPrefix}/${round2 === 1 ? "0/coding" : `${round2 - 1}/findings`}`;
|
|
73931
|
+
const coding = unitListing.runs.filter(
|
|
73932
|
+
(run2) => run2.parentInstanceId === instance.id && isStepAttempt(run2.idempotencyKey, codingKey)
|
|
73933
|
+
);
|
|
73934
|
+
const child = coding.length === 1 ? coding[0] : void 0;
|
|
73935
|
+
if (child?.finished !== true || child.status !== "completed" || child.agent !== "coding" || child.userId !== instance.userId || child.repo?.toLowerCase() !== instance.repo.toLowerCase() || child.threadKey !== unitThreadKey || child.headSha !== reviewedHead || child.pushed?.some((push) => push.ref === row2.branch && push.sha === reviewedHead) !== true || child.pr !== void 0 && (child.pr.number !== pr.number || child.pr.head !== void 0 && child.pr.head !== row2.branch) || child.finishedAt === void 0 || child.startedAt < leaseStartedAt || child.finishedAt > review.startedAt || review.finishedAt === void 0 || review.finishedAt > row2.ending.at || expectedHead !== void 0 && expectedHead !== reviewedHead)
|
|
73936
|
+
return json3(409, { ok: false, error: "recovery_binding_evidence_ambiguous", at });
|
|
73937
|
+
publication = {
|
|
73938
|
+
repo: instance.repo,
|
|
73939
|
+
pr: pr.number,
|
|
73940
|
+
headRef: row2.branch,
|
|
73941
|
+
baseRef: base,
|
|
73942
|
+
expectedHeadSha: reviewedHead,
|
|
73943
|
+
publicationRef: row2.branch,
|
|
73944
|
+
owner: originalOwner
|
|
73945
|
+
};
|
|
73946
|
+
expectedHead = reviewedHead;
|
|
73947
|
+
claimRow = { ...row2, lastPush: reviewedHead, publication };
|
|
73948
|
+
}
|
|
73949
|
+
reviewRunId = review.id;
|
|
73950
|
+
reviewKey = review.idempotencyKey;
|
|
73878
73951
|
try {
|
|
73879
73952
|
facts2 = await deps.fetchPrFacts({ repo: instance.repo, number: pr.number });
|
|
73880
73953
|
} catch (err2) {
|
|
@@ -73888,7 +73961,7 @@ async function recoverOriginalUnit(body, deps, caller) {
|
|
|
73888
73961
|
const findingsPrefix = `${instance.id}:${segmentPrefix}/${boundary.index}/findings`;
|
|
73889
73962
|
const headMoved = facts2.headSha !== reviewedHead || expectedHead !== reviewedHead;
|
|
73890
73963
|
const completedFindings = unitListing.runs.filter(
|
|
73891
|
-
(run2) => boundary.outcome === "request_changes" && row2.ending?.cause === "step_threw" && row2.ending.step === findingsStep && row2.ending.round === boundary.index && (expectedHead === reviewedHead || expectedHead === facts2.headSha) && run2.finished && run2.status === "completed" && run2.agent === "coding" && run2.parentInstanceId === instance.id && run2.userId === instance.userId && run2.repo?.toLowerCase() === instance.repo.toLowerCase() && run2.threadKey === unitThreadKey && (run2.idempotencyKey
|
|
73964
|
+
(run2) => boundary.outcome === "request_changes" && row2.ending?.cause === "step_threw" && row2.ending.step === findingsStep && row2.ending.round === boundary.index && (expectedHead === reviewedHead || expectedHead === facts2.headSha) && run2.finished && run2.status === "completed" && run2.agent === "coding" && run2.parentInstanceId === instance.id && run2.userId === instance.userId && run2.repo?.toLowerCase() === instance.repo.toLowerCase() && run2.threadKey === unitThreadKey && isStepAttempt(run2.idempotencyKey, findingsPrefix) && run2.headSha === facts2.headSha && (!headMoved || run2.pushed?.some((push) => push.ref === row2.branch && push.sha === facts2.headSha) === true) && run2.startedAt >= (candidates2[0].finishedAt ?? candidates2[0].startedAt) && (run2.finishedAt === void 0 || run2.finishedAt <= row2.ending.at)
|
|
73892
73965
|
);
|
|
73893
73966
|
if (completedFindings.length > 1 || headMoved && completedFindings.length !== 1)
|
|
73894
73967
|
return json3(409, { ok: false, error: "recovery_head_moved", at });
|
|
@@ -73899,7 +73972,7 @@ async function recoverOriginalUnit(body, deps, caller) {
|
|
|
73899
73972
|
kind = "review";
|
|
73900
73973
|
round2 = boundary.index + 1;
|
|
73901
73974
|
claimRow = {
|
|
73902
|
-
...
|
|
73975
|
+
...claimRow,
|
|
73903
73976
|
lastPush: expectedHead,
|
|
73904
73977
|
publication: { ...publication, expectedHeadSha: expectedHead }
|
|
73905
73978
|
};
|
|
@@ -74006,6 +74079,12 @@ async function recoverOriginalUnit(body, deps, caller) {
|
|
|
74006
74079
|
...findingsKey !== void 0 ? { findingsKey } : {},
|
|
74007
74080
|
...findings !== void 0 ? { findings } : {},
|
|
74008
74081
|
previousEnding: row2.ending,
|
|
74082
|
+
...claimRow !== row2 ? {
|
|
74083
|
+
previousBinding: {
|
|
74084
|
+
...row2.publication !== void 0 ? { publication: row2.publication } : {},
|
|
74085
|
+
...row2.lastPush !== void 0 ? { lastPush: row2.lastPush } : {}
|
|
74086
|
+
}
|
|
74087
|
+
} : {},
|
|
74009
74088
|
workflowId,
|
|
74010
74089
|
deadlineAt,
|
|
74011
74090
|
reviewKey
|
|
@@ -74090,6 +74169,8 @@ async function prCheck(body, deps) {
|
|
|
74090
74169
|
};
|
|
74091
74170
|
try {
|
|
74092
74171
|
if (follow !== void 0) {
|
|
74172
|
+
if (unit.row?.pr !== void 0 && unit.row.pr.number !== follow)
|
|
74173
|
+
throw new PublicationBindingRefusal("publication_facts_mismatch");
|
|
74093
74174
|
const facts2 = await deps.fetchPrFacts({ repo: instance.repo, number: follow });
|
|
74094
74175
|
if (facts2 === void 0) throw new Error(`could not read ${instance.repo}#${follow}`);
|
|
74095
74176
|
const fallbackUrl = unit.row?.pr?.url ?? `https://github.com/${instance.repo}/pull/${follow}`;
|
|
@@ -74100,7 +74181,7 @@ async function prCheck(body, deps) {
|
|
|
74100
74181
|
await rememberTerminal(followedPr);
|
|
74101
74182
|
return json3(200, { ok: true, ...state2, at });
|
|
74102
74183
|
}
|
|
74103
|
-
const bindingRow = unit.row !== void 0 &&
|
|
74184
|
+
const bindingRow = unit.row !== void 0 && recover !== void 0 ? await advanceFindingsPublication(deps, instance, unit.row, facts2, recover.runId) : unit.row;
|
|
74104
74185
|
await bindOpenPullRequest(deps, instance, bindingRow, followedPr, facts2);
|
|
74105
74186
|
const prRef = { repo: instance.repo, number: follow };
|
|
74106
74187
|
const headSha = facts2.headSha;
|
|
@@ -74505,7 +74586,10 @@ async function round(body, deps) {
|
|
|
74505
74586
|
return json3(200, { ok: true, at: previous.at });
|
|
74506
74587
|
const updated = {
|
|
74507
74588
|
...row2,
|
|
74508
|
-
rounds: [...row2.rounds, note]
|
|
74589
|
+
rounds: [...row2.rounds, note],
|
|
74590
|
+
// The transport has started doing work. Admission rollback must no longer
|
|
74591
|
+
// remove publication authority already used by this recovery.
|
|
74592
|
+
...row2.recovery?.previousBinding !== void 0 ? { recovery: { ...row2.recovery, previousBinding: void 0 } } : {}
|
|
74509
74593
|
};
|
|
74510
74594
|
if (row2.recovery !== void 0) {
|
|
74511
74595
|
let replaced;
|
|
@@ -75531,7 +75615,7 @@ function createAdminCoordinatorHandler(deps) {
|
|
|
75531
75615
|
})();
|
|
75532
75616
|
};
|
|
75533
75617
|
}
|
|
75534
|
-
var COORDINATOR_ADMIN_PREFIX, MAX_SPAWN_PROMPT_CHARS, FINISHED_LOOKBACK_PAGES, MAX_ADMIN_BODY_BYTES, UNIT_ID, PRESETS_OF_KIND, invalid, json3, describe12, isGenerated2, EVERY_RUN, REVIEW_POSTED_CHECKS, REVIEW_POSTED_RECHECK_MS, githubSecondStart, CHILD_SUPERSESSION_REASONS, PublicationBindingRefusal, samePublicationOwner, fullHead, ROUND_OUTCOMES, ENDING_ICON, STEPS;
|
|
75618
|
+
var COORDINATOR_ADMIN_PREFIX, MAX_SPAWN_PROMPT_CHARS, FINISHED_LOOKBACK_PAGES, MAX_ADMIN_BODY_BYTES, UNIT_ID, PRESETS_OF_KIND, invalid, json3, describe12, isGenerated2, EVERY_RUN, REVIEW_POSTED_CHECKS, REVIEW_POSTED_RECHECK_MS, githubSecondStart, CHILD_SUPERSESSION_REASONS, PublicationBindingRefusal, samePublicationOwner, fullHead, isStepAttempt, ROUND_OUTCOMES, ENDING_ICON, STEPS;
|
|
75535
75619
|
var init_adminCoordinator = __esm({
|
|
75536
75620
|
"../../src/channels/adminCoordinator.ts"() {
|
|
75537
75621
|
"use strict";
|
|
@@ -75596,6 +75680,7 @@ var init_adminCoordinator = __esm({
|
|
|
75596
75680
|
};
|
|
75597
75681
|
samePublicationOwner = (left, right) => left?.instanceId === right.instanceId && left.unit === right.unit;
|
|
75598
75682
|
fullHead = (value) => typeof value === "string" && /^[0-9a-f]{40}$/i.test(value);
|
|
75683
|
+
isStepAttempt = (key, step) => key === step || key?.startsWith(`${step}/a`) === true && /^[1-9][0-9]*$/.test(key.slice(step.length + 2));
|
|
75599
75684
|
ROUND_OUTCOMES = [
|
|
75600
75685
|
"started",
|
|
75601
75686
|
"pr_opened",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coreplane/switchboard",
|
|
3
|
-
"version": "1.262.
|
|
3
|
+
"version": "1.262.2",
|
|
4
4
|
"description": "Mention it in Slack and an agent reviews the PR, ships the fix, or answers the question — on the model you choose, with its tools running where you decide.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://openswitchboard.dev",
|