@patronage/software-factory 1.0.0-alpha.31 → 1.0.0-alpha.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -64,9 +64,9 @@ pnpm exec patronage-factory --help # or: psf --help
64
64
 
65
65
  The repository profile declares its admission policy. Operator-local settings and HQ credentials are documented separately, so they do not become committed project configuration.
66
66
 
67
- Generated production workflows may call `psf production:impact --before <sha> --after <sha> --github-output <path>`. The command recomputes the profile's opt-in impact subscriptions from those exact real commits and emits stable demanded/withdrawn target outputs, the changed-file decision basis, and unsubscribed paths. It only withdraws work: missing, zero, malformed, unreachable, non-ancestral, or otherwise unclassifiable identities leave every known target demanded. The factory owns this decision seam; each consumer owns its deploy jobs, commands, credentials, topology, and post-deploy convergence/no-op proof. Merge-target Verify remains full and does not consult this command.
67
+ Generated production workflows may call `psf production:impact --candidate <sha> [--stage <name>] --github-output <path>`. The command resolves demand for each declared impact target from that target's last trusted successful deployment receipt to the checked-out candidate, never from a push's before/after commits, and emits stable demanded/withdrawn target outputs, each target's baseline and delta, and unsubscribed paths. It only withdraws work: a target is withdrawn when its trusted baseline is an ancestor of the candidate and the delta provably misses it, or when the candidate is already behind that baseline (the JSON reads `superseded`); a missing, failed, untrusted, unreachable, or non-ancestor baseline keeps it demanded, and an unusable candidate identity, a missing GitHub Actions run identity, or a profile-scoping refusal leaves every target demanded. After its deploy and health steps, the consumer's job calls `psf production:impact --publish-receipt success|failure --target <name> --candidate <sha>` to write the receipt the next decision reads. The factory owns this decision seam; each consumer owns its deploy jobs, commands, credentials, topology, health policy, and post-deploy convergence/no-op proof. Merge-target Verify remains full and does not consult this command. The [command reference](https://factory.patronage.com/command-reference) defines what makes a receipt trusted.
68
68
 
69
- Generated pull-request workflows may call `psf candidate:impact --base <sha> --head <sha> --github-output <path>` to route Candidate verification surfaces. The command evaluates the same profile impact declarations and pnpm dependency graph from the exact base/head identity, diffed from their merge base; it is the candidate-named sibling of `production:impact` and replaces repository-owned path filters, never adds a second matcher. The `head` commit must be the checked-out `HEAD`, and both commits must be reachable (`fetch-depth: 0`). Every doubt path — unreadable identity, malformed or unsupported graph data, an unowned profile change, a working-tree profile that differs from the head commit, or classifier refusal — fails closed and leaves every declared target demanded. `production:impact` keeps its merge-push `before`/`after` contract unchanged.
69
+ Generated pull-request workflows may call `psf candidate:impact --base <sha> --head <sha> --github-output <path>` to route Candidate verification surfaces. The command evaluates the same profile impact declarations and pnpm dependency graph from the exact base/head identity, diffed from their merge base; it is the candidate-named sibling of `production:impact` and replaces repository-owned path filters, never adds a second matcher. The `head` commit must be the checked-out `HEAD`, and both commits must be reachable (`fetch-depth: 0`). Every doubt path — unreadable identity, malformed or unsupported graph data, an unowned profile change, a working-tree profile that differs from the head commit, or classifier refusal — fails closed and leaves every declared target demanded. `production:impact` decides from deployment receipts, not from a push diff.
70
70
 
71
71
  For factory workflow, install the four canonical skills as the `patronage-software-factory` host plugin (Claude Code, Codex, or Cursor; see [`skills/README.md`](../skills/README.md)). Cloud agents that cannot load host plugins fall back to:
72
72
 
package/dist/index.d.ts CHANGED
@@ -611,7 +611,7 @@ type PolicyResolution = z.infer<typeof policyResolutionSchema>;
611
611
  type ReviewFindingSeverity = "critical" | "high" | "medium" | "low" | "unknown";
612
612
  type ReviewFindingCategory = "correctness" | "safety" | "coordination" | "maintainability" | "unknown";
613
613
  declare const REVIEW_SEVERITIES: readonly ["critical", "high", "medium", "low", "unknown"];
614
- declare const FINDING_DISPOSITIONS: readonly ["open", "fixed-in-thread", "follow-up-filed", "waived", "stale-repeat", "rerun-noise"];
614
+ declare const FINDING_DISPOSITIONS: readonly ["open", "fixed-in-thread", "follow-up-filed", "waived", "policy-accepted", "stale-repeat"];
615
615
  type FindingDisposition = (typeof FINDING_DISPOSITIONS)[number];
616
616
  interface ReviewFindingAttributes {
617
617
  blockingAfterCap?: boolean;
@@ -632,6 +632,26 @@ interface ReviewLadderPolicy {
632
632
  };
633
633
  }
634
634
  //#endregion
635
+ //#region src/review-recovery-item.d.ts
636
+ /**
637
+ * A schema-2 declaration the bounded import could not convert (ADR 0004
638
+ * decision 7): the declaration as stored, the cycle that recorded it, why it
639
+ * was refused and the occurrence addresses it could have meant. While any
640
+ * item is present the proof admits nothing; an operator re-declares by
641
+ * address on the next recording.
642
+ */
643
+ interface ReviewRecoveryItem {
644
+ candidates: string[];
645
+ cycle: number;
646
+ declaration: {
647
+ disposition: "fixed-in-thread" | "follow-up-filed" | "waived";
648
+ file?: string;
649
+ reference?: string;
650
+ title: string;
651
+ };
652
+ reason: string;
653
+ }
654
+ //#endregion
635
655
  //#region src/review-ladder-ledger.d.ts
636
656
  type LadderCycleStage = "interior" | "gate";
637
657
  interface LadderCycleRef {
@@ -639,28 +659,100 @@ interface LadderCycleRef {
639
659
  stage: LadderCycleStage;
640
660
  }
641
661
  type LadderFindingReport = ReviewFindingAttributes;
642
- type DeclarableFindingDisposition = Exclude<FindingDisposition, "open" | "stale-repeat" | "rerun-noise">;
643
- interface LadderDispositionDeclaration {
644
- disposition: DeclarableFindingDisposition;
645
- finding: {
646
- file?: string;
647
- title: string;
648
- };
649
- findingReport?: LadderFindingReport;
662
+ /** One finding as recorded in one cycle (CONTEXT.md: finding occurrence). */
663
+ interface ReviewOccurrenceRecord {
664
+ address: string;
665
+ /** The ladder-assigned cycle number the occurrence was recorded in. */
666
+ declaredCycle: number;
667
+ /**
668
+ * The root occurrence this one repeats: exact `findingKey` equality with an
669
+ * earlier occurrence, or the earlier occurrence an explicit `supersedes`
670
+ * names (decision 3), computed once at recording time. Points at the root
671
+ * of the group, never at an intermediate repeat.
672
+ */
673
+ repeatOf?: string;
674
+ }
675
+ declare const POLICY_DECLARATION_RULES: readonly ["cycle-2-severity-floor", "identity-repeat", "schema-2-import"];
676
+ type PolicyDeclarationRule = (typeof POLICY_DECLARATION_RULES)[number];
677
+ type DeclarationAuthority = "operator" | "reviewer" | "policy";
678
+ /**
679
+ * A disposition declaration as stored (CONTEXT.md: disposition declaration):
680
+ * one occurrence, one disposition, the authority that made it and its
681
+ * reference (operator, reviewer) or rule (policy).
682
+ */
683
+ type ReviewDeclarationRecord = {
684
+ authority: "operator" | "reviewer";
685
+ disposition: "fixed-in-thread" | "follow-up-filed" | "waived";
650
686
  reference?: string;
687
+ target: string;
688
+ } | {
689
+ authority: "policy";
690
+ disposition: "policy-accepted" | "stale-repeat";
691
+ rule: PolicyDeclarationRule;
692
+ target: string;
693
+ };
694
+ /** A recorded occurrence with the disposition its stored declarations give it. */
695
+ interface LedgerOccurrence {
696
+ address: string;
697
+ authority?: DeclarationAuthority;
698
+ cycle: LadderCycleRef;
699
+ disposition: FindingDisposition;
700
+ finding: LadderFindingReport;
701
+ historical: boolean;
702
+ reference?: string;
703
+ /** The root address of this occurrence's finding group. */
704
+ root: string;
705
+ rule?: PolicyDeclarationRule;
651
706
  }
652
707
  interface FindingLedgerEntry extends ReviewFindingAttributes {
708
+ /** The root occurrence's address; the entry's identity. */
709
+ address: string;
653
710
  disposition: FindingDisposition;
654
711
  firstFlagged: LadderCycleRef;
655
712
  flagCount: number;
656
- key: string;
657
713
  lastFlagged: LadderCycleRef;
714
+ occurrences: LedgerOccurrence[];
658
715
  priorDisposition?: FindingDisposition;
659
716
  reference?: string;
660
717
  reopenedCount: number;
661
718
  staleRepeatCount: number;
662
719
  }
663
720
  //#endregion
721
+ //#region src/review-recording-schema.d.ts
722
+ interface ReviewRecordedCycleIdentity {
723
+ /**
724
+ * SHA-256 of the proof file bytes this recording was applied to, or
725
+ * `absent` when no proof existed. The compare-and-swap at write time
726
+ * refuses to replace a proof whose bytes no longer digest to this value.
727
+ */
728
+ appliedTo: string;
729
+ base: {
730
+ ref: string;
731
+ sha: string;
732
+ };
733
+ /** SHA-256 of the dispositions file bytes; absent when none was given. */
734
+ dispositionsDigest?: string;
735
+ /** SHA-256 of the findings file bytes; names the retained copy. */
736
+ findingsDigest: string;
737
+ recordingId: string;
738
+ }
739
+ /**
740
+ * A cycle the bounded importer converted from a schema-2 proof (decision 7).
741
+ * No bytes were read for it, so it carries the digest of the retained source
742
+ * proof instead of a recording identity; it can never answer a retry as
743
+ * `already-recorded`.
744
+ */
745
+ interface ReviewImportedCycleIdentity {
746
+ imported: {
747
+ proofDigest: string;
748
+ schemaVersion: 2;
749
+ };
750
+ }
751
+ type ReviewCycleRecording = {
752
+ declarations: ReviewDeclarationRecord[]; /** Index-aligned with the cycle's `findings`. */
753
+ occurrences: ReviewOccurrenceRecord[];
754
+ } & (ReviewRecordedCycleIdentity | ReviewImportedCycleIdentity);
755
+ //#endregion
664
756
  //#region src/review-ladder.d.ts
665
757
  type ReviewLadderExecutedStage = "interior" | "gate";
666
758
  interface LadderSessionAttribution {
@@ -675,7 +767,6 @@ interface LadderUsageReport {
675
767
  totalTokens?: number;
676
768
  }
677
769
  interface ReviewLadderCycle {
678
- dispositions?: LadderDispositionDeclaration[];
679
770
  engine?: string;
680
771
  findings: LadderFindingReport[];
681
772
  identity?: {
@@ -684,7 +775,7 @@ interface ReviewLadderCycle {
684
775
  };
685
776
  historical?: true;
686
777
  model?: string;
687
- correlatedRerun?: boolean;
778
+ recording: ReviewCycleRecording;
688
779
  session?: LadderSessionAttribution;
689
780
  stage: LadderCycleStage;
690
781
  usage?: LadderUsageReport;
@@ -738,7 +829,7 @@ type ReviewLadderEvaluation = LadderPosition & {
738
829
  };
739
830
  event?: ReviewLadderStageEvent;
740
831
  ledger: FindingLedgerEntry[];
741
- unmatchedDispositions: LadderDispositionDeclaration[];
832
+ occurrences: LedgerOccurrence[];
742
833
  };
743
834
  //#endregion
744
835
  //#region src/pr-readiness/ladder-proof-state.d.ts
@@ -809,7 +900,7 @@ interface PrReviewResult {
809
900
  promptSections?: ReviewPromptSection[];
810
901
  }
811
902
  interface PrReviewProof {
812
- schemaVersion: 2;
903
+ schemaVersion: 3;
813
904
  findingProvenanceVersion: 1;
814
905
  base: string;
815
906
  headSha: string;
@@ -819,6 +910,21 @@ interface PrReviewProof {
819
910
  changedFiles: string[];
820
911
  cleanedPaths: string[];
821
912
  ladder?: PrReviewLadderState;
913
+ /**
914
+ * Present when the ladder's earlier cycles were converted from a schema-2
915
+ * proof by the bounded import (#1068): the digest of the original bytes,
916
+ * retained content-addressed for audit before the converted proof is
917
+ * written.
918
+ */
919
+ importedFrom?: {
920
+ proofDigest: string;
921
+ schemaVersion: 2;
922
+ };
923
+ /**
924
+ * Schema-2 declarations the bounded import refused (#1068). Empty is never
925
+ * written; absent means nothing awaits recovery.
926
+ */
927
+ recoveryRequired?: ReviewRecoveryItem[];
822
928
  /**
823
929
  * The policy identity this review run resolved its demanded kinds under
824
930
  * (#922 Slice B). Optional: a run that could not reach the authority records
@@ -1271,9 +1377,11 @@ interface CheckRunDependencies extends FactoryAppAccessDependencies {
1271
1377
  * Never posts a commit-status fallback: the commit status is a human-readable
1272
1378
  * mirror, not a proof surface, so a caller that needs an App-verified check
1273
1379
  * run must be told plainly whether it got one. Returns `true` only when the
1274
- * App-owned check run landed. Throws when Factory App access is fail-closed
1275
- * (missing mint or Cursor OIDC exchange, #949): `pr:ready` must not notice
1276
- * that miss and exit 0. An optional-App skip still returns `false`.
1380
+ * App-owned check run landed. Throws {@link FactoryAppUnevaluableError} when
1381
+ * Factory App access is fail-closed (missing mint or Cursor OIDC exchange,
1382
+ * #949): `pr:ready` must not notice that miss and exit 0, and must not publish
1383
+ * a red check that reads as candidate refusal (#1125). An optional-App skip
1384
+ * still returns `false`.
1277
1385
  *
1278
1386
  * "Landed" means GitHub serves it, not that the POST was accepted (#520). On
1279
1387
  * PR #519 the POST was accepted, `pr:ready` reported ready and armed, and the
@@ -1309,8 +1417,8 @@ declare const mergeFreezeStateSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
1309
1417
  generationId: z.ZodNumber;
1310
1418
  headSha: z.ZodString;
1311
1419
  outcome: z.ZodEnum<{
1312
- active: "active";
1313
1420
  stale: "stale";
1421
+ active: "active";
1314
1422
  }>;
1315
1423
  reason: z.ZodString;
1316
1424
  recordedAt: z.ZodISODateTime;
@@ -1418,6 +1526,7 @@ interface ProofDescriptor<T> {
1418
1526
  defaultPath: string;
1419
1527
  schemaVersion: number;
1420
1528
  parse: (raw: unknown) => T;
1529
+ parseBytes?: (bytes: string) => T;
1421
1530
  }
1422
1531
  //#endregion
1423
1532
  //#region src/pr-readiness/readiness-ledger.d.ts
@@ -1472,9 +1581,9 @@ declare const managedReadinessLedgerSchema: z.ZodObject<{
1472
1581
  mergeable: z.ZodString;
1473
1582
  requiredChecks: z.ZodEnum<{
1474
1583
  unknown: "unknown";
1475
- pending: "pending";
1476
1584
  passed: "passed";
1477
1585
  failed: "failed";
1586
+ pending: "pending";
1478
1587
  none: "none";
1479
1588
  }>;
1480
1589
  reviewDecision: z.ZodOptional<z.ZodNullable<z.ZodString>>;
@@ -1540,8 +1649,8 @@ declare const managedReadinessLedgerSchema: z.ZodObject<{
1540
1649
  "ready-for-human": "ready-for-human";
1541
1650
  }>;
1542
1651
  stage: z.ZodEnum<{
1543
- interior: "interior";
1544
1652
  gate: "gate";
1653
+ interior: "interior";
1545
1654
  "interior-complete": "interior-complete";
1546
1655
  }>;
1547
1656
  }, z.core.$strip>>;
@@ -1557,10 +1666,10 @@ declare const managedReadinessLedgerSchema: z.ZodObject<{
1557
1666
  reviewedHeadSha: z.ZodOptional<z.ZodString>;
1558
1667
  reviewedPatchId: z.ZodOptional<z.ZodString>;
1559
1668
  status: z.ZodEnum<{
1560
- "not-required": "not-required";
1561
- stale: "stale";
1562
1669
  blocked: "blocked";
1670
+ "not-required": "not-required";
1563
1671
  current: "current";
1672
+ stale: "stale";
1564
1673
  missing: "missing";
1565
1674
  }>;
1566
1675
  }, z.core.$strip>;
@@ -1569,10 +1678,10 @@ declare const managedReadinessLedgerSchema: z.ZodObject<{
1569
1678
  reviewedHeadSha: z.ZodOptional<z.ZodString>;
1570
1679
  reviewedPatchId: z.ZodOptional<z.ZodString>;
1571
1680
  status: z.ZodEnum<{
1572
- "not-required": "not-required";
1573
- stale: "stale";
1574
1681
  blocked: "blocked";
1682
+ "not-required": "not-required";
1575
1683
  current: "current";
1684
+ stale: "stale";
1576
1685
  missing: "missing";
1577
1686
  }>;
1578
1687
  }, z.core.$strip>>;
@@ -1719,8 +1828,11 @@ declare const statusCheckState: (rollup: StatusCheckRollup[] | undefined, exclud
1719
1828
  declare const isSupersededHostedVerifyCheck: (check: StatusCheckRollup, cutoffIso: string) => boolean;
1720
1829
  //#endregion
1721
1830
  //#region src/github-client.d.ts
1722
- /** Read a branch tip from GitHub rather than a checkout's cached remote ref. */
1723
- declare const fetchGitHubBranchTipSha: (repository: CheckoutRepository, branch: string, cwd: string) => string;
1831
+ /**
1832
+ * Read a branch tip from GitHub rather than a checkout's cached remote ref.
1833
+ * `gh` is injectable so a test can hand the real read a fake GitHub.
1834
+ */
1835
+ declare const fetchGitHubBranchTipSha: (repository: CheckoutRepository, branch: string, cwd: string, gh?: <T>(args: string[], cwd: string) => T) => string;
1724
1836
  //#endregion
1725
1837
  //#region src/github-app-client.d.ts
1726
1838
  interface FactoryAppRequest {
@@ -1855,6 +1967,12 @@ interface PrVerifyProof {
1855
1967
  schemaVersion: PrVerifySchemaVersion;
1856
1968
  authoringSession: string;
1857
1969
  base: string;
1970
+ /**
1971
+ * The commit `base` resolved to when this proof was written (#1132): the
1972
+ * live pull request base tip, or the freshly fetched ref. Optional and
1973
+ * additive; a proof written before it existed parses unchanged.
1974
+ */
1975
+ baseSha?: string;
1858
1976
  baselineFullProofs?: {
1859
1977
  base: string;
1860
1978
  changedFiles: string[];
@@ -2680,10 +2798,10 @@ declare const loadEvidenceEnvelopes: (cwd: string) => LoadedEvidenceEnvelope[];
2680
2798
  //#region src/review-proof-applicability.d.ts
2681
2799
  declare const REVIEW_STATUS_VALUES: readonly ["not-required", "current", "stale", "missing", "blocked"];
2682
2800
  declare const reviewStatusSchema: z.ZodEnum<{
2683
- "not-required": "not-required";
2684
- stale: "stale";
2685
2801
  blocked: "blocked";
2802
+ "not-required": "not-required";
2686
2803
  current: "current";
2804
+ stale: "stale";
2687
2805
  missing: "missing";
2688
2806
  }>;
2689
2807
  type ReviewStatus = z.infer<typeof reviewStatusSchema>;
@@ -2755,21 +2873,21 @@ interface ReviewCycleState {
2755
2873
  staleRepeatFindings?: number;
2756
2874
  windowExhausted: boolean;
2757
2875
  }
2758
- declare const reviewCycleStateFor: (proof: Pick<PrReviewProof, "ladder" | "reviewCycle" | "maxReviewCycles" | "reviews">, policy?: ReviewLadderPolicy, adjudicatedLedger?: FindingLedgerEntry[]) => ReviewCycleState;
2759
- type ReviewAcceptance = "accepted-after-cap" | "accepted-clean" | "accepted-through-ladder" | "blocked";
2876
+ declare const reviewCycleStateFor: (proof: Pick<PrReviewProof, "ladder" | "reviewCycle" | "maxReviewCycles" | "reviews">, policy?: ReviewLadderPolicy) => ReviewCycleState;
2877
+ type ReviewAcceptance = "accepted-after-cap" | "accepted-clean" | "accepted-through-ladder" | "blocked" | "recovery-required";
2760
2878
  declare const resolveReviewAcceptance: (proof: PrReviewProof, review: PrReviewResult, policy: ReviewLadderPolicy | undefined) => ReviewAcceptance;
2761
2879
  type ReviewTerminalState = "accepted-with-findings" | "blocked" | "clean";
2762
- declare const resolveReviewTerminalStateForEpoch: (proof: PrReviewProof, review: PrReviewResult, epoch: ReviewEpochMode, adjudicatedLedger?: FindingLedgerEntry[]) => ReviewTerminalState;
2880
+ declare const resolveReviewTerminalStateForEpoch: (proof: PrReviewProof, review: PrReviewResult, epoch: ReviewEpochMode) => ReviewTerminalState;
2763
2881
  declare const resolveReviewTerminalState: (proof: PrReviewProof, review: PrReviewResult, policy: ReviewLadderPolicy | undefined) => ReviewTerminalState;
2764
- declare const resolveReviewTerminalStateAcrossReviews: (proof: PrReviewProof, epoch: ReviewEpochMode, adjudicatedLedger?: FindingLedgerEntry[]) => ReviewTerminalState | undefined;
2882
+ declare const resolveReviewTerminalStateAcrossReviews: (proof: PrReviewProof, epoch: ReviewEpochMode) => ReviewTerminalState | undefined;
2765
2883
  declare const resolveReviewTerminalStateAcrossReviewsForPolicy: (proof: PrReviewProof, policy: ReviewLadderPolicy) => ReviewTerminalState | undefined;
2766
2884
  declare const reviewProofFor: (proof: PrReviewProof | undefined, kind: PrReviewKind, policy: ReviewLadderPolicy | undefined, current?: {
2767
2885
  patchId: string;
2768
2886
  }) => ReviewProofClaim;
2769
2887
  declare namespace review_proof_d_exports {
2770
- export { DEFAULT_PR_REVIEW_MAX_CYCLES, DEFAULT_PR_REVIEW_PROOF_PATH, PR_REVIEW_FINAL_READINESS_NOTICE, PR_REVIEW_FINDING_PROVENANCE_VERSION, PR_REVIEW_PROOF_DESCRIPTOR, PR_REVIEW_SCHEMA_VERSION, PrReviewFinding, PrReviewKind, PrReviewLadderState, PrReviewMode, PrReviewOutcome, PrReviewProof, PrReviewResult, PrReviewStageResolution, REVIEW_SEVERITIES, ReviewAcceptance, ReviewCycleState, ReviewFindingCategory, ReviewFindingSeverity, ReviewTerminalState, prReviewFindingSchema, prReviewProofSchema, prReviewResultSchema, resolveReviewAcceptance, resolveReviewTerminalState, resolveReviewTerminalStateAcrossReviews, resolveReviewTerminalStateAcrossReviewsForPolicy, resolveReviewTerminalStateForEpoch, reviewCycleStateFor, reviewProofFor, validatePrReviewProof, validatePrReviewResult };
2888
+ export { DEFAULT_PR_REVIEW_MAX_CYCLES, DEFAULT_PR_REVIEW_PROOF_PATH, PR_REVIEW_FINAL_READINESS_NOTICE, PR_REVIEW_FINDING_PROVENANCE_VERSION, PR_REVIEW_PROOF_DESCRIPTOR, PR_REVIEW_SCHEMA_VERSION, PrReviewFinding, PrReviewKind, PrReviewLadderState, PrReviewMode, PrReviewOutcome, PrReviewProof, PrReviewResult, PrReviewStageResolution, REVIEW_SEVERITIES, ReviewAcceptance, ReviewCycleState, ReviewFindingCategory, ReviewFindingSeverity, ReviewTerminalState, parseReviewProofBytes, prReviewFindingSchema, prReviewProofSchema, prReviewResultSchema, resolveReviewAcceptance, resolveReviewTerminalState, resolveReviewTerminalStateAcrossReviews, resolveReviewTerminalStateAcrossReviewsForPolicy, resolveReviewTerminalStateForEpoch, reviewCycleStateFor, reviewProofFor, validatePrReviewProof, validatePrReviewResult };
2771
2889
  }
2772
- declare const PR_REVIEW_SCHEMA_VERSION = 2;
2890
+ declare const PR_REVIEW_SCHEMA_VERSION = 3;
2773
2891
  declare const PR_REVIEW_FINDING_PROVENANCE_VERSION = 1;
2774
2892
  declare const DEFAULT_PR_REVIEW_PROOF_PATH = ".factory-memory/pr-review.json";
2775
2893
  declare const DEFAULT_PR_REVIEW_MAX_CYCLES = 5;
@@ -2778,7 +2896,8 @@ declare const prReviewFindingSchema: z.ZodType<PrReviewFinding>;
2778
2896
  declare const prReviewResultSchema: z.ZodType<PrReviewResult>;
2779
2897
  declare const prReviewProofSchema: z.ZodType<PrReviewProof>;
2780
2898
  declare const validatePrReviewResult: (value: unknown) => PrReviewResult;
2781
- declare const validatePrReviewProof: (value: unknown) => PrReviewProof;
2899
+ declare const validatePrReviewProof: (value: unknown, proofDigest?: string) => PrReviewProof;
2900
+ declare const parseReviewProofBytes: (bytes: string) => PrReviewProof;
2782
2901
  declare const PR_REVIEW_PROOF_DESCRIPTOR: ProofDescriptor<PrReviewProof>;
2783
2902
  //#endregion
2784
2903
  //#region src/schemas.d.ts
@@ -2954,6 +3073,29 @@ type HqFlushResult = (HqFlushOrphans & {
2954
3073
  status: "flushed";
2955
3074
  });
2956
3075
  //#endregion
3076
+ //#region src/candidate-base.d.ts
3077
+ interface LivePullRequestBase {
3078
+ baseRefName: string;
3079
+ /** Left unvalidated: the base-policy reader is its one consumer (#922). */
3080
+ isCrossRepository?: unknown;
3081
+ number: number;
3082
+ }
3083
+ /**
3084
+ * What the pull-request lookup found. `none` is a lookup that succeeded and
3085
+ * found no pull request for the branch — the ordinary state before
3086
+ * publication. `unavailable` is a lookup that could not be made (no `gh`, no
3087
+ * network, no auth); it is reported as such and never as "no pull request".
3088
+ */
3089
+ type LivePullRequestBaseLookup = {
3090
+ pullRequest: LivePullRequestBase;
3091
+ status: "found";
3092
+ } | {
3093
+ status: "none";
3094
+ } | {
3095
+ reason: string;
3096
+ status: "unavailable";
3097
+ };
3098
+ //#endregion
2957
3099
  //#region src/catch-up-recognition.d.ts
2958
3100
  type CatchUpRecognitionResult = {
2959
3101
  recognition: CatchUpRecognition;
@@ -2969,8 +3111,19 @@ type CatchUpRecognizer = (input: {
2969
3111
  //#endregion
2970
3112
  //#region src/pr-review.d.ts
2971
3113
  interface PrReviewArgs extends LoadProjectProfileInput {
3114
+ /**
3115
+ * Test-only (#1131): honour `FACTORY_REVIEW_FAULT` outside `NODE_ENV=test`
3116
+ * so the recoverable-recording demonstration can run through the real CLI.
3117
+ */
3118
+ allowFaultInjection?: boolean;
2972
3119
  assemble?: boolean;
2973
- base: string;
3120
+ /**
3121
+ * An assertion about the base, never its source (#1131). When a pull
3122
+ * request exists for the branch its live base is the base and a
3123
+ * disagreeing value is refused; without one, this ref (default
3124
+ * `origin/main`) is fetched fresh and its SHA recorded.
3125
+ */
3126
+ base?: string;
2974
3127
  cycle: number;
2975
3128
  /**
2976
3129
  * Issue carrying the factory-boundary manifest, which may be the work issue
@@ -2991,10 +3144,12 @@ interface PrReviewGitDependencies {
2991
3144
  changedFiles: (cwd: string, base: string) => string[];
2992
3145
  checkoutRepository?: (cwd: string) => CheckoutRepository;
2993
3146
  currentHeadSha: (cwd: string) => string;
3147
+ fetchOriginRef?: (cwd: string, refName: string) => void;
2994
3148
  firstParentCommits?: (cwd: string, base: string) => string[];
2995
3149
  isAncestor: (cwd: string, ancestor: string, descendant: string) => boolean;
2996
3150
  recognizeCatchUp?: CatchUpRecognizer;
2997
3151
  repositoryRoot?: (cwd: string) => string;
3152
+ resolveCommitSha?: (cwd: string, ref: string) => string | undefined;
2998
3153
  showFileAtRef?: (cwd: string, ref: string, absolutePath: string) => string | undefined;
2999
3154
  stablePatchId: (cwd: string, base: string) => string;
3000
3155
  stablePatchIdAtRef?: (cwd: string, base: string, ref: string) => string;
@@ -3006,6 +3161,13 @@ interface PrReviewIssueBodyInput {
3006
3161
  repo: string;
3007
3162
  }
3008
3163
  interface PrReviewDependencies {
3164
+ /**
3165
+ * Test-only seam (#1131): runs after the recording has read the proof
3166
+ * state it is applied to and immediately before the compare-and-swap write,
3167
+ * so a test can state a concurrent writer and prove the loser is refused
3168
+ * with `concurrent-write` and writes nothing.
3169
+ */
3170
+ beforeProofWrite?: () => void;
3009
3171
  /**
3010
3172
  * The authoritative base-policy reader (#922). Injectable for the same
3011
3173
  * reason `pr:ready` injects it: an unstated authority must be a refusal a
@@ -3032,9 +3194,21 @@ interface PrReviewDependencies {
3032
3194
  hq?: HqIngestDependencies;
3033
3195
  /**
3034
3196
  * How this run finds the pull request whose base ref is the policy authority
3035
- * (#922 Slice B). Defaults to the GitHub lookup.
3197
+ * (#922 Slice B). Defaults to the pull request the base binding found
3198
+ * (#1131), so one GitHub read serves both; injectable on its own so a test
3199
+ * can state the policy authority independently of the base.
3036
3200
  */
3037
3201
  lookupPullRequest?: (cwd: string) => CandidatePullRequestLookup;
3202
+ /**
3203
+ * The live tip of a base branch as GitHub serves it now (#1131) — the read
3204
+ * `pr:ready` measures against. Defaults to the GitHub read.
3205
+ */
3206
+ liveBranchTipSha?: (cwd: string, branch: string) => string;
3207
+ /**
3208
+ * How this run finds the pull request whose live base the candidate is
3209
+ * measured against (#1131). Defaults to the GitHub lookup.
3210
+ */
3211
+ lookupPullRequestBase?: (cwd: string) => LivePullRequestBaseLookup;
3038
3212
  }
3039
3213
  declare function runPrReview(args: PrReviewArgs, dependencies?: PrReviewDependencies): Promise<PrReviewProof>;
3040
3214
  //#endregion
@@ -3060,7 +3234,13 @@ interface VerificationCommandOutcome {
3060
3234
  }
3061
3235
  interface PrVerifyArgs extends LoadProjectProfileInput {
3062
3236
  authoringSession?: string;
3063
- base: string;
3237
+ /**
3238
+ * The base to assert (#1131, #1132). A pull request open for the branch
3239
+ * supplies the live base and a disagreeing value is refused; without one
3240
+ * this ref (default `origin/main`) is fetched fresh and its SHA recorded.
3241
+ * Absent means "assert nothing": the binding decides.
3242
+ */
3243
+ base?: string;
3064
3244
  mode: PrVerifyMode;
3065
3245
  output?: string;
3066
3246
  requireKnownAuthoringSession?: boolean;
@@ -3070,8 +3250,12 @@ interface PrVerifyGitDependencies {
3070
3250
  checkoutRepository?: (cwd: string) => CheckoutRepository;
3071
3251
  currentHeadSha: (cwd: string) => string;
3072
3252
  emptyTreeHash: (cwd: string) => string;
3253
+ /** Bring `refs/heads/<refName>` from origin into the checkout (#1132). */
3254
+ fetchOriginRef?: (cwd: string, refName: string) => void;
3073
3255
  mergeBaseSha: (cwd: string, base: string) => string;
3074
3256
  recognizeCatchUp?: CatchUpRecognizer;
3257
+ /** Resolve a ref to a commit SHA, `undefined` when it does not exist (#1132). */
3258
+ resolveCommitSha?: (cwd: string, ref: string) => string | undefined;
3075
3259
  runVerificationCommand: (command: string, cwd: string, env: Record<string, string>) => VerificationCommandOutcome;
3076
3260
  showFileAtRef?: (cwd: string, ref: string, absolutePath: string) => string | undefined;
3077
3261
  statusPorcelain: (cwd: string) => string;
@@ -3094,6 +3278,17 @@ interface PrVerifyDependencies {
3094
3278
  env?: NodeJS.ProcessEnv;
3095
3279
  git?: PrVerifyGitDependencies;
3096
3280
  hq?: HqIngestDependencies;
3281
+ /**
3282
+ * The live tip of a base branch as GitHub serves it now (#1132) — the read
3283
+ * `pr:ready` measures against. Defaults to the GitHub read.
3284
+ */
3285
+ liveBranchTipSha?: (cwd: string, branch: string) => string;
3286
+ /**
3287
+ * How this run finds the pull request whose live base the candidate is
3288
+ * measured against (#1132). Defaults to the GitHub lookup; the same read
3289
+ * also answers the policy authority unless `lookupPullRequest` is given.
3290
+ */
3291
+ lookupPullRequestBase?: (cwd: string) => LivePullRequestBaseLookup;
3097
3292
  postCommitStatus?: PostCommitStatus;
3098
3293
  publishCheckRun?: (input: PublishFactoryCheckInput) => void;
3099
3294
  }
@@ -3143,6 +3338,15 @@ type PrPublishFollowUpOutcome = {
3143
3338
  };
3144
3339
  interface PrPublishResult {
3145
3340
  bodyPatched: boolean;
3341
+ /**
3342
+ * The pull request this publish created and the GitHub branch it targets
3343
+ * (#1153). Absent when the pull request already existed or was named with
3344
+ * `--pr`.
3345
+ */
3346
+ createdPullRequest?: {
3347
+ base: string;
3348
+ number: number;
3349
+ };
3146
3350
  followUp: PrPublishFollowUpOutcome;
3147
3351
  handoff: PrPublishHandoff;
3148
3352
  proof: PrReadyProof;
@@ -3373,11 +3577,11 @@ declare const LegacyWorkerCloseoutStatusTraceEventSchema: z.ZodObject<{
3373
3577
  threadId: z.ZodOptional<z.ZodString>;
3374
3578
  workerId: z.ZodOptional<z.ZodString>;
3375
3579
  archiveStatus: z.ZodOptional<z.ZodEnum<{
3580
+ pending: "pending";
3376
3581
  unavailable: "unavailable";
3377
3582
  archived: "archived";
3378
3583
  skipped: "skipped";
3379
3584
  "not-applicable": "not-applicable";
3380
- pending: "pending";
3381
3585
  }>>;
3382
3586
  closeoutReason: z.ZodOptional<z.ZodString>;
3383
3587
  eventType: z.ZodLiteral<"worker-closeout-lessons">;
@@ -3550,8 +3754,8 @@ declare const FACTORY_TRACE_EVENT_DEFINITIONS: readonly [DefinedTraceEvent<"revi
3550
3754
  sessionId: z.ZodOptional<z.ZodString>;
3551
3755
  }, z.core.$strict>>;
3552
3756
  stage: z.ZodEnum<{
3553
- interior: "interior";
3554
3757
  gate: "gate";
3758
+ interior: "interior";
3555
3759
  }>;
3556
3760
  usage: z.ZodOptional<z.ZodObject<{
3557
3761
  estimatedCostUsd: z.ZodOptional<z.ZodNumber>;
@@ -3603,8 +3807,8 @@ declare const FACTORY_TRACE_EVENT_DEFINITIONS: readonly [DefinedTraceEvent<"revi
3603
3807
  sessionId: z.ZodOptional<z.ZodString>;
3604
3808
  }, z.core.$strict>>;
3605
3809
  stage: z.ZodEnum<{
3606
- interior: "interior";
3607
3810
  gate: "gate";
3811
+ interior: "interior";
3608
3812
  }>;
3609
3813
  usage: z.ZodOptional<z.ZodObject<{
3610
3814
  estimatedCostUsd: z.ZodOptional<z.ZodNumber>;
@@ -3647,8 +3851,8 @@ declare const FACTORY_TRACE_EVENT_DEFINITIONS: readonly [DefinedTraceEvent<"revi
3647
3851
  sessionId: z.ZodOptional<z.ZodString>;
3648
3852
  }, z.core.$strict>>;
3649
3853
  stage: z.ZodEnum<{
3650
- interior: "interior";
3651
3854
  gate: "gate";
3855
+ interior: "interior";
3652
3856
  }>;
3653
3857
  usage: z.ZodOptional<z.ZodObject<{
3654
3858
  estimatedCostUsd: z.ZodOptional<z.ZodNumber>;
@@ -3678,7 +3882,7 @@ declare const FACTORY_TRACE_EVENT_DEFINITIONS: readonly [DefinedTraceEvent<"revi
3678
3882
  staleRepeats: number;
3679
3883
  };
3680
3884
  observedAt: string;
3681
- stage: "interior" | "gate";
3885
+ stage: "gate" | "interior";
3682
3886
  issue?: number | undefined;
3683
3887
  pr?: number | undefined;
3684
3888
  threadId?: string | undefined;
@@ -3771,11 +3975,11 @@ declare const FACTORY_TRACE_EVENT_DEFINITIONS: readonly [DefinedTraceEvent<"revi
3771
3975
  threadId: z.ZodOptional<z.ZodString>;
3772
3976
  workerId: z.ZodOptional<z.ZodString>;
3773
3977
  archiveStatus: z.ZodOptional<z.ZodEnum<{
3978
+ pending: "pending";
3774
3979
  unavailable: "unavailable";
3775
3980
  archived: "archived";
3776
3981
  skipped: "skipped";
3777
3982
  "not-applicable": "not-applicable";
3778
- pending: "pending";
3779
3983
  }>>;
3780
3984
  closeoutReason: z.ZodOptional<z.ZodString>;
3781
3985
  eventType: z.ZodLiteral<"worker-closeout-lessons">;
@@ -3812,11 +4016,11 @@ declare const FACTORY_TRACE_EVENT_DEFINITIONS: readonly [DefinedTraceEvent<"revi
3812
4016
  }, z.core.$strict>], "lessonStatus">;
3813
4017
  readonly 2: z.ZodObject<{
3814
4018
  archiveStatus: z.ZodOptional<z.ZodEnum<{
4019
+ pending: "pending";
3815
4020
  unavailable: "unavailable";
3816
4021
  archived: "archived";
3817
4022
  skipped: "skipped";
3818
4023
  "not-applicable": "not-applicable";
3819
- pending: "pending";
3820
4024
  }>>;
3821
4025
  closeoutReason: z.ZodOptional<z.ZodString>;
3822
4026
  lessonStatus: z.ZodOptional<z.ZodEnum<{
@@ -4495,7 +4699,7 @@ declare const evaluateReadiness: (input: EvaluationInput) => {
4495
4699
  draft: boolean;
4496
4700
  mergeStateStatus: string;
4497
4701
  mergeable: string;
4498
- requiredChecks: "unknown" | "pending" | "passed" | "failed" | "none";
4702
+ requiredChecks: "unknown" | "passed" | "failed" | "pending" | "none";
4499
4703
  unresolvedReviewThreads: number;
4500
4704
  reviewDecision?: string | null | undefined;
4501
4705
  };
@@ -4510,13 +4714,13 @@ declare const evaluateReadiness: (input: EvaluationInput) => {
4510
4714
  reviews: {
4511
4715
  correctness: {
4512
4716
  required: boolean;
4513
- status: "not-required" | "stale" | "blocked" | "current" | "missing";
4717
+ status: "blocked" | "not-required" | "current" | "stale" | "missing";
4514
4718
  reviewedHeadSha?: string | undefined;
4515
4719
  reviewedPatchId?: string | undefined;
4516
4720
  };
4517
4721
  security?: {
4518
4722
  required: boolean;
4519
- status: "not-required" | "stale" | "blocked" | "current" | "missing";
4723
+ status: "blocked" | "not-required" | "current" | "stale" | "missing";
4520
4724
  reviewedHeadSha?: string | undefined;
4521
4725
  reviewedPatchId?: string | undefined;
4522
4726
  } | undefined;
@@ -4579,7 +4783,7 @@ declare const evaluateReadiness: (input: EvaluationInput) => {
4579
4783
  interior: number;
4580
4784
  };
4581
4785
  nextAction: "run-interior-cycle" | "advance-to-gate" | "run-gate-cycle" | "accept-nonblocking-findings" | "escalate-to-triage" | "ready-for-human";
4582
- stage: "interior" | "gate" | "interior-complete";
4786
+ stage: "gate" | "interior" | "interior-complete";
4583
4787
  forcedTransition?: "gate-cap-exhausted" | "interior-cap-reached" | undefined;
4584
4788
  } | undefined;
4585
4789
  reviewRuns?: PrReviewResult[] | undefined;