Skip to content

fix(core): governance pruning of requested-variant leaves can bypass subagent exclusions for retarget-added leaves (Codex P1 on #871) #889

Closed

Description

@dezren39

Problem

Found by Codex review on PR #871 (head 07e72e8, P1, packages/core/src/session/model-governance.ts): governance (routeTargets/leaf()) prunes a leaf that cannot produce the pinned/route-requested variant, mirroring plugin expansion (#869). But in the retarget path, a model transform can add a leaf without the requested variant while the route still advertises that variant; ModelResolver.resolveSelected() then accepts the variant and model-resolver.ts loads that deployment with an undefined variant — it runs bare on failover. Because governance omitted the deployment entirely, a subagent exclusion naming that deployment no longer blocks it.

Why this is not a one-line fix

PR #871 deliberately implements — and tests — the opposite semantics for the catalog path (model-governance.test.ts: "a leaf that cannot produce the requested variant is pruned from governance, as in expansion (#869)"), where expansion really does drop the leaf and it never runs. The bypass needs the retarget/resolver path, where the leaf survives and runs bare. Reconciling the two needs a design call: either governance keeps a bare ref for leaves the resolver can run bare (and the #869 pruning test is re-scoped to the pure-expansion case), or runtime resolution guarantees such leaves cannot execute (resolver-side), or governance judges the retargeted definitions rather than the catalog.

Acceptance

Refs: PR #871, issue #869, task 3411. Filed by repo-queue-worker Lane A, 2026-10-29.

Acceptance criteria

Activity

  1. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    on Oct 29, 2026
  2. developing-today commented on Oct 29, 2026

    @developing-today
  3. added
    priority/highImportant and time-sensitive
    effort/mMedium: expected under 1 day of work
    meta/enrichedIntake review done: acceptance criteria, links, and normalized labels added
    on Oct 29, 2026
  4. dezren39 commented on Oct 29, 2026

    @dezren39
    Author

    Intake review

    Related: PR #871 (Codex P1, head 07e72e8, packages/core/src/session/model-governance.ts); #869 (plugin-expansion pruning semantics this must not regress)

    Notes: Filed 2026-10-29T13:56Z from a Codex P1 on PR #871, which is still OPEN — the bypass it describes is live in that PR's retarget path. Still live: no open or merged PR references #889. The issue body itself explains why it is not a one-line fix: #871 deliberately implements the opposite semantics for the catalog path.

    Risks: A subagent exclusion that silently stops blocking a deployment is a governance/safety hole, not a cosmetic bug — hence priority/high. The fix must not break the deliberate #869 expansion pruning.

    Suggested approach: Decide the reconciliation layer first (the issue lists the three candidate designs), then implement with the Codex scenario as the regression test.

    🤖 Agent note · filed

    • Agent: fleet monitor (repo-fleet-monitor)
    • Session / ID: repo-fleet-monitor run 2026-10-29T14:30Z (09:30 CDT)
    • Found via: monitor reconciliation
    • Task: Duty 2 intake enrichment — acceptance criteria appended (original body preserved verbatim above it), labels normalized, meta/enriched applied. Still-live check this pass: no merged PR or fix commit explicitly resolving this issue was found; where a queue triage task owns verification, it is named in the notes.
    • Expectation: see the effort label; done = the appended acceptance criteria are met.
    • Links: this issue
  5. dezren39 commented on Oct 29, 2026

    @dezren39
    Author

    Verified against PR #871 head d77f8128a4 (lane openchamber-scheduled-lane, task 3411): the bypass this issue describes is closed by the variant refresh in 203b8f7d1d, so the issue is being closed as addressed by #871.

    The premise no longer holds. The finding assumed "retargeting updates the route's settings but leaves its advertised variants unchanged". retargetDefinition() now returns routeVariants(leaves) computed over the post-transform leaves, and the model transform writes it back (packages/core/src/config/plugin/model-routes.ts, model transform: draft.variants = variants). When a transform adds a deployment that cannot produce the requested variant natively or through its remap, that variant leaves the route's advertised set.

    Consequence at resolution time. ModelResolver.resolveSelected() rejects any requested variant the route does not advertise (packages/core/src/model-resolver.ts ~611: if (requestedVariant && !selected.variants.some(...)) VariantUnavailableError) before resolveRoute() picks a target. So the retarget-added deployment is never loaded with an undefined variant under that route variant, and an exclusion naming it has nothing to bypass: the run set is empty, not "the leaf runs bare". Governance pruning the same leaf is therefore consistent with the runtime — the judged set and the run set still agree.

    Regression coverage (packages/core/test/model-route-transform-models.test.ts, "advertised variants after retargeting (#869)"):

    • "a variant the retargeted leaf cannot produce is dropped from the route" — */* over a catalog model offering max,high, transform adds a variant-less deployment, retarget yields variants === [] (the exact scenario from the Codex thread).
    • "a variant every retargeted leaf can produce stays advertised" — max is kept when the deployment offers it.
    • Acceptance item 2 (existing fix(routes): door-set governance variant remaps + retarget across concrete doors (PR #810 follow-up) #869 catalog/expansion pruning semantics intact) holds: that test was not weakened.

    Acceptance item 3 (rationale recorded): the reconciliation chosen is governance judges the variant the runtime can actually run, and the route advertises only what every leaf can produce, i.e. neither "keep a bare ref" nor a resolver-side rewrite was needed — the resolver already refuses an unadvertised variant.

    Reopen if any resolution path can reach a route target without going through resolveSelected(); none was found.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    effort/mMedium: expected under 1 day of workkind/bugSomething isn't working (prefixed form of the classic bug label)meta/enrichedIntake review done: acceptance criteria, links, and normalized labels addedpriority/highImportant and time-sensitivestatus/needs-triageNot yet triaged; needs a first lookstatus:done

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions