@patronage/software-factory 0.23.0 → 0.30.0-alpha.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/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { existsSync, readFileSync, rmSync } from "node:fs";
2
2
  import { Command } from "commander";
3
3
  import { z } from "zod";
4
+ import { readFile } from "node:fs/promises";
4
5
 
5
6
  //#region src/github-issue-comments.d.ts
6
7
  interface GithubIssueCommentApi {
@@ -25,7 +26,7 @@ interface GithubIssueComment {
25
26
  }
26
27
  declare const normalizeIssueComments: (value: GithubIssueCommentApi[] | GithubIssueCommentApi[][]) => GithubIssueComment[];
27
28
  declare namespace boundary_review_proof_d_exports {
28
- export { BOUNDARY_REVIEW_PROOF_KIND, BoundaryReviewProof, BoundaryReviewProofComment, boundaryReviewProofSchema, selectBoundaryReviewProof, undispositionedBlockingFindings };
29
+ export { BOUNDARY_REVIEW_PROOF_KIND, BoundaryReviewProof, BoundaryReviewProofComment, boundaryReviewProofAuthorsSeen, boundaryReviewProofSchema, selectBoundaryReviewProof, undispositionedBlockingFindings };
29
30
  }
30
31
  declare const BOUNDARY_REVIEW_PROOF_KIND = "boundary-review-proof";
31
32
  declare const boundaryReviewProofBaseSchema: z.ZodObject<{
@@ -123,6 +124,7 @@ interface BoundaryReviewProofComment {
123
124
  commentUrl?: string;
124
125
  }
125
126
  declare const selectBoundaryReviewProof: (comments: GithubIssueComment[], boundary: string, declaredBy: string) => BoundaryReviewProofComment | undefined;
127
+ declare const boundaryReviewProofAuthorsSeen: (comments: GithubIssueComment[], boundary: string) => string[];
126
128
  declare const undispositionedBlockingFindings: (proof: BoundaryReviewProof) => string[];
127
129
  //#endregion
128
130
  //#region src/hq-ingest-sink.d.ts
@@ -166,6 +168,195 @@ interface HqIngestDependencies {
166
168
  timeoutMs?: number;
167
169
  transportTimeoutMs?: number;
168
170
  }
171
+ /** The repository whose undelivered evidence a spool holds. */
172
+ interface HqSpoolRepository {
173
+ owner: string;
174
+ repo: string;
175
+ }
176
+ /**
177
+ * What became of one spooled event. `delivered` and `duplicate` both mean HQ
178
+ * holds it (dedup is by content-addressed eventId), so the entry is removed;
179
+ * `rejected` and `unreachable` leave it spooled.
180
+ */
181
+ interface HqSpoolEntryOutcome {
182
+ detail?: string;
183
+ eventId: string;
184
+ kind: string;
185
+ spool: string;
186
+ /**
187
+ * `migrated` belongs to the legacy JSONL journal only: the row was moved
188
+ * into the current spool without being delivered. The spool pass that runs
189
+ * after it in the same drain supersedes that line with a real outcome when
190
+ * it gets to the row; a `migrated` line that survives the run means the row
191
+ * is still waiting.
192
+ *
193
+ * `undeliverable` is the one terminal verdict (#445). Every other status
194
+ * describes a moment: HQ was unreachable, HQ refused this content today, the
195
+ * row moved. Retrying is meaningful for all of them. A wrong-origin entry is
196
+ * different in kind — it is refused here, from the entry's own bytes, with no
197
+ * request made, and the same bytes produce the same verdict on every future
198
+ * run. Leaving it spooled asks the operator to retry something that provably
199
+ * cannot succeed, and the count it inflates is the one doctor goes red on.
200
+ */
201
+ status: "delivered" | "duplicate" | "migrated" | "rejected" | "undeliverable" | "unreachable";
202
+ }
203
+ interface HqSpoolFlushInput {
204
+ clientId: string;
205
+ clientSecret: string;
206
+ /** Repository root whose legacy `.factory-memory` spool is also drained. */
207
+ cwd: string;
208
+ /** The profile's HQ origin; entries recorded against another are refused. */
209
+ endpoint: string;
210
+ /** Operator-named spool directories; replaces the default two locations. */
211
+ explicitDirectories?: string[];
212
+ repository: HqSpoolRepository;
213
+ }
214
+ interface HqSpoolFlushSummary {
215
+ delivered: number;
216
+ duplicate: number;
217
+ /**
218
+ * The drain did not finish: the budget elapsed, or events beyond the
219
+ * rejected ones are still spooled. Never report an incomplete pass as a
220
+ * clean drain — a recovery run reads this to know whether to run again.
221
+ */
222
+ incomplete: boolean;
223
+ outcomes: HqSpoolEntryOutcome[];
224
+ rejected: number;
225
+ /** Events still in the drained spools when the pass ended. */
226
+ remaining: number;
227
+ /** Locations that were read; a missing one is simply absent from the list. */
228
+ spools: string[];
229
+ /**
230
+ * Events dispositioned as permanently undeliverable this pass (#445). They
231
+ * are gone from `remaining` — that is the point — so this is the only place
232
+ * the run says they existed.
233
+ */
234
+ undeliverable: number;
235
+ unreachable: number;
236
+ /** Files retained by a transport failure, counted per file. */
237
+ unreachableFiles: number;
238
+ }
239
+ interface HqSpoolWorkCount {
240
+ /**
241
+ * The earliest moment learned across pending spool files (their own write
242
+ * time) and legacy journal rows (their own `failedAt`). Absent only when
243
+ * `pending` is `0`, or when every timestamp source was unreadable within
244
+ * budget — an estimate for doctor's remediation message (#394), never a
245
+ * precise audit trail.
246
+ */
247
+ oldestQueuedAt?: string;
248
+ /** Spooled events and replayable journals waiting in the locations below. */
249
+ pending: number;
250
+ /** Locations that exist and hold spooled work. */
251
+ spools: string[];
252
+ /**
253
+ * A location existed but could not be listed. The count above saw nothing
254
+ * there, so a caller deciding whether the drain is worth doing must treat a
255
+ * non-zero value as "work may be waiting" — never as an empty spool.
256
+ */
257
+ unlistable: number;
258
+ }
259
+ /**
260
+ * Counts spooled work for a repository without draining it or touching a
261
+ * credential (#414).
262
+ *
263
+ * `hq:flush` used to resolve the HQ Access token before it ever looked at the
264
+ * spool, so a lane with nothing to send still paid a secret-manager round trip
265
+ * — and still failed, opaquely, in a sandbox that has no keychain access. The
266
+ * same locations `flushHqSpool` drains are inspected here, read-only: no
267
+ * directory is created, nothing is secured, and nothing is delivered.
268
+ */
269
+ declare function countHqSpoolWork(input: Pick<HqSpoolFlushInput, "cwd" | "explicitDirectories" | "repository">, dependencies?: {
270
+ budgetMs?: number;
271
+ env?: NodeJS.ProcessEnv;
272
+ }): Promise<HqSpoolWorkCount>;
273
+ /** A spool under an earlier key for this repository, not the current one. */
274
+ interface HqSpoolOrphan {
275
+ /** The `hq-retry-spool` directory itself, ready to pass to `--dir`. */
276
+ directory: string;
277
+ oldestQueuedAt?: string;
278
+ /** Spooled events and journal rows waiting there. */
279
+ pending: number;
280
+ /**
281
+ * The location exists but could not be listed. As everywhere else in this
282
+ * inspection, unknown counts as work: a swept location nobody could read is
283
+ * reported, never quietly dropped as empty.
284
+ */
285
+ unlistable: number;
286
+ }
287
+ interface HqSpoolOrphanSweep {
288
+ /** Only locations holding work; an empty orphan spool is not a finding. */
289
+ orphans: HqSpoolOrphan[];
290
+ root: string;
291
+ }
292
+ /**
293
+ * Reports spooled evidence sitting under an earlier key for *this* repository
294
+ * (#420).
295
+ *
296
+ * The spool is keyed by owner and repo, so a change to the segment encoding
297
+ * itself — which happened during #390's own development — moves the address
298
+ * without moving the evidence. Both readers of the spool resolve exactly one
299
+ * key, so the events under the old one become invisible: the drain reports
300
+ * success, doctor reports empty, and three real proofs sat unread until an
301
+ * attended recovery enumerated the tree by hand.
302
+ *
303
+ * This sweep only reports. Draining another key's events is a decision this
304
+ * does not make — the operator gets the location and the count, and
305
+ * `hq:flush --dir` remains the recovery path.
306
+ *
307
+ * **Why candidate keys and not a walk of the root (#446).** The root is shared
308
+ * by every factory repository on the machine, so enumerating it and calling
309
+ * everything that is not the current key an orphan describes another
310
+ * repository's ordinary, current, correct spool exactly as well as it describes
311
+ * this repository's obsolete one. That made doctor red in one checkout because
312
+ * a different repository had pending work, and told the operator to drain it —
313
+ * confidently prescribing the wrong action. Nothing on disk distinguishes the
314
+ * two cases: an unrecognised key carries no statement about who wrote it.
315
+ *
316
+ * So discovery is scoped to the keys *this* repository could plausibly have
317
+ * produced — the current scheme plus the earlier ones listed in
318
+ * `hqSpoolCandidateKeys` — and a key outside that set is never this
319
+ * repository's business. The #420 incident is inside it: the joined
320
+ * single-segment key is one of the candidates.
321
+ *
322
+ * **And a marked walk beside them (#447).** Derivation's other blind spot is a
323
+ * key whose *encoding* this checkout no longer produces but whose owner and
324
+ * repo are unchanged — the retired non-injective era being the live example.
325
+ * That era cannot be derived safely, because a candidate built from it can
326
+ * equal a different repository's current key, so #446 dropped it rather than
327
+ * risk the cross-repository claim again.
328
+ *
329
+ * `SPOOL_REPOSITORY_MARKER` supplies the proof that derivation could not. Every
330
+ * enqueue stamps its spool with the repository writing it, so the root can be
331
+ * walked again: a directory whose marker names *this* repository is this
332
+ * repository's, whatever key encoding it sits under, and a directory whose
333
+ * marker names another repository is never reported here. That is the
334
+ * discriminator #446 correctly said did not exist — it exists now because
335
+ * something writes it down.
336
+ *
337
+ * The two discoveries are complements, not alternatives. The walk sees only
338
+ * what was stamped; directories written before this shipped have no marker, and
339
+ * candidate keys still find those. An unmarked directory is still never
340
+ * reported, because it still carries no statement about who wrote it.
341
+ *
342
+ * **What this does NOT close, despite being the marker's obvious use: renames
343
+ * and owner changes.** A marker records the identity that was current when the
344
+ * directory was written, so after `patronage/old` becomes `patronage/new` the
345
+ * stranded directory is stamped `patronage/old` — and matching is equality
346
+ * against the checkout's *present* identity, which rejects it. Making that work
347
+ * needs an identifier that survives a rename, which neither the profile nor the
348
+ * marker carries today; accepting a non-matching marker instead would be
349
+ * guessing, which is the #446 defect wearing a new hat. #447 stays open for it.
350
+ *
351
+ * Also outside the sweep, by construction: evidence under a *different state
352
+ * root*, if `XDG_STATE_HOME` moves. No walk of this root can reach another one.
353
+ */
354
+ declare function sweepHqSpoolOrphans(input: {
355
+ repository: HqSpoolRepository;
356
+ }, dependencies?: {
357
+ budgetMs?: number;
358
+ env?: NodeJS.ProcessEnv;
359
+ }): Promise<HqSpoolOrphanSweep>;
169
360
  //#endregion
170
361
  //#region src/demand-waiver.d.ts
171
362
  declare const DEFAULT_DEMAND_WAIVER_PATH = ".factory-memory/demand-waivers.json";
@@ -264,6 +455,34 @@ declare const authorizeDemandWaiver: ({
264
455
  session: string | undefined;
265
456
  }) => AuthorizeDemandWaiverResult;
266
457
  //#endregion
458
+ //#region src/blocked-reasons.d.ts
459
+ declare const blockedReasonSchema: z.ZodObject<{
460
+ code: z.ZodString;
461
+ detail: z.ZodString;
462
+ }, z.core.$strip>;
463
+ type BlockedReason = z.infer<typeof blockedReasonSchema>;
464
+ //#endregion
465
+ //#region src/arm-auto-merge.d.ts
466
+ interface ArmAutoMergeInput {
467
+ cwd: string;
468
+ /** The validated PR head this arming is a compare-and-set against. */
469
+ headSha: string;
470
+ owner: string;
471
+ pr: number;
472
+ repo: string;
473
+ }
474
+ /**
475
+ * What the PR actually did, read back after the call — never inferred from the
476
+ * exit code. `not-armed` is a failure: the candidate is admitted but nothing
477
+ * will merge it, so it carries the detail and `pr:ready` emits a re-dispatch.
478
+ */
479
+ interface ArmAutoMergeOutcome {
480
+ /** Why the arming did not take effect, or what the invocation reported. */
481
+ detail?: string;
482
+ headSha: string;
483
+ outcome: "armed" | "merged" | "not-armed";
484
+ }
485
+ //#endregion
267
486
  //#region src/checkout-repository.d.ts
268
487
  interface CheckoutRepository {
269
488
  name: string;
@@ -300,235 +519,30 @@ declare const followUpFromArgv: (argv: string[]) => FollowUpAction;
300
519
  /** Canonical zod schema for the optional follow-up field on JSON outputs. */
301
520
  declare const FollowUpActionSchema: z.ZodType<FollowUpAction>;
302
521
  //#endregion
303
- //#region src/github-client.d.ts
304
- interface GithubPullRequestReview {
305
- author: {
306
- login?: string;
307
- };
308
- body: string;
309
- /** GraphQL `PullRequestReview.lastEditedAt` — REST's `/pulls/reviews` has no equivalent (#843). */
310
- lastEditedAt?: string;
311
- state: string;
312
- submittedAt?: string;
313
- url: string;
314
- }
315
- //#endregion
316
- //#region src/merge-freeze.d.ts
317
- declare const MERGE_FREEZE_CHECK_NAME = "patronage-factory/merge-freeze";
318
- declare const MERGE_FREEZE_APP_SLUG = "patronage-factory";
319
- declare const mergeFreezeStateSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
320
- active: z.ZodLiteral<true>;
321
- generationId: z.ZodNumber;
322
- headSha: z.ZodString;
323
- outcome: z.ZodEnum<{
324
- stale: "stale";
325
- active: "active";
326
- }>;
327
- reason: z.ZodString;
328
- recordedAt: z.ZodISODateTime;
329
- schemaVersion: z.ZodLiteral<1>;
330
- }, z.core.$strip>, z.ZodObject<{
331
- active: z.ZodLiteral<false>;
332
- clearRationale: z.ZodOptional<z.ZodString>;
333
- generationId: z.ZodNumber;
334
- headSha: z.ZodString;
335
- outcome: z.ZodLiteral<"inactive">;
336
- reason: z.ZodString;
337
- recordedAt: z.ZodISODateTime;
338
- schemaVersion: z.ZodLiteral<1>;
339
- }, z.core.$strip>], "active">;
340
- type MergeFreezeState = z.infer<typeof mergeFreezeStateSchema>;
341
- /**
342
- * The write side of this contract lives in the generated main-push verify
343
- * workflow (#356, ADR 0016 as amended): it is the ONLY producer of
344
- * `patronage-factory/merge-freeze` generations. Its emitted `output.text`
345
- * payload must parse under this exact reader schema, which is what the
346
- * workflow's own tests assert through this export.
347
- */
348
- declare function validateMergeFreezeState(value: unknown): MergeFreezeState;
349
- interface MergeFreezeStoreInput {
350
- cwd: string;
351
- headSha: string;
352
- repository: CheckoutRepository;
353
- }
354
- interface MergeFreezeStore {
355
- read: (input: MergeFreezeStoreInput) => unknown;
356
- }
357
- declare namespace worktree_held_branch_d_exports {
358
- export { MergeOperationalNotices, WorktreeHeldBranch, WorktreeHeldBranchCheck, WorktreeListEntry, checkWorktreeHeldBranch, findWorktreeHeldBranch, formatHeldBranchCloseoutSummary, listWorktreesPorcelain, parseGitWorktreeList, resolveMergeOperationalNotices, worktreeHeldBranchNotice };
359
- }
360
- /**
361
- * Detects when a PR head branch is checked out in a local git worktree.
362
- *
363
- * `gh pr merge --delete-branch` fails when the local branch is held by a
364
- * worktree (git refuses to delete a checked-out branch), which previously
365
- * broke the auto-merge lane mid-merge (patronage/internal#284). The merge
366
- * preflight surfaces this as an actionable notice — not a hard fail — so the
367
- * operator merges without `--delete-branch` (or prunes the worktree first)
368
- * and defers local branch cleanup to closeout.
369
- */
370
- interface WorktreeHeldBranch {
371
- branch: string;
372
- worktreePath: string;
373
- }
374
- interface WorktreeListEntry {
375
- branch?: string;
376
- path: string;
377
- }
378
- /**
379
- * Parses `git worktree list --porcelain` output. Entries are separated by
380
- * blank lines; each starts with `worktree <path>` and carries an optional
381
- * `branch refs/heads/<name>` attribute (detached worktrees have none).
382
- */
383
- declare function parseGitWorktreeList(porcelain: string): WorktreeListEntry[];
384
- /**
385
- * Returns the worktree holding `branch`, or undefined when no local worktree
386
- * has it checked out (or the porcelain listing was unavailable).
387
- */
388
- declare function findWorktreeHeldBranch({
389
- branch,
390
- worktreeListPorcelain
391
- }: {
392
- branch: string;
393
- worktreeListPorcelain: string | undefined;
394
- }): undefined | WorktreeHeldBranch;
395
- declare function worktreeHeldBranchNotice(held: WorktreeHeldBranch): string;
396
- interface MergeOperationalNotices {
397
- notices: string[];
398
- worktreeHeldBranch?: WorktreeHeldBranch;
399
- worktreeHeldBranches?: WorktreeHeldBranch[];
400
- }
401
- /**
402
- * Single entry point for non-blocking merge preflight notices, mirroring
403
- * `resolveMergeGuardIdentity`/`mergeGuardBlockingReasons` in
404
- * merge-identity.ts: adding a notice kind never requires coordinated edits
405
- * in the command runner, and persisted notice strings cannot drift from the
406
- * structured detection result.
407
- */
408
- declare function resolveMergeOperationalNotices({
409
- headRefName,
410
- worktreeListPorcelain
411
- }: {
412
- headRefName: string | undefined;
413
- worktreeListPorcelain: string | undefined;
414
- }): MergeOperationalNotices;
415
- /**
416
- * Shared `git worktree list --porcelain` reader for merge preflight and
417
- * closeout. Returns undefined when the directory is missing, not a git
418
- * repository, or git is unavailable — detection is best-effort and callers
419
- * report "skipped" rather than fabricating a result.
420
- */
421
- declare function listWorktreesPorcelain(cwd: string): string | undefined;
422
- interface WorktreeHeldBranchCheck {
423
- held?: WorktreeHeldBranch;
424
- /** "skipped" means no branch was recorded or worktrees were unlistable. */
425
- status: "clean" | "held" | "skipped";
426
- }
427
- /**
428
- * Closeout-side counterpart to the merge-preflight notice: the merge step
429
- * defers local branch cleanup to closeout, so closeout planning checks
430
- * whether the worker branch is still held by a worktree (#284).
431
- */
432
- declare function checkWorktreeHeldBranch({
433
- branch,
434
- worktreeListPorcelain
435
- }: {
436
- branch: string | undefined;
437
- worktreeListPorcelain: string | undefined;
438
- }): WorktreeHeldBranchCheck;
439
- /** One-line closeout summary, owned beside the detection logic. */
440
- declare function formatHeldBranchCloseoutSummary(check: WorktreeHeldBranchCheck): string;
441
- declare namespace merge_identity_d_exports {
442
- export { LiveHeadInput, MergeGuardIdentity, MergeIdentityResult, PostProofCommit, ReadyProofForMergeIdentity, evaluateMergeIdentity, mergeGuardBlockingReasons, mergeGuardIdentitySchema, resolveMergeGuardIdentity };
443
- }
444
- interface PostProofCommit {
445
- sha: string;
446
- subject: string;
447
- }
522
+ //#region src/pr-readiness/handled-human-comments.d.ts
523
+ declare const HANDLED_COMMENTS_PAYLOAD_KIND: "handled-human-comments";
448
524
  /**
449
- * The slice of the pr:ready proof the merge guard actually consumes, so this
450
- * module does not depend on the parent proof envelope. `PrReadyProof` is
451
- * structurally assignable to this type.
525
+ * Authenticated producer of the durable handled set. Recorded on the readiness
526
+ * ledger; unverifiable producers are ignored on read (fail closed).
452
527
  */
453
- interface ReadyProofForMergeIdentity {
454
- blockingReasons: string[];
455
- ledger: {
456
- /**
457
- * The evaluated head SHA recorded by pr:ready: `pr.headRefOid` captured
458
- * at evaluation time. The merge guard compares it to the live pushed
459
- * HEAD with exact equality.
460
- */
461
- headSha: string;
462
- pr: number;
463
- };
464
- status: string;
528
+ type HandledCommentsProducerMode = "app" | "commit-status";
529
+ interface HandledCommentsProducer {
530
+ /** App id (stringified) or GitHub login, depending on mode. */
531
+ identity: string;
532
+ mode: HandledCommentsProducerMode;
465
533
  }
466
- type MergeIdentityResult = {
467
- kind: "match";
468
- headSha: string;
469
- } | {
470
- kind: "diverged";
471
- liveHeadSha: string;
472
- postProofCommits?: PostProofCommit[];
473
- proofHeadSha: string;
474
- };
475
- type MergeGuardIdentity = MergeIdentityResult | {
476
- kind: "live-head-invalid";
477
- pr: number;
478
- received: string;
479
- } | {
480
- kind: "ready-proof-missing";
481
- errorDetail?: string;
482
- readyProofPath: string;
483
- } | {
484
- kind: "ready-proof-pr-mismatch";
485
- proofPr: number;
486
- requestedPr: number;
487
- } | {
488
- kind: "ready-proof-not-ready";
489
- blockingReasons: string[];
490
- status: string;
491
- };
492
- declare const evaluateMergeIdentity: ({
493
- liveHeadSha,
494
- postProofCommits,
495
- proofHeadSha
496
- }: {
497
- liveHeadSha: string;
498
- postProofCommits?: PostProofCommit[];
499
- proofHeadSha: string;
500
- }) => MergeIdentityResult;
501
- /**
502
- * The live PR head as fetched from GitHub: either a validated 40-hex SHA or
503
- * the raw (stringified) response that failed validation.
504
- */
505
- type LiveHeadInput = {
506
- headSha: string;
507
- } | {
508
- invalidResponse: string;
509
- };
510
- /**
511
- * Single entry point that owns construction of every MergeGuardIdentity
512
- * kind, so adding or changing a kind never requires coordinated edits in the
513
- * command runner.
514
- */
515
- declare const resolveMergeGuardIdentity: ({
516
- commitsBetween,
517
- liveHead,
518
- pr,
519
- readyProof,
520
- readyProofError,
521
- readyProofPath
522
- }: {
523
- commitsBetween?: (fromSha: string, toSha: string) => PostProofCommit[] | undefined;
524
- liveHead: LiveHeadInput;
534
+ interface HandledCommentsCheckPayload {
535
+ clearedAt?: string;
536
+ handledCommentUrls: string[];
537
+ kind: typeof HANDLED_COMMENTS_PAYLOAD_KIND;
525
538
  pr: number;
526
- readyProof: ReadyProofForMergeIdentity | undefined;
527
- readyProofError?: unknown;
528
- readyProofPath: string;
529
- }) => MergeGuardIdentity;
530
- declare const mergeGuardIdentitySchema: z.ZodType<MergeGuardIdentity>;
531
- declare const mergeGuardBlockingReasons: (identity: MergeGuardIdentity) => string[];
539
+ schemaVersion: 1;
540
+ sessionId?: string;
541
+ }
542
+ //#endregion
543
+ //#region src/diff-classification.d.ts
544
+ declare const DIFF_CLASSIFICATIONS: readonly ["docs/process-only", "trivial", "non-trivial"];
545
+ type DiffClassification = (typeof DIFF_CLASSIFICATIONS)[number];
532
546
  //#endregion
533
547
  //#region src/profile.d.ts
534
548
  declare const DEFAULT_FACTORY_REPOSITORY = "unknown/unknown";
@@ -565,12 +579,11 @@ declare const factoryProjectProfileSchema: z.ZodObject<{
565
579
  }>;
566
580
  name: z.ZodString;
567
581
  scope: z.ZodOptional<z.ZodObject<{
568
- classifications: z.ZodOptional<z.ZodArray<z.ZodEnum<{
582
+ classifications: z.ZodArray<z.ZodEnum<{
569
583
  trivial: "trivial";
570
584
  "docs/process-only": "docs/process-only";
571
585
  "non-trivial": "non-trivial";
572
- }>>>;
573
- labels: z.ZodOptional<z.ZodArray<z.ZodString>>;
586
+ }>>;
574
587
  }, z.core.$strict>>;
575
588
  }, z.core.$strict>>>;
576
589
  review: z.ZodObject<{
@@ -620,138 +633,6 @@ interface LoadProjectProfileResult {
620
633
  }
621
634
  declare function loadProjectProfile(input?: LoadProjectProfileInput): LoadProjectProfileResult;
622
635
  //#endregion
623
- //#region src/review-rungs.d.ts
624
- declare const EVIDENCE_REVIEW_RUNGS: readonly ["independent-model", "oracle", "human"];
625
- type EvidenceReviewRung = (typeof EVIDENCE_REVIEW_RUNGS)[number];
626
- //#endregion
627
- //#region src/pr-merge-check.d.ts
628
- interface PrMergeCheckArgs extends LoadProjectProfileInput {
629
- epic?: number;
630
- json?: boolean;
631
- output?: string;
632
- pr: number;
633
- readyProof?: string;
634
- }
635
- interface PrMergeCheckProof {
636
- schemaVersion: 1;
637
- blockingReasons: string[];
638
- boundary?: {
639
- autoMerge: boolean;
640
- epic: number;
641
- review: EvidenceReviewRung;
642
- wave: string;
643
- };
644
- command: "patronage-factory pr:merge-check";
645
- /**
646
- * Machine-safe arg array for the obvious next action (#523): on `pass`, the
647
- * remote-only GitHub merge API invocation for this PR; on `fail`, the re-run that clears the
648
- * block (`pr:ready`, or `pr:verify` then `pr:ready` after a head
649
- * divergence). Same `argv` shape `factory:delegate --print` emits. Optional
650
- * and purely additive — existing consumers ignore it.
651
- */
652
- followUp?: FollowUpAction;
653
- identity: MergeGuardIdentity;
654
- /**
655
- * The pushed PR head SHA fetched from GitHub at check time. Absent only
656
- * when GitHub returned no usable 40-hex head (identity kind
657
- * "live-head-invalid"), which is itself a fail-closed blocking state.
658
- */
659
- liveHeadSha?: string;
660
- /**
661
- * Actionable operational notices that do not block the merge, e.g. a PR
662
- * head branch held by a local worktree, which makes
663
- * `gh pr merge --delete-branch` fail (patronage/internal#284).
664
- */
665
- notices?: string[];
666
- pr: number;
667
- status: "pass" | "fail";
668
- /**
669
- * Demands that were in force, were NOT met, and were waived by the operator
670
- * (#354). Each entry carries the demand's refusals verbatim, so a waived
671
- * demand can never read as a met one; the merge proceeds on the recorded
672
- * operator act, not on evidence.
673
- */
674
- waivedDemands?: WaivedDemand[];
675
- /** Present when the PR head branch is checked out in a local worktree. */
676
- worktreeHeldBranch?: WorktreeHeldBranch;
677
- /** All merge-relevant branches held by local worktrees (head and default). */
678
- worktreeHeldBranches?: WorktreeHeldBranch[];
679
- }
680
- interface PrMergeCheckGitDependencies {
681
- checkoutRepository: (cwd: string) => CheckoutRepository;
682
- commitsBetween: (cwd: string, fromSha: string, toSha: string) => PostProofCommit[] | undefined;
683
- worktreeListPorcelain: (cwd: string) => string | undefined;
684
- showFileAtRef: (cwd: string, ref: string, absolutePath: string) => string | undefined;
685
- objectIdAtRef: (cwd: string, ref: string, absolutePath: string) => string | undefined;
686
- }
687
- interface PrMergeCheckDependencies {
688
- git?: Partial<PrMergeCheckGitDependencies>;
689
- github?: {
690
- fetchClosingPullRequests?: (input: {
691
- issue: number;
692
- owner: string;
693
- repo: string;
694
- }) => {
695
- number: number;
696
- }[];
697
- fetchIssueBody?: (input: {
698
- issue: number;
699
- owner: string;
700
- repo: string;
701
- }) => string;
702
- fetchPullRequestHead: (input: {
703
- cwd: string;
704
- owner: string;
705
- pr: number;
706
- repo: string;
707
- }) => {
708
- headRefName?: string;
709
- headRefOid: string;
710
- baseRefName?: string;
711
- baseRefOid?: string;
712
- mergedAt?: string | null;
713
- remoteHeadRefExists?: boolean;
714
- labels?: {
715
- name: string;
716
- }[];
717
- };
718
- fetchPullRequestReviews?: (input: {
719
- owner: string;
720
- pr: number;
721
- repo: string;
722
- }) => GithubPullRequestReview[];
723
- };
724
- mergeFreeze?: MergeFreezeStore;
725
- }
726
- declare function validatePrMergeCheckProof(value: unknown): PrMergeCheckProof;
727
- declare function readPrMergeCheckProof(filePath: string): PrMergeCheckProof;
728
- declare function runPrMergeCheck(args: PrMergeCheckArgs, dependencies?: PrMergeCheckDependencies): PrMergeCheckProof;
729
- //#endregion
730
- //#region src/pr-readiness/handled-human-comments.d.ts
731
- declare const HANDLED_COMMENTS_PAYLOAD_KIND: "handled-human-comments";
732
- /**
733
- * Authenticated producer of the durable handled set. Recorded on the readiness
734
- * ledger; unverifiable producers are ignored on read (fail closed).
735
- */
736
- type HandledCommentsProducerMode = "app" | "commit-status";
737
- interface HandledCommentsProducer {
738
- /** App id (stringified) or GitHub login, depending on mode. */
739
- identity: string;
740
- mode: HandledCommentsProducerMode;
741
- }
742
- interface HandledCommentsCheckPayload {
743
- clearedAt?: string;
744
- handledCommentUrls: string[];
745
- kind: typeof HANDLED_COMMENTS_PAYLOAD_KIND;
746
- pr: number;
747
- schemaVersion: 1;
748
- sessionId?: string;
749
- }
750
- //#endregion
751
- //#region src/diff-classification.d.ts
752
- declare const DIFF_CLASSIFICATIONS: readonly ["docs/process-only", "trivial", "non-trivial"];
753
- type DiffClassification = (typeof DIFF_CLASSIFICATIONS)[number];
754
- //#endregion
755
636
  //#region src/pr-verify-mode.d.ts
756
637
  /**
757
638
  * The verification mode `pr:verify` resolved for a run.
@@ -877,6 +758,28 @@ interface CheckRunDependencies {
877
758
  * human-readable mirror, not a proof surface, so a caller that needs an
878
759
  * App-verified check run must be told plainly whether it got one. Returns
879
760
  * `true` only when the App-owned check run landed.
761
+ *
762
+ * "Landed" means GitHub serves it, not that the POST was accepted (#520). On
763
+ * PR #519 the POST was accepted, `pr:ready` reported ready and armed, and the
764
+ * source-pinned required check read `in_progress` across three runs — so GitHub
765
+ * never scheduled the merge and emitted no rollup row saying why. Arming
766
+ * already refuses to infer its outcome from the invocation and reads the pull
767
+ * request back (`arm-auto-merge.ts`); publication now does the same.
768
+ *
769
+ * "Landed" is judged against every run of the name from the pinned App, not
770
+ * against the one GitHub collapses to (#524): the newest must be *this* run, in
771
+ * the status and conclusion that were published, and no run of the name may
772
+ * still be unfinished. A surviving `in_progress` run blocks the required check
773
+ * on its own, so confirming past it would report success on a pull request
774
+ * GitHub will never merge. One read, no polling: an unconfirmed publication
775
+ * returns `false`, which `pr:ready` already turns into a notice and an
776
+ * idempotent re-dispatch.
777
+ *
778
+ * Confirmation is a point-in-time read, deliberately: a later publication for
779
+ * the same name changes the answer — a completed one by becoming the newest, an
780
+ * unfinished one by holding the check open beside this verdict rather than
781
+ * replacing it — and every remaining writer publishes once, as the last thing
782
+ * its invocation does. No retry, no poll, no re-confirm.
880
783
  */
881
784
  declare function ensureFactoryCheckRunPublished(input: PublishFactoryCheckInput, dependencies?: CheckRunDependencies & {
882
785
  onDiagnostic?: (message: string) => void;
@@ -890,6 +793,79 @@ interface PublishHandledCommentsCheckInput {
890
793
  sha: string;
891
794
  }
892
795
  //#endregion
796
+ //#region src/merge-freeze.d.ts
797
+ declare const MERGE_FREEZE_CHECK_NAME = "patronage-factory/merge-freeze";
798
+ declare const MERGE_FREEZE_APP_SLUG = "patronage-factory";
799
+ declare const mergeFreezeStateSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
800
+ active: z.ZodLiteral<true>;
801
+ generationId: z.ZodNumber;
802
+ headSha: z.ZodString;
803
+ outcome: z.ZodEnum<{
804
+ active: "active";
805
+ stale: "stale";
806
+ }>;
807
+ reason: z.ZodString;
808
+ recordedAt: z.ZodISODateTime;
809
+ schemaVersion: z.ZodLiteral<1>;
810
+ }, z.core.$strip>, z.ZodObject<{
811
+ active: z.ZodLiteral<false>;
812
+ clearRationale: z.ZodOptional<z.ZodString>;
813
+ generationId: z.ZodNumber;
814
+ headSha: z.ZodString;
815
+ outcome: z.ZodLiteral<"inactive">;
816
+ reason: z.ZodString;
817
+ recordedAt: z.ZodISODateTime;
818
+ schemaVersion: z.ZodLiteral<1>;
819
+ }, z.core.$strip>], "active">;
820
+ type MergeFreezeState = z.infer<typeof mergeFreezeStateSchema>;
821
+ /**
822
+ * The write side of this contract lives in the generated main-push verify
823
+ * workflow (#356, ADR 0016 as amended): it is the ONLY producer of
824
+ * `patronage-factory/merge-freeze` generations. Its emitted `output.text`
825
+ * payload must parse under this exact reader schema, which is what the
826
+ * workflow's own tests assert through this export.
827
+ */
828
+ declare function validateMergeFreezeState(value: unknown): MergeFreezeState;
829
+ interface MergeFreezeStoreInput {
830
+ cwd: string;
831
+ headSha: string;
832
+ repository: CheckoutRepository;
833
+ }
834
+ interface MergeFreezeStore {
835
+ read: (input: MergeFreezeStoreInput) => unknown;
836
+ }
837
+ /**
838
+ * The freeze generation as an arming decision needs to see it (#477).
839
+ *
840
+ * `read` above collapses three different situations into one refusal, because
841
+ * merge time treated all of them as "do not merge". Arming time distinguishes
842
+ * them: a settled generation is the writer's verdict, a **settling** one is the
843
+ * window between a merge landing and its main-push verify completing (a
844
+ * running generation, or a missing generation proven by the immediately prior
845
+ * settled tip plus a current source-pinned verify run), and an **unreadable**
846
+ * one is ambiguous, malformed, foreign-App, or unavailable state. Same
847
+ * selection, same pinned App, same `started_at` ordering — only the reporting
848
+ * is finer.
849
+ */
850
+ type MergeFreezeGeneration = {
851
+ kind: "settled";
852
+ state: MergeFreezeState;
853
+ } | {
854
+ kind: "settling";
855
+ reason: string;
856
+ running?: {
857
+ headSha: string;
858
+ id: number;
859
+ };
860
+ } | {
861
+ kind: "unreadable";
862
+ reason: string;
863
+ };
864
+ /** A merge-freeze reader that can also report the settle window (#477). */
865
+ interface MergeFreezeAuthority extends MergeFreezeStore {
866
+ readGeneration: (input: MergeFreezeStoreInput) => MergeFreezeGeneration;
867
+ }
868
+ //#endregion
893
869
  //#region src/pr-proof-io.d.ts
894
870
  interface ProofDescriptor<T> {
895
871
  label: string;
@@ -953,6 +929,10 @@ interface ReviewLadderPolicy {
953
929
  }
954
930
  declare const resolveReviewLadderPolicy: (profile: Pick<FactoryProjectProfile, "review">) => ReviewLadderPolicy;
955
931
  //#endregion
932
+ //#region src/review-rungs.d.ts
933
+ declare const EVIDENCE_REVIEW_RUNGS: readonly ["independent-model", "oracle", "human"];
934
+ type EvidenceReviewRung = (typeof EVIDENCE_REVIEW_RUNGS)[number];
935
+ //#endregion
956
936
  //#region src/review-ladder-ledger.d.ts
957
937
  type LadderCycleStage = "interior" | "gate";
958
938
  interface LadderCycleRef {
@@ -1239,12 +1219,11 @@ declare const managedReadinessLedgerSchema: z.ZodObject<{
1239
1219
  name: z.ZodString;
1240
1220
  reason: z.ZodOptional<z.ZodString>;
1241
1221
  scope: z.ZodOptional<z.ZodObject<{
1242
- classifications: z.ZodOptional<z.ZodArray<z.ZodEnum<{
1222
+ classifications: z.ZodArray<z.ZodEnum<{
1243
1223
  trivial: "trivial";
1244
1224
  "docs/process-only": "docs/process-only";
1245
1225
  "non-trivial": "non-trivial";
1246
- }>>>;
1247
- labels: z.ZodOptional<z.ZodArray<z.ZodString>>;
1226
+ }>>;
1248
1227
  }, z.core.$strict>>;
1249
1228
  scopeReason: z.ZodString;
1250
1229
  status: z.ZodEnum<{
@@ -1399,8 +1378,8 @@ declare const managedReadinessLedgerSchema: z.ZodObject<{
1399
1378
  status: z.ZodEnum<{
1400
1379
  blocked: "blocked";
1401
1380
  "not-required": "not-required";
1402
- current: "current";
1403
1381
  stale: "stale";
1382
+ current: "current";
1404
1383
  missing: "missing";
1405
1384
  }>;
1406
1385
  }, z.core.$strip>;
@@ -1412,8 +1391,8 @@ declare const managedReadinessLedgerSchema: z.ZodObject<{
1412
1391
  status: z.ZodEnum<{
1413
1392
  blocked: "blocked";
1414
1393
  "not-required": "not-required";
1415
- current: "current";
1416
1394
  stale: "stale";
1395
+ current: "current";
1417
1396
  missing: "missing";
1418
1397
  }>;
1419
1398
  }, z.core.$strip>>;
@@ -1430,8 +1409,8 @@ declare const managedReadinessLedgerSchema: z.ZodObject<{
1430
1409
  docsOnlyDeltaAccepted: z.ZodOptional<z.ZodBoolean>;
1431
1410
  docsOnlyVerifiedHeadSha: z.ZodOptional<z.ZodString>;
1432
1411
  prVerify: z.ZodEnum<{
1433
- passed: "passed";
1434
1412
  stale: "stale";
1413
+ passed: "passed";
1435
1414
  missing: "missing";
1436
1415
  }>;
1437
1416
  trivialDeltaAccepted: z.ZodOptional<z.ZodBoolean>;
@@ -1446,7 +1425,7 @@ type ReadinessStatus = "ready" | "blocked" | "slice-ready/not-final";
1446
1425
  type ManagedReadinessLedger = z.infer<typeof managedReadinessLedgerSchema>;
1447
1426
  declare function validateManagedReadinessLedger(value: unknown): ManagedReadinessLedger;
1448
1427
  declare namespace status_check_rollup_d_exports {
1449
- export { CheckState, StatusCheckRollup, isFactoryReadyCheck, statusCheckState };
1428
+ export { CheckState, HOSTED_VERIFY_CHECK_NAME, StatusCheckRollup, hostedVerifyCheckState, isFactoryReadyCheck, isHostedVerifyCheck, statusCheckState };
1450
1429
  }
1451
1430
  type CheckState = "passed" | "failed" | "pending" | "none" | "unknown";
1452
1431
  interface StatusCheckRollup {
@@ -1461,6 +1440,38 @@ interface StatusCheckRollup {
1461
1440
  workflowName?: string;
1462
1441
  }
1463
1442
  declare const isFactoryReadyCheck: (check: StatusCheckRollup) => boolean;
1443
+ /**
1444
+ * The hosted verification gate's context name — the branch ruleset's other
1445
+ * source-pinned required check, alongside `patronage-factory/pr-ready`. One
1446
+ * name across the fleet because one generator emits the workflow that posts
1447
+ * it (ADR 0016, 2026-07-31 amendment).
1448
+ */
1449
+ declare const HOSTED_VERIFY_CHECK_NAME = "verify";
1450
+ /**
1451
+ * Is this rollup entry the hosted `verify` gate, from the producer the
1452
+ * ruleset pins it to?
1453
+ *
1454
+ * `workflowName` is the producer signal the rollup actually carries: GitHub
1455
+ * Actions check runs name their workflow, App-posted check runs come back with
1456
+ * an empty one, and a user-token commit status arrives as a `context` with no
1457
+ * `name` at all. Epic #473 wave 2 measured that a same-named check from the
1458
+ * wrong writer is as inert as no check at all, so matching the name alone
1459
+ * would accept exactly the thing the pin rejects.
1460
+ */
1461
+ declare const isHostedVerifyCheck: (check: StatusCheckRollup) => boolean;
1462
+ /**
1463
+ * The hosted `verify` gate's state on this head, read by presence (#477).
1464
+ *
1465
+ * This is the one rollup question the rollup can answer honestly. Wave 2
1466
+ * measured that GitHub omits an *unsatisfied pinned requirement* from
1467
+ * `statusCheckRollup` entirely — there is no "expected" or "missing" row — so
1468
+ * a green rollup is not evidence of mergeability. What the rollup does report
1469
+ * faithfully is the checks that ran. Asking whether this specific check ran,
1470
+ * and passed, on this head turns the rollup's silence from a false green into
1471
+ * a named refusal: `"none"` means the pinned requirement is unsatisfied and
1472
+ * nothing else in the rollup would have said so.
1473
+ */
1474
+ declare const hostedVerifyCheckState: (rollup: StatusCheckRollup[] | undefined) => CheckState;
1464
1475
  declare const statusCheckState: (rollup: StatusCheckRollup[] | undefined, exclude?: (check: StatusCheckRollup) => boolean) => CheckState;
1465
1476
  //#endregion
1466
1477
  //#region src/pr-ready.d.ts
@@ -1479,6 +1490,21 @@ interface PrReadyArgs extends LoadProjectProfileInput {
1479
1490
  verifyProof?: string;
1480
1491
  }
1481
1492
  interface GitHubPullRequest {
1493
+ /**
1494
+ * GitHub's live auto-merge enablement, or `null` when nothing is scheduled.
1495
+ *
1496
+ * Required, not optional (#515 review). `gh pr view --json autoMergeRequest`
1497
+ * always returns the key — explicitly `null` when nothing is scheduled — and
1498
+ * errors loudly on a field name it does not know, so there is no shape where
1499
+ * the real CLI omits it. But `fetchPullRequest` is injectable, and an
1500
+ * implementation that left the field out would silently disable the
1501
+ * `boundary-wave` refusal that stops an epicless run from greening a
1502
+ * candidate GitHub is already scheduled to merge. Requiring it makes the
1503
+ * compiler enforce what `gh` already promises.
1504
+ */
1505
+ autoMergeRequest: {
1506
+ enabledAt?: string;
1507
+ } | null;
1482
1508
  baseRefName: string;
1483
1509
  baseRefOid: string;
1484
1510
  body: string;
@@ -1494,9 +1520,6 @@ interface GitHubPullRequest {
1494
1520
  }[];
1495
1521
  headRefOid: string;
1496
1522
  isDraft: boolean;
1497
- labels?: {
1498
- name: string;
1499
- }[];
1500
1523
  mergeStateStatus: string;
1501
1524
  mergeable: string;
1502
1525
  number: number;
@@ -1516,16 +1539,38 @@ interface GitHubPullRequest {
1516
1539
  url: string;
1517
1540
  }
1518
1541
  interface PrReadyProof {
1519
- schemaVersion: 1;
1542
+ schemaVersion: 3;
1543
+ /**
1544
+ * What GitHub actually did when this run armed native auto-merge (#477).
1545
+ * Present exactly when readiness passed and the arming ran; read back from
1546
+ * the PR rather than inferred from the invocation, because `gh pr merge
1547
+ * --auto` arms, merges, or does nothing with the same exit code and the same
1548
+ * empty output. `not-armed` means the candidate is admitted but nothing will
1549
+ * merge it, and `followUp` carries the re-dispatch.
1550
+ */
1551
+ arming?: {
1552
+ detail?: string;
1553
+ headSha: string;
1554
+ outcome: "armed" | "merged" | "not-armed";
1555
+ };
1556
+ /**
1557
+ * Why this run blocked, one entry per refusing demand (#391): `code` is the
1558
+ * demand key from the resolver's vocabulary, `detail` the one-sentence
1559
+ * refusal. Same refusals as `blockingReasons`, in the same order — the
1560
+ * analyzable projection of a flat string list, so a wall of blocked proofs
1561
+ * on one PR can be counted by cause. Absent when nothing blocked.
1562
+ */
1563
+ blockedReasons?: BlockedReason[];
1520
1564
  blockingReasons: string[];
1521
1565
  humanBlockingReasons: string[];
1522
1566
  command: "patronage-factory pr:ready";
1523
1567
  /**
1524
- * Machine-safe arg array for the obvious next action (#523): when `ready`,
1525
- * the `pr:merge-check` that must precede a merge; otherwise the `pr:verify`
1526
- * re-run that the blocking reasons name (re-verify the head, then re-run
1527
- * pr:ready). Same `argv` shape `factory:delegate --print` emits. Optional
1528
- * and purely additive.
1568
+ * Machine-safe arg array for the obvious next action (#523): when `ready`
1569
+ * but arming did not take effect, a `pr:ready` re-dispatch (idempotent on
1570
+ * an unchanged head); otherwise the `gh pr ready` undraft repair or the
1571
+ * `pr:verify` re-run that the blocking reasons name (re-verify the head,
1572
+ * then re-run pr:ready). Same `argv` shape `factory:delegate --print`
1573
+ * emits. Optional and purely additive.
1529
1574
  */
1530
1575
  followUp?: FollowUpAction;
1531
1576
  /**
@@ -1535,6 +1580,12 @@ interface PrReadyProof {
1535
1580
  * pushed HEAD, not a re-derivation. See the ledger schema.
1536
1581
  */
1537
1582
  ledger: ManagedReadinessLedger;
1583
+ /**
1584
+ * Actionable facts that did not block (#477): a settling merge-freeze
1585
+ * generation that armed with a notice, and how each waived demand reads to a
1586
+ * human. Absent when there are none.
1587
+ */
1588
+ notices?: string[];
1538
1589
  /** Committed profile blob evaluated by readiness. Required for merge. */
1539
1590
  profileBlobSha?: string;
1540
1591
  /** Checkout-relative committed profile path evaluated by readiness. */
@@ -1543,9 +1594,22 @@ interface PrReadyProof {
1543
1594
  /** Trusted checkout repository used for all GitHub reads and writes. */
1544
1595
  repository?: string;
1545
1596
  status: ReadinessStatus;
1597
+ /**
1598
+ * Demands that were in force, were NOT met, and were waived by the operator
1599
+ * (#354, moved here from the merge-time proof by #477). Each entry carries
1600
+ * the demand's refusals verbatim, so a waived demand can never read as a met
1601
+ * one; the arming proceeds on the recorded operator act, not on evidence.
1602
+ */
1603
+ waivedDemands?: WaivedDemand[];
1546
1604
  }
1547
1605
  type PublishHandledCommentsResult = HandledCommentsProducer | Promise<HandledCommentsProducer | undefined> | undefined;
1548
1606
  interface PrReadyDependencies {
1607
+ /**
1608
+ * Arms GitHub native auto-merge for a passing candidate (#477). Defaults to
1609
+ * the real `gh pr merge --auto` call plus the read-back that says what
1610
+ * actually happened.
1611
+ */
1612
+ armAutoMerge?: (input: ArmAutoMergeInput) => ArmAutoMergeOutcome;
1549
1613
  github?: {
1550
1614
  fetchClosingPullRequests?: (input: {
1551
1615
  issue: number;
@@ -1581,7 +1645,26 @@ interface PrReadyDependencies {
1581
1645
  };
1582
1646
  git?: PrReadyGitDependencies;
1583
1647
  hq?: HqIngestDependencies;
1584
- publishCheckRun?: (input: PublishFactoryCheckInput) => void;
1648
+ /**
1649
+ * The authoritative merge-freeze reader (#477). Defaults to the pinned-App
1650
+ * GitHub check-run authority — readiness is a control, so an absent
1651
+ * dependency must not mean an unread freeze.
1652
+ */
1653
+ mergeFreeze?: Pick<MergeFreezeAuthority, "readGeneration">;
1654
+ /**
1655
+ * Awaited publication of the branded check, which is a source-pinned
1656
+ * required check rather than a mirror (#477). Returns true only when the
1657
+ * App-owned run landed. Defaults to `ensureFactoryCheckRunPublished`.
1658
+ *
1659
+ * `pr:ready` has no other publication seam, and this one cannot express a
1660
+ * non-terminal run (#526): `status` is omitted from the input, so every run
1661
+ * readiness creates is completed with a conclusion. A `pr:ready` invocation
1662
+ * therefore cannot leave a run holding the source-pinned required check
1663
+ * open, which is a permanent block on the pull request (#524).
1664
+ */
1665
+ publishFinalCheckRun?: (input: Omit<PublishFactoryCheckInput, "status"> & {
1666
+ conclusion: "failure" | "success";
1667
+ }) => Promise<boolean> | boolean;
1585
1668
  /** Injectable delay for the UNKNOWN-mergeable brief poll (tests). */
1586
1669
  sleep?: (ms: number) => Promise<void>;
1587
1670
  /** Injectable clock for CLI handled-comment provenance (tests). */
@@ -1606,16 +1689,14 @@ declare function runPrReady(args: PrReadyArgs, dependencies?: PrReadyDependencie
1606
1689
  declare const EVIDENCE_CHECK_TYPES: readonly ["review", "verify"];
1607
1690
  type EvidenceCheckType = (typeof EVIDENCE_CHECK_TYPES)[number];
1608
1691
  declare const requiredCheckScopeSchema: z.ZodObject<{
1609
- classifications: z.ZodOptional<z.ZodArray<z.ZodEnum<{
1692
+ classifications: z.ZodArray<z.ZodEnum<{
1610
1693
  trivial: "trivial";
1611
1694
  "docs/process-only": "docs/process-only";
1612
1695
  "non-trivial": "non-trivial";
1613
- }>>>;
1614
- labels: z.ZodOptional<z.ZodArray<z.ZodString>>;
1696
+ }>>;
1615
1697
  }, z.core.$strict>;
1616
1698
  type RequiredCheckScope = z.infer<typeof requiredCheckScopeSchema>;
1617
1699
  interface RequiredCheckScopeContext {
1618
- labels: string[] | undefined;
1619
1700
  classification: DiffClassification | undefined;
1620
1701
  }
1621
1702
  interface ScopeDecision {
@@ -1779,8 +1860,8 @@ declare const REVIEW_STATUS_VALUES: readonly ["not-required", "current", "stale"
1779
1860
  declare const reviewStatusSchema: z.ZodEnum<{
1780
1861
  blocked: "blocked";
1781
1862
  "not-required": "not-required";
1782
- current: "current";
1783
1863
  stale: "stale";
1864
+ current: "current";
1784
1865
  missing: "missing";
1785
1866
  }>;
1786
1867
  type ReviewStatus = z.infer<typeof reviewStatusSchema>;
@@ -2366,8 +2447,54 @@ declare const runDemandWaive: (args: DemandWaiveArgs, dependencies?: DemandWaive
2366
2447
  //#region src/commands/demand-waive.d.ts
2367
2448
  type DemandWaiveAction = (args: DemandWaiveArgs) => DemandWaiver;
2368
2449
  //#endregion
2369
- //#region src/commands/pr-merge-check.d.ts
2370
- type PrMergeCheckAction = (args: PrMergeCheckArgs) => PrMergeCheckProof;
2450
+ //#region src/hq-credentials.d.ts
2451
+ /**
2452
+ * Why a reference did not resolve. Each value names a different remedy, and
2453
+ * none of them can be inferred from a value-or-nothing result:
2454
+ *
2455
+ * - `resolver-missing` — no secret-manager binary on PATH.
2456
+ * - `resolver-blocked` — a binary exists but this session may not execute it
2457
+ * (a sandbox denying exec). The command belongs outside the sandbox.
2458
+ * - `resolver-timeout` — the probe expired: a desktop agent waiting on an
2459
+ * approval nobody can give here.
2460
+ * - `resolver-refused` — the binary ran and produced no value. Its store is
2461
+ * unreachable from this session (a sandbox with no keychain access) or the
2462
+ * reference is not readable. These two stay one status on purpose: telling
2463
+ * them apart would mean reading resolver output.
2464
+ */
2465
+ type SecretResolutionFailure = "resolver-blocked" | "resolver-missing" | "resolver-refused" | "resolver-timeout";
2466
+ /** A structured resolution outcome. The value travels only when resolved. */
2467
+ type SecretResolution = {
2468
+ status: "resolved";
2469
+ value: string;
2470
+ } | {
2471
+ status: SecretResolutionFailure;
2472
+ };
2473
+ /** Resolves a secret reference. Injected so tests stay offline. */
2474
+ type SecretReferenceResolver = (reference: string) => SecretResolution;
2475
+ //#endregion
2476
+ //#region src/hq-flush.d.ts
2477
+ /**
2478
+ * Spool locations this run did not drain because they sit under an earlier
2479
+ * key for this repository. Reported, never drained: draining an older key's
2480
+ * events is an operator decision, made with `--dir`.
2481
+ */
2482
+ interface HqFlushOrphans {
2483
+ orphans: HqSpoolOrphan[];
2484
+ }
2485
+ type HqFlushResult = (HqFlushOrphans & {
2486
+ reason: string;
2487
+ /**
2488
+ * Events still spooled when the command gave up. Non-zero means the
2489
+ * skip retained work: the exit status says so, and no caller may read
2490
+ * the skip as "there was nothing to do".
2491
+ */
2492
+ retained: number;
2493
+ status: "skipped";
2494
+ }) | (HqFlushOrphans & HqSpoolFlushSummary & {
2495
+ endpoint: string;
2496
+ status: "flushed";
2497
+ });
2371
2498
  //#endregion
2372
2499
  //#region src/pr-review.d.ts
2373
2500
  interface PrReviewArgs extends LoadProjectProfileInput {
@@ -2449,6 +2576,14 @@ type FollowUpRunner = (followUp: FollowUpAction, cwd: string) => Promise<void> |
2449
2576
  interface PrPublishHandoff {
2450
2577
  humanActions: string[];
2451
2578
  routeOwnedRepairs: ReadinessRepair[];
2579
+ /**
2580
+ * Whether the merge is actually on GitHub's schedule (#477, #515 review).
2581
+ * An admitted candidate is not a handed-off one: `pr:ready` arms only when a
2582
+ * boundary wave authorized it, and the arming can fail to take effect. A
2583
+ * readiness status of `ready` says the candidate passed, never that anything
2584
+ * will merge it — so publish reads this before claiming nothing is owed.
2585
+ */
2586
+ scheduled: boolean;
2452
2587
  status: PrReadyProof["status"];
2453
2588
  }
2454
2589
  type PrPublishFollowUpOutcome = {
@@ -2514,6 +2649,15 @@ interface PrPublishDependencies extends PrReadyDependencies {
2514
2649
  runFollowUp?: FollowUpRunner;
2515
2650
  runPrReady?: typeof runPrReady;
2516
2651
  runPrReview?: (args: PrReviewArgs) => Promise<PrReviewProof>;
2652
+ /** Settles asynchronously scheduled sink work before the handoff drain. */
2653
+ awaitPendingIngest?: () => Promise<void>;
2654
+ /**
2655
+ * The handoff drain (#390). Advisory everywhere: publish reports what it
2656
+ * found and never changes its verdict or exit status on the result.
2657
+ */
2658
+ flushHqSpool?: (args: {
2659
+ cwd: string;
2660
+ }) => Promise<HqFlushResult>;
2517
2661
  runPrVerify?: (args: PrVerifyArgs) => Promise<PrVerifyProof>;
2518
2662
  /**
2519
2663
  * Injectable delay for the bounded hosted-run await (#348; tests only —
@@ -3225,8 +3369,8 @@ declare const FACTORY_TRACE_EVENT_DEFINITIONS: readonly [DefinedTraceEvent<"revi
3225
3369
  interiorCycle: z.ZodOptional<z.ZodNumber>;
3226
3370
  observedAt: z.ZodString;
3227
3371
  purpose: z.ZodEnum<{
3228
- code: "code";
3229
3372
  review: "review";
3373
+ code: "code";
3230
3374
  }>;
3231
3375
  slotResolution: z.ZodObject<{
3232
3376
  effort: z.ZodEnum<{
@@ -3304,8 +3448,8 @@ declare const FACTORY_TRACE_EVENT_DEFINITIONS: readonly [DefinedTraceEvent<"revi
3304
3448
  interiorCycle: z.ZodOptional<z.ZodNumber>;
3305
3449
  observedAt: z.ZodString;
3306
3450
  purpose: z.ZodEnum<{
3307
- code: "code";
3308
3451
  review: "review";
3452
+ code: "code";
3309
3453
  }>;
3310
3454
  slotResolution: z.ZodObject<{
3311
3455
  effort: z.ZodEnum<{
@@ -3374,8 +3518,8 @@ declare const FACTORY_TRACE_EVENT_DEFINITIONS: readonly [DefinedTraceEvent<"revi
3374
3518
  interiorCycle: z.ZodOptional<z.ZodNumber>;
3375
3519
  observedAt: z.ZodString;
3376
3520
  purpose: z.ZodEnum<{
3377
- code: "code";
3378
3521
  review: "review";
3522
+ code: "code";
3379
3523
  }>;
3380
3524
  slotResolution: z.ZodObject<{
3381
3525
  effort: z.ZodEnum<{
@@ -3447,7 +3591,7 @@ declare const FACTORY_TRACE_EVENT_DEFINITIONS: readonly [DefinedTraceEvent<"revi
3447
3591
  exitCode: number | null;
3448
3592
  harness: string;
3449
3593
  observedAt: string;
3450
- purpose: "code" | "review";
3594
+ purpose: "review" | "code";
3451
3595
  slotResolution: {
3452
3596
  effort: "high" | "low" | "medium" | "xhigh";
3453
3597
  engine: string;
@@ -3594,7 +3738,7 @@ declare const scanFactoryTraceDiagnostics: ({
3594
3738
  //#endregion
3595
3739
  //#region src/interior-telemetry/gate-timing.d.ts
3596
3740
  declare const GATE_TIMING_SCHEMA_VERSION = 1;
3597
- type FactoryGateName = "boundary:check" | "factory:closeout" | "pr:merge-check" | "pr:ready" | "pr:review" | "pr:verify";
3741
+ type FactoryGateName = "boundary:check" | "factory:closeout" | "pr:ready" | "pr:review" | "pr:verify";
3598
3742
  type GateTimingOutcome = "fail" | "pass";
3599
3743
  interface GateTimingRecord {
3600
3744
  agentRunId: string;
@@ -3666,13 +3810,39 @@ declare const isBotLogin: (login?: string | undefined) => boolean;
3666
3810
  declare const SHA_MATCH_MIN_LENGTH = 7;
3667
3811
  declare const sameHeadSha: (left: string, right: string) => boolean;
3668
3812
  //#endregion
3669
- //#region src/doctor.d.ts
3813
+ //#region src/doctor-hq-checks.d.ts
3814
+ /**
3815
+ * Two `doctor` checks that make silent HQ delivery failure loud (#394,
3816
+ * epic #389 wave 2).
3817
+ *
3818
+ * Both consume #414's structured credential resolution rather than inventing
3819
+ * a second classification: a sandboxed session that cannot reach the
3820
+ * keychain is reported as "cannot resolve credentials here", never as
3821
+ * "credentials are wrong" or a silent pass. Both name `psf hq:flush` and
3822
+ * `psf pr:publish` as the commands that need a trusted local session.
3823
+ *
3824
+ * Advisory stays advisory: neither check can block anything but doctor's own
3825
+ * exit status (epic #389 design decision 4). Absent credentials make the
3826
+ * remote check a skip with a printed reason, never a silent pass.
3827
+ */
3670
3828
  type DoctorCheckStatus = "error" | "ok" | "warning";
3671
3829
  interface DoctorCheck {
3672
3830
  message: string;
3673
3831
  name: string;
3674
3832
  status: DoctorCheckStatus;
3675
3833
  }
3834
+ interface HqSpoolCheckDependencies {
3835
+ countSpool?: typeof countHqSpoolWork;
3836
+ sweepOrphans?: typeof sweepHqSpoolOrphans;
3837
+ }
3838
+ interface HqRetroReadbackCheckDependencies {
3839
+ fetch?: typeof fetch;
3840
+ readFile?: typeof readFile;
3841
+ resolve?: SecretReferenceResolver;
3842
+ timeoutMs?: number;
3843
+ }
3844
+ //#endregion
3845
+ //#region src/doctor.d.ts
3676
3846
  interface DoctorReport {
3677
3847
  checks: DoctorCheck[];
3678
3848
  ok: boolean;
@@ -3689,6 +3859,10 @@ interface DoctorProjectProfileInput extends LoadProjectProfileInput {
3689
3859
  /** Diff base for the admission preflight; ignored unless `preflight`. */
3690
3860
  base?: string;
3691
3861
  env?: NodeJS.ProcessEnv;
3862
+ /** Test seam for the local HQ spool check (#394); production never sets this. */
3863
+ hqRetroReadbackDependencies?: HqRetroReadbackCheckDependencies;
3864
+ /** Test seam for the remote HQ retro-envelope read-back check (#394). */
3865
+ hqSpoolDependencies?: HqSpoolCheckDependencies;
3692
3866
  /**
3693
3867
  * Append the read-only admission checklist (#292): every requirement this
3694
3868
  * candidate must satisfy before merge, named in one pass. Preflight checks
@@ -3697,7 +3871,7 @@ interface DoctorProjectProfileInput extends LoadProjectProfileInput {
3697
3871
  preflight?: boolean;
3698
3872
  userConfig?: LoadUserConfigResult;
3699
3873
  }
3700
- declare function doctorProjectProfile(input?: DoctorProjectProfileInput): DoctorReport;
3874
+ declare function doctorProjectProfile(input?: DoctorProjectProfileInput): Promise<DoctorReport>;
3701
3875
  //#endregion
3702
3876
  //#region src/pr-review-gate-trace.d.ts
3703
3877
  interface ReviewGateTraceIdentity {
@@ -3749,8 +3923,8 @@ type WorktreeRootLister = (worktreePath: string) => string[] | undefined;
3749
3923
  declare function checkWorktreeScratchFiles(worktreePath?: string, listRootEntries?: WorktreeRootLister): WorktreeScratchFileCheck;
3750
3924
  /**
3751
3925
  * Human-readable one-line summary of a scratch-file check, owned beside the
3752
- * detection logic (mirroring worktreeHeldBranchNotice) so planner output,
3753
- * docs, and future surfaces cannot drift apart.
3926
+ * detection logic so planner output, docs, and future surfaces cannot drift
3927
+ * apart.
3754
3928
  */
3755
3929
  declare function formatScratchFileCheckSummary(check: WorktreeScratchFileCheck): string;
3756
3930
  declare namespace pr_body_metadata_d_exports {
@@ -3999,6 +4173,8 @@ interface EvaluationInput {
3999
4173
  reviews?: PrReadinessReview[];
4000
4174
  unresolvedReviewThreads: number;
4001
4175
  requiredChecks: CheckState;
4176
+ hostedVerifyCheck?: CheckState;
4177
+ autoMergeEnabled?: boolean;
4002
4178
  draft: boolean;
4003
4179
  currentWithBase: boolean;
4004
4180
  mergeable: string;
@@ -4013,7 +4189,6 @@ interface EvaluationInput {
4013
4189
  evidenceEnvelopes?: LoadedEvidenceEnvelope[];
4014
4190
  mergeBaseSha?: string;
4015
4191
  authoringSessionIds?: string[];
4016
- prLabels?: string[];
4017
4192
  priorLedger?: Pick<ManagedReadinessLedger, "reviewRuns">;
4018
4193
  handledCommentUrls?: string[];
4019
4194
  checkRunHandledCommentUrls?: string[];
@@ -4023,9 +4198,18 @@ interface EvaluationInput {
4023
4198
  };
4024
4199
  handledCommentSessionId?: string;
4025
4200
  handledCommentClearedAt?: string;
4026
- waveReviewDemand?: Pick<WaveReviewDemand, "review" | "wave">;
4201
+ waveReviewDemand?: Pick<WaveReviewDemand, "autoMerge" | "review" | "wave">;
4202
+ mergeFreeze?: {
4203
+ blockingReasons: string[];
4204
+ notices: string[];
4205
+ };
4206
+ waivers?: DemandWaiver[];
4027
4207
  }
4028
4208
  declare const evaluateReadiness: (input: EvaluationInput) => {
4209
+ blockedReasons: {
4210
+ code: string;
4211
+ detail: string;
4212
+ }[];
4029
4213
  blockingReasons: string[];
4030
4214
  humanBlockingReasons: string[];
4031
4215
  ledger: {
@@ -4053,14 +4237,14 @@ declare const evaluateReadiness: (input: EvaluationInput) => {
4053
4237
  reviews: {
4054
4238
  correctness: {
4055
4239
  required: boolean;
4056
- status: "blocked" | "not-required" | "current" | "stale" | "missing";
4240
+ status: "blocked" | "not-required" | "stale" | "current" | "missing";
4057
4241
  docsOnlyDeltaAccepted?: boolean | undefined;
4058
4242
  reviewedHeadSha?: string | undefined;
4059
4243
  reviewedPatchId?: string | undefined;
4060
4244
  };
4061
4245
  security?: {
4062
4246
  required: boolean;
4063
- status: "blocked" | "not-required" | "current" | "stale" | "missing";
4247
+ status: "blocked" | "not-required" | "stale" | "current" | "missing";
4064
4248
  docsOnlyDeltaAccepted?: boolean | undefined;
4065
4249
  reviewedHeadSha?: string | undefined;
4066
4250
  reviewedPatchId?: string | undefined;
@@ -4070,7 +4254,7 @@ declare const evaluateReadiness: (input: EvaluationInput) => {
4070
4254
  stackRole: "slice" | "single" | "rollup" | "merge-gate prerequisite";
4071
4255
  verification: {
4072
4256
  command: "patronage-factory pr:verify";
4073
- prVerify: "passed" | "stale" | "missing";
4257
+ prVerify: "stale" | "passed" | "missing";
4074
4258
  docsOnlyDeltaAccepted?: boolean | undefined;
4075
4259
  docsOnlyVerifiedHeadSha?: string | undefined;
4076
4260
  trivialDeltaAccepted?: boolean | undefined;
@@ -4085,8 +4269,7 @@ declare const evaluateReadiness: (input: EvaluationInput) => {
4085
4269
  status: "satisfied" | "unmet" | "out-of-scope";
4086
4270
  reason?: string | undefined;
4087
4271
  scope?: {
4088
- classifications?: ("trivial" | "docs/process-only" | "non-trivial")[] | undefined;
4089
- labels?: string[] | undefined;
4272
+ classifications: ("trivial" | "docs/process-only" | "non-trivial")[];
4090
4273
  } | undefined;
4091
4274
  }[] | undefined;
4092
4275
  handledCommentsProducer?: {
@@ -4155,12 +4338,19 @@ declare const evaluateReadiness: (input: EvaluationInput) => {
4155
4338
  reviewRuns?: PrReviewResult[] | undefined;
4156
4339
  reviewTerminalState?: "blocked" | "accepted-with-findings" | "clean" | undefined;
4157
4340
  };
4341
+ /**
4342
+ * Facts a reader needs that are not refusals (#477): the settle-window
4343
+ * arming notice, and every waived demand rendered so it can never read as
4344
+ * a met one.
4345
+ */
4346
+ notices: string[];
4158
4347
  repairs: {
4159
4348
  action: string;
4160
4349
  code: "undraft-pr" | "render-pr-body-sections" | "await-post-undraft-checks";
4161
4350
  command: string;
4162
4351
  }[];
4163
- status: ReadinessStatus;
4352
+ status: ReadinessStatus; /** Demands that were in force, were NOT met, and the operator waived. */
4353
+ waivedDemands: WaivedDemand[];
4164
4354
  };
4165
4355
  //#endregion
4166
4356
  //#region src/evidence-emit.d.ts
@@ -4356,6 +4546,7 @@ type FetchLike = (input: string, init?: {
4356
4546
  headers?: Record<string, string>;
4357
4547
  method?: string;
4358
4548
  redirect?: "error";
4549
+ signal?: AbortSignal;
4359
4550
  }) => Promise<{
4360
4551
  json: () => Promise<unknown>;
4361
4552
  status: number;
@@ -4370,6 +4561,14 @@ interface PublishEpicStructureArgs {
4370
4561
  event: EpicStructureEvent;
4371
4562
  url: string;
4372
4563
  fetchImpl?: FetchLike;
4564
+ /**
4565
+ * Optional caller-owned abort signal. A caller that bounds this call with
4566
+ * its own deadline (e.g. `factory:closeout`'s advisory re-emission, #392)
4567
+ * can abort the in-flight request itself instead of merely abandoning the
4568
+ * `await` — leaving the outbound socket alive past the caller's own
4569
+ * declared timeout.
4570
+ */
4571
+ signal?: AbortSignal;
4373
4572
  }
4374
4573
  interface PublishEpicStructureResult {
4375
4574
  duplicate: boolean;
@@ -4492,7 +4691,6 @@ interface CreateProgramOptions {
4492
4691
  actions?: {
4493
4692
  boundaryCheck?: BoundaryCheckAction;
4494
4693
  demandWaive?: DemandWaiveAction;
4495
- prMergeCheck?: PrMergeCheckAction;
4496
4694
  prPublish?: PrPublishAction;
4497
4695
  prReady?: PrReadyAction;
4498
4696
  prReview?: PrReviewAction;
@@ -4503,4 +4701,4 @@ interface CreateProgramOptions {
4503
4701
  declare function createProgram(options?: CreateProgramOptions): Command;
4504
4702
  declare function run(argv?: string[]): Promise<void>;
4505
4703
  //#endregion
4506
- export { type AppendFactoryTraceEventResult, type AssembledReviewPrompt, type BoundaryCheckArgs, type BoundaryCheckDependencies, type BoundaryCheckProofRecord, type BuildEpicStructureEventInput, type BuildRetroEnvelopeInput, type CloudflareAccessServiceToken, CreateProgramOptions, DEFAULT_DEMAND_WAIVER_PATH, DEFAULT_FACTORY_REPOSITORY, type DagDocument, type DemandWaiveArgs, type DemandWaiveDependencies, DemandWaiveRefusalError, type DemandWaiver, type DemandWaiverStore, EPIC_STRUCTURE_NODE_STATUSES, EPIC_STRUCTURE_SCHEMA_VERSION, type EpicStructureEvent, type EpicStructureGraphPayload, type EpicStructureNodeStatus, EpicStructureValidationError, type EvidenceEmitArgs, type EvidenceEmitDependencies, type EvidenceEmitResult, type FactoryCliInvocation, FactoryCliInvocationSchema, type FactoryProjectProfile, type FactoryTraceDiagnostic, type FactoryTraceEnvelope, type FactoryTraceEvent, type FindingDisposition, type FindingLedgerEntry, type FollowUpAction, FollowUpActionSchema, type IssueReviewFocus, type LadderDispositionDeclaration, MERGE_FREEZE_APP_SLUG, MERGE_FREEZE_CHECK_NAME, type MergeFreezeState, type ParsedRetroEnvelope, type PrPublishArgs, type PrPublishDependencies, PrPublishFollowUpError, type PrPublishFollowUpOutcome, type PrPublishHandoff, type PrPublishResult, type PrReadyArgs, type PrReadyProof, type PublishEpicStructureArgs, type PublishEpicStructureResult, type PublishFollowUpPlan, RETRO_ENVELOPE_SCHEMA_VERSION, RETRO_ENVELOPE_VALIDATORS, REVIEW_FOCUS_SECTION, type RetroEnvelope, type ReviewGateNotRequiredProof, type ReviewGateTraceIdentity, type ReviewLadderCycle, type ReviewLadderEvaluation, type ReviewLadderPolicy, type ReviewLadderStageEvent, type ReviewLadderTraceIdentity, type ReviewPromptSection, type ReviewPromptSectionProvenance, SUPPORTED_RETRO_ENVELOPE_SCHEMA_VERSIONS, type ScanFactoryTraceDiagnosticsOptions, type ScanFactoryTraceOptions, type ScanFactoryTraceResult, type TraceMirrorDiagnostic, type TraceSink, type TraceWriteResult, type TraceWriteSinks, type VerificationReuse, type WaivedDemand, WorkerCheckoutGuardError, type WorkerCloseoutLessonsTraceEvent, appendReviewLadderStageTraceEvent, appendWithTraceSinks, applyDemandWaiver, assembleReviewPrompt, assertWorkerCheckoutAllowed, authorizeDemandWaiver, blockingLadderFindings, boundary_manifest_d_exports as boundaryManifest, boundary_review_proof_d_exports as boundaryReviewProof, buildEpicStructureEvent, buildEpicStructurePayload, buildEvidenceEnvelope, buildRetroEnvelope, buildReviewGateNotRequiredTraceEvent, buildReviewLadderStageTraceEvent, comment_provenance_d_exports as commentProvenance, createLocalJsonlTraceSink, createProgram, doctorProjectProfile, epicStructureEventId, evaluateReviewLadder, evidenceEnvelopeFilename, findingKey, followUpFromArgv, inferFixedInThreadDispositions, isProductionHqUrl, isRetroEnvelopeWireComplete, loadProjectProfile, normalizeIssueComments, openLadderFindings, parseRetroEnvelope, planPublishFollowUp, worktree_held_branch_d_exports as prMergePreflight, pr_body_metadata_d_exports as prReadinessBodyMetadata, readiness_evaluation_d_exports as prReadinessEvaluation, external_evidence_d_exports as prReadinessExternalEvidence, merge_identity_d_exports as prReadinessMergeIdentity, post_readiness_comments_d_exports as prReadinessPostComments, pr_body_renderer_d_exports as prReadinessPrBodyRenderer, proof_identity_d_exports as prReadinessProofIdentity, review_proof_d_exports as prReadinessReviewProof, status_check_rollup_d_exports as prReadinessStatusChecks, verification_proof_d_exports as prReadinessVerificationProof, publishEpicStructure, readDemandWaivers, readPrMergeCheckProof, readPrReadyProof, readPrReviewProof, resolveFactoryRepository, resolveFindingBlocking, resolveReviewFindingCategory, resolveReviewFindingSeverity, resolveReviewLadderPolicy, resolveTraceWriteSinks, retroEnvelopeSchemaVersionOf, retroEnvelopeV1Schema, retroEpicReference, reviewCycleStateFor, reviewFocusFromIssueBody, reviewPromptSectionSchema, reviewPromptSectionsSchema, run, runBoundaryCheck, runDemandWaive, runEvidenceEmit, runPrMergeCheck, runPrPublish, runPrReady, runPrReview, runPrVerify, scanFactoryTraceDiagnostics, scanFactoryTraceEvents, selectWaiversForCandidate, staleRepeatLadderFindings, toFactoryTraceEnvelope, tryAppendReviewGateNotRequiredTraceEvent, tryAppendReviewLadderStageTraceEvent, validateBoundaryCheckProof, validateDagDocument, validateDemandWaiverStore, validateFactoryTraceEvent, validateMergeFreezeState, validatePrMergeCheckProof, validatePrReadyProof, validatePrReviewProof, validatePrVerifyProof, waivedDemandNotice, waivedDemandSchema, worktree_scratch_files_d_exports as worktreeScratchFiles };
4704
+ export { type AppendFactoryTraceEventResult, type AssembledReviewPrompt, type BoundaryCheckArgs, type BoundaryCheckDependencies, type BoundaryCheckProofRecord, type BuildEpicStructureEventInput, type BuildRetroEnvelopeInput, type CloudflareAccessServiceToken, CreateProgramOptions, DEFAULT_DEMAND_WAIVER_PATH, DEFAULT_FACTORY_REPOSITORY, type DagDocument, type DemandWaiveArgs, type DemandWaiveDependencies, DemandWaiveRefusalError, type DemandWaiver, type DemandWaiverStore, EPIC_STRUCTURE_NODE_STATUSES, EPIC_STRUCTURE_SCHEMA_VERSION, type EpicStructureEvent, type EpicStructureGraphPayload, type EpicStructureNodeStatus, EpicStructureValidationError, type EvidenceEmitArgs, type EvidenceEmitDependencies, type EvidenceEmitResult, type FactoryCliInvocation, FactoryCliInvocationSchema, type FactoryProjectProfile, type FactoryTraceDiagnostic, type FactoryTraceEnvelope, type FactoryTraceEvent, type FindingDisposition, type FindingLedgerEntry, type FollowUpAction, FollowUpActionSchema, type IssueReviewFocus, type LadderDispositionDeclaration, MERGE_FREEZE_APP_SLUG, MERGE_FREEZE_CHECK_NAME, type MergeFreezeState, type ParsedRetroEnvelope, type PrPublishArgs, type PrPublishDependencies, PrPublishFollowUpError, type PrPublishFollowUpOutcome, type PrPublishHandoff, type PrPublishResult, type PrReadyArgs, type PrReadyProof, type PublishEpicStructureArgs, type PublishEpicStructureResult, type PublishFollowUpPlan, RETRO_ENVELOPE_SCHEMA_VERSION, RETRO_ENVELOPE_VALIDATORS, REVIEW_FOCUS_SECTION, type RetroEnvelope, type ReviewGateNotRequiredProof, type ReviewGateTraceIdentity, type ReviewLadderCycle, type ReviewLadderEvaluation, type ReviewLadderPolicy, type ReviewLadderStageEvent, type ReviewLadderTraceIdentity, type ReviewPromptSection, type ReviewPromptSectionProvenance, SUPPORTED_RETRO_ENVELOPE_SCHEMA_VERSIONS, type ScanFactoryTraceDiagnosticsOptions, type ScanFactoryTraceOptions, type ScanFactoryTraceResult, type TraceMirrorDiagnostic, type TraceSink, type TraceWriteResult, type TraceWriteSinks, type VerificationReuse, type WaivedDemand, WorkerCheckoutGuardError, type WorkerCloseoutLessonsTraceEvent, appendReviewLadderStageTraceEvent, appendWithTraceSinks, applyDemandWaiver, assembleReviewPrompt, assertWorkerCheckoutAllowed, authorizeDemandWaiver, blockingLadderFindings, boundary_manifest_d_exports as boundaryManifest, boundary_review_proof_d_exports as boundaryReviewProof, buildEpicStructureEvent, buildEpicStructurePayload, buildEvidenceEnvelope, buildRetroEnvelope, buildReviewGateNotRequiredTraceEvent, buildReviewLadderStageTraceEvent, comment_provenance_d_exports as commentProvenance, createLocalJsonlTraceSink, createProgram, doctorProjectProfile, epicStructureEventId, evaluateReviewLadder, evidenceEnvelopeFilename, findingKey, followUpFromArgv, inferFixedInThreadDispositions, isProductionHqUrl, isRetroEnvelopeWireComplete, loadProjectProfile, normalizeIssueComments, openLadderFindings, parseRetroEnvelope, planPublishFollowUp, pr_body_metadata_d_exports as prReadinessBodyMetadata, readiness_evaluation_d_exports as prReadinessEvaluation, external_evidence_d_exports as prReadinessExternalEvidence, post_readiness_comments_d_exports as prReadinessPostComments, pr_body_renderer_d_exports as prReadinessPrBodyRenderer, proof_identity_d_exports as prReadinessProofIdentity, review_proof_d_exports as prReadinessReviewProof, status_check_rollup_d_exports as prReadinessStatusChecks, verification_proof_d_exports as prReadinessVerificationProof, publishEpicStructure, readDemandWaivers, readPrReadyProof, readPrReviewProof, resolveFactoryRepository, resolveFindingBlocking, resolveReviewFindingCategory, resolveReviewFindingSeverity, resolveReviewLadderPolicy, resolveTraceWriteSinks, retroEnvelopeSchemaVersionOf, retroEnvelopeV1Schema, retroEpicReference, reviewCycleStateFor, reviewFocusFromIssueBody, reviewPromptSectionSchema, reviewPromptSectionsSchema, run, runBoundaryCheck, runDemandWaive, runEvidenceEmit, runPrPublish, runPrReady, runPrReview, runPrVerify, scanFactoryTraceDiagnostics, scanFactoryTraceEvents, selectWaiversForCandidate, staleRepeatLadderFindings, toFactoryTraceEnvelope, tryAppendReviewGateNotRequiredTraceEvent, tryAppendReviewLadderStageTraceEvent, validateBoundaryCheckProof, validateDagDocument, validateDemandWaiverStore, validateFactoryTraceEvent, validateMergeFreezeState, validatePrReadyProof, validatePrReviewProof, validatePrVerifyProof, waivedDemandNotice, waivedDemandSchema, worktree_scratch_files_d_exports as worktreeScratchFiles };