Skip to content

Bug: define successor routing contract for disabled collaborators #1292

Description

@whutzefengxie-ops

What happened?

When a collaborator is disabled in the public roster but has a replacement identity, routing surfaces do not share one accepted contract for legacy explicit mentions, implicit/default targets, pre-resolved execution, and no-target behavior.

Concrete case:

  • opus.available=false
  • opus.successor=opus-5
  • opus-5.available=true

Without a central contract, the system can continue generating or persisting the legacy opus target, while individual consumers independently decide whether to redirect, warn, choose another cat, or persist a sentinel.

PR #1283 is a broad candidate implementation, but it is intentionally on HOLD pending maintainer acceptance of this focused scope.

Steps to reproduce

  1. Configure the roster so opus is disabled and points to available, registered opus-5.
  2. Exercise each boundary:
    • send a message with no explicit mention in a new thread;
    • load an existing routing policy or participant containing opus;
    • explicitly send @opus;
    • invoke a system producer with pre-resolved targetCats=['opus'];
    • disable/remove the configured successor and request a guardian/default target;
    • inspect public template/backfill behavior on an existing runtime catalog.
  3. Observe that these boundaries historically used different fallback and validation rules.

There is also a roster metadata edge in the current candidate patch: the disabled legacy opus remains lead:true, while available opus-5 is lead:false. With the current ActorResolver, that changes deep repo-watcher and health-monitor selection from the former Opus lead to codex, rather than transferring the lead role to the explicit successor.

Expected behavior / focused contract

  • Explicit legacy mention: @opus remains fail-closed. Return a structured cat_disabled warning; put canonical @opus-5 first in alternatives. Do not silently retarget explicit user intent.
  • Implicit/default/persisted target: a disabled cat may resolve through exactly one explicitly configured successor edge.
  • Successor validity: the successor must be registered and available. Do not infer replacements from family or version ordering.
  • Central execution admission: pre-resolved system targets must pass the same availability/successor guard before serial or parallel execution.
  • No valid successor: return an explicit no-target result. Do not choose an unrelated service and do not persist __none__ as a guardian/member identity.
  • Public seed/backfill ownership: public template data may add the necessary public variant/roster metadata, but must not overwrite runtime-owned private catalog fields. Backfill must be explicit and allowlisted.
  • Operational identity metadata: clarify whether lead status belongs to the retired identity or transfers to its explicit successor. For this Opus replacement, scheduler selection should not drift accidentally because the only active Ragdoll successor lost lead metadata.
  • Active prompts/UI: only current canonical generation surfaces should emit/save opus-5; historical records should not be globally rewritten.

Acceptance criteria

  • Maintainers accept this issue before PR deep review resumes.
  • Resolver tests cover available, missing, self-loop, unknown, and disabled successors.
  • Explicit @opus stays fail-closed with @opus-5 as the first alternative.
  • Default, persisted policy, participant/last-replier, and pre-resolved execution paths resolve to opus-5 through the same contract.
  • Invalid/no successor produces no executable target and cannot create a guardian assignment/token.
  • Public template/backfill tests prove runtime-owned fields are preserved.
  • A scheduler/ActorResolver integration test locks the accepted lead-metadata behavior using the real public roster.
  • The implementation branch is based on current main and contains no unrelated patch-equivalent upstream commits.
  • Exact-HEAD CI and a non-author formal review are green.

Alternatives considered

  • Hard-code opus-5 in each consumer: rejected because it will drift at the next identity upgrade.
  • Pick the newest available member of the same family: rejected because family/version naming is not a safe successor contract.
  • Silently redirect explicit @opus: rejected because it changes explicit user intent.
  • Bulk-copy the private runtime catalog into the public seed: rejected because it would leak installation-owned aliases and runtime state.

Environment

[砚砚/gpt-5.6-sol🐾]

Metadata

Metadata

Assignees

No one assigned

    Labels

    acceptedMaintainer accepted: ready for implementation/mergebugSomething isn't workingtriagedMaintainer reviewed, replied, and made an initial triage decision

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions