@davidahmann/mill 0.1.4 → 0.2.1
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/README.md +141 -32
- package/dist/audit/repository.d.ts.map +1 -1
- package/dist/audit/repository.js +2 -0
- package/dist/audit/repository.js.map +1 -1
- package/dist/cli-program.d.ts.map +1 -1
- package/dist/cli-program.js +189 -0
- package/dist/cli-program.js.map +1 -1
- package/dist/contracts/schemas.d.ts +467 -2
- package/dist/contracts/schemas.d.ts.map +1 -1
- package/dist/contracts/schemas.js +211 -1
- package/dist/contracts/schemas.js.map +1 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/planning/closure.d.ts +24 -0
- package/dist/planning/closure.d.ts.map +1 -0
- package/dist/planning/closure.js +68 -0
- package/dist/planning/closure.js.map +1 -0
- package/dist/planning/outcomes.d.ts +6 -0
- package/dist/planning/outcomes.d.ts.map +1 -0
- package/dist/planning/outcomes.js +36 -0
- package/dist/planning/outcomes.js.map +1 -0
- package/dist/planning/tasks.d.ts +32 -0
- package/dist/planning/tasks.d.ts.map +1 -0
- package/dist/planning/tasks.js +305 -0
- package/dist/planning/tasks.js.map +1 -0
- package/dist/repository/intelligence.d.ts +78 -0
- package/dist/repository/intelligence.d.ts.map +1 -0
- package/dist/repository/intelligence.js +537 -0
- package/dist/repository/intelligence.js.map +1 -0
- package/dist/repository/native-adoption.d.ts +36 -0
- package/dist/repository/native-adoption.d.ts.map +1 -0
- package/dist/repository/native-adoption.js +146 -0
- package/dist/repository/native-adoption.js.map +1 -0
- package/dist/repository/scan.d.ts.map +1 -1
- package/dist/repository/scan.js +4 -0
- package/dist/repository/scan.js.map +1 -1
- package/dist/runtime/authority-plans.d.ts +43 -0
- package/dist/runtime/authority-plans.d.ts.map +1 -0
- package/dist/runtime/authority-plans.js +173 -0
- package/dist/runtime/authority-plans.js.map +1 -0
- package/dist/runtime/codex.d.ts.map +1 -1
- package/dist/runtime/codex.js +24 -5
- package/dist/runtime/codex.js.map +1 -1
- package/dist/runtime/context.d.ts.map +1 -1
- package/dist/runtime/context.js +36 -0
- package/dist/runtime/context.js.map +1 -1
- package/dist/runtime/delivery.d.ts +8 -1
- package/dist/runtime/delivery.d.ts.map +1 -1
- package/dist/runtime/delivery.js +187 -74
- package/dist/runtime/delivery.js.map +1 -1
- package/dist/runtime/effect-boundary.d.ts +38 -0
- package/dist/runtime/effect-boundary.d.ts.map +1 -0
- package/dist/runtime/effect-boundary.js +59 -0
- package/dist/runtime/effect-boundary.js.map +1 -0
- package/dist/runtime/github.d.ts +24 -0
- package/dist/runtime/github.d.ts.map +1 -1
- package/dist/runtime/github.js +117 -2
- package/dist/runtime/github.js.map +1 -1
- package/dist/runtime/inputs.js.map +1 -1
- package/dist/runtime/lifecycle.d.ts +5 -0
- package/dist/runtime/lifecycle.d.ts.map +1 -1
- package/dist/runtime/lifecycle.js +60 -16
- package/dist/runtime/lifecycle.js.map +1 -1
- package/dist/runtime/merge.d.ts +73 -0
- package/dist/runtime/merge.d.ts.map +1 -0
- package/dist/runtime/merge.js +299 -0
- package/dist/runtime/merge.js.map +1 -0
- package/dist/runtime/public-metadata.d.ts +3 -0
- package/dist/runtime/public-metadata.d.ts.map +1 -0
- package/dist/runtime/public-metadata.js +15 -0
- package/dist/runtime/public-metadata.js.map +1 -0
- package/dist/runtime/repair.d.ts +12 -0
- package/dist/runtime/repair.d.ts.map +1 -0
- package/dist/runtime/repair.js +34 -0
- package/dist/runtime/repair.js.map +1 -0
- package/dist/runtime/repository.d.ts +18 -0
- package/dist/runtime/repository.d.ts.map +1 -1
- package/dist/runtime/repository.js +114 -2
- package/dist/runtime/repository.js.map +1 -1
- package/dist/runtime/state.d.ts +42 -1
- package/dist/runtime/state.d.ts.map +1 -1
- package/dist/runtime/state.js +324 -11
- package/dist/runtime/state.js.map +1 -1
- package/dist/runtime/usage.d.ts +12 -0
- package/dist/runtime/usage.d.ts.map +1 -0
- package/dist/runtime/usage.js +45 -0
- package/dist/runtime/usage.js.map +1 -0
- package/dist/runtime/verifier.d.ts.map +1 -1
- package/dist/runtime/verifier.js +12 -0
- package/dist/runtime/verifier.js.map +1 -1
- package/dist/runtime/worker.d.ts +1 -0
- package/dist/runtime/worker.d.ts.map +1 -1
- package/dist/runtime/worker.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/workflows/founder.d.ts.map +1 -1
- package/dist/workflows/founder.js +5 -0
- package/dist/workflows/founder.js.map +1 -1
- package/node_modules/typescript/LICENSE.txt +55 -0
- package/node_modules/typescript/README.md +50 -0
- package/node_modules/typescript/SECURITY.md +39 -0
- package/node_modules/typescript/ThirdPartyNoticeText.txt +193 -0
- package/node_modules/typescript/bin/tsc +2 -0
- package/node_modules/typescript/bin/tsserver +2 -0
- package/node_modules/typescript/lib/_tsc.js +134465 -0
- package/node_modules/typescript/lib/_tsserver.js +659 -0
- package/node_modules/typescript/lib/_typingsInstaller.js +222 -0
- package/node_modules/typescript/lib/cs/diagnosticMessages.generated.json +2129 -0
- package/node_modules/typescript/lib/de/diagnosticMessages.generated.json +2125 -0
- package/node_modules/typescript/lib/es/diagnosticMessages.generated.json +2129 -0
- package/node_modules/typescript/lib/fr/diagnosticMessages.generated.json +2129 -0
- package/node_modules/typescript/lib/it/diagnosticMessages.generated.json +2125 -0
- package/node_modules/typescript/lib/ja/diagnosticMessages.generated.json +2129 -0
- package/node_modules/typescript/lib/ko/diagnosticMessages.generated.json +2129 -0
- package/node_modules/typescript/lib/lib.d.ts +20 -0
- package/node_modules/typescript/lib/lib.decorators.d.ts +382 -0
- package/node_modules/typescript/lib/lib.decorators.legacy.d.ts +20 -0
- package/node_modules/typescript/lib/lib.dom.asynciterable.d.ts +18 -0
- package/node_modules/typescript/lib/lib.dom.d.ts +45125 -0
- package/node_modules/typescript/lib/lib.dom.iterable.d.ts +18 -0
- package/node_modules/typescript/lib/lib.es2015.collection.d.ts +150 -0
- package/node_modules/typescript/lib/lib.es2015.core.d.ts +595 -0
- package/node_modules/typescript/lib/lib.es2015.d.ts +26 -0
- package/node_modules/typescript/lib/lib.es2015.generator.d.ts +75 -0
- package/node_modules/typescript/lib/lib.es2015.iterable.d.ts +603 -0
- package/node_modules/typescript/lib/lib.es2015.promise.d.ts +79 -0
- package/node_modules/typescript/lib/lib.es2015.proxy.d.ts +126 -0
- package/node_modules/typescript/lib/lib.es2015.reflect.d.ts +142 -0
- package/node_modules/typescript/lib/lib.es2015.symbol.d.ts +44 -0
- package/node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts +324 -0
- package/node_modules/typescript/lib/lib.es2016.array.include.d.ts +114 -0
- package/node_modules/typescript/lib/lib.es2016.d.ts +19 -0
- package/node_modules/typescript/lib/lib.es2016.full.d.ts +21 -0
- package/node_modules/typescript/lib/lib.es2016.intl.d.ts +29 -0
- package/node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts +19 -0
- package/node_modules/typescript/lib/lib.es2017.d.ts +24 -0
- package/node_modules/typescript/lib/lib.es2017.date.d.ts +29 -0
- package/node_modules/typescript/lib/lib.es2017.full.d.ts +21 -0
- package/node_modules/typescript/lib/lib.es2017.intl.d.ts +42 -0
- package/node_modules/typescript/lib/lib.es2017.object.d.ts +47 -0
- package/node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts +133 -0
- package/node_modules/typescript/lib/lib.es2017.string.d.ts +43 -0
- package/node_modules/typescript/lib/lib.es2017.typedarrays.d.ts +51 -0
- package/node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts +75 -0
- package/node_modules/typescript/lib/lib.es2018.asynciterable.d.ts +51 -0
- package/node_modules/typescript/lib/lib.es2018.d.ts +22 -0
- package/node_modules/typescript/lib/lib.es2018.full.d.ts +22 -0
- package/node_modules/typescript/lib/lib.es2018.intl.d.ts +81 -0
- package/node_modules/typescript/lib/lib.es2018.promise.d.ts +28 -0
- package/node_modules/typescript/lib/lib.es2018.regexp.d.ts +35 -0
- package/node_modules/typescript/lib/lib.es2019.array.d.ts +77 -0
- package/node_modules/typescript/lib/lib.es2019.d.ts +22 -0
- package/node_modules/typescript/lib/lib.es2019.full.d.ts +22 -0
- package/node_modules/typescript/lib/lib.es2019.intl.d.ts +21 -0
- package/node_modules/typescript/lib/lib.es2019.object.d.ts +31 -0
- package/node_modules/typescript/lib/lib.es2019.string.d.ts +35 -0
- package/node_modules/typescript/lib/lib.es2019.symbol.d.ts +22 -0
- package/node_modules/typescript/lib/lib.es2020.bigint.d.ts +763 -0
- package/node_modules/typescript/lib/lib.es2020.d.ts +25 -0
- package/node_modules/typescript/lib/lib.es2020.date.d.ts +40 -0
- package/node_modules/typescript/lib/lib.es2020.full.d.ts +22 -0
- package/node_modules/typescript/lib/lib.es2020.intl.d.ts +472 -0
- package/node_modules/typescript/lib/lib.es2020.number.d.ts +26 -0
- package/node_modules/typescript/lib/lib.es2020.promise.d.ts +45 -0
- package/node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts +97 -0
- package/node_modules/typescript/lib/lib.es2020.string.d.ts +42 -0
- package/node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts +39 -0
- package/node_modules/typescript/lib/lib.es2021.d.ts +21 -0
- package/node_modules/typescript/lib/lib.es2021.full.d.ts +22 -0
- package/node_modules/typescript/lib/lib.es2021.intl.d.ts +164 -0
- package/node_modules/typescript/lib/lib.es2021.promise.d.ts +46 -0
- package/node_modules/typescript/lib/lib.es2021.string.d.ts +31 -0
- package/node_modules/typescript/lib/lib.es2021.weakref.d.ts +76 -0
- package/node_modules/typescript/lib/lib.es2022.array.d.ts +119 -0
- package/node_modules/typescript/lib/lib.es2022.d.ts +23 -0
- package/node_modules/typescript/lib/lib.es2022.error.d.ts +73 -0
- package/node_modules/typescript/lib/lib.es2022.full.d.ts +22 -0
- package/node_modules/typescript/lib/lib.es2022.intl.d.ts +143 -0
- package/node_modules/typescript/lib/lib.es2022.object.d.ts +24 -0
- package/node_modules/typescript/lib/lib.es2022.regexp.d.ts +37 -0
- package/node_modules/typescript/lib/lib.es2022.string.d.ts +23 -0
- package/node_modules/typescript/lib/lib.es2023.array.d.ts +922 -0
- package/node_modules/typescript/lib/lib.es2023.collection.d.ts +19 -0
- package/node_modules/typescript/lib/lib.es2023.d.ts +20 -0
- package/node_modules/typescript/lib/lib.es2023.full.d.ts +22 -0
- package/node_modules/typescript/lib/lib.es2023.intl.d.ts +62 -0
- package/node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts +63 -0
- package/node_modules/typescript/lib/lib.es2024.collection.d.ts +27 -0
- package/node_modules/typescript/lib/lib.es2024.d.ts +24 -0
- package/node_modules/typescript/lib/lib.es2024.full.d.ts +22 -0
- package/node_modules/typescript/lib/lib.es2024.object.d.ts +27 -0
- package/node_modules/typescript/lib/lib.es2024.promise.d.ts +33 -0
- package/node_modules/typescript/lib/lib.es2024.regexp.d.ts +23 -0
- package/node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts +66 -0
- package/node_modules/typescript/lib/lib.es2024.string.d.ts +27 -0
- package/node_modules/typescript/lib/lib.es2025.collection.d.ts +94 -0
- package/node_modules/typescript/lib/lib.es2025.d.ts +23 -0
- package/node_modules/typescript/lib/lib.es2025.float16.d.ts +443 -0
- package/node_modules/typescript/lib/lib.es2025.full.d.ts +22 -0
- package/node_modules/typescript/lib/lib.es2025.intl.d.ts +200 -0
- package/node_modules/typescript/lib/lib.es2025.iterator.d.ts +146 -0
- package/node_modules/typescript/lib/lib.es2025.promise.d.ts +32 -0
- package/node_modules/typescript/lib/lib.es2025.regexp.d.ts +30 -0
- package/node_modules/typescript/lib/lib.es5.d.ts +4599 -0
- package/node_modules/typescript/lib/lib.es6.d.ts +21 -0
- package/node_modules/typescript/lib/lib.esnext.array.d.ts +33 -0
- package/node_modules/typescript/lib/lib.esnext.collection.d.ts +47 -0
- package/node_modules/typescript/lib/lib.esnext.d.ts +27 -0
- package/node_modules/typescript/lib/lib.esnext.date.d.ts +21 -0
- package/node_modules/typescript/lib/lib.esnext.decorators.d.ts +26 -0
- package/node_modules/typescript/lib/lib.esnext.disposable.d.ts +191 -0
- package/node_modules/typescript/lib/lib.esnext.error.d.ts +22 -0
- package/node_modules/typescript/lib/lib.esnext.full.d.ts +22 -0
- package/node_modules/typescript/lib/lib.esnext.intl.d.ts +107 -0
- package/node_modules/typescript/lib/lib.esnext.sharedmemory.d.ts +23 -0
- package/node_modules/typescript/lib/lib.esnext.temporal.d.ts +485 -0
- package/node_modules/typescript/lib/lib.esnext.typedarrays.d.ts +90 -0
- package/node_modules/typescript/lib/lib.scripthost.d.ts +320 -0
- package/node_modules/typescript/lib/lib.webworker.asynciterable.d.ts +18 -0
- package/node_modules/typescript/lib/lib.webworker.d.ts +15606 -0
- package/node_modules/typescript/lib/lib.webworker.importscripts.d.ts +21 -0
- package/node_modules/typescript/lib/lib.webworker.iterable.d.ts +18 -0
- package/node_modules/typescript/lib/pl/diagnosticMessages.generated.json +2129 -0
- package/node_modules/typescript/lib/pt-br/diagnosticMessages.generated.json +2129 -0
- package/node_modules/typescript/lib/ru/diagnosticMessages.generated.json +2125 -0
- package/node_modules/typescript/lib/tr/diagnosticMessages.generated.json +2129 -0
- package/node_modules/typescript/lib/tsc.js +8 -0
- package/node_modules/typescript/lib/tsserver.js +8 -0
- package/node_modules/typescript/lib/tsserverlibrary.d.ts +17 -0
- package/node_modules/typescript/lib/tsserverlibrary.js +21 -0
- package/node_modules/typescript/lib/typesMap.json +497 -0
- package/node_modules/typescript/lib/typescript.d.ts +11448 -0
- package/node_modules/typescript/lib/typescript.js +201039 -0
- package/node_modules/typescript/lib/typingsInstaller.js +8 -0
- package/node_modules/typescript/lib/watchGuard.js +53 -0
- package/node_modules/typescript/lib/zh-cn/diagnosticMessages.generated.json +2129 -0
- package/node_modules/typescript/lib/zh-tw/diagnosticMessages.generated.json +2125 -0
- package/node_modules/typescript/package.json +118 -0
- package/package.json +7 -4
- package/schemas/audit-report.schema.json +1 -0
- package/schemas/change-request.schema.json +189 -0
- package/schemas/context-manifest.schema.json +50 -0
- package/schemas/delivery-record.schema.json +109 -0
- package/schemas/mill-config.schema.json +50 -2
- package/schemas/repository-intelligence.schema.json +200 -0
- package/schemas/review-result.schema.json +21 -0
- package/schemas/task-packet.schema.json +12 -0
package/dist/runtime/delivery.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
+
import { assertEffectAllowsNewWork, assertNoUnresolvedEffect, reconcilableDraftEffect, maximumRemoteEffectAttempts, } from "./effect-boundary.js";
|
|
2
|
+
import { parse as parseYaml } from "yaml";
|
|
1
3
|
import { canonicalDigest } from "../contracts/canonical.js";
|
|
2
|
-
import { deliveryRecordSchema, reviewResultSchema, validationEvidenceSchema, } from "../contracts/schemas.js";
|
|
4
|
+
import { deliveryRecordSchema, millConfigSchema, reviewResultSchema, validationEvidenceSchema, } from "../contracts/schemas.js";
|
|
3
5
|
import { ExitCode, MillError, asMillError } from "../errors.js";
|
|
4
6
|
import { createGitHubAdapter, } from "./github.js";
|
|
5
7
|
import { loadRuntimeInputs } from "./inputs.js";
|
|
6
8
|
import { assertRunBindings } from "./lifecycle.js";
|
|
7
|
-
import {
|
|
9
|
+
import { publicPullRequestTitle } from "./public-metadata.js";
|
|
10
|
+
import { commonGitDirectory, captureReviewScope, readCommittedFile, repositoryRemoteUrl, } from "./repository.js";
|
|
8
11
|
import { acquireWriterLease, publicRunRecord, StateStore, } from "./state.js";
|
|
9
|
-
const maximumRemoteEffectAttempts = 2;
|
|
10
12
|
function operationDeadline(config) {
|
|
11
13
|
return Date.now() + config.pollTimeoutSeconds * 1000;
|
|
12
14
|
}
|
|
@@ -69,35 +71,27 @@ function setRunBlocker(store, run, code, eventType, details = {}) {
|
|
|
69
71
|
: store.transition(run.id, "blocked", eventType, { code, ...details });
|
|
70
72
|
}
|
|
71
73
|
function reconcileAbsentEffect(store, run, delivery, effectValue) {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (effectValue.attemptCount >= maximumRemoteEffectAttempts) {
|
|
76
|
-
const blocked = persistDelivery(store, run.id, {
|
|
77
|
-
...upsertEffect(delivery, {
|
|
78
|
-
...effectValue,
|
|
79
|
-
status: "blocked",
|
|
80
|
-
errorCode: "REMOTE_EFFECT_RETRY_EXHAUSTED",
|
|
81
|
-
updatedAt: new Date().toISOString(),
|
|
82
|
-
}),
|
|
83
|
-
state: "blocked",
|
|
84
|
-
lastErrorCode: "REMOTE_EFFECT_RETRY_EXHAUSTED",
|
|
85
|
-
}, "delivery.retry_exhausted", { effectId: effectValue.id });
|
|
86
|
-
const blockedRun = setRunBlocker(store, run, "REMOTE_EFFECT_RETRY_EXHAUSTED", "delivery.blocked");
|
|
87
|
-
return { run: publicRunRecord(blockedRun), delivery: blocked };
|
|
88
|
-
}
|
|
89
|
-
const retryable = persistDelivery(store, run.id, {
|
|
74
|
+
const exhausted = effectValue.attemptCount >= maximumRemoteEffectAttempts;
|
|
75
|
+
const errorCode = exhausted ? "REMOTE_EFFECT_RETRY_EXHAUSTED" : null;
|
|
76
|
+
return completeDraftReconciliation(store, run, {
|
|
90
77
|
...upsertEffect(delivery, {
|
|
91
78
|
...effectValue,
|
|
92
|
-
status: "retryable_absent",
|
|
93
|
-
errorCode
|
|
79
|
+
status: exhausted ? "blocked" : "retryable_absent",
|
|
80
|
+
errorCode,
|
|
94
81
|
updatedAt: new Date().toISOString(),
|
|
95
82
|
}),
|
|
96
|
-
state: "proposing",
|
|
97
|
-
lastErrorCode:
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
|
|
83
|
+
state: exhausted ? "blocked" : "proposing",
|
|
84
|
+
lastErrorCode: errorCode,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
function completeDraftReconciliation(store, run, delivery) {
|
|
88
|
+
if (run.deliveryJson === undefined)
|
|
89
|
+
throw new MillError("DELIVERY_PLAN_MISSING", "The journal disappeared during readback.", ExitCode.data);
|
|
90
|
+
const settled = store.settleDraftEffect(run.id, run.deliveryJson, JSON.stringify(deliveryRecordSchema.parse(delivery)));
|
|
91
|
+
const journal = storedDelivery(settled);
|
|
92
|
+
if (journal.state === "cancelled")
|
|
93
|
+
throw new MillError("OPERATOR_CANCELLED", "The effect was reconciled; cancellation prevents further mutation.", ExitCode.temporary);
|
|
94
|
+
return { run: publicRunRecord(settled), delivery: journal };
|
|
101
95
|
}
|
|
102
96
|
async function assertReviewedCandidate(root, run, inputs) {
|
|
103
97
|
if (run.status !== "reviewed" && run.status !== "proposing") {
|
|
@@ -122,8 +116,31 @@ async function assertReviewedCandidate(root, run, inputs) {
|
|
|
122
116
|
throw new MillError("LOCAL_EVIDENCE_STALE", "Validation and local review must pass on the exact candidate head.", ExitCode.configuration);
|
|
123
117
|
}
|
|
124
118
|
const candidate = await assertRunBindings(root, run, inputs);
|
|
119
|
+
const expectedScope = await captureReviewScope(candidate.worktree, review.scope?.baseCommit ??
|
|
120
|
+
(inputs.config.propose === undefined
|
|
121
|
+
? run.baseCommit
|
|
122
|
+
: `refs/heads/${inputs.config.propose.baseBranch}`), candidate.commit);
|
|
123
|
+
if (review.scope?.digest !== expectedScope.digest) {
|
|
124
|
+
throw new MillError("REVIEW_SCOPE_STALE", "Delivery requires a fresh complete base-to-candidate review, including preparatory commits.", ExitCode.configuration);
|
|
125
|
+
}
|
|
125
126
|
return { commit: candidate.commit, tree: candidate.tree };
|
|
126
127
|
}
|
|
128
|
+
async function assertProviderReviewScope(run, config, adapter, deadlineMs) {
|
|
129
|
+
const base = await adapter.readBranch({
|
|
130
|
+
config,
|
|
131
|
+
branch: config.baseBranch,
|
|
132
|
+
deadlineMs,
|
|
133
|
+
});
|
|
134
|
+
if (base === null ||
|
|
135
|
+
run.candidateCommit === undefined ||
|
|
136
|
+
run.worktreePath === undefined)
|
|
137
|
+
throw new MillError("REVIEW_BASE_UNAVAILABLE", "The authoritative PR base and local candidate must be available.", ExitCode.configuration);
|
|
138
|
+
const review = reviewResultSchema.parse(JSON.parse(run.reviewJson ?? "null"));
|
|
139
|
+
const scope = await captureReviewScope(run.worktreePath, base, run.candidateCommit);
|
|
140
|
+
if (review.scope?.digest !== scope.digest)
|
|
141
|
+
throw new MillError("REVIEW_SCOPE_STALE", "The reviewed diff differs from GitHub's actual PR base. Before any remote attempt, use attended review --refresh --base with the exact locally available provider commit, then plan delivery again. Do not change frozen refs.", ExitCode.configuration);
|
|
142
|
+
// Reading the provider SHA never fetches or changes local refs implicitly.
|
|
143
|
+
}
|
|
127
144
|
function expectedRemoteUrls(config, cloneUrl) {
|
|
128
145
|
return new Set([
|
|
129
146
|
cloneUrl,
|
|
@@ -164,6 +181,17 @@ function target(config, binding) {
|
|
|
164
181
|
actorId: binding.actorId,
|
|
165
182
|
};
|
|
166
183
|
}
|
|
184
|
+
function postMergeRequiredChecks(config) {
|
|
185
|
+
return config.postMergeRequiredChecks ?? config.requiredChecks;
|
|
186
|
+
}
|
|
187
|
+
function postMergePolicySource(config) {
|
|
188
|
+
return config.postMergeRequiredChecks === undefined
|
|
189
|
+
? "implicit_default"
|
|
190
|
+
: "configured";
|
|
191
|
+
}
|
|
192
|
+
function isCheckSubset(candidate, required) {
|
|
193
|
+
return candidate.every((check) => required.includes(check));
|
|
194
|
+
}
|
|
167
195
|
function proposalDigest(input) {
|
|
168
196
|
return canonicalDigest({
|
|
169
197
|
schemaVersion: "1",
|
|
@@ -176,6 +204,10 @@ function proposalDigest(input) {
|
|
|
176
204
|
branchName: input.branchName,
|
|
177
205
|
approvalExpiresAt: input.approvalExpiresAt,
|
|
178
206
|
requiredChecks: input.config.requiredChecks,
|
|
207
|
+
...(input.config.checkProducers === undefined
|
|
208
|
+
? {}
|
|
209
|
+
: { checkProducers: input.config.checkProducers }),
|
|
210
|
+
postMergeRequiredChecks: postMergeRequiredChecks(input.config),
|
|
179
211
|
reviewPolicy: input.config.reviewPolicy,
|
|
180
212
|
allowedMergeMethods: input.config.allowedMergeMethods,
|
|
181
213
|
allowedMergerLogins: input.config.allowedMergerLogins,
|
|
@@ -240,10 +272,54 @@ function assertPushBoundaryPullRequest(pullRequest, delivery, expectedOldCommit)
|
|
|
240
272
|
throw new MillError("PULL_REQUEST_IDENTITY_MISMATCH", "The recorded pull request is no longer the expected open draft at the observed branch head.", ExitCode.configuration);
|
|
241
273
|
}
|
|
242
274
|
}
|
|
243
|
-
function
|
|
275
|
+
async function candidateUsedImplicitPostMergePolicy(root, delivery) {
|
|
276
|
+
let source;
|
|
277
|
+
try {
|
|
278
|
+
source = await readCommittedFile(root, delivery.candidateCommit, "mill.yaml");
|
|
279
|
+
}
|
|
280
|
+
catch (error) {
|
|
281
|
+
throw new MillError("LEGACY_POST_MERGE_POLICY_SOURCE_UNAVAILABLE", "The reviewed candidate's mill.yaml cannot be read to prove an implicit post-merge policy.", ExitCode.configuration, { cause: String(error) });
|
|
282
|
+
}
|
|
283
|
+
let raw;
|
|
284
|
+
try {
|
|
285
|
+
raw = parseYaml(source);
|
|
286
|
+
}
|
|
287
|
+
catch (error) {
|
|
288
|
+
throw new MillError("LEGACY_POST_MERGE_POLICY_SOURCE_INVALID", "The reviewed candidate's mill.yaml is not valid YAML.", ExitCode.configuration, { cause: String(error) });
|
|
289
|
+
}
|
|
290
|
+
const parsed = millConfigSchema.safeParse(raw);
|
|
291
|
+
if (!parsed.success) {
|
|
292
|
+
throw new MillError("LEGACY_POST_MERGE_POLICY_SOURCE_INVALID", "The reviewed candidate's mill.yaml does not satisfy the Mill configuration contract.", ExitCode.configuration);
|
|
293
|
+
}
|
|
294
|
+
return parsed.data.propose?.postMergeRequiredChecks === undefined;
|
|
295
|
+
}
|
|
296
|
+
async function assertDeliveryContinuity(input) {
|
|
244
297
|
const { run, inputs, config, delivery, binding } = input;
|
|
298
|
+
const configuredPostMergeChecks = postMergeRequiredChecks(config);
|
|
299
|
+
const configuredLegacyPolicy = input.allowLegacyPostMergePolicy === true &&
|
|
300
|
+
delivery.postMergePolicySource === undefined &&
|
|
301
|
+
delivery.postMergeRequiredChecks !== undefined &&
|
|
302
|
+
delivery.legacyPostMergePolicyConfigDigest === undefined &&
|
|
303
|
+
config.postMergeRequiredChecks !== undefined &&
|
|
304
|
+
isCheckSubset(config.postMergeRequiredChecks, delivery.requiredChecks) &&
|
|
305
|
+
JSON.stringify(delivery.postMergeRequiredChecks) ===
|
|
306
|
+
JSON.stringify(delivery.requiredChecks) &&
|
|
307
|
+
(await candidateUsedImplicitPostMergePolicy(input.root, delivery));
|
|
308
|
+
const bindLegacyPostMergePolicy = input.allowLegacyPostMergePolicy === true &&
|
|
309
|
+
(delivery.postMergeRequiredChecks === undefined ||
|
|
310
|
+
configuredLegacyPolicy) &&
|
|
311
|
+
delivery.legacyPostMergePolicyConfigDigest === undefined &&
|
|
312
|
+
config.postMergeRequiredChecks !== undefined &&
|
|
313
|
+
isCheckSubset(config.postMergeRequiredChecks, delivery.requiredChecks);
|
|
314
|
+
const hasBoundLegacyPostMergePolicy = input.allowLegacyPostMergePolicy === true &&
|
|
315
|
+
delivery.postMergeRequiredChecks !== undefined &&
|
|
316
|
+
delivery.postMergePolicySource === "legacy_migrated" &&
|
|
317
|
+
delivery.legacyPostMergePolicyConfigDigest === inputs.configDigest;
|
|
318
|
+
const configDigestMatches = bindLegacyPostMergePolicy ||
|
|
319
|
+
hasBoundLegacyPostMergePolicy ||
|
|
320
|
+
run.configDigest === inputs.configDigest;
|
|
245
321
|
if (run.taskDigest !== inputs.taskDigest ||
|
|
246
|
-
|
|
322
|
+
!configDigestMatches ||
|
|
247
323
|
delivery.runId !== run.id ||
|
|
248
324
|
delivery.candidateCommit !== run.candidateCommit ||
|
|
249
325
|
delivery.candidateTree !== run.candidateTree ||
|
|
@@ -258,6 +334,15 @@ function assertDeliveryContinuity(input) {
|
|
|
258
334
|
delivery.target.cloneUrl !== binding.cloneUrl ||
|
|
259
335
|
JSON.stringify(delivery.requiredChecks) !==
|
|
260
336
|
JSON.stringify(config.requiredChecks) ||
|
|
337
|
+
canonicalDigest(delivery.checkProducers ?? {}) !==
|
|
338
|
+
canonicalDigest(config.checkProducers ?? {}) ||
|
|
339
|
+
(delivery.postMergeRequiredChecks !== undefined &&
|
|
340
|
+
!bindLegacyPostMergePolicy &&
|
|
341
|
+
!hasBoundLegacyPostMergePolicy &&
|
|
342
|
+
JSON.stringify(delivery.postMergeRequiredChecks) !==
|
|
343
|
+
JSON.stringify(configuredPostMergeChecks)) ||
|
|
344
|
+
(delivery.legacyPostMergePolicyConfigDigest !== undefined &&
|
|
345
|
+
!hasBoundLegacyPostMergePolicy) ||
|
|
261
346
|
JSON.stringify(delivery.reviewPolicy) !==
|
|
262
347
|
JSON.stringify(config.reviewPolicy) ||
|
|
263
348
|
JSON.stringify(delivery.allowedMergerLogins) !==
|
|
@@ -266,13 +351,21 @@ function assertDeliveryContinuity(input) {
|
|
|
266
351
|
JSON.stringify(config.allowedMergeMethods)) {
|
|
267
352
|
throw new MillError("DELIVERY_AUTHORITY_DRIFT", "The task, configuration, candidate, actor, or repository changed after delivery approval.", ExitCode.configuration);
|
|
268
353
|
}
|
|
354
|
+
return { bindLegacyPostMergePolicy };
|
|
269
355
|
}
|
|
270
|
-
function checkDecision(required, checks) {
|
|
356
|
+
export function checkDecision(required, checks, producers, phase = "pull_request", headSha) {
|
|
271
357
|
const missing = [];
|
|
272
358
|
const failed = [];
|
|
273
359
|
let pending = false;
|
|
274
360
|
for (const name of required) {
|
|
275
|
-
const
|
|
361
|
+
const producer = producers?.[name];
|
|
362
|
+
const matching = checks.filter((check) => check.name === name &&
|
|
363
|
+
(producers === undefined ||
|
|
364
|
+
(producer !== undefined &&
|
|
365
|
+
check.appId === producer.appId &&
|
|
366
|
+
check.workflowPath === producer.workflowPath &&
|
|
367
|
+
check.event === phase &&
|
|
368
|
+
check.headSha === headSha)));
|
|
276
369
|
if (matching.length === 0) {
|
|
277
370
|
missing.push(name);
|
|
278
371
|
pending = true;
|
|
@@ -292,14 +385,14 @@ function checkDecision(required, checks) {
|
|
|
292
385
|
failed,
|
|
293
386
|
};
|
|
294
387
|
}
|
|
295
|
-
function actionableFeedback(observation, reviewPolicy, candidateCommit) {
|
|
388
|
+
export function actionableFeedback(observation, reviewPolicy, candidateCommit) {
|
|
296
389
|
if (reviewPolicy.mode !== "github_required")
|
|
297
390
|
return [];
|
|
298
391
|
return observation.feedback.filter((item) => item.commitId === candidateCommit &&
|
|
299
392
|
reviewPolicy.requiredReviewerLogins.includes(item.actorLogin) &&
|
|
300
393
|
item.priority !== "P3");
|
|
301
394
|
}
|
|
302
|
-
function reviewsPassed(observation, reviewPolicy, candidateCommit) {
|
|
395
|
+
export function reviewsPassed(observation, reviewPolicy, candidateCommit) {
|
|
303
396
|
if (reviewPolicy.mode === "local_only")
|
|
304
397
|
return true;
|
|
305
398
|
return reviewPolicy.requiredReviewerLogins.every((login) => {
|
|
@@ -353,6 +446,7 @@ export async function planDraftPr(input) {
|
|
|
353
446
|
try {
|
|
354
447
|
lease = await acquireWriterLease(store);
|
|
355
448
|
const run = store.getRun(input.runId);
|
|
449
|
+
assertEffectAllowsNewWork(run);
|
|
356
450
|
const candidate = await assertReviewedCandidate(input.root, run, inputs);
|
|
357
451
|
const adapter = input.adapter ?? createGitHubAdapter(input.root);
|
|
358
452
|
const binding = await adapter.inspect({
|
|
@@ -361,6 +455,7 @@ export async function planDraftPr(input) {
|
|
|
361
455
|
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
362
456
|
});
|
|
363
457
|
await assertBinding(input.root, config, binding);
|
|
458
|
+
await assertProviderReviewScope(run, config, adapter, operationDeadline(config));
|
|
364
459
|
const plannedBranch = branchName(config, run);
|
|
365
460
|
const approvalExpiresAt = new Date(Date.now() + config.approvalTtlSeconds * 1000).toISOString();
|
|
366
461
|
const plannedTarget = target(config, binding);
|
|
@@ -393,6 +488,11 @@ export async function planDraftPr(input) {
|
|
|
393
488
|
candidateCommit: candidate.commit,
|
|
394
489
|
candidateTree: candidate.tree,
|
|
395
490
|
requiredChecks: config.requiredChecks,
|
|
491
|
+
...(config.checkProducers === undefined
|
|
492
|
+
? {}
|
|
493
|
+
: { checkProducers: config.checkProducers }),
|
|
494
|
+
postMergeRequiredChecks: postMergeRequiredChecks(config),
|
|
495
|
+
postMergePolicySource: postMergePolicySource(config),
|
|
396
496
|
reviewPolicy: config.reviewPolicy,
|
|
397
497
|
allowedMergerLogins: config.allowedMergerLogins,
|
|
398
498
|
allowedMergeMethods: config.allowedMergeMethods,
|
|
@@ -452,6 +552,7 @@ export async function openDraftPr(input) {
|
|
|
452
552
|
if (run.status !== "proposing") {
|
|
453
553
|
throw new MillError("DELIVERY_NOT_APPLICABLE", "Draft-PR open requires an approved plan with no unknown external effect.", ExitCode.configuration);
|
|
454
554
|
}
|
|
555
|
+
assertEffectAllowsNewWork(run);
|
|
455
556
|
const candidate = await assertReviewedCandidate(input.root, run, inputs);
|
|
456
557
|
let delivery = storedDelivery(run);
|
|
457
558
|
assertRemoteMutationNotCancelled(store, run.id, delivery);
|
|
@@ -469,6 +570,7 @@ export async function openDraftPr(input) {
|
|
|
469
570
|
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
470
571
|
});
|
|
471
572
|
await assertBinding(input.root, config, binding);
|
|
573
|
+
await assertProviderReviewScope(run, config, adapter, deadlineMs);
|
|
472
574
|
const liveDigest = proposalDigest({
|
|
473
575
|
run,
|
|
474
576
|
candidate,
|
|
@@ -495,10 +597,6 @@ export async function openDraftPr(input) {
|
|
|
495
597
|
readback.branchSha !== delivery.remoteHeadCommit) {
|
|
496
598
|
throw new MillError("REMOTE_BRANCH_CONFLICT", "The Mill delivery branch moved outside the exact expected-head precondition.", ExitCode.configuration);
|
|
497
599
|
}
|
|
498
|
-
if (push !== undefined &&
|
|
499
|
-
(push.status === "call_started" || push.status === "effect_unknown")) {
|
|
500
|
-
markUnknown(store, run, delivery, push, "GITHUB_PUSH_OUTCOME_UNKNOWN");
|
|
501
|
-
}
|
|
502
600
|
if (push?.status === "verified") {
|
|
503
601
|
throw new MillError("REMOTE_BRANCH_CONFLICT", "A previously verified Mill branch no longer has its bound candidate head.", ExitCode.configuration);
|
|
504
602
|
}
|
|
@@ -529,6 +627,7 @@ export async function openDraftPr(input) {
|
|
|
529
627
|
throw new MillError("REMOTE_BRANCH_CONFLICT", "The Mill delivery branch changed after push intent and before the mutation boundary.", ExitCode.configuration);
|
|
530
628
|
}
|
|
531
629
|
assertPushBoundaryPullRequest(readback.pullRequest, delivery, push.expectedOldCommit);
|
|
630
|
+
await assertProviderReviewScope(run, config, adapter, deadlineMs);
|
|
532
631
|
push = {
|
|
533
632
|
...push,
|
|
534
633
|
status: "call_started",
|
|
@@ -618,11 +717,6 @@ export async function openDraftPr(input) {
|
|
|
618
717
|
throw new MillError("PULL_REQUEST_DISAPPEARED", "The recorded pull request cannot be found by immutable delivery identity.", ExitCode.configuration);
|
|
619
718
|
}
|
|
620
719
|
if (pullRequest === null) {
|
|
621
|
-
if (prEffect !== undefined &&
|
|
622
|
-
(prEffect.status === "call_started" ||
|
|
623
|
-
prEffect.status === "effect_unknown")) {
|
|
624
|
-
markUnknown(store, run, delivery, prEffect, "GITHUB_PR_OUTCOME_UNKNOWN");
|
|
625
|
-
}
|
|
626
720
|
if (prEffect?.status === "verified" || delivery.pullRequest !== null) {
|
|
627
721
|
throw new MillError("PULL_REQUEST_DISAPPEARED", "A previously verified pull request cannot be found by immutable delivery identity.", ExitCode.configuration);
|
|
628
722
|
}
|
|
@@ -642,6 +736,7 @@ export async function openDraftPr(input) {
|
|
|
642
736
|
updatedAt: new Date().toISOString(),
|
|
643
737
|
};
|
|
644
738
|
delivery = persistDelivery(store, run.id, upsertEffect(delivery, prEffect), "delivery.pull_request_intent", { effectId: prEffect.id });
|
|
739
|
+
await assertProviderReviewScope(run, config, adapter, deadlineMs);
|
|
645
740
|
prEffect = {
|
|
646
741
|
...prEffect,
|
|
647
742
|
status: "call_started",
|
|
@@ -654,7 +749,7 @@ export async function openDraftPr(input) {
|
|
|
654
749
|
pullRequest = await adapter.createDraftPullRequest({
|
|
655
750
|
config,
|
|
656
751
|
branch: delivery.branchName,
|
|
657
|
-
title: inputs.task.commit.message
|
|
752
|
+
title: publicPullRequestTitle(inputs.task.commit.message),
|
|
658
753
|
body: `${deliveryMarker(delivery.deliveryKey)}\n\nGenerated by Mill from an attended, locally validated and reviewed run. The current candidate identity is the pull-request head; configured human merge authority remains external.`,
|
|
659
754
|
deadlineMs,
|
|
660
755
|
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
@@ -738,11 +833,9 @@ export async function reconcileDraftPr(input) {
|
|
|
738
833
|
let lease;
|
|
739
834
|
try {
|
|
740
835
|
lease = await acquireWriterLease(store);
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
}
|
|
745
|
-
let delivery = storedDelivery(run);
|
|
836
|
+
const run = store.getRun(input.runId);
|
|
837
|
+
const unknownEffect = reconcilableDraftEffect(run);
|
|
838
|
+
const delivery = storedDelivery(run);
|
|
746
839
|
const adapter = input.adapter ?? createGitHubAdapter(input.root);
|
|
747
840
|
const deadlineMs = operationDeadline(config);
|
|
748
841
|
const binding = await adapter.inspect({
|
|
@@ -751,7 +844,14 @@ export async function reconcileDraftPr(input) {
|
|
|
751
844
|
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
752
845
|
});
|
|
753
846
|
await assertBinding(input.root, config, binding);
|
|
754
|
-
assertDeliveryContinuity({
|
|
847
|
+
await assertDeliveryContinuity({
|
|
848
|
+
root: input.root,
|
|
849
|
+
run,
|
|
850
|
+
inputs,
|
|
851
|
+
config,
|
|
852
|
+
delivery,
|
|
853
|
+
binding,
|
|
854
|
+
});
|
|
755
855
|
const readback = await reconcileReadback({
|
|
756
856
|
adapter,
|
|
757
857
|
config,
|
|
@@ -759,14 +859,6 @@ export async function reconcileDraftPr(input) {
|
|
|
759
859
|
deadlineMs,
|
|
760
860
|
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
761
861
|
});
|
|
762
|
-
const unknownEffects = delivery.effects.filter((item) => item.status === "effect_unknown");
|
|
763
|
-
if (unknownEffects.length !== 1) {
|
|
764
|
-
throw new MillError("GITHUB_RECONCILIATION_STATE_INVALID", "Exactly one external effect must be unknown before reconciliation.", ExitCode.data);
|
|
765
|
-
}
|
|
766
|
-
const unknownEffect = unknownEffects[0];
|
|
767
|
-
if (unknownEffect === undefined) {
|
|
768
|
-
throw new MillError("GITHUB_RECONCILIATION_STATE_INVALID", "The unknown external effect could not be identified.", ExitCode.data);
|
|
769
|
-
}
|
|
770
862
|
if (unknownEffect.kind === "push" &&
|
|
771
863
|
(readback.branchSha === unknownEffect.expectedOldCommit ||
|
|
772
864
|
readback.branchSha === delivery.candidateCommit)) {
|
|
@@ -787,7 +879,7 @@ export async function reconcileDraftPr(input) {
|
|
|
787
879
|
if (unknownEffect.kind === "push" &&
|
|
788
880
|
readback.branchSha === delivery.candidateCommit &&
|
|
789
881
|
readback.pullRequest === null) {
|
|
790
|
-
|
|
882
|
+
return completeDraftReconciliation(store, run, {
|
|
791
883
|
...upsertEffect(delivery, {
|
|
792
884
|
...unknownEffect,
|
|
793
885
|
status: "verified",
|
|
@@ -797,10 +889,7 @@ export async function reconcileDraftPr(input) {
|
|
|
797
889
|
state: "proposing",
|
|
798
890
|
remoteHeadCommit: readback.branchSha,
|
|
799
891
|
lastErrorCode: null,
|
|
800
|
-
}
|
|
801
|
-
assertRemoteMutationNotCancelled(store, run.id, delivery);
|
|
802
|
-
run = store.transition(run.id, "proposing", "delivery.reconciled");
|
|
803
|
-
return { run: publicRunRecord(run), delivery };
|
|
892
|
+
});
|
|
804
893
|
}
|
|
805
894
|
if (readback.branchSha !== delivery.candidateCommit) {
|
|
806
895
|
throw new MillError("REMOTE_BRANCH_CONFLICT", "Authoritative readback found a branch head outside the reconciled effect precondition.", ExitCode.configuration);
|
|
@@ -809,7 +898,7 @@ export async function reconcileDraftPr(input) {
|
|
|
809
898
|
throw new MillError("GITHUB_RECONCILIATION_STATE_INVALID", "Reconciliation could not classify the pull-request effect.", ExitCode.data);
|
|
810
899
|
}
|
|
811
900
|
assertExactPullRequest(readback.pullRequest, delivery, true);
|
|
812
|
-
|
|
901
|
+
return completeDraftReconciliation(store, run, {
|
|
813
902
|
...delivery,
|
|
814
903
|
state: "awaiting_ci",
|
|
815
904
|
remoteHeadCommit: readback.branchSha,
|
|
@@ -827,10 +916,7 @@ export async function reconcileDraftPr(input) {
|
|
|
827
916
|
}
|
|
828
917
|
: item),
|
|
829
918
|
lastErrorCode: null,
|
|
830
|
-
}
|
|
831
|
-
assertRemoteMutationNotCancelled(store, run.id, delivery);
|
|
832
|
-
run = store.transition(run.id, "awaiting_ci", "delivery.awaiting_ci");
|
|
833
|
-
return { run: publicRunRecord(run), delivery };
|
|
919
|
+
});
|
|
834
920
|
}
|
|
835
921
|
finally {
|
|
836
922
|
try {
|
|
@@ -868,10 +954,12 @@ export async function observeDraftPr(input) {
|
|
|
868
954
|
["REMOTE_CHECKS_FAILED", "REMOTE_REVIEW_FINDINGS"].includes(run.blockCode ?? ""))) {
|
|
869
955
|
throw new MillError("DELIVERY_NOT_OBSERVABLE", "Run has no draft pull request awaiting policy observation.", ExitCode.configuration);
|
|
870
956
|
}
|
|
957
|
+
assertEffectAllowsNewWork(run);
|
|
871
958
|
let delivery = storedDelivery(run);
|
|
872
959
|
if (delivery.pullRequest === null) {
|
|
873
960
|
throw new MillError("PULL_REQUEST_IDENTITY_MISSING", "Delivery has no verified pull request identity.", ExitCode.configuration);
|
|
874
961
|
}
|
|
962
|
+
const pullRequest = delivery.pullRequest;
|
|
875
963
|
const adapter = input.adapter ?? createGitHubAdapter(input.root);
|
|
876
964
|
const deadlineMs = operationDeadline(config);
|
|
877
965
|
const binding = await adapter.inspect({
|
|
@@ -880,15 +968,22 @@ export async function observeDraftPr(input) {
|
|
|
880
968
|
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
881
969
|
});
|
|
882
970
|
await assertBinding(input.root, config, binding);
|
|
883
|
-
assertDeliveryContinuity({
|
|
971
|
+
await assertDeliveryContinuity({
|
|
972
|
+
root: input.root,
|
|
973
|
+
run,
|
|
974
|
+
inputs,
|
|
975
|
+
config,
|
|
976
|
+
delivery,
|
|
977
|
+
binding,
|
|
978
|
+
});
|
|
884
979
|
const observation = await adapter.observe({
|
|
885
980
|
config,
|
|
886
|
-
pullRequestNumber:
|
|
981
|
+
pullRequestNumber: pullRequest.number,
|
|
887
982
|
deadlineMs,
|
|
888
983
|
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
889
984
|
});
|
|
890
985
|
assertObservationIdentity(observation, delivery, false);
|
|
891
|
-
const checks = checkDecision(delivery.requiredChecks, observation.checks);
|
|
986
|
+
const checks = checkDecision(delivery.requiredChecks, observation.checks, delivery.checkProducers, "pull_request", delivery.candidateCommit);
|
|
892
987
|
const feedback = actionableFeedback(observation, delivery.reviewPolicy, delivery.candidateCommit);
|
|
893
988
|
const observationRecord = {
|
|
894
989
|
headSha: observation.pullRequest.headSha,
|
|
@@ -974,10 +1069,12 @@ export async function finalizeDraftPr(input) {
|
|
|
974
1069
|
!(run.status === "blocked" && run.blockCode === "POST_MERGE_CHECKS_FAILED")) {
|
|
975
1070
|
throw new MillError("DELIVERY_NOT_FINALIZABLE", "Run is not awaiting human merge or post-merge verification.", ExitCode.configuration);
|
|
976
1071
|
}
|
|
1072
|
+
assertNoUnresolvedEffect(run);
|
|
977
1073
|
let delivery = storedDelivery(run);
|
|
978
1074
|
if (delivery.pullRequest === null) {
|
|
979
1075
|
throw new MillError("PULL_REQUEST_IDENTITY_MISSING", "Delivery has no verified pull request identity.", ExitCode.configuration);
|
|
980
1076
|
}
|
|
1077
|
+
const pullRequest = delivery.pullRequest;
|
|
981
1078
|
const adapter = input.adapter ?? createGitHubAdapter(input.root);
|
|
982
1079
|
const deadlineMs = operationDeadline(config);
|
|
983
1080
|
const binding = await adapter.inspect({
|
|
@@ -986,10 +1083,18 @@ export async function finalizeDraftPr(input) {
|
|
|
986
1083
|
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
987
1084
|
});
|
|
988
1085
|
await assertBinding(input.root, config, binding);
|
|
989
|
-
|
|
1086
|
+
const continuity = await assertDeliveryContinuity({
|
|
1087
|
+
root: input.root,
|
|
1088
|
+
run,
|
|
1089
|
+
inputs,
|
|
1090
|
+
config,
|
|
1091
|
+
delivery,
|
|
1092
|
+
binding,
|
|
1093
|
+
allowLegacyPostMergePolicy: true,
|
|
1094
|
+
});
|
|
990
1095
|
const observation = await adapter.observe({
|
|
991
1096
|
config,
|
|
992
|
-
pullRequestNumber:
|
|
1097
|
+
pullRequestNumber: pullRequest.number,
|
|
993
1098
|
deadlineMs,
|
|
994
1099
|
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
995
1100
|
});
|
|
@@ -1021,6 +1126,14 @@ export async function finalizeDraftPr(input) {
|
|
|
1021
1126
|
if (mergeCommit.tree !== delivery.candidateTree) {
|
|
1022
1127
|
throw new MillError("MERGE_TREE_REVALIDATION_REQUIRED", "The merged tree differs from the reviewed candidate and requires a fresh exact-tree validation before closure.", ExitCode.configuration);
|
|
1023
1128
|
}
|
|
1129
|
+
if (continuity.bindLegacyPostMergePolicy) {
|
|
1130
|
+
delivery = persistDelivery(store, run.id, {
|
|
1131
|
+
...delivery,
|
|
1132
|
+
postMergeRequiredChecks: postMergeRequiredChecks(config),
|
|
1133
|
+
postMergePolicySource: "legacy_migrated",
|
|
1134
|
+
legacyPostMergePolicyConfigDigest: inputs.configDigest,
|
|
1135
|
+
}, "delivery.legacy_post_merge_policy_bound", { checks: postMergeRequiredChecks(config).length });
|
|
1136
|
+
}
|
|
1024
1137
|
delivery = persistDelivery(store, run.id, {
|
|
1025
1138
|
...delivery,
|
|
1026
1139
|
state: "merged",
|
|
@@ -1041,7 +1154,7 @@ export async function finalizeDraftPr(input) {
|
|
|
1041
1154
|
if (run.status !== "merged" && run.status !== "post_merge_verified") {
|
|
1042
1155
|
run = store.transition(run.id, "merged", "delivery.merged");
|
|
1043
1156
|
}
|
|
1044
|
-
const checks = checkDecision(delivery.requiredChecks, observation.mergeChecks);
|
|
1157
|
+
const checks = checkDecision(delivery.postMergeRequiredChecks ?? delivery.requiredChecks, observation.mergeChecks, delivery.checkProducers, "push", observation.mergeCommit.sha);
|
|
1045
1158
|
if (checks.status === "pending") {
|
|
1046
1159
|
return { run: publicRunRecord(run), delivery };
|
|
1047
1160
|
}
|