Skip to content

fix(ownership): fold repo casing in the ownership-reconciliation pr-fix reads (#1370) - #1373

Merged
joryirving merged 1 commit into
mainfrom
courier/misospace/dispatch/issue-1370
Oct 23, 2026
Merged

joryirving merged 1 commit into
mainfrom
courier/misospace/dispatch/issue-1370

Conversation

@itsmiso-ai

Copy link
Copy Markdown
Contributor

Closes #1370

What

PrFixQueueItem.repo is canonicalized (trim + lowercase via normalizeQueueRepo, #1145; migration 20261008000000 backfolded legacy rows) on every write, but both prFixQueueItem.findFirst reads in src/lib/ownership-reconciliation.ts matched on the raw cached repository.fullName. When the issue cache carries GitHub's canonical casing (Org/Repo), Postgres case-sensitive = matches no row and both reads silently miss an open PR-fix queue row:

  • the §8.7 commit-time re-check can release a REVIEW_OWNED claim while the queue still owns the PR, and the release path strips the agent/* label;
  • the bounded ladder scan under-reports review ownership, degrading the derived state without an error.

Fix: fold both query arguments through normalizeQueueRepo, mirroring the identical fix in src/lib/ownership-inspect.ts (#1357). No data migration needed.

Invariants this change must keep, and every other code path enforcing the same rule

Rule: every read of PrFixQueueItem.repo must use the folded identity (writes already fold in pr-fix-queue.ts entry points).

Rule: the fold is scoped to the queue query only — forge label reads/removals (getLiveIssueLabels, removeIssueLabel) keep using the raw cached repository.fullName, since the forge needs its canonical casing. Verified: both folds are confined to the where.repo argument.

Tests

Two regression tests in src/lib/ownership-reconciliation.test.ts (fake uses strict === string matching, faithful to Postgres =):

  1. Commit-time re-check: open PR-fix row (folded repo) + uppercase cached casing landing before Phase A withholds the release with reason review ownership (open PR-fix queue row); zero forge removals, no intent committed.
  2. Ladder scan: intercepts prFixQueueItem.findFirst and asserts the query always receives the folded value (never the raw mixed-case name), and the review-owned claim is retained.

Both tests fail against the unfixed source (verified by stashing only the source change: exactly these 2 fail, the other 48 pass) and pass with the fix.

Local validation (run on the integrated head)

CI status: not observed from this run (operator verifies after exit).

…ix reads (#1370)

PrFixQueueItem.repo is canonicalized (trim + lowercase) on every write
(normalizeQueueRepo, #1145), but both ownership-reconciliation reads
matched on the raw cached repository.fullName: the §8.7 commit-time
re-check and the bounded ladder scan. With uppercase-cased cache rows
both reads silently found no queue row — the re-check could release a
REVIEW_OWNED claim while the queue still owned the PR (stripping the
agent/* label), and the ladder under-reported review ownership.

Fold both query arguments through normalizeQueueRepo, mirroring the
#1357 fix in ownership-inspect, with regression tests asserting the
withhold fires and the queries receive the folded value.

@its-saffron its-saffron Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI Automated Review

Full PR review.

Analysis engine: MiniMax-M3.1-Flash (anthropic) — primary route · pr-reviewer-action v3.7.0

Findings (1 info)

Severity Location Finding
Info src/lib/ownership-reconciliation.ts:47 The PR body's repository-wide “every read” invariant is not currently true because ownership inspection and stale-item reconciliation still query the queue with raw-cased repo values, although both are outside this PR's scoped reads.

Recommendation: approve—the scoped normalization and both regression tests correctly preserve review-owned claims.

Claims: 2/4 claims held; 2/4 were violated only by broader repository-state wording, without affecting this PR’s scoped fix. The two ownership-reconciliation reads now use the canonical queue identity, while the repository-wide “every read” claim remains false because ownership-inspect.ts and stale-item reconciliation still contain raw-cased reads.

Linked Issue Fit: Both prFixQueueItem.findFirst reads requested by PR 1370 now fold repository.fullName through normalizeQueueRepo; the added tests exercise the commit-time re-check and ladder scan with mixed-case cached repositories, and no migration was added.

Tool Harness Findings: Repository searches confirmed the two changed reads, the normalizeQueueRepo trim/lowercase implementation, and migration 20261008000000 canonicalizing existing rows. The failed GitHub API lookup prevented independent verification of PR 1357, and the exhausted tool budget limited the exhaustive sibling-call-site audit.

Unknowns or Needs Verification: Whether PR 1357 has merged independently remains unverified; this does not affect the correctness of PR 1370’s two scoped reads.

@joryirving joryirving left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Secondary hard-blocker review on exact head 50b95633ae96eab894401339b0018f9c1ba74438: APPROVE. Both ownership-reconciliation reads now use the same canonical queue identity as the write paths: normalizeQueueRepo(repository.fullName) is applied at the §8.7 commit-time review-ownership re-check and at the bounded ladder scan, while forge-facing repository casing remains untouched. The regressions are load-bearing: the fake uses case-sensitive equality like PostgreSQL, the commit-time test proves an uppercase cached repo cannot cause a review-owned claim to be released, and the scan test captures the actual query arguments and proves the folded identity retains the claim.

The primary's lone info finding is partly stale on this exact base: #1371 is already merged as base 1227acee, so ownership-inspect.ts is no longer a raw-cased queue read. The remaining same-class gap in reconcileStalePrFixItems is real, but this PR body explicitly calls it out and tracks it separately as #1372; it is fail-safe in the direction of retaining stale queue items and is outside #1370's two ownership-release reads. Exact-head primary review and all CI/security/image/smoke checks are green. No independent blocker found.

@joryirving
joryirving merged commit 65ca72c into main Oct 23, 2026
13 checks passed
@joryirving
joryirving deleted the courier/misospace/dispatch/issue-1370 branch October 23, 2026 17:14
@its-miso its-miso Bot mentioned this pull request Oct 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(ownership): fold repo casing in the ownership-reconciliation pr-fix reads like every other queue lookup

2 participants