Skip to content

fix: harden synced registry contracts - #2806

Merged
stranske merged 4 commits into
mainfrom
fix/sync-review-contract-hardening
Jul 22, 2026
Merged

fix: harden synced registry contracts#2806
stranske merged 4 commits into
mainfrom
fix/sync-review-contract-hardening

Conversation

@stranske

@stranske stranske commented Jul 22, 2026

Copy link
Copy Markdown
Owner

Summary

  • validate legacy slot pins before advisory fallback and normalize profile selection
  • improve registry/client diagnostics and capability-bundle string validation
  • synchronize affected consumer templates

Validation

  • python -m pytest tests/test_check_model_registry_freshness.py tests/tools/test_llm_registry_selection.py tests/tools/test_langchain_client.py -q
  • node --test .github/scripts/__tests__/capability-bundle-contract.test.js
  • python scripts/validate_template_sync.py

Summary by CodeRabbit

  • Bug Fixes
    • Capability bundle validation now strictly rejects invalid gates entries and malformed/empty playbooks.
    • Model profile selection now trims whitespace and consistently defaults when a profile is empty.
    • Slot configuration loading now fails closed (logs a warning) and skips affected slots when unavailable.
    • Model freshness checks now reject unknown and blocked pinned models.
    • When a slot’s resolved model is missing, a warning is emitted instead of silently skipping.
  • Tests
    • Expanded negative validation, added whitespace normalization coverage, updated legacy pin handling expectations, and added a freshness-script execution test.

Copilot AI review requested due to automatic review settings July 22, 2026 16:35
@cursor

cursor Bot commented Jul 22, 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 temporarily deployed to agent-standard July 22, 2026 16:35 — with GitHub Actions Inactive
@agents-workflows-bot

Copy link
Copy Markdown
Contributor

Workflow source needed

PR #2806 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 22, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5c0ea4f0-2ecb-4d81-aecc-9769b8e09275

📥 Commits

Reviewing files that changed from the base of the PR and between d91ab9e and e006de1.

📒 Files selected for processing (1)
  • .github/scripts/__tests__/capability-bundle-contract.test.js

📝 Walkthrough

Walkthrough

Capability bundle validation rejects invalid gate and playbook values. Model registry selection normalizes profiles, slot loading fails closed when configuration is unavailable, freshness checks classify invalid pins, and unresolved model slots emit warnings. Consumer templates and tests are updated.

Changes

Capability bundle validation

Layer / File(s) Summary
Bundle field validation
.github/scripts/capability_bundle.js, templates/consumer-repo/.github/scripts/capability_bundle.js, .github/scripts/__tests__/*
gates entries must be non-empty strings, and provided playbooks entries must also be non-empty strings; contract tests cover invalid object and numeric values.

Model registry resolution

Layer / File(s) Summary
Profile normalization and compatibility
tools/llm_registry.py, templates/consumer-repo/tools/llm_registry.py, tests/tools/test_llm_registry_selection.py
Profile selection trims whitespace and uses the default profile when empty; the consumer template mirrors the behavior and tests verify padded input.
Fail-closed slot loading
tools/llm_registry.py, templates/consumer-repo/tools/llm_registry.py, tools/langchain_client.py
Unavailable slot configuration paths return no slots with a warning, and unresolved model slots are logged before being skipped.
Pin freshness evaluation
tools/check_model_registry_freshness.py, tests/test_check_model_registry_freshness.py
Legacy pin resolution uses the default profile, explicit unknown and blocked pins produce findings, and direct script execution is tested.

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

Possibly related PRs

Suggested labels: sync, maintenance

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the main change: hardening synced registry contracts.
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.
✨ 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 fix/sync-review-contract-hardening

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

@stranske
stranske force-pushed the fix/sync-review-contract-hardening branch from 0879079 to e6c53d3 Compare July 22, 2026 16:36
@agents-workflows-bot

agents-workflows-bot Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Automated Status Summary

Head SHA: 801bed8
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: 1

Coverage Trend

Metric Value
Current 75.73%
Baseline 85.00%
Delta -9.27%
Minimum 70.00%
Status ✅ Pass

Top Coverage Hotspots (lowest coverage)

File Coverage Missing
scripts/issue_dedup_smoke.py 0.0% 4
scripts/runner_lib/__main__.py 0.0% 3
scripts/validate_template_sync.py 0.0% 81
scripts/langchain/topic_splitter.py 19.1% 57
tools/codex_log_analyzer.py 19.6% 140
scripts/repo_review_round2_runner.py 25.1% 344
scripts/prune_agent_stubs.py 39.7% 26
scripts/repo_review_round1_runner.py 40.7% 133
tools/ensure_workflow_timeout_variables.py 42.1% 74
scripts/sync_label_docs.py 42.9% 64
tools/discover_model_catalog.py 44.8% 55
scripts/repo_review_backlog_scan.py 45.3% 116
scripts/repo_review_body_writer.py 46.5% 86
tools/codex_session_analyzer.py 47.9% 59
scripts/create_verifier_labels.py 48.3% 58

Low Coverage Files (<50.0%)

File Coverage Missing
scripts/issue_dedup_smoke.py 0.0% 4
scripts/runner_lib/__main__.py 0.0% 3
scripts/validate_template_sync.py 0.0% 81
scripts/langchain/topic_splitter.py 19.1% 57
tools/codex_log_analyzer.py 19.6% 140
scripts/repo_review_round2_runner.py 25.1% 344
scripts/prune_agent_stubs.py 39.7% 26
scripts/repo_review_round1_runner.py 40.7% 133
tools/ensure_workflow_timeout_variables.py 42.1% 74
scripts/sync_label_docs.py 42.9% 64
tools/discover_model_catalog.py 44.8% 55
scripts/repo_review_backlog_scan.py 45.3% 116
scripts/repo_review_body_writer.py 46.5% 86
tools/codex_session_analyzer.py 47.9% 59
scripts/create_verifier_labels.py 48.3% 58

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

Copilot AI 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.

Pull request overview

This PR hardens the model registry / slot selection contracts and capability-bundle validation, tightening “fail closed” behavior around legacy pins and improving diagnostics. It also mirrors the relevant contract changes into the consumer template copy to avoid drift.

Changes:

  • Normalize selection profile inputs (trim whitespace / default to DEFAULT_SELECTION_PROFILE) and tighten legacy slot-pin behavior to only honor current, unblocked pins.
  • Improve client/runtime diagnostics when skipping unresolved slot models and strengthen capability-bundle string validation (gates + optional playbooks).
  • Update tests and synchronize the affected template copies under templates/consumer-repo/.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
tools/llm_registry.py Normalizes profile selection and hardens slot-config behavior around advisory legacy pins.
tools/langchain_client.py Adds explicit warning when a slot has no resolved model before skipping it.
tools/check_model_registry_freshness.py Tightens freshness gate behavior for legacy pins; now rejects unknown/blocked pins and uses shared default profile constant.
tests/tools/test_llm_registry_selection.py Adds coverage ensuring profile selection normalizes whitespace.
tests/test_check_model_registry_freshness.py Updates expectations: unknown/blocked legacy pins are now rejected (new findings asserted).
templates/consumer-repo/tools/llm_registry.py Mirrors the registry/profile/slot hardening changes into the consumer template copy.
.github/scripts/capability_bundle.js Strengthens schema validation: gates must be non-empty strings; playbooks (if present) must be non-empty strings.
templates/consumer-repo/.github/scripts/capability_bundle.js Mirrors capability-bundle validation hardening into the consumer template copy.
.github/scripts/tests/capability-bundle-contract.test.js Adds tests for non-string gates and invalid playbooks entries.

Comment on lines +351 to 355
if selected.get("model_id") != explicit_model:
if not profile:
continue
findings.append(
_finding(

@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: 08790793cd

ℹ️ 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".

Comment thread tools/check_model_registry_freshness.py Outdated
from pathlib import Path
from typing import Any

from tools.llm_registry import DEFAULT_SELECTION_PROFILE

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep the freshness gate runnable as a script

When Maint 77 invokes this file as python3 tools/check_model_registry_freshness.py --json (as in .github/workflows/maint-77-model-registry-freshness.yml), Python puts tools/ rather than the repo root on sys.path, so this new absolute import fails before any JSON is written: ModuleNotFoundError: No module named 'tools'. I verified the command from the repo root and it exits 1, which makes PR freshness checks fail and scheduled runs create malformed/empty reports instead of evaluating the registry.

Useful? React with 👍 / 👎.

@stranske
stranske temporarily deployed to agent-standard July 22, 2026 16:38 — with GitHub Actions Inactive

@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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tools/check_model_registry_freshness.py (1)

343-380: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Remove the duplicate invalid-pin checks.

Lines 343-356 now emit unknown_pin/blocked_pin for every explicit pin, but Lines 367-380 emit the same finding again when profile is set. Remove the retained profile-specific block and assert each finding kind occurs once in the rejection tests.

Proposed fix
-            if profile and model is None:
-                findings.append(
-                    _finding(
-                        "unknown_pin",
-                        f"slot {name!r} pins absent model {provider}/{explicit_model}.",
-                    )
-                )
-            elif profile and model.get("blocked"):
-                findings.append(
-                    _finding(
-                        "blocked_pin",
-                        f"slot {name!r} pins blocked model {provider}/{explicit_model}.",
-                    )
-                )
             continue

As per path instructions, “Prioritize correctness, error handling, and test coverage. Flag new or changed behavior with no accompanying test.”

🤖 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/check_model_registry_freshness.py` around lines 343 - 380, Remove the
redundant profile-gated unknown_pin/blocked_pin block after the
selection_override handling, keeping the earlier checks as the single source of
these findings for all explicit pins. Update the rejection tests to assert each
finding kind is emitted only once, including cases with a profile.

Source: Path instructions

🤖 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 @.github/scripts/__tests__/capability-bundle-contract.test.js:
- Around line 123-130: Add a contract assertion alongside the existing
validateCapabilityBundle cases that passes validBundle({ gates: [] }) and
expects the “at least one gate ref” validation error, covering the newly
rejected empty-gates boundary.

In `@tests/tools/test_llm_registry_selection.py`:
- Around line 71-78: Extend test_profile_selection_normalizes_whitespace to
assert that an empty or whitespace-only profile falls back to
DEFAULT_SELECTION_PROFILE and returns the expected default model. Reuse the
existing registry setup and select_model_for_profile call, covering the
profile.strip() or default branch without changing the valid-profile assertion.

---

Outside diff comments:
In `@tools/check_model_registry_freshness.py`:
- Around line 343-380: Remove the redundant profile-gated
unknown_pin/blocked_pin block after the selection_override handling, keeping the
earlier checks as the single source of these findings for all explicit pins.
Update the rejection tests to assert each finding kind is emitted only once,
including cases with a profile.
🪄 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: 181e1a4f-952c-4d23-a5f2-53cbb357ad49

📥 Commits

Reviewing files that changed from the base of the PR and between 01d9d86 and e6c53d3.

📒 Files selected for processing (9)
  • .github/scripts/__tests__/capability-bundle-contract.test.js
  • .github/scripts/capability_bundle.js
  • templates/consumer-repo/.github/scripts/capability_bundle.js
  • templates/consumer-repo/tools/llm_registry.py
  • tests/test_check_model_registry_freshness.py
  • tests/tools/test_llm_registry_selection.py
  • tools/check_model_registry_freshness.py
  • tools/langchain_client.py
  • tools/llm_registry.py

Comment thread .github/scripts/__tests__/capability-bundle-contract.test.js
Comment on lines +71 to +78
def test_profile_selection_normalizes_whitespace(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
registry_path = tmp_path / "registry.json"
_write_registry(registry_path)
monkeypatch.setenv(registry.ENV_MODEL_REGISTRY_CONFIG, str(registry_path))

assert registry.select_model_for_profile(provider="openai", profile=" verifier-balanced ") == "model-balanced"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Cover the whitespace-only default-profile branch.

This verifies trimming around a valid profile, but not profile.strip() or DEFAULT_SELECTION_PROFILE. Add an assertion for profile="" or " " so the fallback cannot regress.

🤖 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_llm_registry_selection.py` around lines 71 - 78, Extend
test_profile_selection_normalizes_whitespace to assert that an empty or
whitespace-only profile falls back to DEFAULT_SELECTION_PROFILE and returns the
expected default model. Reuse the existing registry setup and
select_model_for_profile call, covering the profile.strip() or default branch
without changing the valid-profile assertion.

Source: Path instructions

@stranske

stranske commented Jul 22, 2026

Copy link
Copy Markdown
Owner Author

Runner dispatch state for autofix on PR #2806. Do not edit.

@github-actions github-actions Bot added the autofix Opt-in automated formatting & lint remediation label Jul 22, 2026
@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Runner dispatch state for codex on PR #2806. Do not edit.

@stranske
stranske temporarily deployed to agent-standard July 22, 2026 16:46 — with GitHub Actions Inactive
@github-actions

Copy link
Copy Markdown
Contributor

Autofix updated these files:

  • tests/tools/test_llm_registry_selection.py

@stranske
stranske temporarily deployed to agent-standard July 22, 2026 18:29 — with GitHub Actions Inactive
@stranske
stranske merged commit d492af9 into main Jul 22, 2026
57 checks passed
@stranske
stranske deleted the fix/sync-review-contract-hardening branch July 22, 2026 20:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

autofix:patch autofix Opt-in automated formatting & lint remediation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants