feat(routing)!: autorouting with policy-derived tiers, preset layer removed - #4543
feat(routing)!: autorouting with policy-derived tiers, preset layer removed#4543Yeachan-Heo wants to merge 44 commits into
Conversation
daca464 to
874c59f
Compare
Draft hold — reconciliation evidence vs current dev and #4561Recorded heads before any action (exact-head discipline): this draft PR head Reconciliation findings1. Absorption by dev: none. 0 of the 30 PR commit patch-ids appear in dev since the base; dev contains no 2. Textual conflicts vs current dev: exactly 1 file, mechanical. A real trial merge of this PR head into dev 3. Overlap with #4561 (oMLX presets): 4 files, 1 real conflict. Trial merge of #4561 onto this PR's head conflicts only in 4. Not supersession — disjoint preset layers. The breaking removal here ( 5. Residual semantic risk. #4561's thinking-level fallback changes the same resolution path this PR's routed Owner decision (blocking)
This lane stays an explicitly owned draft hold: not marked ready, not pushed, not merged, not closed. All merge trials ran in throwaway worktrees and were aborted; no branch or ref was mutated. — gaebal-gajae |
Evidence refresh — dev advanced to
|
OWNER-CONTROLLED DRAFT HOLD — verdict + evidence update (needs-human)Verdict: NEEDS-HUMAN — owner decision required. Bound to the submitted PR digest via Conflict / supersession matrix (recomputed against exact
|
| Surface | Result |
|---|---|
| Absorption of this PR by dev | 0 of 30 commit patch-ids in dev since base; 0 autorouting files/symbols anywhere on 96e718a2 |
This PR → dev 96e718a2 trial merge |
1 conflict file: scripts/telegram-daemon-generation-manifest.json (single digest hunk, createNotificationsExtension: dev 32faaf97… vs PR ba9b4354…); mechanical — regenerate digest as commit 874c59f949 did before. #4540's session-runtime.ts/CHANGELOG.md edits auto-merge with this PR's |
#4561 (49e790f4f8) → this PR head trial merge |
2 conflict files: task/executor.ts (~line 1713 explicitThinkingLevel hunk; #4561 commit bb1403448b removes it and switches to resolvedThinkingLevel ?? thinkingLevel) and telegram-daemon-generation-manifest.json (new: #4561's rebase brought manifest edits). Overlap set: model-registry.ts, model-selector.ts, executor.ts, task/index.ts, CHANGELOG.md |
| Supersession | None. Preset layers are disjoint: this PR removes only AUTOROUTING_PRESETS, AUTOROUTING_PRESET_IDS, AutoroutingPresetId, resolveTierMap, task.autorouting.preset; #4561 never touches those symbols (0 matches) and builds model-profiles() presets + oMLX provider plumbing, untouched here. Partial overlap, not replacement |
| Residual semantic risk | #4561's thinking-level fallback changes the same resolution path this PR's routed :effort selectors depend on (AUTOROUTING_SELECTOR_PATTERN → explicitThinkingLevel → effectiveThinkingLevel at executor.ts:1786) — the executor hunk must be hand-re-resolved at rebase time |
Exact owner choices (pick one)
- (a) Rebase this draft onto post-feat(ai,config): add oMLX hybrid role-optimized presets #4561 dev: 1 mechanical digest regeneration + 1 hand-re-resolved
executor.tshunk +CHANGELOG.md. Requires feat(ai,config): add oMLX hybrid role-optimized presets #4561 to merge first; until then "post-feat(ai,config): add oMLX hybrid role-optimized presets #4561 dev" does not exist. - (b) Hold as competing direction: draft stays as-is; revisit after feat(ai,config): add oMLX hybrid role-optimized presets #4561 merges or is rejected.
- (c) Close as superseded: not supported by evidence (0/30 absorbed, disjoint layers).
Lane state (unchanged by this update)
Draft, open, head 874c59f949, not pushed, not marked ready, not merged, not closed. Local worktree fast-forwarded to 96e718a2 (read-only bookkeeping; no push). All trial merges ran in throwaway worktrees, aborted and removed. Resumption of this lane requires fresh owner direction; the agent must not pick (a)/(b)/(c) on its own — choosing is a product-default decision reserved to the owner.
— gaebal-gajae
|
Correction (exact-head discipline): #4561 head moved again after the hold comment posted. Current — gaebal-gajae |
874c59f to
4b9fea8
Compare
|
Rebased onto Rebase: 29 of 30 commits replayed with no conflicts. The only conflict was the regenerable telegram digest commit, which was skipped and regenerated against the new base instead of hand-merged. Two adaptations dev forced:
Focused verification on this base: Pre-existing dev failures (unchanged conclusion, re-measured against a pristine
Still a draft for the reason #3764 was closed: the MERGE_READY bar wants green current CI, and those surfaces are red at this base independent of this branch. |
Draft CI classification at exact head
|
4b9fea8 to
d28445e
Compare
|
CI repair pushed to Fixed product blockers:
Validation on the rebased head:
Run — |
|
Additional local fresh-process evidence: — |
|
A queued affected-path regression exposed an additional PR-scope staging bug before its job terminalized. Fixed and pushed
Validation: — |
|
Terminal Dev CI classification for exact Draft head Green repaired surfaces include Telegram generation guard ( Failures are classified as:
Explicit owner-controlled Draft dependency hold: #4575 ( — |
4ead72d to
c9b6e0e
Compare
|
Rebased the owner-controlled Draft onto exact #4577 overlap review: Command Code GOAT is retained as its own bundled model profile/provider recommendation and preset. Autorouting remains policy-tier derived and does not restore the removed preset layer; no duplicate profile removal or selector collision was introduced. Focused validation: routing/model/ACP cohort 117 pass; replay/staging/Telegram cohort 162 pass; Command Code GOAT profile catalog 16 pass; provider onboarding 28 pass; coding-agent check and generation authority/current-tree validation pass.
— |
|
Terminal replacement CI classification: run Only product failure is shard-1
— |
c9b6e0e to
15f657c
Compare
|
Freshness reset completed after #4575 merged. PR #4543 is rebased onto current Semantic overlap review retained #4575 Chrome default-root repair and later detached-managed snapshot work from dev; #4543 preserves policy-derived autorouting tiers and does not restore the removed preset layer. Protected Telegram lifecycle changes were regenerated atomically at generation 170. Current evidence: routing/model/ACP/staging cohort 83 pass; replay/Telegram/browser cohort 190 pass; coding-agent check, binary build, guard authority/current-tree validation, and affected planner passed. Replacement Dev CI Honest Draft needs-human verdict: code and local verification are current, but readiness remains owner-controlled and CI must terminalize before any completion assessment. The obsolete #4575 dependency hold is removed; the live Ultragoal G001 ledger records this current-dev hold. Draft remains Draft: no Ready, approval request, merge, close, release, or tag action. — |
15f657c to
1152631
Compare
|
Current exact-head terminal classification for owner-controlled Draft #4543:
This remains a Draft, owner-controlled readiness hold. No review, ready transition, merge, close, release, or tag action was taken. — |
Adds a fixed fast/balanced/strong tier vocabulary for Task-tool spawns, gated behind `task.autorouting.enabled` (default false). Tiers come from `task.autorouting.tiers` or one of the locked presets (anthropic, openai-codex, google, xai); explicit tiers win over a preset and an omitted item tier routes as balanced. An autorouting pin overrides the manual model chain, unmatched tiers fall back to manual resolution per item with a bounded reason, and routing evidence propagates to the task result, receipt, renderer, and task-summary prompt. Selectors must be exact provider-qualified `provider/modelId` strings with an optional thinking suffix; globs, bare ids, and `pi/<role>` aliases are rejected by the generated config schema. With autorouting disabled the model-resolution path is unchanged.
Turn sub-agent autorouting from a hand-authored tier map into a declare-your-providers flow: pick the providers you have, in priority order, and gjc materializes deterministic fast/balanced/strong cross-provider fallback chains for you. Curation and generation: - Add a curated per-model tier-label map in packages/coding-agent, seeded 1:1 from the shipped presets, with a skip list and a version fingerprint. Only current models carry labels, so "newest only" is a curation discipline rather than recency-filter code. - Add a pure, clock-free generator over the full model catalog. Given a declaration it emits byte-identical chains: declaration order, then curation rank, then a stable tie-break, deduped. Credentials and disabled providers are routing-time facts and never generation inputs, so adding an API key cannot silently change routing. - A provider that lacks a label for a tier contributes nothing to it. Unfillable tiers stay absent and fall through to the existing manual fallback rather than being padded with a model that was never labeled for that tier. - Add a CI forcing gate, wired into both required root checks, that fails when a new current catalog model is neither labeled nor skip-listed. Settings and TUI: - Add typed task.autorouting.setup and .provenance behind a narrow optional-object setting kind, wired through the schema, validation, reconciliation and JSON-schema generation. Both default to absent, so untouched configs round-trip byte-identically and the disabled path is unchanged. - Add a smart-routing panel reachable from the /model presets landing. It edits provider order and an optional model allowlist, previews the generated chains, and commits tiers + setup + provenance in one atomic batch, so what you previewed is exactly what is written. Refresh regenerates from the recorded declaration, Clear removes the generated keys while preserving preset and enabled, and the toggle writes only enabled. Hand-edited tiers are detected by fingerprint and never overwritten without explicit confirmation. Runtime: - Add an autorouting preflight mode for initial routed tasks: a bounded ledger of at most three unique candidates spanning throwaway probes and the durable attempt. Only typed transient pre-start failures advance; anything unclassified is terminal, and nothing switches once the request fence is crossed, so there is still no mid-run failover. - Publish failed attempts nowhere. Staged sessions suppress breadcrumbs and session-list registration until a single commit point, staged paths are excluded from every discovery reader, and artifacts are attempt-scoped with a serialized reservation and a frozen id map that is applied before the transcript becomes visible. - Record why routing did what it did: bounded skip evidence that distinguishes disabled providers from missing snapshot entries and missing credentials, a phase-tagged attempt ledger, and terminal outcomes that survive even when no candidate ever executes. The declaration is the only priority channel; the optional model list filters eligibility and never reorders. Manual tiers and presets remain as the power-user escape hatch.
The current artifact manager reserves numeric IDs with hidden claim files, and the landed catalog includes selector-invalid model IDs. Preserve claim authority during staged publication, exclude unrouteable catalog keys, and align managed identity and residue assertions with the current storage contract. Tested: 130 autorouting tests across 11 files; coding-agent biome and type checks; schema sync; CHANGELOG history guard.
Preserve dev session authority while staging autorouting attempts, gate transient breadcrumbs, and refresh tier-map baseline coverage for newly declared providers. Lore-id: pr-3764-repair Tested: autorouting preflight and boundary red-team contracts; coding-agent typecheck and schema/tier-map gates Confidence: high Scope-risk: targeted Reversibility: revertable
The smart-routing panel was reachable only from the /model preset landing. A session with zero model profiles skips that landing on open and has no way back to it, so autorouting setup was unreachable for exactly the users who had not configured presets yet. /routing mounts the panel directly (cancel closes the selector instead of falling back to the landing), and /routing on|off|status manages task.autorouting.enabled without a TUI round trip. Also canonicalizes the gen8 red-team temp roots so the managed-store boundary tests stop failing on macOS symlinked tmpdirs.
The task tool description reads settings.getEffectiveAutorouting(), which the tool-catalog generator's hand-rolled settings stub does not implement, so `bun run prepack` died with TOOL_CATALOG_GENERATION_FAILED and the generated catalog never picked up the new `tier` parameter. The stub now reports the default (autorouting off) and the catalog is regenerated. docs/tools/task.md gains the `tier` field and an Autorouting section. Also binds the models-schema selector assertion to ProfileModelSelectorPattern instead of a hardcoded copy. The provider- agnostic preset work changed the pattern without updating the test; it only surfaces on branches whose affected paths schedule that test.
Two review findings on /routing: The selector grammar rejects whitespace but not ESC/BEL, so a hand-edited task.autorouting.tiers entry could smuggle terminal control sequences into the status text rendered in the chat container. Status lines now run through validateDisplayLine + replaceTabs and are width-bounded, matching the renderer sanitization contract. /routing on|off wrote task.autorouting.enabled directly, bypassing the scoped-session and durable-config guards the smart-routing panel enforces. The TUI path now delegates to SelectorController.setAutoroutingEnabled.
session-runtime only threads the internal boolean; the host owns the emission, so importing the constant here was dead and failed check:tools. Lore-id: e6b104f7 Confidence: high Scope-risk: narrow Reversibility: clean Tested: biome check across 3653 files exits 0; check:types clean
The bootstrap cleared record.routingInactiveNotice before awaiting the publish, which broke two things the auth-failure precedent gets right: a later loadSession or resumeSession legitimately re-announces the condition and would now stay silent, and a rejected publish lost the warning outright because the enclosing bootstrap task swallows failures. Read without consuming, exactly like authFailure; one emission per bootstrap already follows from bootstrap running once per attach. Also covers the accessor contract directly rather than only its pure projection: catalog spelling is preserved for the case-sensitive generator, catalog-absent declarations are dropped, configured priority leads, and duplicate spellings collapse to the first occurrence. Lore-id: 7b90e2ac Constraint: a diagnostic must survive a failed publish and re-announce on resume Confidence: high Scope-risk: narrow Reversibility: clean Tested: acp-autorouting-notice and acp-lazy-startup 3 pass; autorouting-provider-order 12 pass; check:types clean
sdk/index.ts re-exports the host namespace and ./sdk is a published entry, so adding autoroutingInactive to SessionSdkHostOptions let any consumer inject the internal diagnostic through a public API. The approved plan forbids growing the public surface for this warning. The state now travels through a package-private module keyed by the object the session factory already owns, and that module is explicitly mapped to null in the package exports so it cannot be imported from outside. The field and its layer-by-layer threading are gone from the host, runtime, and bus option types; behaviour is unchanged. Lore-id: c082da4b Constraint: an internal diagnostic must not be settable through a published type Confidence: high Scope-risk: narrow Reversibility: clean Tested: 44 pass across acp-autorouting-notice, acp-lazy-startup, acp-event-mapper and sdk-acp-production-path; AC10b still fails when the host emission is disabled, proving behaviour survived the seam change; check:types clean
The flag was publicly settable once; nothing stopped it regressing. Asserts the two null export-map entries that block the internal module, that the host barrel does not re-export it, and that no published option type declares the field. Lore-id: 1d4f6b70 Confidence: high Scope-risk: narrow Reversibility: clean Tested: 4 pass; removing the export-map null entry fails the guard
AC13/D7 asked for a golden that actually runs the provider-order derivation. The four existing fixtures hand the generator an already-sorted setup, so they only ever proved that declaration order dominates tier order; none of them touch the projection. This one starts from configured order plus catalog, runs the real projection, and pins the resulting bytes. It also pins the two behaviours that motivated the accessor: a configured provider missing from the catalog is dropped before it can reach setup.providers and pollute declarationFingerprint, and catalog order supplies the remainder. Lore-id: 5ec1f0d3 Confidence: high Scope-risk: narrow Reversibility: clean Tested: autorouting-generator 8 pass; removing the catalog-append branch from projectProviderOrder fails this fixture
Three gaps, all real. The selector check claimed to validate "every generated tier selector" but tested three hardcoded strings and never touched CURATED_TIER_MAP or the generator, so deleting the preset exhaustive loop silently lost that coverage. It now walks every curated key and every selector the generator actually emits from that catalog, with a negative control for unfit selectors and an explicit note that a colon is legal inside a model id. The accessor tests reimplemented the accessor body, so they could not catch a regression inside it. The spelling-restore logic moved into projectCatalogProviderOrder, which autoroutingProviderOrder now simply calls, and the tests exercise that function directly. Real-instance tests remain for the properties observable without global settings: no parameters, catalog-only output, first-wins order, credential invariance, determinism. Also proved the model-registry baseline claim instead of inheriting it: the same four failures appear at HEAD and at pristine dev 178fc26, so they are pre-existing and unrelated to autorouting. Lore-id: 4f8ba7c1 Constraint: a test must fail when the behaviour it names is removed Confidence: high Scope-risk: narrow Reversibility: clean Tested: 57 pass across autorouting-provider-order, task-autorouting-redteam, autorouting-generator and smart-routing integration; removing the spelling restore fails 4 of them; model-registry failures diffed identical against pristine dev
…settings The cleaner lane caught me repeating the exact mistake the terminal critic had just corrected: the policy-derived golden rebuilt the catalog, spelling map, and projection inline instead of calling projectCatalogProviderOrder, so it could not fail if that function broke. It now calls the shipped function, which is what ModelRegistry.autoroutingProviderOrder delegates to. The real-registry suite also only assumed the global settings singleton was uninitialized. A prior test setting modelProviderOrder would have silently reordered the expected catalog projection and made those assertions accidental, so the precondition is now reset around each test and asserted outright. Lore-id: 6a4c0e93 Constraint: a golden must exercise shipped code, never a copy of it Confidence: high Scope-risk: narrow Reversibility: clean Tested: autorouting-generator 8 pass, autorouting-provider-order 19 pass; removing the spelling restore now fails 5 across both files where it previously failed 4, proving the golden is bound to the real function
…t rebase Rebasing onto the current dev tip pulled in 44 new catalog keys the autorouting tier map has never seen, so check:autorouting-map failed closed on uncurated coverage. Record them as baseline skips with an explicit rationale rather than inventing tier/rank data nobody reviewed. Lore-id: 9d1f6b3a Constraint: an uncurated catalog key is a skip with a rationale, never a guessed tier Confidence: high Scope-risk: narrow Reversibility: clean Tested: check-autorouting-tier-map gate passed (4264 in-scope keys); autorouting suites 98 pass
…lution Removing the #writeTerminalBreadcrumb wrapper during the dev rebase left a double blank line that check:tools rejects. Kept as its own commit rather than folded into the regenerable telegram digest commit, which a later rebase skips and would have discarded this fix with it. Lore-id: 4e7a2b81 Confidence: high Scope-risk: narrow Reversibility: clean Tested: biome check across 3714 files exits 0
Managed session opens must sanitize stale OpenAI Responses metadata in memory without appending durable patches. The autorouting selector must also tolerate minimal settings adapters while retaining its provider-order listener when available.\n\nLore-id: 4543-ci-fixforward-0647\nConstraint: preserve replay safety without rewriting managed transcripts on open\nTested: focused replay, onboarding, session-storage, model-selector, and daemon guard suites\nConfidence: high\nScope-risk: narrow\nReversibility: simple
The autorouting ACP fixture closed only its connection signal, leaving its session adapter alive while broker-root cleanup removed the fixture. Register and await the owned ACP session teardown before releasing the broker lease.\n\nLore-id: 4543-ci-fixforward-0647\nConstraint: fixture roots must remain absent after teardown\nTested: repeated fresh Bun ACP notice regression\nConfidence: high\nScope-risk: narrow\nReversibility: simple
Unpublished autorouting candidates must not replace the terminal continuation breadcrumb. Publish it only when a staged candidate is finalized; give the durable staged regression its required bounded test window.\n\nLore-id: 4543-ci-fixforward-0647\nConstraint: failed candidates leave no durable discovery residue\nTested: autorouting boundary and preflight regressions; coding-agent check\nConfidence: high\nScope-risk: narrow\nReversibility: simple
Current dev now includes the prior generation boundary, while autorouting still changes protected notification lifecycle code. Regenerate the complete guard-owned authority set atomically.\n\nLore-id: 4543-ci-fixforward-0647\nTested: telegram guard, topic registry, and focused routing/session suites\nConfidence: high\nScope-risk: narrow\nReversibility: simple
SDK patches and config CLI writes could bypass nested autorouting validation, while task creation prefiltered credential failures as recoverable absences.\n\nValidate typed autorouting objects at every mutation ingress and leave credential classification to executor preflight so unexpected lookup faults fail closed.\n\nLore-id: pr4543-fixforward\nConstraint: preserve owner-controlled Draft state\nConfidence: high\nScope-risk: focused\nReversibility: revertable\nTested: focused autorouting ingress and preflight suites
Autorouting preflight resolved exact keys against the execution session instead of the distinct credential session.\n\nUse the propagated credential session identity so managed credentials remain available to pinned candidates.\n\nLore-id: pr4543-credential-scope\nConstraint: preserve fail-closed autorouting preflight\nConfidence: high\nScope-risk: focused\nReversibility: revertable\nTested: task-autorouting-preflight
Reject malformed autorouting tier maps before SDK config.patch persists them.\n\nTested: autorouting-settings-contract
Keep truthful missing-credential skips while propagating unexpected lookup errors and using the credential session scope.\n\nTested: autorouting boundary and preflight suites
Defer unexpected TaskTool credential probe failures to executor preflight so routing receipts remain fail-closed and auditable.\n\nTested: autorouting preflight, integration, boundary suites
Root TypeScript validation requires the optional credential session argument to exclude null.\n\nTested: ci-dev-affected root-check
Carry TaskTool credential lookup exceptions into the authoritative preflight ledger instead of retrying and losing one-shot failures.\n\nTested: routing preflight, integration, and boundary suites
f2ade94 to
44f9e7b
Compare
Ensure TaskTool transfers an observed credential lookup fault into executor preflight without retrying it.\n\nTested: routing preflight, integration, boundary suites
Use Map presence rather than value truthiness so every captured JavaScript throw reaches terminal preflight evidence.\n\nTested: routing preflight, integration, boundary suites
|
Current-head CI failure classification for Draft #4543 (
PR #4543 remains an owner-controlled Draft with — |
Verdict: Request changesBlocking findings:
No tests or gates were run as part of this review. |
Correction / superseding verdictThe earlier review comment on this PR was based on an incorrect diff scope and is superseded. A subsequent exact-head review of Corrected verdict: Approve / no actionable findings. No tests or gates were run as part of the read-only review. |
Final correction / superseding verdictThe previous approval correction was also based on an incomplete/local diff inspection and is superseded. The live PR has 44 commits and 71 changed files at the exact head. Corrected verdict: Request changes.
No tests or gates were run as part of this read-only review. |
Additional exact-head finding
This supplements the existing P1 provider-ID case-normalization finding. Overall verdict remains Request changes. |
gajae.pr-review-verdict.v1 needs-human sha256:288ef0b4b70db0ba4eb6aa9a3c9651f171e548ac7801eed5fdca48083d4db7de reviewer:human reviewer-id:pending evidence:exact-head-e7b95a4ce957c00d36d532eae62a959ee9295c1b-current-dev-cd51365cc270e27dceccfc2c184fadc9c1ddbe18-owner-controlled-draft-fix-forward-owner-readiness-required
Supersedes #3764, which was closed without merge because exact-head Dev CI was red across unrelated current-dev surfaces. This is the same reviewed branch, rebased onto a much newer
dev(271 commits of drift), with the preset layer now removed.What
Opt-in sub-agent model autorouting for the Task tool, with tier chains derived from the provider-selection policy rather than a hardcoded preset table.
task.autorouting.enabled(defaultfalse) activates the fixedfast/balanced/strongtier vocabulary. Tiers come fromtask.autorouting.tiers; an omittedtieron a Task item routes asbalanced; an autorouting pin overrides the manual model chain.provider/modelIdstrings with an optional thinking suffix. Globs and prefixes are rejected./routingopens the smart-routing panel directly;/routing on|offtoggles enablement;/routing statusreports settings-derived state.Preset-source unification
task.autorouting.presetand the whole preset layer are removed, not deprecated — there is no compatibility shim, per the repo's no-backward-compatibility rule.projectProviderOrderis extracted as the single implementation of "configuredmodelProviderOrderfirst, then first-wins catalog order", andcreateProviderSelectionPolicyis rewritten on top of it.ModelRegistry.autoroutingProviderOrder()takes no session and bypasses the policy builder entirely, so noeffectiveAuthmap is assembled: auth-independence is structural, not conventional. Auth-aware banding stays private torank().CustomRouterwould silently empty that provider's tiers.setup.providers, so a dead declaration cannot pollutedeclarationFingerprint.Breaking changes
task.autorouting.presetis gone. A preset-only configuration routes manually until tiers are generated.AutoroutingEffective.source,RoutingOutcome.source,AUTOROUTING_PRESETS,AutoroutingPresetId, andresolveTierMapfrom./config/*, plusTaskRoutingEvidence.sourcefrom./task/*. The union collapsed to one value, so keeping it would have published a meaningless required field on a durable receipt.notevalues change format to tier/fallback/resume components only./routing statusrelabels settings-derived tiers, with malformed provenance failing closed as hand-authored instead of reportinggenerated.Inactive-autorouting warning
Enabling autorouting without usable tiers previously failed silently. The host now decides once, where settings are already available, and reports through one shared uninterpolated constant on all three surfaces:
session.configWarnings;SessionEventStreamretains frames, so a notice published at hoststart()reaches a client that attaches later; ACP captures it onto the session record without early render and republishes once during deferred bootstrap beside the auth-failure branch.No new public query, no
getSdkConfigItems/config.listexpansion, no generalconfigWarningsexposure, and no new event kind. The internal flag lives in a package-private module that is null-mapped in the package exports, with a guard test asserting it is unreachable from any published type.Rebase notes
Rebased onto
devatf0453b6ab1. Two commits were dropped as genuinely obsolete rather than force-fitted:turn.steer_statusdisposition expectation, because dev splitsdk-adapter-dispositions.test.ts(ci(coding-agent): sdk-adapter-dispositions test file harness timeout (shard4) #4475) and fixed the same defect better, by supplying a real{ clientRef }input instead of assertinginvalid_requeston a bare probe;Substantive conflict resolutions: dev removed its
#writeTerminalBreadcrumbwrapper and the staged-publication/terminalBreadcrumbsfields, so those callsites were converted to dev's direct free-function form rather than reintroducing a wrapper; the/themeand/routingslash-command test blocks were unioned with correct closers.Verification
check:typesclean; repo-widebiome checkexits 0.AcpAgentdriven throughnewSessionagainst a fixture broker observes exactly one[warning:autorouting]chunk, which only holds because the notice survives late-attach replay. Disabling the host emission fails it while the zero-notice case still passes.config/settings-schema.ts).bun run checkcurrently fails on two gates that fail identically on a pristineorigin/devworktree atf0453b6ab1, measured on the same machine with sharednode_modules:verify-gjc-sdk-canonicalizationreports 27 violations on both sides, and the sorted violation sets diff empty. Every chain is rooted at the dev-ownedsession-state-sidecar.ts -> tools/descriptors.tsedge, and this branch touches none of the chain roots.Opened as a draft because #3764's closure standard requires green current CI, and those dev-side surfaces are still red at this base.