@@ -32637,6 +32637,18 @@ var import_crypto4 = require("crypto");
3263732637var LEDGER_MARKER = "reviewrouter-ledger:v1";
3263832638var LEDGER_RE = /<!--\s*reviewrouter-ledger:v1\s+payload=([A-Za-z0-9_-]+)\s+signature=([a-f0-9]{64})\s*-->/;
3263932639var MAX_LEDGER_ENTRIES = 200;
32640+ function commandLedgerWatermark(payload) {
32641+ return Math.max(
32642+ 0,
32643+ ...payload.entries.map((entry) => {
32644+ const value = entry.commandCommentId ?? entry.parentCommentId;
32645+ if (!Number.isSafeInteger(value) || value <= 0) {
32646+ throw new Error("review_router_command_ledger_watermark_invalid");
32647+ }
32648+ return value;
32649+ })
32650+ );
32651+ }
3264032652var ReviewLedger = class {
3264132653 constructor(client, secret, dryRun = false) {
3264232654 this.client = client;
@@ -71093,19 +71105,24 @@ var GitHubReviewRevisionGuard = class {
7109371105 }
7109471106};
7109571107var FreshGitHubLifecycleInventory = class {
71096- loader;
71097- constructor(client) {
71108+ constructor(client, ledger) {
71109+ this.ledger = ledger;
7109871110 this.loader = new ReviewThreadInventoryLoader(client);
7109971111 }
71112+ loader;
7110071113 async loadCurrent(query) {
71101- return mapFreshInventory(
71102- await this.loader.load(query.scope.pullRequestNumber),
71103- query.scope.reviewedHeadSha
71104- );
71114+ const [raw, ledger] = await Promise.all([
71115+ this.loader.load(query.scope.pullRequestNumber),
71116+ this.ledger.load(query.scope.pullRequestNumber)
71117+ ]);
71118+ return mapFreshInventory(raw, query.scope.reviewedHeadSha, ledger);
7110571119 }
7110671120 async loadForPrompt(pullRequestNumber, expectedHeadSha) {
71107- const raw = await this.loader.load(pullRequestNumber);
71108- const inventory = mapFreshInventory(raw, expectedHeadSha);
71121+ const [raw, ledger] = await Promise.all([
71122+ this.loader.load(pullRequestNumber),
71123+ this.ledger.load(pullRequestNumber)
71124+ ]);
71125+ const inventory = mapFreshInventory(raw, expectedHeadSha, ledger);
7110971126 return Object.freeze({
7111071127 inventory,
7111171128 promptTargets: Object.freeze([
@@ -71115,7 +71132,7 @@ var FreshGitHubLifecycleInventory = class {
7111571132 });
7111671133 }
7111771134};
71118- function mapFreshInventory(raw, expectedHeadSha) {
71135+ function mapFreshInventory(raw, expectedHeadSha, ledger ) {
7111971136 if (raw.failed) {
7112071137 throw new Error("review_action_v2_lifecycle_inventory_unavailable");
7112171138 }
@@ -71138,13 +71155,10 @@ function mapFreshInventory(raw, expectedHeadSha) {
7113871155 if (new Set(rawTargets.map(({ target }) => target.targetId)).size !== rawTargets.length) {
7113971156 throw new Error("review_action_v2_lifecycle_inventory_duplicate_target");
7114071157 }
71141- const missingDatabaseId = rawTargets.some(
71142- ({ target }) => target.parentCommentDatabaseId === void 0
71143- );
71144- const warnings = [
71145- ...raw.warnings,
71146- ...missingDatabaseId ? ["review thread comment watermark is incomplete"] : []
71147- ].sort();
71158+ if (!ledger.valid) {
71159+ throw new Error("review_action_v2_command_ledger_unavailable");
71160+ }
71161+ const warnings = [...raw.warnings].sort();
7114871162 const targets = rawTargets.map(({ target, manual }) => ({
7114971163 targetId: target.targetId,
7115071164 threadId: target.threadId,
@@ -71169,16 +71183,12 @@ function mapFreshInventory(raw, expectedHeadSha) {
7116971183 }
7117071184 } : {}
7117171185 }));
71172- const commandLedgerWatermark = String(
71173- Math.max(
71174- 0,
71175- ...rawTargets.map(({ target }) => target.parentCommentDatabaseId ?? 0)
71176- )
71177- );
71186+ const commandWatermark = commandLedgerWatermark(ledger.payload);
71187+ const commandLedgerWatermarkValue = String(commandWatermark);
7117871188 const lifecycleStateHash = sha2568(
7117971189 canonicalJson9({
71180- commandLedgerWatermark,
71181- complete: !missingDatabaseId ,
71190+ commandLedgerWatermark: commandLedgerWatermarkValue ,
71191+ complete: true ,
7118271192 loadedForHeadSha,
7118371193 targets,
7118471194 warnings
@@ -71188,8 +71198,8 @@ function mapFreshInventory(raw, expectedHeadSha) {
7118871198 inventoryVersion: "review_lifecycle_inventory.v1",
7118971199 loadedForHeadSha,
7119071200 lifecycleStateHash,
71191- commandLedgerWatermark,
71192- complete: !missingDatabaseId ,
71201+ commandLedgerWatermark: commandLedgerWatermarkValue ,
71202+ complete: true ,
7119371203 warnings: Object.freeze(warnings),
7119471204 targets: Object.freeze(targets)
7119571205 });
@@ -71303,7 +71313,7 @@ var BuildCurrentReviewProjection = class {
7130371313 occurrences = applyPlacementDecisions(occurrences, presentation.placements);
7130471314 const coverageOnly = coverage.state === "partial" /* Partial */;
7130571315 const allClear = !coverageOnly && canClaimAllClear(coverage, inventory, occurrences, gate);
71306- const lifecycleFacts = coverageOnly ? [] : buildLifecycleFacts(
71316+ const lifecycleFacts = buildLifecycleFacts(
7130771317 inventory,
7130871318 lifecycleDecisions,
7130971319 command.priorLineageHints,
@@ -73591,7 +73601,10 @@ var ProductionT0ReviewRunner = class {
7359173601 if (pr2.baseSha.toLowerCase() !== authorization.facts.baseSha || pr2.headSha.toLowerCase() !== authorization.facts.headSha) {
7359273602 return { outcome: "superseded" /* Superseded */ };
7359373603 }
73594- const lifecycleInventory = new FreshGitHubLifecycleInventory(github);
73604+ const lifecycleInventory = new FreshGitHubLifecycleInventory(
73605+ github,
73606+ new ReviewLedger(github, process.env.REVIEW_ROUTER_LEDGER_KEY)
73607+ );
7359573608 const initialLifecycle = await lifecycleInventory.loadForPrompt(
7359673609 pr2.number,
7359773610 authorization.facts.headSha
0 commit comments