Skip to content

fix(cursor-review): survive a rebranded model family in the drift check (BE-4852) - #88

Merged
mattmillerai merged 3 commits into
matt/be-4819-cursor-review-catalog-driftfrom
matt/be-4852-catalog-drift-lab-heuristics
Aug 4, 2026
Merged

fix(cursor-review): survive a rebranded model family in the drift check (BE-4852)#88
mattmillerai merged 3 commits into
matt/be-4819-cursor-review-catalog-driftfrom
matt/be-4852-catalog-drift-lab-heuristics

Conversation

@mattmillerai

Copy link
Copy Markdown
Contributor

STACKED — merging lands on matt/be-4819-cursor-review-catalog-drift (owned by @mattmillerai, PR #87), NOT main. The file this PR edits does not exist on main yet; #87 introduces it. Merge #87 first, then this.

ELI-5

The weekly checker looks at Cursor's model list and tells us if one of our four review models disappeared, or if a newer one showed up that we might want to switch to. It figures out "which lab is this?" by taking the first chunk of the model's name — gpt-5.6-sol-maxgpt. That works right up until a lab renames its family: when OpenAI ships o5-pro next to gpt-*, the checker reads the lab as o5, sees that nobody pins o5, and quietly drops it. So the one thing the check exists for — "a newer model shipped and nobody noticed" — is exactly the thing it could miss. This PR adds a second, quieter list for models from families we pin nothing from, so a renamed family shows up instead of vanishing. It also stops the parser treating a hyphenated English word at the start of a line (gpt-based models are …) as if it were a model name.

What changed

Both fixes were deferred out of #87 by the review panel (thread) — they change the design, not just a regex, so they were out of scope there.

1. Unpinned-families catch-all (analyzereport["unpinned_labs"]). Catalog ids whose family prefix matches no pin at all now get their own section instead of being dropped. This is the ticket's first proposed option, chosen over an explicit LAB_ALIASES = {"gpt": "openai", "o": "openai", …} table: the alias table is more precise but reintroduces exactly the hand-maintained list that deriving labs from the pins exists to avoid, and it needs a human edit before the rebrand it is supposed to catch. The catch-all costs one section and stays zero-maintenance.

Deliberate placement, per the ticket's "keep it out of urgent" note:

  • Not urgent — it is a standing watchlist, not breakage, so it never reddens the weekly run.
  • Counted in has_findings — it has to be, or a rebranded family renders into a body nobody ever sees (a clean has_findings closes the sticky issue). See the judgment call below on auto-close.
  • Rendered collapsed, below the same-lab review-me list, so the labs Comfy will never pin cannot crowd out the list that actually needs a decision.

A lab whose only pin was just delisted still counts as pinned, so its successors stay in the same-lab list (with the delisted-pin context) rather than being demoted into the quiet fold — covered by a test.

2. Model ids must carry a digit (_is_model_id). gpt-based is lowercase, hyphenated and has letters, so it parsed as a catalog id and added noise to the review-me list — and, with change 1 in place, would have minted a phantom gpt-based family. Every id a lab has actually shipped carries a version number somewhere; a prose word does not. This is the ticket's first proposed option for (2); "require the token to appear in more than one catalog line" was rejected because a real catalog lists each id exactly once.

The rule lives in the single _is_model_id that the pin validator and the catalog parser both call, so the two cannot drift apart — relaxing it is one edit, not two. test_pin_and_catalog_id_shape_rules_agree (the tripwire the ticket names) is extended rather than weakened: it now also pins code-supernova/gpt-based as rejected and o5-pro as accepted, on both sides.

Verification

python3 -m unittest discover -s .github/cursor-review/tests -p 'test_*.py'103 passed (the exact command test-cursor-review-scripts.yml runs). check_agents_md.py --root . passes (one pre-existing CODEOWNERS warning). No shell changed, so shellcheck is unaffected.

New tests: the rebranded-family catch-all (o5-pro → family o5, invisible to the same-lab list), finding-but-never-urgent, a catalog of exactly the pins still having no unpinned families (the auto-close path is not unconditionally broken), a delisted pin's lab still counting as pinned, prose-word rejection, digit-less pin rejected loudly rather than read as delisted, the collapsed-section render, backtick-injection safety on the new section's notes, and a 4000-family catalog still fitting under GitHub's body cap.

Judgment calls

  • The tightened id rule binds pins too, on purpose. _is_model_id gates extraction, so a digit-less pin (code-supernova is a real Cursor id shape) now fails extraction with an actionable message and a red run, rather than being silently reported as delisted every Monday. I checked this against the live pins rather than assuming: gpt-5.6-sol-max, claude-opus-4-8-thinking-max, gemini-3.1-pro, kimi-k2.7-code and the judge_model default all carry digits and extract cleanly from the real cursor-review.yml (asserted by test_extraction_works_against_the_real_cursor_review_workflow). The cost of the tightening is that a digit-less catalog id would no longer reach the review-me list — it is still reproduced verbatim in the raw-catalog fold, which is where a human reads it anyway.
  • The sticky issue's auto-close, which the ticket asked me to consider. Folding unpinned_labs into has_findings does not newly pin the issue open: unpinned alone already makes has_findings true on any real catalog, since Cursor lists more tiers per pinned lab than the panel pins four of. The auto-close path was already reserved for a catalog trimmed to exactly the pins, and still is. I chose not to add a separate "worth an issue" gate — it would reintroduce the miss (a rebranded family with no other drift would file nothing), and splitting the two flags is a bigger design change than this deferred follow-up should carry. Flagging it in case you'd rather the catch-all be step-summary-only.
  • Not done, deliberately: no LAB_ALIASES table (see above), and lab_of itself is unchanged — its docstring now says plainly that it returns a family prefix, not a vendor, and points at the catch-all.

…ck (BE-4852)

Two heuristics in the catalog-drift checker distorted the "unpinned same-lab
ids" review-me list. Neither could produce a false green, but both undercut the
headline feature — noticing that a newer model shipped.

1. `lab_of()` equates a lab with an id's first `-`/`.`-separated token, and labs
   are derived from the pins themselves (what keeps the checker
   zero-maintenance). An existing lab shipping under a NEW family prefix —
   OpenAI's `o<n>` series alongside `gpt-*` is the precedent — therefore
   resolves to a lab nobody pins and vanished from the review-me list. Add a
   quieter catch-all section listing catalog ids whose family prefix matches no
   pin at all, so a rebranded family surfaces instead of dropping out. It is a
   finding (or a rebranded family would render into a body nobody sees) but
   never `urgent`, and renders collapsed below the same-lab list.

2. `catalog_entries()` admitted any lowercase hyphenated first token with a
   letter, so a prose line ("gpt-based models are …") parsed as a bogus id.
   Require a digit as well: every id a lab has shipped carries a version number,
   a prose word does not. The rule lives in the one `_is_model_id` the pin
   validator and the catalog parser share, so the two cannot drift apart —
   `test_pin_and_catalog_id_shape_rules_agree` stays the tripwire, and all five
   live pins satisfy it.
@mattmillerai mattmillerai added agent-coded Authored by the agent-work loop cursor-review Multi-model cursor review labels Jul 28, 2026
@mattmillerai
mattmillerai marked this pull request as ready for review July 28, 2026 09:00
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 33657e78-e0ae-449a-999e-afce67748524

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch matt/be-4852-catalog-drift-lab-heuristics
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch matt/be-4852-catalog-drift-lab-heuristics

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔍 Cursor Review — Consolidated panel

Triggered by @mattmillerai.

Found 4 finding(s).

Severity Count
🟡 Medium 3
⚪ Nit 1

Panel: 8/8 reviewers contributed findings.

Comment thread .github/cursor-review/catalog-drift.py Outdated
Comment thread .github/cursor-review/catalog-drift.py Outdated
Comment thread .github/cursor-review/catalog-drift.py Outdated
Comment thread .github/cursor-review/catalog-drift.py Outdated
… hole (BE-4852)

Review panel findings on the BE-4852 catch-all:

* `_HAS_DIGIT` gated the CATALOG parser as well as the pin validator, so a
  real digit-less id — Cursor ships `code-supernova` — was dropped by
  `catalog_entries` before parsing. A newly shipped digit-less family
  therefore never reached the unpinned-families catch-all this PR adds and
  the run reported clean: exactly the "a new model shipped and nobody
  noticed" silence the check exists to end. It also hard-failed extraction
  every run if such an id were ever pinned. Removed: over-reporting a
  hyphenated prose word (one row in a collapsed, never-urgent list) is the
  cheaper failure, and the direction `present` already argues for.

* `extract_judge_model` never called `_is_model_id`, though the docstring
  claimed it did. Its scalar/whitespace checks are strictly weaker, so a
  separator-less judge default (`o3`) extracted cleanly and then failed
  `present()` — a phantom URGENT "delisted pin" every Monday. It now applies
  the same shared rule, making the "both sides agree" contract true.

* The unpinned-families fold had no per-section budget, so the blunt
  `MAX_BODY_CHARS` clamp could cut into it, leaving `<details>` unterminated
  and dropping the stale-audit and raw-catalog sections below. Budgeted at
  `MAX_FAMILY_LABS`/`MAX_FAMILY_IDS`, naming what it truncated.

* Grouping now keys a dict by lab instead of a linear scan per entry (O(N)).

Tests updated to match: the two that encoded the digit rule now assert the
accepted trade, plus new coverage for the judge shape hole, a digit-less
family reaching the catch-all, and the fold staying well-formed under
truncation.
@mattmillerai mattmillerai added cursor-review Multi-model cursor review and removed cursor-review Multi-model cursor review labels Jul 28, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔍 Cursor Review — Consolidated panel

Triggered by @mattmillerai.

Found 3 finding(s).

Severity Count
🟡 Medium 1
🟢 Low 2

Panel: 8/8 reviewers contributed findings.

Comment thread .github/cursor-review/catalog-drift.py Outdated
Comment thread .github/cursor-review/catalog-drift.py
Comment thread .github/cursor-review/tests/test_catalog_drift.py Outdated
…es ids (BE-4852)

Review-panel fixes on the drift check:

- Body budget (medium): cap every per-lab id list (same-lab review-me,
  delisted alternatives) at MAX_FAMILY_IDS, cap rendered notes at
  MAX_NOTE_CHARS, char-budget the families fold (row caps bound counts,
  not chars), and grant the raw-catalog fold only the MAX_BODY_CHARS the
  report has not used instead of a fixed 40K — so the blunt body[:N]
  clamp can no longer slice mid-markup. Worst-case test added.
- Bare separator-less ids (low): `_is_model_id` now requires a `-`/`.`
  OR a digit (plus a letter), so a real bare id (`o3`) reaches the
  unpinned-families catch-all instead of being dropped before parsing —
  the same silence that got the digit rule reverted.
- Prose-token masking (low): the test comment overclaimed that admitted
  prose "cannot mask a real pin"; a line whose first token equals a pin
  does mask its delisted finding. Comment corrected and the limitation
  pinned by an explicit test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mattmillerai
mattmillerai merged commit 3167ec1 into matt/be-4819-cursor-review-catalog-drift Aug 4, 2026
4 checks passed
@mattmillerai
mattmillerai deleted the matt/be-4852-catalog-drift-lab-heuristics branch August 4, 2026 17:55
@mattmillerai

Copy link
Copy Markdown
Contributor Author

Merged without pre-merge approval — SOC 2 justification

Justification: Advisory CI-only change to this repo's own reusable workflows, merged by the author to clear a backlog that was going stale and self-conflicting; no consumer repo can adopt it without a separate, separately-reviewed SHA-bump PR, so the peer-approval gate still applies downstream before this change reaches any other repo's CI.

Detail:

  • Scope is CI-only. This change touches reusable GitHub Actions workflows and the scripts backing them. This repo has no application runtime and ships no deployed artifact — there is no production code path affected by the merge.

  • Staleness was the forcing function. This PR was merged as part of a backlog-clearing batch that had been accumulating since 2026-07-24. The PRs in the batch overlap heavily on the same files, so every additional day open raised the chance of the set deadlocking on mutual conflicts. That risk was real: several PRs in the batch became conflicting as earlier ones landed and were deliberately left open for manual rebase rather than force-resolved.

  • Downstream adoption is still gated by peer review. Every consumer pins this repo by full commit SHA. Merging to main here changes nothing in any other repo. Adoption requires a separate SHA-bump PR in each consumer repo, and those bump PRs go through that repo's normal review and approval. The peer-approval gate is therefore not bypassed — it moves to the point of actual adoption, which is where the change can first affect another repo's CI.

  • Automated review ran and was addressed. All CI checks were green at merge time, and every CodeRabbit and cursor-review panel thread on this PR was resolved before merge (zero unresolved review threads). PRs in the batch whose review panel had not completed, or whose panel returned no contributed findings, were held back and not merged.

Post-merge peer review remains welcome and will be tracked on the corresponding tracking issue.

mattmillerai added a commit that referenced this pull request Aug 4, 2026
…ck (BE-4852) (#88)

* fix(cursor-review): survive a rebranded model family in the drift check (BE-4852)

Two heuristics in the catalog-drift checker distorted the "unpinned same-lab
ids" review-me list. Neither could produce a false green, but both undercut the
headline feature — noticing that a newer model shipped.

1. `lab_of()` equates a lab with an id's first `-`/`.`-separated token, and labs
   are derived from the pins themselves (what keeps the checker
   zero-maintenance). An existing lab shipping under a NEW family prefix —
   OpenAI's `o<n>` series alongside `gpt-*` is the precedent — therefore
   resolves to a lab nobody pins and vanished from the review-me list. Add a
   quieter catch-all section listing catalog ids whose family prefix matches no
   pin at all, so a rebranded family surfaces instead of dropping out. It is a
   finding (or a rebranded family would render into a body nobody sees) but
   never `urgent`, and renders collapsed below the same-lab list.

2. `catalog_entries()` admitted any lowercase hyphenated first token with a
   letter, so a prose line ("gpt-based models are …") parsed as a bogus id.
   Require a digit as well: every id a lab has shipped carries a version number,
   a prose word does not. The rule lives in the one `_is_model_id` the pin
   validator and the catalog parser share, so the two cannot drift apart —
   `test_pin_and_catalog_id_shape_rules_agree` stays the tripwire, and all five
   live pins satisfy it.

* fix(cursor-review): drop the digit id rule, close the judge-pin shape hole (BE-4852)

Review panel findings on the BE-4852 catch-all:

* `_HAS_DIGIT` gated the CATALOG parser as well as the pin validator, so a
  real digit-less id — Cursor ships `code-supernova` — was dropped by
  `catalog_entries` before parsing. A newly shipped digit-less family
  therefore never reached the unpinned-families catch-all this PR adds and
  the run reported clean: exactly the "a new model shipped and nobody
  noticed" silence the check exists to end. It also hard-failed extraction
  every run if such an id were ever pinned. Removed: over-reporting a
  hyphenated prose word (one row in a collapsed, never-urgent list) is the
  cheaper failure, and the direction `present` already argues for.

* `extract_judge_model` never called `_is_model_id`, though the docstring
  claimed it did. Its scalar/whitespace checks are strictly weaker, so a
  separator-less judge default (`o3`) extracted cleanly and then failed
  `present()` — a phantom URGENT "delisted pin" every Monday. It now applies
  the same shared rule, making the "both sides agree" contract true.

* The unpinned-families fold had no per-section budget, so the blunt
  `MAX_BODY_CHARS` clamp could cut into it, leaving `<details>` unterminated
  and dropping the stale-audit and raw-catalog sections below. Budgeted at
  `MAX_FAMILY_LABS`/`MAX_FAMILY_IDS`, naming what it truncated.

* Grouping now keys a dict by lab instead of a linear scan per entry (O(N)).

Tests updated to match: the two that encoded the digit rule now assert the
accepted trade, plus new coverage for the judge shape hole, a digit-less
family reaching the catch-all, and the fold staying well-formed under
truncation.

* fix(cursor-review): budget every drift-report list, admit bare o-series ids (BE-4852)

Review-panel fixes on the drift check:

- Body budget (medium): cap every per-lab id list (same-lab review-me,
  delisted alternatives) at MAX_FAMILY_IDS, cap rendered notes at
  MAX_NOTE_CHARS, char-budget the families fold (row caps bound counts,
  not chars), and grant the raw-catalog fold only the MAX_BODY_CHARS the
  report has not used instead of a fixed 40K — so the blunt body[:N]
  clamp can no longer slice mid-markup. Worst-case test added.
- Bare separator-less ids (low): `_is_model_id` now requires a `-`/`.`
  OR a digit (plus a letter), so a real bare id (`o3`) reaches the
  unpinned-families catch-all instead of being dropped before parsing —
  the same silence that got the digit rule reverted.
- Prose-token masking (low): the test comment overclaimed that admitted
  prose "cannot mask a real pin"; a line whose first token equals a pin
  does mask its delisted finding. Comment corrected and the limitation
  pinned by an explicit test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
mattmillerai added a commit that referenced this pull request Aug 4, 2026
…E-4819) (#87)

* feat(cursor-review): weekly catalog-drift check for the panel pins (BE-4819)

The per-PR preflight fails loud when a PINNED model id is delisted, but only
at review time, on somebody's PR, after the pin has already gone bad — and by
design it never notices a NEWER model shipping. The only guard against that
second case was a `last checked <date>` comment above the pins, which went
stale inside 13 days: both catalog swaps found by the BE-4817 audit landed in
that window and were caught by hand, not by CI.

Add a scheduled (Mon 06:17 UTC) + dispatchable check that reads the pins OUT
of cursor-review.yml — panel heredoc plus the judge_model input default, never
a duplicated list — diffs them against `cursor-agent models`, and reports
delisted pins, unpinned same-lab catalog ids (a review-me list, not a
recommendation; NO-ZDR markers surfaced verbatim), and a `last checked` date
older than 30 days through one sticky issue that is updated in place and
closed automatically on a clean run.

Comparison + rendering live in .github/cursor-review/catalog-drift.py with
unit tests, including one that runs the extractors against the real
cursor-review.yml so a refactor there can't silently blind the check;
cursor-review.yml joins that test job's path filter for the same reason.

* fix(cursor-review): least-privilege the drift check + paginate the sticky lookup (BE-4819)

Addresses the CodeRabbit review on #87.

* Split the sticky-issue write out of the job that runs Cursor's installer.
  `curl https://cursor.com/install | bash` is a mutable third-party artifact
  Cursor ships no checksummed/versioned build of, and it ran in the same job
  that held `issues: write` — so every later `gh` call was downstream of code
  Cursor can change at will. The `drift` job now holds only `contents: read`
  and hands its rendered report to a new credential-free-of-installers
  `sticky` job via an artifact; `issues: write` exists in exactly one job that
  runs no third-party code. Same boundary groom.yml draws around its agent
  jobs, and it drops the workflow-level `permissions:` block that granted the
  write scope to every job (zizmor's excess-permissions finding).

* Paginate the sticky-issue lookup. `gh issue list --limit 500` silently
  truncates, and a sticky that falls off the end of that page gets filed a
  second time. Walk every open issue with `gh api --paginate`, filtering out
  PRs (`/issues` returns them; `gh issue list` did not).

* `value[0] not in ">|"` instead of `not value[0] in ">|"` (Ruff E713).

No behavior change to the drift report itself; the 78 script tests still pass.

* fix(cursor-review): close the drift check's silent-miss paths from the review panel (BE-4819)

Addresses the cursor-review panel findings on #87.

* HIGH — `present()` scanned the RAW catalog text with a whole-token regex, so
  a delisted pin still mentioned anywhere in the output was treated as present
  and never reported. That is not an edge case: a delist normally ships WITH a
  note naming the id it replaces ("kimi-k3-code (replaces kimi-k2.7-code)"), so
  the urgent path would have stayed silent in exactly the scenario it exists
  for, while consumer PRs went red at preflight. Now compares against the
  PARSED catalog ids, the same source `unpinned` already used. The trade-off is
  deliberate: a garbled catalog over-reports (loud) instead of under-reporting.

* HIGH — the installer step no longer receives `CURSOR_API_KEY`. The presence
  check moved to its own step, so the org secret is not in scope while an
  unpinned remote script runs. (Pinning the installer itself spans all four
  call sites in this repo — proposed as a follow-up.)

* MEDIUM — a new finding class: a pinned model whose catalog line now carries a
  NO-ZDR marker. Nothing breaks, so nothing flagged it, and private review
  diffs kept flowing to a model that may retain them. This is the one marker
  the checker interprets rather than reproduces, and only for a pin already in
  service; it reddens the run like a delisted pin.

* MEDIUM — the quoted-token fallback (and the strict-JSON path) now reject any
  entry that is not a bare model id, instead of adopting a quoted comment or a
  "TODO pick a fifth" placeholder as a pin. A guessed pin set means false
  delisted alarms every Monday, or a real pin dropped and never monitored.

* MEDIUM — the raw catalog is never echoed unfenced. `cursor-agent` output is
  semi-trusted and this repo is public, so a line beginning `::` would be
  executed by the runner (`::add-mask::`, `::stop-commands::`). The shell echo
  is wrapped in a `::stop-commands::<random>` fence; the script's
  unparseable-catalog dump prefixes every line with `| `.

* MEDIUM — the sticky key is now label + title prefix, not the public title
  alone, so the bot cannot adopt and rewrite an issue anyone opened with that
  title. (Pagination landed in the previous commit.)

* LOW — `last checked` is read only within 40 lines above the panel heredoc, so
  an unrelated match elsewhere can't shadow the real audit date; and a
  future-dated typo now counts as stale instead of reading as fresh and
  suppressing the alert until 30 days past that future date.

* LOW — merge catalog notes on a repeated id instead of keeping the first, so a
  later `(NO ZDR)` on a duplicate line is not discarded.

* NIT — catalog notes render in a length-sized, padded inline code span. They
  are unconstrained third-party text reproduced in a bot-authored issue in a
  PUBLIC repo; a bare single-backtick span let a note break out and inject
  markdown or an `@mention` that notifies real people.

Also requires a model id to contain a letter, so a numbered catalog format
parses as nothing (routing to the diagnostic "format may have changed" exit)
rather than as the ids ['1.', '2.'] with every real pin reading as delisted.

The `lab_of` prefix heuristic dropping a lab's rebranded family, and prose
words admitted as catalog ids, are left as-is and proposed as a follow-up —
both distort the review-me list, neither can produce a false green.

94 tests (was 78); actionlint + shellcheck clean.

* fix(cursor-review): survive a rebranded model family in the drift check (BE-4852) (#88)

* fix(cursor-review): survive a rebranded model family in the drift check (BE-4852)

Two heuristics in the catalog-drift checker distorted the "unpinned same-lab
ids" review-me list. Neither could produce a false green, but both undercut the
headline feature — noticing that a newer model shipped.

1. `lab_of()` equates a lab with an id's first `-`/`.`-separated token, and labs
   are derived from the pins themselves (what keeps the checker
   zero-maintenance). An existing lab shipping under a NEW family prefix —
   OpenAI's `o<n>` series alongside `gpt-*` is the precedent — therefore
   resolves to a lab nobody pins and vanished from the review-me list. Add a
   quieter catch-all section listing catalog ids whose family prefix matches no
   pin at all, so a rebranded family surfaces instead of dropping out. It is a
   finding (or a rebranded family would render into a body nobody sees) but
   never `urgent`, and renders collapsed below the same-lab list.

2. `catalog_entries()` admitted any lowercase hyphenated first token with a
   letter, so a prose line ("gpt-based models are …") parsed as a bogus id.
   Require a digit as well: every id a lab has shipped carries a version number,
   a prose word does not. The rule lives in the one `_is_model_id` the pin
   validator and the catalog parser share, so the two cannot drift apart —
   `test_pin_and_catalog_id_shape_rules_agree` stays the tripwire, and all five
   live pins satisfy it.

* fix(cursor-review): drop the digit id rule, close the judge-pin shape hole (BE-4852)

Review panel findings on the BE-4852 catch-all:

* `_HAS_DIGIT` gated the CATALOG parser as well as the pin validator, so a
  real digit-less id — Cursor ships `code-supernova` — was dropped by
  `catalog_entries` before parsing. A newly shipped digit-less family
  therefore never reached the unpinned-families catch-all this PR adds and
  the run reported clean: exactly the "a new model shipped and nobody
  noticed" silence the check exists to end. It also hard-failed extraction
  every run if such an id were ever pinned. Removed: over-reporting a
  hyphenated prose word (one row in a collapsed, never-urgent list) is the
  cheaper failure, and the direction `present` already argues for.

* `extract_judge_model` never called `_is_model_id`, though the docstring
  claimed it did. Its scalar/whitespace checks are strictly weaker, so a
  separator-less judge default (`o3`) extracted cleanly and then failed
  `present()` — a phantom URGENT "delisted pin" every Monday. It now applies
  the same shared rule, making the "both sides agree" contract true.

* The unpinned-families fold had no per-section budget, so the blunt
  `MAX_BODY_CHARS` clamp could cut into it, leaving `<details>` unterminated
  and dropping the stale-audit and raw-catalog sections below. Budgeted at
  `MAX_FAMILY_LABS`/`MAX_FAMILY_IDS`, naming what it truncated.

* Grouping now keys a dict by lab instead of a linear scan per entry (O(N)).

Tests updated to match: the two that encoded the digit rule now assert the
accepted trade, plus new coverage for the judge shape hole, a digit-less
family reaching the catch-all, and the fold staying well-formed under
truncation.

* fix(cursor-review): budget every drift-report list, admit bare o-series ids (BE-4852)

Review-panel fixes on the drift check:

- Body budget (medium): cap every per-lab id list (same-lab review-me,
  delisted alternatives) at MAX_FAMILY_IDS, cap rendered notes at
  MAX_NOTE_CHARS, char-budget the families fold (row caps bound counts,
  not chars), and grant the raw-catalog fold only the MAX_BODY_CHARS the
  report has not used instead of a fixed 40K — so the blunt body[:N]
  clamp can no longer slice mid-markup. Worst-case test added.
- Bare separator-less ids (low): `_is_model_id` now requires a `-`/`.`
  OR a digit (plus a letter), so a real bare id (`o3`) reaches the
  unpinned-families catch-all instead of being dropped before parsing —
  the same silence that got the digit rule reverted.
- Prose-token masking (low): the test comment overclaimed that admitted
  prose "cannot mask a real pin"; a line whose first token equals a pin
  does mask its delisted finding. Comment corrected and the limitation
  pinned by an explicit test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent-coded Authored by the agent-work loop cursor-review Multi-model cursor review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants