Skip to content

maint-78: derive pilot candidates from the registry + auto-trigger (#2819 move 1) - #2831

Merged
stranske merged 1 commit into
mainfrom
model-eval-auto-candidates
Jul 25, 2026
Merged

maint-78: derive pilot candidates from the registry + auto-trigger (#2819 move 1)#2831
stranske merged 1 commit into
mainfrom
model-eval-auto-candidates

Conversation

@stranske

@stranske stranske commented Jul 25, 2026

Copy link
Copy Markdown
Owner

Part of #2819 (self-feeding verifier-model promotion) — move 1 of 3.

Problem

The verifier-pilot candidate list (config/model_eval_candidates.json) was hand-maintained and had already drifted: it listed models that were no longer current while omitting now-current ones (notably claude-opus-4-8). A stale candidate list is why a superseded model stays incumbent forever — nothing ever pilots its replacement.

Change

  • tools/refresh_model_eval_candidates.pyderive_candidates() builds the candidate set straight from config/model_registry.json: incumbent = the profile's reviewed selections entry; candidates = every other current, non-blocked, same-provider catalogued model whose positioning is a plausible verifier tier (excludes efficient / coding-worker-profile). --write regenerates the config; --check is a drift gate.
  • Tests — derivation logic (incumbent/candidate split, exclusions, determinism, profile scoping) + a drift-gate test asserting the committed config equals the derivation, so any catalog change that forgets to refresh candidates reddens CI.
  • config/model_eval_candidates.json — regenerated from the registry (adds claude-fable-5, claude-opus-4-8, and a github-models candidate that the hand list had dropped).
  • maint-78 — auto-runs on catalog change (push to main touching the registry) + weekly cron, and refreshes candidates from the registry before each run so the pilot always tests every now-current model.

Not in scope (later moves)

  • Move 2: grow the 75-case corpus from live pr_verifier decisions labeled by realized PR outcomes.
  • Move 3: tiered auto-promote (same-family, non-inferior, cost ≤) + auto-rollback on quality-gate breach.

Promotion semantics are unchanged here: the pilot only narrows candidates; approval still requires the 75-case corpus + model_selection_policy.json gate (new_catalog_models_auto_promote=false).

Verification

  • black==26.5.1 --check --line-length 100 clean on both new files
  • ruff==0.15.20 clean
  • mypy==2.1.0 clean on the new module
  • pytest tests/tools/test_refresh_model_eval_candidates.py — 7 passed

Summary by CodeRabbit

  • New Features

    • Added automatic model candidate list generation from the model registry.
    • Added validation to detect when the committed candidate list is out of sync.
    • Expanded the evaluation pilot with additional model candidates.
    • Evaluation pilots now run automatically when relevant configuration changes and on a weekly schedule.
  • Tests

    • Added coverage for candidate selection, filtering, sorting, regeneration, and drift detection.

Move 1 of the self-feeding verifier-model promotion system (#2819).

The pilot candidate list was hand-maintained and had already drifted: it
listed models that were no longer current while omitting now-current ones
(e.g. claude-opus-4-8). A stale candidate list means a superseded model
stays the incumbent forever because nothing ever pilots its replacement.

- tools/refresh_model_eval_candidates.py: derive_candidates() builds the
  candidate set straight from config/model_registry.json — incumbent =
  the profile's reviewed selection, candidates = every other current,
  non-blocked, same-provider model whose positioning is a plausible
  verifier tier (excludes efficient / coding-worker-profile). --write
  regenerates the config; --check is a drift gate.
- tests: derivation logic + a gate test asserting the committed
  config/model_eval_candidates.json equals the derivation, so a catalog
  change that forgets to refresh candidates reddens CI.
- config/model_eval_candidates.json: regenerated from the registry (adds
  claude-fable-5, claude-opus-4-8, and a github-models candidate).
- maint-78: auto-run on catalog change (push to main touching the
  registry) + weekly cron, and refresh candidates from the registry
  before each run so the pilot always tests every now-current model.
  Promotion is unchanged — the pilot only narrows candidates; approval
  still requires the 75-case corpus + policy gate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@cursor

cursor Bot commented Jul 25, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@stranske
stranske enabled auto-merge (squash) July 25, 2026 22:42
@stranske
stranske temporarily deployed to agent-high-privilege July 25, 2026 22:42 — with GitHub Actions Inactive
@stranske-keepalive

Copy link
Copy Markdown
Contributor

Workflow source needed

PR #2831 needs either a linked GitHub issue or one valid non-issue Workflow Source before PR metadata automation can manage it safely.

Please do one of:

  • Add <!-- meta:issue:123 --> or a normal Closes #123 / Related to #123 line.
  • Check one Workflow Source option in the PR body.
  • Add a hidden marker such as <!-- workflow-source:local_request -->, <!-- workflow-source:manual_remote -->, <!-- workflow-source:review_followup -->, <!-- workflow-source:sync_campaign -->, or <!-- workflow-source:dependabot -->.
  • Add a workflow source label such as workflow:source-direct-pr, workflow:source-local-request, workflow:source-review-followup, workflow:source-sync, or workflow:no-automation.

Once a valid source is present, this warning will not be reposted.

@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds registry-driven candidate derivation, CLI drift checks, tests for filtering and ordering, updated candidate configuration, and workflow triggers that refresh candidates before running the evaluation pilot.

Changes

Model evaluation pilot

Layer / File(s) Summary
Candidate derivation and committed output
tools/refresh_model_eval_candidates.py, config/model_eval_candidates.json
Candidates are derived per provider from profile incumbents and eligible current registry models, serialized deterministically, and reflected in the committed configuration.
Candidate derivation and drift validation tests
tests/tools/test_refresh_model_eval_candidates.py
Tests cover role assignment, filtering, profile scoping, ordering, drift detection, write/check roundtrips, and committed registry consistency.
Automated pilot refresh and triggers
.github/workflows/maint-78-model-evaluation-pilot.yml
The pilot runs on relevant main configuration changes and weekly, refreshing candidates before execution.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related issues

Possibly related PRs

Suggested labels: automation, maintenance

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: deriving pilot candidates from the registry and auto-triggering the workflow.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch model-eval-auto-candidates

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

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cbb2113703

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

workflow_dispatch: {}
# Auto-run when the catalog changes so a newly-current model is piloted without
# anyone editing the candidate list (stranske/Workflows#2819, move 1).
push:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Document the automatic pilot triggers

The workflow now runs automatically on matching pushes and a weekly schedule, but the inspected inventory still describes it as workflow_dispatch / “Manual evaluation” in docs/ci/WORKFLOW_SYSTEM.md:748, while docs/ci/WORKFLOWS.md:209 also omits the new cadence and registry-derived behavior. This leaves operators relying on the documented topology unaware that the credential-backed, 30-case evaluation will execute automatically; update both contract documents with these trigger changes.

AGENTS.md reference: AGENTS.md:L62-L65

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/tools/test_refresh_model_eval_candidates.py`:
- Around line 21-65: Extend the model fixture used by the refresh-candidate test
with a current same-provider entry whose positioning is coding-worker-profile,
then update the relevant assertions to verify that model is excluded from the
candidates. Keep the existing efficient, non-current, and blocked exclusion
coverage unchanged.

In `@tools/refresh_model_eval_candidates.py`:
- Around line 99-102: Update the args.write branch in the CLI flow to catch
OSError from args.candidates.write_text, print a useful diagnostic to stderr,
and return a non-zero exit code; preserve the existing success message and
return 0 when writing succeeds.
- Around line 66-67: Update _load and the derive_candidates entry path to
validate that registry JSON is an object with correctly typed selections and
models fields, rejecting arrays and malformed values before candidate
derivation. Route JSON parsing and shape-validation failures through the
existing error-message path and return exit code 2 instead of allowing uncaught
exceptions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 045b5614-69aa-4708-ba20-bc487929656a

📥 Commits

Reviewing files that changed from the base of the PR and between 9aa4404 and cbb2113.

📒 Files selected for processing (4)
  • .github/workflows/maint-78-model-evaluation-pilot.yml
  • config/model_eval_candidates.json
  • tests/tools/test_refresh_model_eval_candidates.py
  • tools/refresh_model_eval_candidates.py

Comment on lines +21 to +65
"models": [
{
"provider": "openai",
"model_id": "gpt-5.4",
"lifecycle": "current",
"positioning": "incumbent-verifier",
},
{
"provider": "openai",
"model_id": "gpt-5.6-terra",
"lifecycle": "current",
"positioning": "balanced",
},
{
"provider": "openai",
"model_id": "gpt-5.6-luna",
"lifecycle": "current",
"positioning": "efficient",
}, # excluded
{
"provider": "openai",
"model_id": "gpt-5.5",
"lifecycle": "compatibility",
"positioning": "frontier",
}, # excluded (not current)
{
"provider": "openai",
"model_id": "gpt-blocked",
"lifecycle": "current",
"positioning": "frontier",
"blocked": True,
}, # excluded (blocked)
{
"provider": "anthropic",
"model_id": "claude-opus-4-6",
"lifecycle": "current",
"positioning": "incumbent-verifier",
},
{
"provider": "anthropic",
"model_id": "claude-opus-4-8",
"lifecycle": "current",
"positioning": "high-capability",
},
],

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Cover the coding-worker-profile exclusion.

The fixture and assertions only exercise efficient; a regression that admits coding-worker-profile models passes all tests. Add a current same-provider model with that positioning and assert it is absent. As per path instructions, “Flag new or changed behavior with no accompanying test.”

Also applies to: 78-82

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/tools/test_refresh_model_eval_candidates.py` around lines 21 - 65,
Extend the model fixture used by the refresh-candidate test with a current
same-provider entry whose positioning is coding-worker-profile, then update the
relevant assertions to verify that model is excluded from the candidates. Keep
the existing efficient, non-current, and blocked exclusion coverage unchanged.

Source: Path instructions

Comment on lines +66 to +67
def _load(path: Path) -> dict[str, Any]:
return json.loads(path.read_text(encoding="utf-8"))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Reject structurally invalid registry JSON cleanly.

A valid JSON array or malformed selections/models value reaches derive_candidates() and raises an uncaught exception. Validate the expected registry shape and return exit code 2 with the existing error-message path. As per path instructions, “Prioritize correctness, error handling, and test coverage.”

Also applies to: 88-94

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tools/refresh_model_eval_candidates.py` around lines 66 - 67, Update _load
and the derive_candidates entry path to validate that registry JSON is an object
with correctly typed selections and models fields, rejecting arrays and
malformed values before candidate derivation. Route JSON parsing and
shape-validation failures through the existing error-message path and return
exit code 2 instead of allowing uncaught exceptions.

Source: Path instructions

Comment on lines +99 to +102
if args.write:
args.candidates.write_text(_serialize(derived), encoding="utf-8")
print(f"wrote {len(derived['candidates'])} candidate rows to {args.candidates}")
return 0

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Handle candidate-file write failures.

An unwritable target or nonexistent parent raises OSError and produces a traceback, rather than a controlled CLI failure. Catch it, print a stderr diagnostic, and return a non-zero exit code. As per path instructions, “Prioritize correctness, error handling, and test coverage.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tools/refresh_model_eval_candidates.py` around lines 99 - 102, Update the
args.write branch in the CLI flow to catch OSError from
args.candidates.write_text, print a useful diagnostic to stderr, and return a
non-zero exit code; preserve the existing success message and return 0 when
writing succeeds.

Source: Path instructions

@stranske-keepalive

stranske-keepalive Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Automated Status Summary

Head SHA: dbc3550
Latest Runs: ⏳ pending — Gate
Required contexts: Gate / gate, Health 45 Agents Guard / guard
Required: core tests (3.12): ⏳ pending, core tests (3.13): ⏳ pending, docker smoke: ⏳ pending, gate: ⏳ pending

Workflow / Job Result Logs
(no jobs reported) ⏳ pending

Coverage Overview

  • Coverage history entries: 0

Failure triage

Detected failure types: pytest.

  • error_type: pytest
    root_cause: Pytest reported failing tests.
    suggested_fix: Inspect failing tests in the reported files and fix the regression or update expectations.
    playbook_url: docs/INTEGRATION_GUIDE.md#scenario-1-tests-failing

Updated automatically; will refresh on subsequent CI/Docker completions.


Keepalive checklist

Scope

No scope information available

Tasks

  • No tasks defined

Acceptance criteria

  • No acceptance criteria defined

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant