CodeIssuesPull RequestsActionsSecurityInsights
✨ AI
More
Settings

fix(api): one definition of `state`, and a gate that fails when two drift #5571

MergedXSccantynz wants to mergefix/state-filter-paritymainopened 2d ago
ccantynzcommented 2d ago

Follow-up to 07773a06, taking the reporting instance's suggestion.

That fix corrected two endpoints. It did not stop them diverging again — and divergence, not the missing branch, was the actual defect. gluecron_list_prs had the contract right (enum-checked, all-aware) while the REST endpoint serving the same rows one directory away had neither, and nothing compared them.

One definition

The vocabulary moves to src/lib/state-filters.ts — state lists, accepted set, refusal message, where-clause builder — and both surfaces derive from it. gluecron_list_prs no longer carries its own inline list or its own state === "all" ? … : …; it passes the shared clause into and(), which drops undefined. Divergence becomes impossible rather than merely unlikely.

The gate is behavioural, and that's the point

Asserting "both files import the same symbol" passes the instant someone re-inlines a list — precisely how these two drifted. So it drives each surface with every real state, the all sentinel, and clear rubbish, then compares what they accept. A mismatch reports which value they disagreed about, not expected true, got false. The refusal message is pinned identical too — a caller debugging one surface shouldn't get a different story from the other.

Verified by mutation, not by passing. Re-inlining a list in the MCP tool that accepts one extra value:

(fail) accepts and refuses exactly the same values
(fail) the refusal message is identical, not merely similar
   Expected to contain: "...open|closed|merged|all (got \"banana\")"
   Received:            "...open|closed|merged|all|any (got \"banana\")"

Credential-free, because the tool validates state before its auth check — a parity gate that doesn't run in CI is not a gate.

Recorded, not changed

github-compat answers a mistyped state with an empty list where GitHub answers 422. Reasoning is in the file: that surface exists to be mistaken for GitHub by clients we have no inventory of, and one that has quietly coped with an empty list for months would start getting an error it has no handler for. Correct-in-isolation and safe-under-live-clients are different questions, and only the first is answerable from the code. Revisit with a client inventory, not a code review.

Live verification of the predecessor

Checked against production at 07773a06 before writing this:

(default)      -> 16
?state=open    -> 16
?state=all     -> 17
?state=closed  ->  1
?state=banana  -> 400  state must be one of open|closed|all (got "banana")

17 = 16 + 1 is the check that matters — all returning a bigger number wouldn't have proven anything.

Suggested-by: vapron-0d (cross-instance)

ccantynzAI Reviewcommented 2d ago

AI Triage

(no summary)

Priority: medium Risk area: mixed

Suggested labels: (no label suggestions) Suggested reviewers: (no reviewer suggestions)

Suggestions only — nothing has been applied. The PR author stays in control.

gluecron[bot]🤖 botAI Reviewcommented 2d ago

AI review unavailable

The platform's AI balance is exhausted, so AI generation is temporarily unavailable. Nothing was lost. You can queue this as a repair for the internal agent from the repository's Health page, or try again once the balance is restored. The PR is otherwise unchanged.

Cross-repo impact

See what breaks downstream if this PR merges.

Analyze →
⮌ Merged

This pull request was merged into main.

c comment · e edit title · m merge · a approve · r request changes · ? shortcuts