Files
6krrt/tests/fixtures/route_decision_no_header.json
adlee-was-taken 92031dbd0b feat(classifier): record the classifier's own attempt on route_decisions
A declined answer left one number behind and it was in a log line, so
confidence_min could not be tuned from data. route_decisions.confidence cannot
say it either: the chat path re-routes through the override branch, which
hard-codes 1.0, and 43,804 of 43,856 live rows hold exactly that.

Measured on 2026-10-04 under local_decision (qwen3.5:4b): 769 of 2,557 turns
(30%) had no fresh classification, against 0% for local_llm and local_encoder.
The cause was recorded in only 4 of them.

- classifier_confidence, classifier_coverage and classifier_reject on
  route_decisions, filled from a ClassifierAttempt carried on Classification.
  Both accepted and declined answers carry one, so the two distributions can be
  compared around the floor. Reason codes are listed in docs/data-model.md.
- ClassifierRejected (a RuntimeError subclass, messages unchanged) replaces the
  plain RuntimeErrors at the six floor-miss sites and the two local_decision
  refusals, so the number and reason travel out of the raise site.
- The chat path captures the classifier's verdict before the re-route and passes
  it to persist_route_decision (attempt_of), like it already does for source.
- Admin decisions page: the source badge's tooltip shows the attempt, and
  session_history / session_stale get an amber badge instead of neutral grey.
  TUI detail popup and the live event carry the same three keys.
- The /metrics degraded-share warning lists the recorded reasons instead of
  claiming the classifier "has been failing", which was wrong for a classifier
  that answers and is declined.
- degraded_warn_threshold must be in (0, 1] and degraded_warn_min at least 1,
  refused at load: a value above 1 could never fire.

The three columns arrive by ALTER and are NULL on every earlier row; metrics
selects them only when present, so the live DB reads NULL until its restart.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkCGRantZsSwmcFpet6FTa
2026-10-04 22:06:57 -04:00

38 lines
1.0 KiB
JSON

{
"agent": null,
"candidates_considered": 2,
"classification_source": "classifier",
"classifier_confidence": null,
"classifier_coverage": null,
"classifier_reject": null,
"confidence": 0.9,
"est_cost_usd": 0.00016,
"est_proficiency": 0.5,
"exploration": 0,
"flex_forced": 0,
"flex_preference": "auto",
"flex_swapped": 0,
"images": 0,
"json_mode": 0,
"kind": "chat",
"latency_tolerance": "interactive",
"parent_key": null,
"pinch_final_tokens": 5,
"pinch_original_tokens": 5,
"prefix_divergence_index": null,
"prefix_prev_message_count": null,
"prefix_tokens_after_divergence": null,
"profile": "default",
"rejected_reason": null,
"request_id": "chatcmpl-test",
"required_context_tokens": 100,
"runner_up_models": "[{\"model_id\": \"dear-model\", \"provider\": \"neuralwatt\"}]",
"selected_model": "cheap-model",
"selected_provider": "neuralwatt",
"session_key": "be41d1308beb018d",
"streamed": 0,
"task_category": "coding_general",
"task_tier": 2,
"tools": 0
}