feat(api): AIN-218 §16 schema lock — task_type, task_type_source, policy_version, cell#56
Conversation
…icy_version, cell
Adds the four §16 fields the methodology v1.1 schema lock requires on
every routed inference.
· alembic 0022: additive columns on `inferences` (all nullable), two
Pg enums (inference_task_type, inference_task_type_source), partial
indexes on task_type + policy_version for the dashboard cell-coverage
gauge (AIN-210) and policy-drift telemetry.
· services/section16: pure helpers (policy_version, cell, resolve_task_type,
constraint_band) + v0 Anthropic-haiku classifier with 1.5s timeout and
best-effort fallback to "general" / source="default" on any failure.
· services/routing.dispatch_inference: resolve task_type (caller →
classifier → default), look up tenant's active routing policy,
populate all four fields on the InferenceORM row AND on the
inference.routed audit payload. Hash-chain invariant preserved:
old events keep their old payloads + hashes; new events carry the
richer payload and hash over it.
· routers/inference: accept optional task_type on InferenceRequest,
thread it through both the ainfera-auto and the direct dispatch
paths, expose the four fields on GET /v1/inferences/{id}.
Backfill: deferred (separate migration after Phase B Manwe traffic
produces enough rows). NOT NULL tightening also deferred.
Tests: 16 new unit tests cover policy_version determinism, ruleset_hash
drift detection, cell format, resolve_task_type precedence + enum
guard, classifier response parsing. 448/448 unit+smoke green.
Closes AIN-218 Phase 1 (code). DDL run is the next founder tap.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
AIN-218 [§16 P0] Outcome-capture schema migration — task_type + task_type_source + policy_version + cell
§16 schema LOCKED 2026-05-21 — one-shot immutable decisionThe audit chain is append-only + hash-chained → no backfill. Every routed call captured without these fields is a permanent gap. Manwe pipe is currently dead (zero traffic), so this is a lucky near-zero-loss window. Land this migration BEFORE the pipe is fixed and traffic resumes. Full spec: Methodology v1.1 §16 schema section Add to
|
|
You have used all Bugbot PR reviews included in your free trial for your GitHub account on this workspace. To continue using Bugbot reviews, enable Bugbot for your team in the Cursor dashboard. |
…empotency mock
The §16 classifier in services/section16.py calls api.anthropic.com/v1/messages —
the same URL the idempotency test mocks for the provider. The respx route matches
by URL only (not by model), so without an explicit task_type the classifier fires
on the first request and inflates call_count to 2.
Providing task_type="chat" in the request body routes resolve_task_type down the
source="caller" branch, skipping the classifier entirely. The test's intent
("provider hit once despite idempotent replay") is then accurately measured,
and we incidentally cover the §16 caller-supplied path.
Production code is unchanged. The classifier is already gated behind the
idempotency check in dispatch_inference, so idempotent replays never re-classify.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
You have used all Bugbot PR reviews included in your free trial for your GitHub account on this workspace. To continue using Bugbot reviews, enable Bugbot for your team in the Cursor dashboard. |
Summary
Implements the methodology v1.1 §16 schema lock on every routed inference. Audit chain stays valid: old events keep old payloads + old hashes; new events hash over the richer payload.
inferences, 2 Pg enums (inference_task_type,inference_task_type_source), partial indexes ontask_type+policy_versionfor the AIN-210 cell-coverage gauge + policy-drift telemetry.services/section16.py— pure helpers (policy_version,cell,constraint_band,resolve_task_type) + v0 Anthropic-haiku classifier with 1.5s timeout, best-effortNone-on-failure (caller falls through togeneral/ source=default).services/routing.dispatch_inference— resolves task_type (caller → classifier → default), looks up tenant's active routing policy, populates all 4 fields on theInferenceORMrow AND on theinference.routedaudit payload.routers/inference— accepts optionaltask_typeonInferenceRequest, threads through both the ainfera-auto and direct dispatch paths, exposes the 4 fields onGET /v1/inferences/{id}.Backfill + NOT NULL tightening deferred (depends on Phase B Manwe traffic producing enough rows).
Test plan
policy_versiondeterminism, ruleset_hash drift detection,cellformat,resolve_task_typeprecedence + enum guard, classifier response parse toleranceruff checkcleanmypy --strictclean (71 files)alembic upgrade headon prod Supabase: real inference call writes all 4 fields, verify withcurl /v1/inferences/<id> | jq '{task_type, task_type_source, policy_version, cell}'— all non-nullCloses AIN-218 Phase 1.