# Contextual Auto routing and captured selection reasons

Tracking: [#258](https://github.com/cloga/dsh-github-copilot/issues/258) and
[evidence refinement #262](https://github.com/cloga/dsh-github-copilot/issues/262).

**Current contract:** source candidate `0.4.0-alpha.99`, targeting official DSH/Windows
Desktop `0.2.0-rc.2`. Semantic assessment is enabled by default with bounded
auxiliary timing/output evidence. Neither publication nor
these explanations prove the version loaded in a particular Host.

## Using Auto

Choose **Auto · Balance**, **Auto · Efficiency**, or **Auto · Intelligence**
under the managed **GitHub Copilot** provider in DSH's ordinary picker. This is
the plugin's DSH routing policy, not Copilot App's Auto or GitHub's private
router. Selecting a real model exits Auto. Every step/retry in an admitted
turn keeps its chosen model; the next turn can choose differently.

Semantic assessment is already enabled for locally unknown tasks. To opt out,
set `autoSemanticAssessment: false` in the plugin's `github-copilot` settings
namespace; it is not an extra switch on the Web search card. This changes
subsequent assessments, not an already-admitted turn. Local known-task rules
still work when auxiliary inference is disabled.

Open the reply's selection explanation for the captured decision. Evidence is
Host-lifetime and bounded; after restart or eviction it can be unavailable.
Do not infer an old decision from the current picker.

| Reading | Meaning |
|---|---|
| `uncertain` / unknown demand | The available evidence does not establish task demand; it is not a failed answer or a claim that the conversation is empty. |
| `insufficient-evidence` | A local rule or classifier could not justify a demand category. Longer context alone does not guarantee classification. |
| Semantic timeout | No validated result within the 8-second end-to-end budget. Demand remains unknown; Efficiency/Balance target Versatile, Intelligence targets Powerful, subject to eligibility and category fallback. |
| Missing auxiliary milestone | That milestone was not observed before assessment settled, not proof the supplier never sent data. |
| Selection unknown/unavailable | Captured selection evidence is absent or could not be read, distinct from unknown task demand. |

There is no assessment retry or automatic deadline extension. Caller cancellation
aborts the turn rather than invoking fallback. Auxiliary calls can add latency
and supplier charges outside native answer Usage.

## Requirements

Auto must explain why the initiating turn chose its actual route, not display a
generic description of prompt length. Intelligence prioritizes response quality
but can use a lightweight model for demonstrably simple work. Efficiency favors
economical capability without knowingly underpowering difficult work. Balance
trades those priorities. All preferences share the same account model pool.

The authenticated account `/models` response supplies `model_picker_category`.
The October 3, 2026 bounded live probe observed `powerful`, `versatile` and
`lightweight`; some records lacked the field. This is supplier classification,
not measured latency, quality, price or health. Official documentation's
`category` is a different source and must not override live account facts.
Do not derive category from IDs, names, reasoning effort or context capacity.

Hard constraints remain first: entitlement, exclusions, current proof, text/image
capability and actual image format, estimated input headroom and final native
admission. No preference overrides these. No new Core owner, adapter, credential
record, general transport, global model mutation or durable decision event.

## Task assessment

Assessment concerns the task in context, not the last sentence's length.
An isolated greeting may be low demand. A short request to prove a theorem or
continue an unresolved investigation is not evidence of low demand. Long input
can be mechanical. Prior assistant/tool work prevents a greeting/continuation
shortcut from claiming an isolated simple task.

Use conservative local assessment first. Unknown is an explicit outcome, never
silently translated into simple. Semantic assessment is enabled by default for
locally unknown demand at the user's explicit rollout request (#267), not
because labeled-corpus calibration has completed. Set
`github-copilot.autoSemanticAssessment: false` to disable auxiliary inference;
an existing explicit false remains respected. It must not claim equivalence to
GitHub's private router or calibrated confidence.

Local rules recognize an isolated, explicit JSON-to-CSV/CSV-to-JSON conversion
or line sort only when the entire request is at most 1600 characters, contains
one fenced payload and no other work or non-text content. This establishes
routine, not simple. An exact acknowledgement/continue request can inherit
explicit difficult-task evidence from the nearest non-continuation user request
among the preceding twelve user requests. A new task or an expired anchor stops
inheritance; simple/routine demand is never inherited.

`uncertain` remains a valid task result. `insufficient-evidence` means the local
rules cannot establish demand, not that the conversation has no context.
Attachments, unfamiliar instructions and genuinely ambiguous continuations can
still remain unknown. These refinements do not change category preference,
previous-model eligibility or semantic safeguards.

The semantic assessment uses one fixed concrete account candidate through the
existing managed native adapter, never an Auto ID and never `ctx.llm` routing
recursion. It has no tools, bounded text-only assessment context, bounded JSON
output, one deadline, cancellation and no new retry loop. Text from messages is
untrusted data, not router instructions. Files/images/reasoning/replay bodies
are not read; omitted context makes classification uncertain and cannot justify
downshifting. Assessment input is a separate projection: the actual chat
messages, system prompt, tools, attachments and history stay untouched.

Only locally unknown tasks invoke the enabled assessment. Context packing keeps
the current and nearest preceding user requests before recent output, then
restores chronological order. At most twelve complete JSON rows, 1600 text
characters per row and 8000 serialized characters are retained; JSON escape
expansion is included in this bound. Any truncation, skipped row or non-text
content still marks omitted context and prevents a semantic simple/routine
downshift. Prioritization does not prove omitted output irrelevant.

The strict result contains demand (`simple`, `routine`, `complex`, `unknown`)
and finite evidence signals, not a model ID, raw reasoning or an uncalibrated
confidence percentage. Invalid/truncated/failed results retain the local
assessment with an explicit diagnostic. Caller cancellation aborts the turn
instead of falling back. Credential, metadata and account revocation remain
fatal through existing lease/admission guards. Extra calls incur supplier
charges and latency, separate from native chat Usage and without inferred cost.

## Auxiliary latency evidence and bounded output (#272)

The assessment still has one **8-second end-to-end deadline**, including account
discovery/preparation and the native call. It does not retry on timeout or try a
second classifier. The response budget is now 128 output tokens, with compact
demand JSON and at most three fixed signals requested. The strict decoder
retains its seven-signal compatibility bound; raw explanation, confidence
percentages and reasoning are not requested. Truncation remains failure, never
accepted partial JSON.

One monotonic deadline is checked after context packing, discovery and native
preparation, before native request admission, on delivered chunks and before
accepting a decoded result. A delayed event-loop timer cannot authorize a
late auxiliary request or result. It can still delay observed settlement:
this is not a hard real-time guarantee or evidence of a supplier latency cause.
The owned timer and abort listener are disposed when assessment settles.

Among eligible supplier Lightweight text candidates with the existing input
headroom, prefer advertised reasoning `off`, then deterministic ID ordering.
This does not infer speed, health or quality from effort or model names, and
does not launch a second candidate if native support is absent.

Request `off` reasoning only when both current supplier metadata advertises it
and the public prepared model lists an `off` effort. Supplier metadata alone
does not establish native support: current managed reasoning maps can decline
`off` even when advertised. In that case retain native policy, do not change
shared model maps, guess a wire value or patch Core. No claim that reasoning is
disabled for all classifiers is made.

Captured Host-lifetime selection evidence now includes a bounded public
classifier ID, configured waiting budget, total monotonic assessment elapsed
time, adapter-invocation start, first nonempty text time, native stop/non-stop
finish observation and strict result-validation status. Preparation is included
in total time; adapter start is **not HTTP dispatch**, first text is **not first
network byte**, and these milestones cannot distinguish connection, supplier
queueing or computation. Absence says no milestone was observed before
settlement, not that the provider never emitted data. Counts are characters,
not tokens/credits; no prompt, text, replay, credential, error body or hash is
captured. Late callbacks cannot rewrite a settled decision.

The existing strict explicit-Session/turn Remote carries this optional evidence;
old explanations remain decodable without it. Numeric/model evidence is copied
and frozen in the bounded store and routing decision, not durably appended or
reconstructed after restart. The footer says assessment timed out, demand
remained unknown, and preference policy selected the category as a fallback.
Progressive details show milestones separately from native answer Usage.
Continuity copy states the fitting category-pool count and explicitly disclaims
semantic ranking; all-input-fit counts are not the final category pool.

This is instrumentation and bounded output optimization, not a measured latency
improvement or classifier calibration. No latency-ranked classifier pool,
cross-turn assessment cache, cooldown, adaptive deadline or extra probe is
introduced without reviewing real observations. Existing local evidence,
omitted-context downshift protection, cancellation, account/proof revocation,
same-turn single-flight, eligibility and category policy remain unchanged.

## Category policy and continuity

| Task demand | Efficiency | Balance | Intelligence |
| --- | --- | --- | --- |
| Proven simple | Lightweight | Lightweight | Lightweight |
| Routine | Lightweight | Versatile | Powerful |
| Complex | Powerful | Powerful | Powerful |
| Unknown | Versatile | Versatile | Powerful |

These are declared routing policies, not benchmark rankings. Efficiency's
routine lightweight selection is an economical approximation, not proof of
equal output quality. Intelligence never treats short length alone as simple.

When the target has no fitting candidates, use the disclosed category fallback:
powerful -> versatile -> lightweight; versatile -> powerful -> lightweight;
lightweight -> versatile -> powerful. Unclassified candidates are a final
explicit fallback only, not fabricated categories. Unknown/new category values
remain unclassified with a bounded catalog diagnostic.

Within the first fitting category, use stable positive weighted Session/turn
allocation: ordinary weight 1, user-marked high-cost weight 0.2, eligible previous
model multiplier 1.5. Cost is independent of task difficulty and supplier
category. There is no absolute continuity shortcut or extreme-only gate.
These are reviewed policy parameters, not measured quality or prices. Once
admitted, all steps/retries share the decision. No supplier cache savings are
claimed. See [high-cost requirements](./auto-high-cost.md) and
[evidence-driven iteration](./evidence-driven-iteration.md).

If no model fits, preserve the existing largest-admissible-input recovery
branch and its native compaction diagnostics. Explain that no candidate fits;
never label this as a successful preference match or silently trim history.

## Explanation contract

Capture task source/demand/signals, assessment diagnostic, target/selected
category, fitting category count, category fallback and continuity/tie-break or
no-fit selection at decision time. Client reads the explicit viewed Session and
turn through the existing strict Remote. It does not rerun classification or
consult today's picker. Show a decisive first sentence, bounded supporting
facts and optional policy detail. Do not repeat the model name or change Usage.

Legacy records without detailed evidence show that the reason was not retained.
Host-lifetime evidence may be lost on restart; do not invent durable history or
promise cold-history reconstruction.

## Acceptance and rollout

Regress category normalization (missing/new/invalid values), short difficult
tasks, isolated greetings, contextual continuation, long mechanical input,
category fallback, previous-model eligibility, deterministic tie breaking,
no-fit recovery, one-call semantic success/invalid/failure/timeout/cancellation,
omitted context, strict Remote and footer English/Chinese explanations.

Retain native gateway, parent following, manual selection, exclusion admission,
account proof, image admission and compaction fixture coverage. Full protected
Windows/Linux CI remains mandatory before merge/release. No live inference
calls are needed for synthetic acceptance.

Default enablement is a user-authorized rollout, not an accuracy qualification.
Evaluate a reviewed labeled corpus for complex-to-simple errors, extra latency,
token use and stability across languages and prompt injection before claiming
calibrated performance. Synthetic passing tests are not this evaluation.
Missing settings adopt the enabled default after upgrade; explicit false is
not overwritten and no profile migration is performed. Publication does not
authorize profile installation or restart.
