@patronage/factory-ci 1.0.0-alpha.21 → 1.0.0-alpha.23

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/index.js CHANGED
@@ -32,6 +32,18 @@ const NODE_PNPM_ACTION_FAMILY_NODE24 = {
32
32
  uses: "pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271"
33
33
  }
34
34
  };
35
+ /**
36
+ * The one `actions/upload-artifact` pin every workflow generator shares
37
+ * (#887). Two callers pinned two different commits of the same tag family;
38
+ * naming the pin here makes that drift impossible the same way the Node/pnpm
39
+ * family does. A caller passes this through `factoryWorkflow`'s
40
+ * `additionalActions.uploadArtifact` explicitly — the catalog names the pin,
41
+ * it does not inject the upload step.
42
+ */
43
+ const UPLOAD_ARTIFACT = {
44
+ tag: "v4.6.2",
45
+ uses: "actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02"
46
+ };
35
47
  //#endregion
36
48
  //#region src/candidate-lifecycle.ts
37
49
  /**
@@ -251,12 +263,10 @@ const transition = (state, event) => {
251
263
  evidence: [],
252
264
  outcome: "pending"
253
265
  },
254
- cleanupStatus: "pending",
255
266
  convergence: {
256
267
  detail: event.convergence.detail,
257
268
  status: "passed"
258
269
  },
259
- convergenceStatus: "passed",
260
270
  headSha: event.candidate.headSha,
261
271
  pr: event.candidate.pr,
262
272
  proof: {
@@ -269,7 +279,6 @@ const transition = (state, event) => {
269
279
  detail: event.smoke.detail,
270
280
  outcome: "passed"
271
281
  },
272
- smokeStatus: "passed",
273
282
  source: "local-self-certified",
274
283
  stack: event.stack,
275
284
  stage,
@@ -288,12 +297,11 @@ const transition = (state, event) => {
288
297
  };
289
298
  return replaceRegistration$1(state, {
290
299
  ...existing,
291
- cleanup,
292
- cleanupStatus: event.outcome
300
+ cleanup
293
301
  });
294
302
  };
295
303
  const recover = (state, event) => {
296
- if (state.registrations.find((row) => row.pr === event.pr && row.stack === event.stack && row.stage === event.stage)?.cleanupStatus !== "failed") throw new Error("Preview proof recovery re-enters cleanup only from a failed outcome.");
304
+ if (state.registrations.find((row) => row.pr === event.pr && row.stack === event.stack && row.stage === event.stage)?.cleanup.outcome !== "failed") throw new Error("Preview proof recovery re-enters cleanup only from a failed outcome.");
297
305
  return transition(state, event);
298
306
  };
299
307
  const previewProofLifecycle = {
@@ -519,6 +527,22 @@ const FACTORY_PROOF_GATE_GUARD = `steps.${FACTORY_PROOF_GATE_STEP_ID}.outputs.${
519
527
  */
520
528
  const FACTORY_PROOF_GATE_IF = "github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch))";
521
529
  /**
530
+ * Pull requests only — the condition a consumer gets with
531
+ * `reuse: "pull-request"` (#873).
532
+ *
533
+ * A repository may want its default-branch pushes to always execute the full
534
+ * hosted suite, whatever proof exists: the merged commit is what the fleet
535
+ * deploys, so a periodic unconditional run of every command is a deliberate
536
+ * cost some consumers choose to pay. This condition is that choice, expressed
537
+ * once here rather than as a consumer-written override string. The mode also
538
+ * skips the push-event merge fallback, so no push path can reuse proof even if
539
+ * the workflow reaches the step through some other trigger.
540
+ *
541
+ * Which branches trigger the workflow at all stays repository-owned. This
542
+ * condition only keeps the gate from consulting proof outside a pull request.
543
+ */
544
+ const FACTORY_PROOF_GATE_PULL_REQUEST_IF = "github.event_name == 'pull_request'";
545
+ /**
522
546
  * The complete refusal vocabulary. Deliberately few, because these are the
523
547
  * only distinctions the gate can honestly make from its Checks API reads.
524
548
  * The merge fallback adds no words: a fallback that establishes nothing
@@ -570,7 +594,7 @@ const COMMAND_IDENTITY_MAX_LENGTH = 120;
570
594
  * cannot subtract hosted work. This is intentionally fail-closed and moves in
571
595
  * lockstep with the proof/check-payload producer.
572
596
  */
573
- const TRUSTED_IMPACT_STAMP_VERSION = 3;
597
+ const TRUSTED_IMPACT_STAMP_VERSION = 4;
574
598
  const isProofReuseCommand = (value) => {
575
599
  if (!(value && typeof value === "object")) return false;
576
600
  const entry = value;
@@ -763,7 +787,9 @@ def releaseAuthorized($release; $commands; $stamp):
763
787
  type == "object"
764
788
  and (.name | nonemptyString)
765
789
  and (.basis | nonemptyString)
766
- and ((.impact == "affected") or (.impact == "not-affected"))))
790
+ and ((.impact == "affected") or (.impact == "not-affected"))
791
+ and ((.subscribedPaths | type) == "array")
792
+ and (.subscribedPaths | all(.[]; type == "string"))))
767
793
  and ($stamp.targets | distinctNames)
768
794
  and (([$releases[].name] - $executed | length) == ($releases | length))
769
795
  and ($releases | all(.[]; releaseAuthorized(.; $commands; $stamp)))
@@ -814,7 +840,92 @@ const safeLabel$1 = (surface) => {
814
840
  const cleaned = (typeof surface === "string" ? surface : "").replaceAll(/[^\w -]/gu, "").trim().slice(0, 60);
815
841
  return cleaned.length > 0 ? cleaned : "verification";
816
842
  };
817
- const gateScript = (required, surface) => String.raw`
843
+ /**
844
+ * The push-event merge fallback (#611), emitted only for the default reuse
845
+ * mode. `reuse: "pull-request"` omits it: that mode's whole point is that a
846
+ * default-branch push executes everything, so a push path that could still
847
+ * reuse proof would contradict the condition above it.
848
+ */
849
+ const MERGE_FALLBACK_BLOCK = String.raw`# Merge fallback (#611). A squash merge mints a new commit, so the direct
850
+ # read at a pushed merge-target head finds nothing even when the factory
851
+ # proved the producing pull request head. Only when the direct read found no
852
+ # generation at all on a push event, look up the producing pull request and
853
+ # reuse its head proof — and only when the merge commit's TREE id equals the
854
+ # proven head's tree id, which makes the pushed content byte-identical to
855
+ # what was verified (a clean squash of an unchanged tip). Patch identity was
856
+ # considered and rejected as the comparator: git patch-id normalizes
857
+ # whitespace and ignores base motion, so an identical patch can still
858
+ # produce an integrated tree that was never tested. Everything else — no
859
+ # unique merged producing PR, an unreadable commit, a proof that is anything
860
+ # but proven, or tree drift from a dirty or stale merge — leaves the direct
861
+ # "none" refusal standing, so the full suite runs (fail open).
862
+ if [ "$reason" = 'none' ] && [ "${shellExpansion$1("GITHUB_EVENT_NAME:-")}" = 'push' ]; then
863
+ producing_head=''
864
+ merge_tree=''
865
+ head_tree=''
866
+ if ! producing_pulls=$(gh api --method GET --paginate \
867
+ "repos/$GITHUB_REPOSITORY/commits/$HEAD_SHA/pulls" \
868
+ -f per_page=100 2>&1); then
869
+ detail="producing pull request unreadable: $producing_pulls"
870
+ elif ! producing_head=$(printf '%s' "$producing_pulls" | jq -r -s \
871
+ --arg sha "$HEAD_SHA" \
872
+ '[ add[]?
873
+ | select((.merged_at // null) != null)
874
+ | select((.merge_commit_sha // "") == $sha)
875
+ | ((.head.sha // "") | tostring) ]
876
+ | if length == 1 then .[0] else "" end' 2>&1); then
877
+ detail="producing pull request unreadable: $producing_head"
878
+ producing_head=''
879
+ elif ! [[ "$producing_head" =~ ^[0-9a-f]{40}$ ]]; then
880
+ detail='no single merged producing pull request at this commit'
881
+ producing_head=''
882
+ elif ! merge_commit=$(gh api --method GET \
883
+ "repos/$GITHUB_REPOSITORY/commits/$HEAD_SHA" 2>&1); then
884
+ detail="merge commit unreadable: $merge_commit"
885
+ elif ! merge_tree=$(printf '%s' "$merge_commit" | jq -r \
886
+ '(.commit.tree.sha // "") | tostring' 2>&1); then
887
+ detail="merge commit unreadable: $merge_tree"
888
+ merge_tree=''
889
+ elif ! [[ "$merge_tree" =~ ^[0-9a-f]{40}$ ]]; then
890
+ detail='merge commit carries no readable tree id'
891
+ merge_tree=''
892
+ elif ! head_commit=$(gh api --method GET \
893
+ "repos/$GITHUB_REPOSITORY/commits/$producing_head" 2>&1); then
894
+ detail="producing head commit unreadable: $head_commit"
895
+ elif ! head_tree=$(printf '%s' "$head_commit" | jq -r \
896
+ '(.commit.tree.sha // "") | tostring' 2>&1); then
897
+ detail="producing head commit unreadable: $head_tree"
898
+ head_tree=''
899
+ elif ! [[ "$head_tree" =~ ^[0-9a-f]{40}$ ]]; then
900
+ detail='producing head commit carries no readable tree id'
901
+ head_tree=''
902
+ elif [ "$merge_tree" != "$head_tree" ]; then
903
+ detail='tree drift: the merge result is not the proven head tree'
904
+ merge_tree=''
905
+ fi
906
+ if [[ "$producing_head" =~ ^[0-9a-f]{40}$ ]] \
907
+ && [[ "$merge_tree" =~ ^[0-9a-f]{40}$ ]] \
908
+ && [ "$merge_tree" = "$head_tree" ]; then
909
+ gate_lookup "$producing_head"
910
+ if [ "$lookup_reason" = 'proven' ]; then
911
+ reason=proven
912
+ mode="$lookup_mode"
913
+ missing=''
914
+ source_url="$lookup_url"
915
+ proof_head="$producing_head"
916
+ merge_note="the merge result's tree is byte-identical to the tree proven at pull request head $producing_head"
917
+ else
918
+ detail="producing pull request proof not reusable ($lookup_reason)"
919
+ fi
920
+ fi
921
+ fi`;
922
+ /**
923
+ * The fallback with the blank lines that surround it, or nothing at all. Kept
924
+ * as one piece so the default mode emits the exact script it emitted before
925
+ * this option existed.
926
+ */
927
+ const mergeFallbackSection = (reuse) => reuse === "pull-request" ? "\n" : `\n${MERGE_FALLBACK_BLOCK}\n\n`;
928
+ const gateScript = (required, surface, reuse) => String.raw`
818
929
  set -uo pipefail
819
930
 
820
931
  CHECK_NAME=${shellSingleQuote(FACTORY_PROOF_GATE_CHECK_NAME)}
@@ -901,82 +1012,7 @@ else
901
1012
  missing="$lookup_missing"
902
1013
  source_url="$lookup_url"
903
1014
  fi
904
-
905
- # Merge fallback (#611). A squash merge mints a new commit, so the direct
906
- # read at a pushed merge-target head finds nothing even when the factory
907
- # proved the producing pull request head. Only when the direct read found no
908
- # generation at all on a push event, look up the producing pull request and
909
- # reuse its head proof — and only when the merge commit's TREE id equals the
910
- # proven head's tree id, which makes the pushed content byte-identical to
911
- # what was verified (a clean squash of an unchanged tip). Patch identity was
912
- # considered and rejected as the comparator: git patch-id normalizes
913
- # whitespace and ignores base motion, so an identical patch can still
914
- # produce an integrated tree that was never tested. Everything else — no
915
- # unique merged producing PR, an unreadable commit, a proof that is anything
916
- # but proven, or tree drift from a dirty or stale merge — leaves the direct
917
- # "none" refusal standing, so the full suite runs (fail open).
918
- if [ "$reason" = 'none' ] && [ "${shellExpansion$1("GITHUB_EVENT_NAME:-")}" = 'push' ]; then
919
- producing_head=''
920
- merge_tree=''
921
- head_tree=''
922
- if ! producing_pulls=$(gh api --method GET --paginate \
923
- "repos/$GITHUB_REPOSITORY/commits/$HEAD_SHA/pulls" \
924
- -f per_page=100 2>&1); then
925
- detail="producing pull request unreadable: $producing_pulls"
926
- elif ! producing_head=$(printf '%s' "$producing_pulls" | jq -r -s \
927
- --arg sha "$HEAD_SHA" \
928
- '[ add[]?
929
- | select((.merged_at // null) != null)
930
- | select((.merge_commit_sha // "") == $sha)
931
- | ((.head.sha // "") | tostring) ]
932
- | if length == 1 then .[0] else "" end' 2>&1); then
933
- detail="producing pull request unreadable: $producing_head"
934
- producing_head=''
935
- elif ! [[ "$producing_head" =~ ^[0-9a-f]{40}$ ]]; then
936
- detail='no single merged producing pull request at this commit'
937
- producing_head=''
938
- elif ! merge_commit=$(gh api --method GET \
939
- "repos/$GITHUB_REPOSITORY/commits/$HEAD_SHA" 2>&1); then
940
- detail="merge commit unreadable: $merge_commit"
941
- elif ! merge_tree=$(printf '%s' "$merge_commit" | jq -r \
942
- '(.commit.tree.sha // "") | tostring' 2>&1); then
943
- detail="merge commit unreadable: $merge_tree"
944
- merge_tree=''
945
- elif ! [[ "$merge_tree" =~ ^[0-9a-f]{40}$ ]]; then
946
- detail='merge commit carries no readable tree id'
947
- merge_tree=''
948
- elif ! head_commit=$(gh api --method GET \
949
- "repos/$GITHUB_REPOSITORY/commits/$producing_head" 2>&1); then
950
- detail="producing head commit unreadable: $head_commit"
951
- elif ! head_tree=$(printf '%s' "$head_commit" | jq -r \
952
- '(.commit.tree.sha // "") | tostring' 2>&1); then
953
- detail="producing head commit unreadable: $head_tree"
954
- head_tree=''
955
- elif ! [[ "$head_tree" =~ ^[0-9a-f]{40}$ ]]; then
956
- detail='producing head commit carries no readable tree id'
957
- head_tree=''
958
- elif [ "$merge_tree" != "$head_tree" ]; then
959
- detail='tree drift: the merge result is not the proven head tree'
960
- merge_tree=''
961
- fi
962
- if [[ "$producing_head" =~ ^[0-9a-f]{40}$ ]] \
963
- && [[ "$merge_tree" =~ ^[0-9a-f]{40}$ ]] \
964
- && [ "$merge_tree" = "$head_tree" ]; then
965
- gate_lookup "$producing_head"
966
- if [ "$lookup_reason" = 'proven' ]; then
967
- reason=proven
968
- mode="$lookup_mode"
969
- missing=''
970
- source_url="$lookup_url"
971
- proof_head="$producing_head"
972
- merge_note="the merge result's tree is byte-identical to the tree proven at pull request head $producing_head"
973
- else
974
- detail="producing pull request proof not reusable ($lookup_reason)"
975
- fi
976
- fi
977
- fi
978
-
979
- # The binding is App-verified, but it still reaches markdown. Only a plain
1015
+ ${mergeFallbackSection(reuse)}# The binding is App-verified, but it still reaches markdown. Only a plain
980
1016
  # lowercase token is quoted back; anything else is reported as unknown.
981
1017
  case "$mode" in
982
1018
  '') ;;
@@ -1099,9 +1135,9 @@ fi
1099
1135
  * `FACTORY_PROOF_GATE_SHELL` — so the script is also written to reach that
1100
1136
  * write under errexit, and the tests execute it both ways.
1101
1137
  */
1102
- const factoryProofGateScript = ({ commands, surface }) => {
1138
+ const factoryProofGateScript = ({ commands, reuse = "pull-request-and-default-branch", surface }) => {
1103
1139
  const required = proofReuseRequiredCommands(commands);
1104
- return required ? gateScript(required, safeLabel$1(surface)) : UNUSABLE_SELECTION_SCRIPT;
1140
+ return required ? gateScript(required, safeLabel$1(surface), reuse) : UNUSABLE_SELECTION_SCRIPT;
1105
1141
  };
1106
1142
  /**
1107
1143
  * The step itself, structurally accepted by gagen's `step()` without adding a
@@ -1111,7 +1147,9 @@ const factoryProofGateScript = ({ commands, surface }) => {
1111
1147
  * `checks: read`; the push-event merge fallback additionally reads the
1112
1148
  * producing pull request (`pull-requests: read`) and the two commit objects
1113
1149
  * whose tree ids it compares (`contents: read`). A job that grants less
1114
- * loses only the fallback — the failed read degrades to the full suite.
1150
+ * loses only the fallback — the failed read degrades to the full suite. A
1151
+ * consumer that passes `reuse: "pull-request"` emits neither the push clause
1152
+ * nor the fallback, and needs only `checks: read` (#873).
1115
1153
  *
1116
1154
  * A **step, not a job**, and that is not a style preference. A separate gate
1117
1155
  * job that errored would leave the guarded job `skipped`, and a summary job
@@ -1127,7 +1165,7 @@ const factoryProofGateStep = (options) => Object.freeze({
1127
1165
  HEAD_SHA: githubExpression$1("github.event.pull_request.head.sha || github.sha")
1128
1166
  }),
1129
1167
  id: FACTORY_PROOF_GATE_STEP_ID,
1130
- if: FACTORY_PROOF_GATE_IF,
1168
+ if: options.reuse === "pull-request" ? FACTORY_PROOF_GATE_PULL_REQUEST_IF : FACTORY_PROOF_GATE_IF,
1131
1169
  name: FACTORY_PROOF_GATE_STEP_NAME,
1132
1170
  run: factoryProofGateScript(options),
1133
1171
  shell: FACTORY_PROOF_GATE_SHELL
@@ -1185,6 +1223,20 @@ const assertProofReuseCoverage = (input) => {
1185
1223
  */
1186
1224
  /** Check-run name that carries one registration JSON payload. Not a gate. */
1187
1225
  const PREVIEW_PROOF_INVENTORY_CHECK_NAME = "patronage-factory/preview-proof";
1226
+ /**
1227
+ * Least privilege for a job whose `GITHUB_TOKEN` calls `list`. A
1228
+ * `permissions` block zeroes every unlisted scope, and `list` reads five
1229
+ * endpoints: `pulls/{pr}`, `pulls/{pr}/commits`, and the GraphQL force-push
1230
+ * timeline (`pull-requests`), `compare` (`contents`), and
1231
+ * `commits/{sha}/check-runs` (`checks`). A job
1232
+ * that omits `pull-requests: read` fails with 403 on a same-repository PR
1233
+ * and skips every destroy matrix (#859). Give the discovery job this object.
1234
+ */
1235
+ const PREVIEW_PROOF_INVENTORY_LIST_PERMISSIONS = Object.freeze({
1236
+ checks: "read",
1237
+ contents: "read",
1238
+ "pull-requests": "read"
1239
+ });
1188
1240
  const INVENTORY_KIND = "patronage-factory-preview-proof";
1189
1241
  const INVENTORY_SCHEMA_VERSION = 1;
1190
1242
  const DEFAULT_TIMEOUT_MS = 5e3;
@@ -1238,7 +1290,7 @@ const readIdentity = (value) => {
1238
1290
  const readCleanup = (value) => {
1239
1291
  if (!isRecord$1(value.cleanup) || !isCleanupStatus(value.cleanup.outcome)) return null;
1240
1292
  const evidence = readStringArray(value.cleanup.evidence);
1241
- if (evidence === null || value.cleanupStatus !== value.cleanup.outcome) return null;
1293
+ if (evidence === null) return null;
1242
1294
  return typeof value.cleanup.runUrl === "string" && value.cleanup.runUrl.length > 0 ? {
1243
1295
  evidence,
1244
1296
  outcome: value.cleanup.outcome,
@@ -1250,7 +1302,7 @@ const readCleanup = (value) => {
1250
1302
  };
1251
1303
  const readPassingChecks = (value) => {
1252
1304
  if (!isRecord$1(value.convergence) || !isRecord$1(value.smoke)) return null;
1253
- if (value.convergence.status !== "passed" || value.convergenceStatus !== "passed" || typeof value.convergence.detail !== "string" || value.smoke.outcome !== "passed" || value.smokeStatus !== "passed" || typeof value.smoke.detail !== "string") return null;
1305
+ if (value.convergence.status !== "passed" || typeof value.convergence.detail !== "string" || value.smoke.outcome !== "passed" || typeof value.smoke.detail !== "string") return null;
1254
1306
  return {
1255
1307
  convergence: {
1256
1308
  detail: value.convergence.detail,
@@ -1285,14 +1337,11 @@ const readPreviewProofRegistration = (value) => {
1285
1337
  if (identity === null || cleanup === null || passing === null || proof === null) return null;
1286
1338
  return {
1287
1339
  cleanup,
1288
- cleanupStatus: cleanup.outcome,
1289
1340
  convergence: passing.convergence,
1290
- convergenceStatus: "passed",
1291
1341
  headSha: identity.headSha,
1292
1342
  pr: identity.pr,
1293
1343
  proof,
1294
1344
  smoke: passing.smoke,
1295
- smokeStatus: "passed",
1296
1345
  source: "local-self-certified",
1297
1346
  stack: identity.stack,
1298
1347
  stage: identity.stage,
@@ -1311,6 +1360,7 @@ const assertPersistableRegistration = (registration) => {
1311
1360
  const registrationKey = (row) => `${row.stack}:${row.stage}`;
1312
1361
  const replaceRegistration = (rows, next) => [...rows.filter((row) => registrationKey(row) !== registrationKey(next)), next];
1313
1362
  const memoryKey = (owner, repo, pr) => `${owner}/${repo}#${pr}`;
1363
+ /** Reached by callers as `previewProofInventory.memoryStore`. */
1314
1364
  const memoryStore = () => {
1315
1365
  const byPr = /* @__PURE__ */ new Map();
1316
1366
  return {
@@ -1449,7 +1499,7 @@ const registrationFromCheckRun = (run, expectedHeadSha) => {
1449
1499
  const checkRunOutput = (registration) => {
1450
1500
  const title = `Preview proof ${registration.stack}/${registration.stage}`.slice(0, 255);
1451
1501
  return {
1452
- summary: `cleanup ${registration.cleanupStatus}`,
1502
+ summary: `cleanup ${registration.cleanup.outcome}`,
1453
1503
  text: JSON.stringify({
1454
1504
  kind: INVENTORY_KIND,
1455
1505
  registration,
@@ -1601,6 +1651,7 @@ const takeNewestRegistrations = (runsBySha, pr) => {
1601
1651
  }
1602
1652
  return [...byKey.values()];
1603
1653
  };
1654
+ /** Reached by callers as `previewProofInventory.githubStore`. */
1604
1655
  const githubStore = (transport) => {
1605
1656
  const request = transport.fetch ?? fetch;
1606
1657
  const timeoutMs = transport.timeoutMs ?? DEFAULT_TIMEOUT_MS;
@@ -1695,6 +1746,7 @@ const recordCleanup = async (input, access) => {
1695
1746
  const previewProofInventory = {
1696
1747
  githubStore,
1697
1748
  list,
1749
+ listPermissions: PREVIEW_PROOF_INVENTORY_LIST_PERMISSIONS,
1698
1750
  memoryStore,
1699
1751
  persist,
1700
1752
  recordCleanup
@@ -1807,9 +1859,8 @@ const factoryWorkflow = (options) => {
1807
1859
  const EXECUTE_ALCHEMY_ENTRY_MAX_BUFFER = 64 * 1024 * 1024;
1808
1860
  let spawnImplementation = spawnSync;
1809
1861
  const resolveBundledEntry = async (options) => {
1810
- if (options.bundledEntry !== void 0) return path.resolve(options.cwd ?? options.bundle?.absWorkingDir ?? process.cwd(), options.bundledEntry);
1811
- if (options.bundle === void 0) throw new Error("executeAlchemyEntry requires bundle or bundledEntry.");
1812
- return await bundleAlchemyEntry(options.bundle);
1862
+ if (options.bundledEntry === void 0) return await bundleAlchemyEntry(options.bundle);
1863
+ return path.resolve(options.cwd ?? process.cwd(), options.bundledEntry);
1813
1864
  };
1814
1865
  const captured = (text, redact) => {
1815
1866
  if (text === null || redact === void 0) return text;
@@ -1819,8 +1870,8 @@ const captured = (text, redact) => {
1819
1870
  * Bundle a consumer-owned Alchemy entry, resolve that consumer's Alchemy CLI,
1820
1871
  * run it under the current Node binary, and return only on a successful exit.
1821
1872
  *
1822
- * Pass `bundledEntry` to skip the bundle step and reuse an already-bundled
1823
- * path. Captured stdout and stderr (`stdio: "pipe"`) can be redacted through
1873
+ * Options carry exactly one entry source: `bundle` to bundle now, or
1874
+ * `bundledEntry` to reuse an already-bundled path. Captured stdout and stderr (`stdio: "pipe"`) can be redacted through
1824
1875
  * `redact` before they are returned; `stdio: "inherit"` still streams the
1825
1876
  * child unredacted.
1826
1877
  *
@@ -1926,6 +1977,24 @@ const factoryProofReuseSummaryStep = (options) => Object.freeze({
1926
1977
  run: factoryProofReuseSummaryScript(options)
1927
1978
  });
1928
1979
  //#endregion
1980
+ //#region src/impact-demand-condition.ts
1981
+ const impactDemandConditions = (options) => {
1982
+ const targetOutput = (targetName) => {
1983
+ const output = options.targetOutputs[targetName];
1984
+ if (output === void 0) throw new Error(`Unknown ${options.label} impact target "${targetName}".`);
1985
+ return output;
1986
+ };
1987
+ const condition = (source, outcomeKey, output) => `${source}.${outcomeKey} != 'success' || ${source}.outputs.${options.decisionOutput} != 'usable' || ${source}.outputs.${output} != 'withdrawn'`;
1988
+ return Object.freeze({
1989
+ demandedIf: (targetName, decisionJob) => {
1990
+ const output = targetOutput(targetName);
1991
+ if (decisionJob.trim().length === 0) throw new Error(`A cross-job ${options.label} impact condition requires the decision job id.`);
1992
+ return `always() && (${condition(`needs.${decisionJob}`, "result", output)})`;
1993
+ },
1994
+ demandedIfAtStep: (targetName) => condition(`steps.${options.stepId}`, "outcome", targetOutput(targetName))
1995
+ });
1996
+ };
1997
+ //#endregion
1929
1998
  //#region src/production-impact-workflow.ts
1930
1999
  const FACTORY_PRODUCTION_IMPACT_STEP_ID = "production_impact";
1931
2000
  const FACTORY_PRODUCTION_IMPACT_DECISION_OUTPUT = "decision";
@@ -1962,6 +2031,12 @@ const factoryProductionImpactWorkflow = (options) => {
1962
2031
  name: "Classify production impact",
1963
2032
  run: `${cli} production:impact --before "$FACTORY_BEFORE_SHA" --after "$FACTORY_AFTER_SHA" --github-output "$GITHUB_OUTPUT" --github-summary "$GITHUB_STEP_SUMMARY"${profile}`
1964
2033
  };
2034
+ const conditions = impactDemandConditions({
2035
+ decisionOutput: FACTORY_PRODUCTION_IMPACT_DECISION_OUTPUT,
2036
+ label: "production",
2037
+ stepId: FACTORY_PRODUCTION_IMPACT_STEP_ID,
2038
+ targetOutputs
2039
+ });
1965
2040
  return Object.freeze({
1966
2041
  decisionJobOutputs: Object.freeze({
1967
2042
  basis: `\${{ steps.${FACTORY_PRODUCTION_IMPACT_STEP_ID}.outputs.${FACTORY_PRODUCTION_IMPACT_BASIS_OUTPUT} }}`,
@@ -1970,13 +2045,8 @@ const factoryProductionImpactWorkflow = (options) => {
1970
2045
  ...Object.fromEntries(Object.values(targetOutputs).map((output) => [output, `\${{ steps.${FACTORY_PRODUCTION_IMPACT_STEP_ID}.outputs.${output} }}`]))
1971
2046
  }),
1972
2047
  decisionStep: Object.freeze(decisionStep),
1973
- demandedIf: (targetName, decisionJob) => {
1974
- const output = targetOutputs[targetName];
1975
- if (output === void 0) throw new Error(`Unknown production impact target "${targetName}".`);
1976
- const source = decisionJob ? `needs.${decisionJob}` : `steps.${FACTORY_PRODUCTION_IMPACT_STEP_ID}`;
1977
- const condition = `${source}.${decisionJob ? "result" : "outcome"} != 'success' || ${source}.outputs.${FACTORY_PRODUCTION_IMPACT_DECISION_OUTPUT} != 'usable' || ${source}.outputs.${output} != 'withdrawn'`;
1978
- return decisionJob ? `always() && (${condition})` : condition;
1979
- },
2048
+ demandedIf: conditions.demandedIf,
2049
+ demandedIfAtStep: conditions.demandedIfAtStep,
1980
2050
  targetOutputs: Object.freeze(targetOutputs)
1981
2051
  });
1982
2052
  };
@@ -2019,6 +2089,12 @@ const factoryCandidateImpactWorkflow = (options) => {
2019
2089
  name: "Classify candidate impact",
2020
2090
  run: `${cli} candidate:impact --base "$FACTORY_BASE_SHA" --head "$FACTORY_HEAD_SHA" --github-output "$GITHUB_OUTPUT" --github-summary "$GITHUB_STEP_SUMMARY"${profile}`
2021
2091
  };
2092
+ const conditions = impactDemandConditions({
2093
+ decisionOutput: FACTORY_CANDIDATE_IMPACT_DECISION_OUTPUT,
2094
+ label: "candidate",
2095
+ stepId: FACTORY_CANDIDATE_IMPACT_STEP_ID,
2096
+ targetOutputs
2097
+ });
2022
2098
  return Object.freeze({
2023
2099
  decisionJobOutputs: Object.freeze({
2024
2100
  basis: `\${{ steps.${FACTORY_CANDIDATE_IMPACT_STEP_ID}.outputs.${FACTORY_CANDIDATE_IMPACT_BASIS_OUTPUT} }}`,
@@ -2028,13 +2104,8 @@ const factoryCandidateImpactWorkflow = (options) => {
2028
2104
  ...Object.fromEntries(Object.values(targetOutputs).map((output) => [output, `\${{ steps.${FACTORY_CANDIDATE_IMPACT_STEP_ID}.outputs.${output} }}`]))
2029
2105
  }),
2030
2106
  decisionStep: Object.freeze(decisionStep),
2031
- demandedIf: (targetName, decisionJob) => {
2032
- const output = targetOutputs[targetName];
2033
- if (output === void 0) throw new Error(`Unknown candidate impact target "${targetName}".`);
2034
- const source = decisionJob ? `needs.${decisionJob}` : `steps.${FACTORY_CANDIDATE_IMPACT_STEP_ID}`;
2035
- const condition = `${source}.${decisionJob ? "result" : "outcome"} != 'success' || ${source}.outputs.${FACTORY_CANDIDATE_IMPACT_DECISION_OUTPUT} != 'usable' || ${source}.outputs.${output} != 'withdrawn'`;
2036
- return decisionJob ? `always() && (${condition})` : condition;
2037
- },
2107
+ demandedIf: conditions.demandedIf,
2108
+ demandedIfAtStep: conditions.demandedIfAtStep,
2038
2109
  targetOutputs: Object.freeze(targetOutputs)
2039
2110
  });
2040
2111
  };
@@ -2106,6 +2177,180 @@ const factoryLifecycleContractLane = (declaration) => {
2106
2177
  });
2107
2178
  };
2108
2179
  //#endregion
2180
+ //#region src/merge-freeze-job.ts
2181
+ /**
2182
+ * The merge-freeze writer job (#356, ADR 0016 as amended; #429 for the
2183
+ * merge-target namespace; #872 for this package).
2184
+ *
2185
+ * A consumer's generated merge-target-push Verify workflow is the ONLY
2186
+ * producer of `patronage-factory/merge-freeze` generations: a red
2187
+ * push-triggered Verify completes a generation active, a green one completes
2188
+ * it inactive, and no command, scheduled job, or second workflow writes this
2189
+ * check run. The operator override (`demand:waive --demand merge-freeze`)
2190
+ * waives the demand for one candidate during a fix-forward; it never writes
2191
+ * here.
2192
+ *
2193
+ * The write mirrors what the factory package's merge-freeze reader parses:
2194
+ * create the run `in_progress` with no `started_at` (GitHub stamps the
2195
+ * generation clock — the ordering rule itself is owned by the reader), then
2196
+ * PATCH it completed with the reader-shaped state payload in `output.text`.
2197
+ * The consumer's workflow test executes this exact script against a stubbed
2198
+ * `gh` and parses the emitted payload with `validateMergeFreezeState`, so
2199
+ * writer and reader cannot drift silently.
2200
+ *
2201
+ * Those two API calls are what the reader's two settling phases observe
2202
+ * (#890). Before the POST the merge target carries no generation at all, which
2203
+ * a reader reports as `awaiting-generation` while the pushed hosted verify run
2204
+ * is still pending. Between the POST and the PATCH the generation exists with
2205
+ * no `output.text`, which a reader reports as `running-generation`. Only the
2206
+ * PATCH writes a verdict, so neither phase is one.
2207
+ *
2208
+ * Failure posture is deliberately fail-closed and the opposite of the
2209
+ * proof-reuse gate's fail-open shell: this step runs under the runner's
2210
+ * default `bash -e {0}`, so any API refusal aborts the step, the generation
2211
+ * stays absent or `in_progress`, and every `pr:ready` arming-time read of
2212
+ * that tip (#477) refuses until a later push writes a green generation.
2213
+ *
2214
+ * **Ownership line.** This package owns the check name, the App identity, the
2215
+ * `create-github-app-token` inputs, the result fold, the merge-target `if`,
2216
+ * and the job's `permissions`. The caller owns the runner, the `needs` list
2217
+ * of every authoritative verification leaf, the timeout, and any extra env.
2218
+ */
2219
+ /** The one check-run name every merge-freeze reader pins. */
2220
+ const FACTORY_MERGE_FREEZE_CHECK_NAME = "patronage-factory/merge-freeze";
2221
+ const FACTORY_MERGE_FREEZE_JOB_ID = "freeze";
2222
+ const FACTORY_MERGE_FREEZE_JOB_NAME = "Report the merge freeze";
2223
+ /** Step id the token step exposes its installation token under. */
2224
+ const FACTORY_MERGE_FREEZE_APP_TOKEN_STEP_ID = "factory-app-token";
2225
+ /**
2226
+ * The GitHub Actions expression that folds the needed jobs' results into the
2227
+ * one word the script branches on. It mirrors the `verify` summary job's
2228
+ * failure condition exactly: any failed or cancelled needed job is a red
2229
+ * merge target.
2230
+ */
2231
+ const FACTORY_MERGE_FREEZE_VERIFY_RESULT_EXPRESSION = `\${{ (contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')) && 'failure' || 'success' }}`;
2232
+ /**
2233
+ * The literal factory merge-target namespace, as a `github.ref` predicate
2234
+ * (#429).
2235
+ *
2236
+ * `main` plus the repository-owned `epic/**` integration branches, and
2237
+ * nothing else. Deliberately literal rather than profile-declared: one
2238
+ * implementation per contract, no configuration machinery for a two-element
2239
+ * namespace whose second element is a namespace prefix the repository itself
2240
+ * owns. `epic/` needs the trailing slash — `startsWith` would otherwise admit
2241
+ * `epicness/…`, which is not a merge target.
2242
+ *
2243
+ * A caller's `push` trigger branch list names the same namespace. That list
2244
+ * decides which events produce a run at all; this predicate decides which of
2245
+ * those runs may write a generation.
2246
+ */
2247
+ const FACTORY_MERGE_TARGET_REF_CONDITION = ["github.ref == 'refs/heads/main'", "startsWith(github.ref, 'refs/heads/epic/')"].join(" || ");
2248
+ /**
2249
+ * `always()` is load-bearing: the red path is the whole point, so the job must
2250
+ * run when a needed verification job failed. `github.event_name == 'push'` is
2251
+ * load-bearing twice over: a `pull_request` run must never write a generation
2252
+ * (its `github.sha` is a synthetic merge commit that no base tip is ever read
2253
+ * at), and a `pull_request` trigger with an `epic/**` base list would
2254
+ * otherwise reach this job.
2255
+ */
2256
+ const FACTORY_MERGE_FREEZE_IF = `always() && github.event_name == 'push' && (${FACTORY_MERGE_TARGET_REF_CONDITION})`;
2257
+ /**
2258
+ * Least privilege for the writer job: no `GITHUB_TOKEN` scope at all.
2259
+ *
2260
+ * Both Checks API calls run under the App installation token minted in the
2261
+ * first step (`GH_TOKEN: steps.factory-app-token.outputs.token`), and the job
2262
+ * never checks out the repository. So the job uses no `GITHUB_TOKEN` scope,
2263
+ * and an empty block is the honest declaration. A `checks: write` here would
2264
+ * hand every adopter an unused write scope (#884).
2265
+ */
2266
+ const FACTORY_MERGE_FREEZE_PERMISSIONS = Object.freeze({});
2267
+ /**
2268
+ * The script the freeze step runs. Environment contract:
2269
+ * - `GH_TOKEN`: an installation token for the pinned Patronage Factory App —
2270
+ * the check run must carry that App's identity or every reader rejects it.
2271
+ * - `VERIFY_RESULT`: `success` or `failure` (see the fold expression above).
2272
+ * - `GITHUB_SHA` / `GITHUB_REPOSITORY` / `GITHUB_SERVER_URL` /
2273
+ * `GITHUB_RUN_ID` / `GITHUB_REF_NAME`: runner-provided.
2274
+ *
2275
+ * `GITHUB_REF_NAME` is the merge target this run pushed to — `main` or an
2276
+ * `epic/**` integration branch (#429). The job is gated to `push` events on
2277
+ * exactly those refs, so the branch name is always the short name of a
2278
+ * repository-owned merge target and never a pull-request merge ref. It
2279
+ * appears in both recovery sentences because a reader of an epic-targeting
2280
+ * candidate must be told which branch to fix forward on: the generation lives
2281
+ * on that branch's tip, and only a later green push to THAT branch clears it.
2282
+ */
2283
+ const factoryMergeFreezeScript = () => [
2284
+ `sha="$GITHUB_SHA"`,
2285
+ `branch="$GITHUB_REF_NAME"`,
2286
+ `repo="$GITHUB_REPOSITORY"`,
2287
+ `run_url="$GITHUB_SERVER_URL/$repo/actions/runs/$GITHUB_RUN_ID"`,
2288
+ `created=$(gh api --method POST "repos/$repo/check-runs" \\`,
2289
+ ` -f "name=${FACTORY_MERGE_FREEZE_CHECK_NAME}" \\`,
2290
+ ` -f "head_sha=$sha" \\`,
2291
+ ` -f "status=in_progress" \\`,
2292
+ ` -f "details_url=$run_url" \\`,
2293
+ ` -f "output[title]=merge freeze generation in progress" \\`,
2294
+ ` -f "output[summary]=This generation blocks factory merge handoffs until the merge-target Verify result is recorded.")`,
2295
+ `id=$(jq -r '.id' <<<"$created")`,
2296
+ `case "$id" in`,
2297
+ ` ''|*[!0-9]*) echo "unusable check-run id: $id" >&2; exit 1;;`,
2298
+ `esac`,
2299
+ `if [ "$VERIFY_RESULT" = 'success' ]; then`,
2300
+ ` active=false; outcome=inactive; conclusion=success`,
2301
+ ` reason="Merge-target Verify passed on $branch at $sha ($run_url)."`,
2302
+ `else`,
2303
+ ` active=true; outcome=active; conclusion=failure`,
2304
+ ` reason="Merge-target Verify failed on $branch at $sha; fix forward or revert on $branch and let the next green Verify push on $branch clear this freeze ($run_url)."`,
2305
+ `fi`,
2306
+ `state=$(jq -cn --argjson active "$active" --argjson id "$id" --arg headSha "$sha" --arg outcome "$outcome" --arg reason "$reason" \\`,
2307
+ ` '{active: $active, generationId: $id, headSha: $headSha, outcome: $outcome, reason: $reason, recordedAt: (now | todate), schemaVersion: 1}')`,
2308
+ `gh api --method PATCH "repos/$repo/check-runs/$id" \\`,
2309
+ ` -f "status=completed" \\`,
2310
+ ` -f "conclusion=$conclusion" \\`,
2311
+ ` -f "details_url=$run_url" \\`,
2312
+ ` -f "output[title]=merge freeze $outcome" \\`,
2313
+ ` -f "output[summary]=$reason" \\`,
2314
+ ` -f "output[text]=$state" >/dev/null`,
2315
+ `echo "merge-freeze generation $id completed $outcome on $branch at $sha"`
2316
+ ].join("\n");
2317
+ /**
2318
+ * Build the merge-freeze writer job.
2319
+ *
2320
+ * An empty `needs` list is refused: a fold over no verification leaf is
2321
+ * always `success`, so such a job would write a green generation for a merge
2322
+ * target nothing verified — the exact failure the freeze exists to prevent.
2323
+ */
2324
+ const factoryMergeFreezeJob = (options) => {
2325
+ assertPinnedAction("createGithubAppToken", options.createGithubAppToken, "actions/create-github-app-token");
2326
+ if (options.needs.length === 0) throw new Error("factoryMergeFreezeJob needs at least one verification job: a fold over no needed job always reports success, so the freeze would report a merge target nothing verified as green.");
2327
+ const mintStep = Object.freeze({
2328
+ id: FACTORY_MERGE_FREEZE_APP_TOKEN_STEP_ID,
2329
+ name: "Mint the factory App token",
2330
+ uses: options.createGithubAppToken.uses,
2331
+ with: Object.freeze({
2332
+ "client-id": FACTORY_PROOF_GATE_APP_ID,
2333
+ "private-key": `\${{ secrets.FACTORY_GITHUB_APP_PRIVATE_KEY }}`
2334
+ })
2335
+ });
2336
+ const reportStep = Object.freeze({
2337
+ env: Object.freeze({
2338
+ GH_TOKEN: `\${{ steps.${FACTORY_MERGE_FREEZE_APP_TOKEN_STEP_ID}.outputs.token }}`,
2339
+ VERIFY_RESULT: options.verifyResultExpression ?? FACTORY_MERGE_FREEZE_VERIFY_RESULT_EXPRESSION
2340
+ }),
2341
+ name: "Complete the merge-freeze generation",
2342
+ run: factoryMergeFreezeScript()
2343
+ });
2344
+ return Object.freeze({
2345
+ if: FACTORY_MERGE_FREEZE_IF,
2346
+ jobId: FACTORY_MERGE_FREEZE_JOB_ID,
2347
+ jobName: FACTORY_MERGE_FREEZE_JOB_NAME,
2348
+ needs: Object.freeze([...options.needs]),
2349
+ permissions: FACTORY_MERGE_FREEZE_PERMISSIONS,
2350
+ steps: Object.freeze([mintStep, reportStep])
2351
+ });
2352
+ };
2353
+ //#endregion
2109
2354
  //#region src/pr-status-hud-workflow.ts
2110
2355
  const FACTORY_PR_STATUS_HUD_JOB_ID = "status-hud";
2111
2356
  const FACTORY_PR_STATUS_HUD_JOB_NAME = "Present PR status HUD";
@@ -2118,15 +2363,11 @@ const FACTORY_PR_STATUS_HUD_PLAN_PATH = ".factory-memory/preview-plan.json";
2118
2363
  */
2119
2364
  const FACTORY_PR_STATUS_HUD_IF = "always() && github.event_name == 'pull_request' && github.event.pull_request.draft != true";
2120
2365
  /**
2121
- * Least privilege for the job that runs the present step. A `permissions`
2122
- * block zeroes every unlisted GITHUB_TOKEN scope, and the inventory list
2123
- * reads pull requests, commits, and check-runs with that token (#825).
2366
+ * Least privilege for the job that runs the present step. The present step
2367
+ * is an inventory `list`, so the job needs exactly the inventory's read
2368
+ * permissions (#825, #859).
2124
2369
  */
2125
- const FACTORY_PR_STATUS_HUD_PERMISSIONS = Object.freeze({
2126
- checks: "read",
2127
- contents: "read",
2128
- "pull-requests": "read"
2129
- });
2370
+ const FACTORY_PR_STATUS_HUD_PERMISSIONS = PREVIEW_PROOF_INVENTORY_LIST_PERMISSIONS;
2130
2371
  const FACTORY_PR_STATUS_HUD_CONCURRENCY = Object.freeze({
2131
2372
  cancelInProgress: false,
2132
2373
  group: `status-hud-\${{ github.repository }}-\${{ github.event.pull_request.number }}`
@@ -2220,7 +2461,16 @@ const factoryPreviewCleanupTopology = (options) => {
2220
2461
  };
2221
2462
  //#endregion
2222
2463
  //#region src/push-identity-workflow.ts
2464
+ /** `owner/repo` slice of the catalog's `UPLOAD_ARTIFACT` pin, derived rather
2465
+ * than repeated as a literal, so a repository rename here cannot drift from
2466
+ * the pin it validates against. */
2467
+ const UPLOAD_ARTIFACT_REPOSITORY = UPLOAD_ARTIFACT.uses.slice(0, UPLOAD_ARTIFACT.uses.indexOf("@"));
2223
2468
  const FACTORY_PUSH_IDENTITY_SCHEMA_VERSION = 1;
2469
+ /**
2470
+ * The artifact prefix and the five step IDs are module-private. Alpha.13
2471
+ * removed them from the public surface (#719). A caller reads a step ID from
2472
+ * the builder's returned `steps` (`step.id`) or from `artifactName`.
2473
+ */
2224
2474
  const FACTORY_PUSH_IDENTITY_ARTIFACT_PREFIX = "factory-push-identity";
2225
2475
  const FACTORY_PUSH_IDENTITY_RECORD_STEP_ID = "factory_push_identity_record";
2226
2476
  const FACTORY_PUSH_IDENTITY_LOOKUP_STEP_ID = "factory_push_identity_lookup";
@@ -2470,7 +2720,7 @@ printf 'disposition=%s\nreason=%s\nbefore=%s\nafter=%s\nprovenance=%s\n' \
2470
2720
  * result when transport is unavailable. Place these steps after verification.
2471
2721
  */
2472
2722
  const factoryPushIdentityProducer = (options) => {
2473
- assertPinnedAction("uploadArtifact", options.uploadArtifact, "actions/upload-artifact");
2723
+ assertPinnedAction("uploadArtifact", options.uploadArtifact, UPLOAD_ARTIFACT_REPOSITORY);
2474
2724
  const condition = options.if ? `github.event_name == 'push' && (${options.if})` : "github.event_name == 'push'";
2475
2725
  const name = artifactName(expression("github.run_id"));
2476
2726
  return Object.freeze({
@@ -3286,4 +3536,4 @@ const assertWorkflowShellParses = (yaml, options) => {
3286
3536
  throw new Error(`${options.source} emits shell bash cannot parse; the runner would treat it as a no-op:\n${detail}`);
3287
3537
  };
3288
3538
  //#endregion
3289
- export { EXECUTE_ALCHEMY_ENTRY_MAX_BUFFER, FACTORY_CANDIDATE_IMPACT_BASIS_OUTPUT, FACTORY_CANDIDATE_IMPACT_DECISION_OUTPUT, FACTORY_CANDIDATE_IMPACT_INERT_OUTPUT, FACTORY_CANDIDATE_IMPACT_STEP_ID, FACTORY_CANDIDATE_IMPACT_UNSUBSCRIBED_OUTPUT, FACTORY_CANDIDATE_PULL_REQUEST_TYPES, FACTORY_LIFECYCLE_CONTRACT_JOB_ID, FACTORY_LIFECYCLE_CONTRACT_JOB_NAME, FACTORY_PREVIEW_CLEANUP_AUDIT_JOB_ID, FACTORY_PREVIEW_CLEANUP_AUDIT_JOB_NAME, FACTORY_PRODUCTION_IMPACT_BASIS_OUTPUT, FACTORY_PRODUCTION_IMPACT_DECISION_OUTPUT, FACTORY_PRODUCTION_IMPACT_STEP_ID, FACTORY_PRODUCTION_IMPACT_UNSUBSCRIBED_OUTPUT, FACTORY_PROOF_GATE_APP_ID, FACTORY_PROOF_GATE_CHECK_NAME, FACTORY_PROOF_GATE_GUARD, FACTORY_PROOF_GATE_IF, FACTORY_PROOF_GATE_MODE_OUTPUT, FACTORY_PROOF_GATE_OUTPUT, FACTORY_PROOF_GATE_REASONS, FACTORY_PROOF_GATE_REASON_OUTPUT, FACTORY_PROOF_GATE_SHELL, FACTORY_PROOF_GATE_SOURCE_URL_OUTPUT, FACTORY_PROOF_GATE_STEP_ID, FACTORY_PROOF_GATE_STEP_NAME, FACTORY_PR_STATUS_HUD_APP_TOKEN_STEP_ID, FACTORY_PR_STATUS_HUD_CONCURRENCY, FACTORY_PR_STATUS_HUD_IF, FACTORY_PR_STATUS_HUD_JOB_ID, FACTORY_PR_STATUS_HUD_JOB_NAME, FACTORY_PR_STATUS_HUD_PERMISSIONS, FACTORY_PR_STATUS_HUD_PLAN_PATH, FACTORY_PUSH_IDENTITY_SCHEMA_VERSION, GitHubApiError, NODE_PNPM_ACTION_FAMILY_NODE24, PREVIEW_PROOF_INVENTORY_CHECK_NAME, PreviewProofTransportError, VITEST_PROFILE_SCHEMA_VERSION, VITEST_PROFILE_TOOL, VitestProfileError, assertProofReuseCoverage, assertWorkflowShellParses, bundleAlchemyEntry, captureVitestProfileEnvironment, executeAlchemyEntry, factoryCandidateImpactWorkflow, factoryCandidateOrPushCondition, factoryLifecycleContractLane, factoryPrStatusHudWorkflow, factoryPreviewCleanupTopology, factoryProductionImpactWorkflow, factoryProofGateScript, factoryProofGateStep, factoryProofReuseSummaryStep, factoryProofTimingStartStep, factoryPushIdentityConsumer, factoryPushIdentityProducer, factoryWorkflow, githubAppJwt, isLocalPreviewStage, localPreviewStage, mintInstallationToken, normalizeVitestProfileSample, parseLocalPreviewStage, previewCleanupDestroyJobId, previewProofInventory, previewProofLifecycle, productionImpactTargetOutput, proofReuseCoverage, proofReuseRequiredCommands, readVitestProfileDocument, resolveProofReuseCommands, runVitestProfile, workflowRunBlocks, workflowShellParseFailures, writeVitestProfile };
3539
+ export { EXECUTE_ALCHEMY_ENTRY_MAX_BUFFER, FACTORY_CANDIDATE_IMPACT_BASIS_OUTPUT, FACTORY_CANDIDATE_IMPACT_DECISION_OUTPUT, FACTORY_CANDIDATE_IMPACT_INERT_OUTPUT, FACTORY_CANDIDATE_IMPACT_STEP_ID, FACTORY_CANDIDATE_IMPACT_UNSUBSCRIBED_OUTPUT, FACTORY_CANDIDATE_PULL_REQUEST_TYPES, FACTORY_LIFECYCLE_CONTRACT_JOB_ID, FACTORY_LIFECYCLE_CONTRACT_JOB_NAME, FACTORY_MERGE_FREEZE_APP_TOKEN_STEP_ID, FACTORY_MERGE_FREEZE_CHECK_NAME, FACTORY_MERGE_FREEZE_IF, FACTORY_MERGE_FREEZE_JOB_ID, FACTORY_MERGE_FREEZE_JOB_NAME, FACTORY_MERGE_FREEZE_PERMISSIONS, FACTORY_MERGE_FREEZE_VERIFY_RESULT_EXPRESSION, FACTORY_MERGE_TARGET_REF_CONDITION, FACTORY_PREVIEW_CLEANUP_AUDIT_JOB_ID, FACTORY_PREVIEW_CLEANUP_AUDIT_JOB_NAME, FACTORY_PRODUCTION_IMPACT_BASIS_OUTPUT, FACTORY_PRODUCTION_IMPACT_DECISION_OUTPUT, FACTORY_PRODUCTION_IMPACT_STEP_ID, FACTORY_PRODUCTION_IMPACT_UNSUBSCRIBED_OUTPUT, FACTORY_PROOF_GATE_APP_ID, FACTORY_PROOF_GATE_CHECK_NAME, FACTORY_PROOF_GATE_GUARD, FACTORY_PROOF_GATE_IF, FACTORY_PROOF_GATE_MODE_OUTPUT, FACTORY_PROOF_GATE_OUTPUT, FACTORY_PROOF_GATE_PULL_REQUEST_IF, FACTORY_PROOF_GATE_REASONS, FACTORY_PROOF_GATE_REASON_OUTPUT, FACTORY_PROOF_GATE_SHELL, FACTORY_PROOF_GATE_SOURCE_URL_OUTPUT, FACTORY_PROOF_GATE_STEP_ID, FACTORY_PROOF_GATE_STEP_NAME, FACTORY_PR_STATUS_HUD_APP_TOKEN_STEP_ID, FACTORY_PR_STATUS_HUD_CONCURRENCY, FACTORY_PR_STATUS_HUD_IF, FACTORY_PR_STATUS_HUD_JOB_ID, FACTORY_PR_STATUS_HUD_JOB_NAME, FACTORY_PR_STATUS_HUD_PERMISSIONS, FACTORY_PR_STATUS_HUD_PLAN_PATH, FACTORY_PUSH_IDENTITY_SCHEMA_VERSION, GitHubApiError, NODE_PNPM_ACTION_FAMILY_NODE24, PREVIEW_PROOF_INVENTORY_CHECK_NAME, PREVIEW_PROOF_INVENTORY_LIST_PERMISSIONS, PreviewProofTransportError, UPLOAD_ARTIFACT, VITEST_PROFILE_SCHEMA_VERSION, VITEST_PROFILE_TOOL, VitestProfileError, assertProofReuseCoverage, assertWorkflowShellParses, bundleAlchemyEntry, captureVitestProfileEnvironment, executeAlchemyEntry, factoryCandidateImpactWorkflow, factoryCandidateOrPushCondition, factoryLifecycleContractLane, factoryMergeFreezeJob, factoryMergeFreezeScript, factoryPrStatusHudWorkflow, factoryPreviewCleanupTopology, factoryProductionImpactWorkflow, factoryProofGateScript, factoryProofGateStep, factoryProofReuseSummaryStep, factoryProofTimingStartStep, factoryPushIdentityConsumer, factoryPushIdentityProducer, factoryWorkflow, githubAppJwt, isLocalPreviewStage, localPreviewStage, mintInstallationToken, normalizeVitestProfileSample, parseLocalPreviewStage, previewCleanupDestroyJobId, previewProofInventory, previewProofLifecycle, productionImpactTargetOutput, proofReuseCoverage, proofReuseRequiredCommands, readVitestProfileDocument, resolveProofReuseCommands, runVitestProfile, workflowRunBlocks, workflowShellParseFailures, writeVitestProfile };