Skip to content

feat(registry): derive selector decode hints from a classified registry - #5177

Merged
lidge-jun merged 3 commits into
devfrom
codex/5115-registry-decode-hints
Sep 19, 2026
Merged

lidge-jun merged 3 commits into
devfrom
codex/5115-registry-decode-hints

Conversation

@lidge-jun

@lidge-jun lidge-jun commented Sep 19, 2026 •

Copy link
Copy Markdown
Owner

Summary

An encoded provider/model selector is decoded against the native model ids a provider is known to accept. That set was a hand-written list of eight registry maps in knownModelIdsForProvider, so an id declared only in an unlisted identity-bearing map could not be decoded, and every new model-keyed registry field had to be remembered by whoever added it.

src/providers/registry/model-ids.ts now classifies every field of ProviderRegistryEntry by what its keys mean, and the router asks the registry instead of listing maps itself. Fifteen direct maps contribute keys, keyAuthServiceTier contributes its nested modelSupportsServiceTier keys, and the remaining seventy-five fields are classified as carrying no identity.

Classifying the non-model fields is the point. The satisfies clause makes a new registry field a type error until somebody decides what its keys mean, rather than letting it default to invisible. That is the difference between this and a longer hand-written list.

Two exhaustiveness checks, deliberately different

  • The satisfies Record<keyof ProviderRegistryEntry, RegistryFieldModelIdRole> clause proves the classification covers the interface, at compile time.
  • A parity case proves the fields the shipped entries actually carry are all classified, at runtime.

A new interface field fails the first. A shipped entry carrying something unclassified fails the second. Neither substitutes for the other.

What a hint is not

Decode hints are identity evidence only. They publish no catalog row, grant no availability or entitlement, and confer no transport authority: registry transport identity is still checked before hints are read, and an ambiguous selector is still rejected rather than guessed. virtualModels contributes its selectable keys only, never the wireModelId transport target, which is not something a client may name.

Closes #5115.

Verification

  • Local checks: NOT RUN. This lane is prohibited from running local suites, focused tests, typecheck, builds, installs or any live ocx command. Exact-head hosted CI on this PR is the executable evidence.
  • Static screen: every changed file parses cleanly under an AST error-node check.
  • Route-level coverage in tests/codex-integration/slug-codec.test.ts: all fifteen classified direct maps and the nested key-auth map each independently seed a decode, using distinct synthetic slash-bearing ids so the encoded selector round-trip is meaningful. Negatives cover unknown pass-through, ambiguous-selector rejection, transport mismatch inheriting nothing, and no catalog row from a hint alone. The helper contract is asserted too: the returned array is frozen and rejects a push, and the registry entry is byte-identical afterwards, which matters because the registry is process-wide shared state.
  • Registry parity in tests/providers/provider-registry-parity.test.ts: runtime classification coverage over the shipped registry, per-map independent collection, non-model records (staticHeaders, reasoningEffortMap, the repair object) contributing nothing, stable first-seen-wins ordering in registry-field order, and a shipped entry whose ids are recovered from its classified maps even though the helper never reads its models list.
  • No file-size cap moves, and no layout inventory changes because no test file was added.

Checklist

  • Scope stays focused and avoids unrelated cleanup.
  • Docs or release notes were updated when needed.
  • Security-sensitive changes were reviewed for secrets, auth, and unsafe defaults.

Summary by CodeRabbit

  • Enhancements

    • Selector decoding now recognizes model IDs defined across the provider registry, including nested service-tier mappings and virtual model identities.
    • Model IDs are deduplicated consistently while preserving registry order.
    • Unknown and ambiguous selectors continue to be handled safely, without treating decode hints as availability or entitlement.
  • Documentation

    • Clarified how registry model IDs support selector decoding and documented the associated behavior.

An encoded selector decodes against the native model ids a provider is known to
accept. That set was a hand-written list of eight registry maps, so an id
declared only in an unlisted map could not be decoded, and every new
model-keyed field had to be remembered by whoever added it.

src/providers/registry/model-ids.ts now classifies every field of
ProviderRegistryEntry by what its KEYS mean, and knownModelIdsForProvider asks
the registry instead of listing maps itself. Fifteen direct maps contribute
keys, keyAuthServiceTier contributes its nested modelSupportsServiceTier keys,
and the remaining seventy-five fields are classified as carrying no identity.
Classifying the non-model fields is the point: the satisfies clause makes a new
registry field a type error until somebody decides what its keys mean, rather
than letting it default to invisible.

Two checks, deliberately different. The satisfies clause proves the
classification is exhaustive over the interface when this compiles; a parity
case proves the fields the shipped entries actually carry are all classified. A
new interface field fails the first, a shipped entry carrying something
unclassified fails the second.

Hints stay identity evidence. They publish no catalog row, grant no
availability or entitlement, and confer no transport authority: registry
transport identity is still checked before hints are read, an ambiguous selector
is still rejected rather than guessed, and virtualModels contributes its
selectable keys only, never the wire target a client may not name.

Coverage: each formerly omitted direct map and the nested map independently seed
a decode, with negatives for unknown pass-through, ambiguous rejection,
transport mismatch and no catalog row from a hint alone; the parity suite adds
per-map collection, non-model records contributing nothing, stable
first-seen-wins ordering, and the shipped meta-muse entry recovering its ids
from maps alone.

The classifier is imported directly rather than through the registry facade,
which is at its file-size cap. No layout inventory changes, since no test file
was added. routedProviderConfig and the static policy paths are untouched.

Local checks: NOT RUN.
… level

The route-level table proved the seven maps the old hand-written list omitted.
The other eight rested on helper parity alone, which is a weaker claim: after
this change every one of them is reached through the same classification, so
they belong in the same public table. It now covers all fifteen direct maps
plus the nested key-auth map.

Also asserts the helper's documented contract rather than assuming it: the
returned array is frozen and rejects a push, and the registry entry is
byte-identical afterwards. That matters because the registry is process-wide
shared state, and a decode path that sorted an entry's maps in place would be
editing it for every later caller.

A shipped-entry route case was written and then dropped rather than kept. The
Muse ids contain no slash, so encoding them is the identity function and the
route would pass them through whether or not any hint existed. It proved
nothing about decoding, and the fifteen synthetic slash-bearing rows already
prove that at the public level. The shipped-entry evidence stays where it is
honest: the parity suite asserts the helper recovers those ids from the
classified maps, which is meaningful because the helper never reads the models
list even though this entry also declares them there.

No production change, no new fixture mechanism, no cap or layout movement.

Local checks: NOT RUN.
@lidge-jun
lidge-jun requested a review from Ingwannu as a code owner September 19, 2026 14:29
@github-actions

Copy link
Copy Markdown
Contributor

✅ Deterministic PR hygiene checks passed.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 19, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-19T14:33:13.486030Z 8502bf9 PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions github-actions Bot added the enhancement New feature or request label Sep 19, 2026
@coderabbitai

coderabbitai Bot commented Sep 19, 2026 •

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: lidge-jun/opencodex/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: b5ffc1c0-db98-411f-9c80-2f215472126a

📥 Commits

Reviewing files that changed from the base of the PR and between 8502bf9 and a654ee8.

📒 Files selected for processing (1)
  • src/providers/registry/model-ids.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 3 remain after this review.


📝 Walkthrough

Walkthrough

The change classifies all ProviderRegistryEntry fields and extracts native model IDs from classified maps. Selector decoding now uses these IDs as hints. Tests and documentation preserve transport checks, ambiguity rejection, and separation from catalog availability.

Changes

Registry selector hints

Layer / File(s) Summary
Classify and extract registry model IDs
src/providers/registry/model-ids.ts, tests/providers/provider-registry-parity.test.ts
REGISTRY_FIELD_MODEL_ID_ROLES classifies every registry field. registryModelIdKeys collects non-empty keys, preserves registry-field order, deduplicates keys, reads nested service-tier keys, and excludes virtualModels.wireModelId values.
Wire extracted IDs into selector decoding
src/router.ts, structure/catalog.md, structure/providers-and-adapters.md
knownModelIdsForProvider now calls registryModelIdKeys(registry) instead of using a hardcoded map list. Documentation defines these IDs as decode evidence, not catalog or transport authority.
Validate selector behavior
tests/codex-integration/slug-codec.test.ts
Tests cover direct and nested registry hints, frozen extraction results, unknown and ambiguous selectors, transport mismatches, and the absence of catalog rows from decode-only hints.

Priority: ⬇️ Low

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

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant Selector as Selector decoder
  participant Router as knownModelIdsForProvider
  participant Registry as registryModelIdKeys
  participant Catalog as Catalog
  Selector->>Router: Request known model IDs
  Router->>Registry: Extract classified registry keys
  Registry-->>Router: Return deduplicated native model IDs
  Router-->>Selector: Return decode hints
  Selector->>Catalog: Decode without adding a catalog row
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 4 files. 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 clearly and concisely describes the main change: deriving selector decode hints from a classified provider registry.
Linked Issues check ✅ Passed Issue #5115 requirements are met. src/providers/registry/model-ids.ts classifies every ProviderRegistryEntry field with satisfies Record<keyof ProviderRegistryEntry, RegistryFieldModelIdRole> an…
Out of Scope Changes check ✅ Passed The changes stay within Issue #5115. The production changes add exhaustive registry identity classification and replace the router's partial hand-maintained map list. The tests verify the requested se…
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

The decode-hint loop asserted the entry to Record<string, unknown> to index it
by field name. ProviderRegistryEntry is an interface, so it carries no implicit
index signature and tsc rejected the conversion as insufficiently overlapping
(TS2352 at model-ids.ts:152), which failed Typecheck and the test that spawns a
real tsc over the production graph.

Iterating the classification's own keys as keyof ProviderRegistryEntry keeps the
read typed: the record-keys branch indexes the entry by that key and guards the
value as a non-null object, and the nested role reads keyAuthServiceTier by name
because that field is its only owner. Same fields, same order, same keys; no
assertion through unknown and no behavior change.

local checks: NOT RUN
@lidge-jun

Copy link
Copy Markdown
Owner Author

Maintainer integration into dev under MAINTAINERS.md by the authenticated project owner, not a self-approval.

Reviewed head a654ee8e4469f2c431b378a4ec42c68765e74a90 passed all applicable hosted checks and aggregate in https://github.com/lidge-jun/opencodex/actions/runs/35449601489 . macOS logs explicitly execute all classified direct-map public decodes, nested key-auth map decoding, immutability, unknown pass-through and ambiguity rejection. Independent source, test and typed-iteration reviews passed; no unresolved threads or maintainer objections remain.

Current dev 40670044146adc714f06d8b2e29f9114f8393df5 has no changed-file intersection with this six-file change since its base. Integration tree eb3742407d206a7a4d427c601004c5b495448c02 preserves all six files byte-for-byte and passed independent union review. The earlier policy-first merge order was a coordination preference: both source audit and the policy implementation owner confirm no semantic dependency. This PR can land standalone; the later policy consumer rebase must preserve both router imports and this helper and obtain fresh CI.

Native-stack membership is empty. Actor, dev base and exact head were revalidated. No local suites, typecheck, build or runtime execution were performed. Issue #5115 closes only after actual dev landing and ancestry verification.

@lidge-jun
lidge-jun merged commit 838af40 into dev Sep 19, 2026
33 checks passed
@lidge-jun
lidge-jun deleted the codex/5115-registry-decode-hints branch September 19, 2026 15:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant