daari — Task tracking¶
Last updated: 2026-09-23 (config TLS/auth rows — #998) Update this file when phases/tasks complete.
Repo layout and request flow: ARCHITECTURE.md
Legend¶
- [x] done [ ] pending [~] in progress [-] deferred
GTM¶
Launch spine and always-on measure/draft rules: docs/gtm/PLAN.md. Scoreboard lands in #229. Issues #226–#233.
| Item | Status | Notes |
|---|---|---|
| Repo listing (description, topics, homepage, Discussions) | [x] | Set 2026-08-26 via gh repo edit |
| Honest public copy (#228) | [x] | #234 |
| Scoreboard (#229) | [x] | #237 |
| Launch drafts (#230) | [x] | #236 |
| Comparison SEO (#231) | [x] | #239 |
| Shipping-note generator (#232) | [x] | #240 |
| Discussions welcome (#233) | [x] | templates in docs/gtm/discussions/; welcome is #238 |
| Cloud autodev secret (#226) | [ ] | HITL — cycle is a 6s no-op |
| Dual-license (#227) | [x] | Full Apache 2.0 — ADR-0016 |
Setup / self-service¶
| Item | Status | Notes |
|---|---|---|
S1 daari onboard (#256) |
[x] | pip/brew first-run without a git clone |
| S2 Install CI gate (#257) | [x] | install job in .github/workflows/ci.yml |
| S3 Default pull L3+embed (#258) | [x] | install.sh + doctor require embed when L1 on |
| S4 Cursor tunnel one-liner (#259) | [x] | daari setup cursor --tunnel --yes --daemonize |
S5 daari service (#260) |
[x] | user systemd / launchd unit files; no CI start; restart added in #323 |
| S6 Windows/WSL (#261) | [x] | install-windows.md + scripts/install.ps1 (WSL only) |
S7 onboard --serve (#268) |
[x] | background daemon after first-run |
Phase A — Tracer bullet¶
| Task | Status | Notes |
|---|---|---|
Scaffold (pyproject.toml, Typer CLI) |
[x] | cf50264 |
Config (Settings, ~/.daari/config.yaml) |
[x] | |
Internal model (InternalRequest / InternalResponse) |
[x] | |
| L0 exact cache | [x] | diskcache |
| ProviderRegistry (cache + Ollama) | [x] | |
OpenAI gateway (POST /v1/chat/completions) |
[x] | |
FastAPI server (daari serve) |
[x] | port 11435 |
| Ollama executor (L3) | [x] | |
| Router L0 → L3 | [x] | |
Metrics / daari stats |
[x] | |
| Agent passthrough (exact L0 on identical tools+history) | [x] | ADR-0004 / G1 #223 |
X-Daari-No-Cache / tier override headers |
[x] | |
| Ollama-down → 503 | [x] | |
| Eval file GP-01–GP-10 | [x] | |
| Routing eval pytest | [x] | 6768fb8 |
| Live Ollama integration test (optional) | [x] | skipped without OLLAMA_HOST |
| Manual Cursor doc | [x] | setup/cursor.md |
| Dev pickup docs | [x] | DEVELOPING.md |
| Streaming SSE | [x] | basic OpenAI-style SSE passthrough for stream=true |
Exit criteria
| Criterion | Status | Notes |
|---|---|---|
| Second identical prompt hits L0 | [x] | |
daari stats shows tier breakdown |
[x] | |
| Cursor via tunnel setup | [x] | scripts/tunnel.sh --setup-cursor + daari setup cursor --base-url/--tunnel |
| GP-01–GP-10 pass MVP criteria | [x] | tests/test_routing_eval.py |
Tests: see Testing below.
Testing¶
| Layer | Location | CI | Notes |
|---|---|---|---|
| Unit | tests/unit/ |
✅ | cache keys, semantic similarity, metrics, settings, internal models, confidence, L6 escalation |
| Integration (mocked) | tests/integration/test_gateway_flow.py, tests/integration/test_l1_semantic_cache.py, tests/integration/test_l6_escalation.py, tests/test_phase_a.py, tests/test_routing_eval.py |
✅ | gateway + router + L0/L1 cache + L6; Ollama mocked |
| Integration (live Ollama) | tests/integration/test_ollama_live.py, test_sampling_live.py, test_client_path_live.py |
skipped | @pytest.mark.integration; run with OLLAMA_HOST=http://127.0.0.1:11434 pytest -m integration |
| Benchmark | tests/benchmark/ |
skipped | @pytest.mark.benchmark; L0 vs L3 latency |
| Setup / doctor | tests/test_setup.py, tests/test_doctor.py |
✅ | dry-run, backup, doctor checks |
Commands
pytest # default: unit + mocked integration (no live Ollama)
pytest -m "not integration and not benchmark" # same as CI
pytest -m integration # live Ollama only (needs OLLAMA_HOST + model pulled)
pytest -m benchmark # optional latency checks
./scripts/demo.sh # one-click smoke (serve + curl + stats)
CI: .github/workflows/ci.yml — Python 3.12, pytest -m "not integration and not benchmark" on push/PR. No secrets.
Gaps (planned): L6 live API integration test (optional, requires frontier key/model); richer streaming metadata.
Count: 1221 passed (pytest -m "not integration and not benchmark", 2026-09-01)
Phase A.1 — Install & setup¶
| Task | Status | Notes |
|---|---|---|
scripts/install.sh |
[x] | venv + pip + Ollama pull; 13a2345 |
daari doctor |
[x] | daari/setup/doctor.py |
daari setup cursor --dry-run |
[x] | |
daari setup cursor (apply + backup) |
[x] | aaf3f06 |
daari setup --undo cursor |
[x] | daari/setup/backup.py |
Interactive daari setup wizard |
[x] | daari/setup/wizard.py — partial vs spec (see gaps below) |
daari setup models |
[x] | daari/setup/models.py |
| JSONC patch helpers | [x] | daari/setup/jsonc.py |
| Setup tests | [x] | tests/test_setup.py |
daari install (Typer) |
[x] | wrapper command to scripts/install.sh with --run-doctor; pip/brew falls back to onboard |
daari onboard |
[x] | #256 — first-run without a git clone |
| L6 frontier executor | [x] | daari/router/frontier.py — OpenAI-compat httpx |
| Confidence scoring → L6 | [x] | daari/router/confidence.py — binary heuristic per routing-spec |
Exit criteria
| Criterion | Status | Notes |
|---|---|---|
./install.sh && daari doctor passes |
[x] | CI install job: RUN_DOCTOR=0 ./scripts/install.sh + .venv/bin/daari --help; doctor required checks stay in tests/test_doctor.py |
daari setup cursor --dry-run shows diff |
[x] | covered by tests |
| Low-confidence response escalates to L6 | [x] | when frontier.enabled + API key present |
Wizard gaps (A.1 spec vs shipped): single-choice menu (not multi-select); frontier helper writes hints/templates only (no secret capture by design); IntelliJ/Claude deferred to Phase B per setup-spec.
Key commits: 13a2345 (scaffold), aaf3f06 (apply, undo, wizard, models)
Phase B — Full local-first stack¶
| Task | Status | Notes |
|---|---|---|
| L1 semantic cache | [x] | Ollama embeddings + diskcache; router L0 → L1 → L3 |
| L2 rules engine | [x] | JSON/YAML deterministic transforms before model tiers |
| L2-dev developer commands | [x] | regex rules for git/test/lint + readonly command-context prompts |
| CCS command context store | [x] | disk-backed command output reuse with TTL |
| PolicyEngine B.0 | [x] | allow/block + unknown deny/ask outcomes for Lt execution |
| Lt B.0 CLI tools | [x] | git status, git diff, pytest, eslint command dispatch |
| L4 medium model | [x] | second local model tier + L3→L4→L6 escalation path |
daari setup openai-compat |
[x] | prints OPENAI_* exports + writes ~/.daari/.env.example |
| Wizard frontier key helper | [x] | optional profile hint + env template (no config secret storage) |
daari context clear |
[x] | clears L0/L1/CCS caches |
daari setup all auto-detect run |
[x] | detects registered clients and runs applicable recipes |
daari setup intellij |
[x] | minimal IntelliJ helper config + dry-run + undo path |
daari setup vscode |
[x] | VS Code dry-run/apply/undo recipe with marker + docs |
daari setup claude-code |
[x] | minimal env helper + config pointer recipe with dry-run/apply |
| Lt ask/confirm UX | [x] | daari_meta.confirmation_prompt + X-Daari-Confirm: yes |
Lt --yes support |
[x] | --yes in prompt text now confirms unknown-policy commands |
| Doctor L4 pull hint | [x] | optional model_l4 hint with pull command |
| Install optional L4/L5 pull flags | [x] | daari install --pull-l4 --pull-l5 + scripts/install.sh env knobs |
| Eval expansion GP-11–GP-20 | [x] | prompts + regression assertions updated |
Exit criteria (Phase B — partial)
| Criterion | Status | Notes |
|---|---|---|
| Paraphrased prompt hits L1 | [x] | mocked embedder tests |
L1 metrics in daari stats |
[x] | tier counter L1 |
| L0 → CCS → L1 → L2-dev → L2 → Lt → L3/L4 routing order | [x] | with tool_calls bypass caches retained |
| $0-tier rate ≥30% on eval | [x] | Measured 2026-07-23 on GP-01–GP-20 mocked suite: 55% $0-tier (L0/L1/CCS/L2/Lt) — see scripts/measure_phase_b_metrics.py |
| Routing accuracy ≥90% on 20-prompt eval | [x] | Same run: 100% of eval assertions green (20/20) |
Tests: see Testing below.
Phase C bootstrap — in progress¶
| Task | Status | Notes |
|---|---|---|
Gateway adapter protocol (daari/gateway/base.py) |
[x] | OpenAI adapter now implements protocol |
Anthropic gateway adapter (/v1/messages) |
[x] | non-stream + SSE event streaming (stream: true) |
| MCP gateway ingress | [x] | JSON-RPC 2.0 at POST /mcp; /v1/mcp/query deprecated alias |
| L5 local tier wiring | [~] | config + routing/escalation support; large model remains optional |
| Sourcegraph/GHE provider depth (C3) | [x] | Sourcegraph GraphQL + GHE repo/issue search with configurable base URLs and token envs |
| GitLab self-hosted provider depth (C3) | [x] | REST project/issue search + @gitlab trigger + MCP tool support |
| L2-live URL fetch | [x] | simple fetch trigger (fetch/read/summarize/get <url>) + L3 summarization |
| SSE metadata enrichment | [x] | stream chunks now include daari_meta tier/provider/model |
| Browser extension MVP | [x] | MV3 popup UI in packages/browser-extension/ sends prompts to local daemon (:11435) |
| Browser extension options + error UX | [x] | API base URL options page + popup guidance when daemon is unreachable |
| Browser extension content script | [x] | #171 — site profiles, intercept + boundary header, transparent fallback, session savings in popup |
| Router integration prefixes | [x] | @sourcegraph / @ghe now route to integration providers before L3 |
| Per-project profiles | [x] | ~/.daari/profiles/<hash|slug>.yaml + DAARI_PROFILE support |
| Skills loader stub | [x] | ~/.daari/skills/*.md merged into system prompt prefix |
| Anthropic stream fallback | [x] | stream error now emits SSE error and falls back to non-stream response events |
| Web UI MVP dashboard | [x] | daari web-ui serve + static dashboard (packages/web-ui/) |
| Web UI auto-refresh + tier chart | [x] | dashboard now includes periodic refresh controls and tier distribution bars |
| Web UI export + theme controls | [x] | export current stats as JSON, dark/light toggle, and org-learning summary line |
| Enterprise scaffold | [x] | daari/enterprise/ added with minimal OrgSettings models |
| Enterprise E1 runtime scaffold | [x] | org cache path resolver + daari serve --org + DAARI_ORG_ID + doctor org check |
| Enterprise E2 org shared cache service | [x] | daari org-cache serve + org cache client + router L0-org/L1-org lookup + write-through |
context clear stale-cache warning |
[x] | prints restart note when daemon is running to avoid stale in-memory cache handles |
| Hot cache reload endpoint | [x] | POST /v1/daari/reload-caches reloads cache handles in running daemon; daari context clear invokes it automatically |
| Enterprise periodic profile sync | [x] | startup + interval sync via org.learning_sync_seconds, plus force-sync endpoint/CLI |
| Org cache retry/backoff hardening | [x] | retries transient org cache failures with exponential backoff |
| L1 semantic threshold + bench hardening | [x] | default threshold tuned to 0.88; scripts/bench.sh now deterministically checks L0 and L1 |
| Doctor embedding-model check | [x] | daari doctor now validates cache.l1.embedding_model (nomic-embed-text) |
| PyPI publish | [x] | daari==1.2.0 is on PyPI. Trusted publisher registered 2026-08-13; python scripts/release_pypi.py publish --target pypi reran the failed v1.2.0 job and verify installed from the index. Homebrew tap already pointed at the same tarball. · #160 |
| Cursor setup smoke script | [x] | scripts/smoke-cursor-dry-run.sh for CI/local setup dry-run validation |
| Cursor tunnel setup script | [x] | scripts/tunnel.sh starts local daemon + cloudflared and prints /v1 URL |
Cursor E2E BYOK — POC (2026-06-23)¶
Manual validation: Cursor Ask + daari model via cloudflared tunnel → local Ollama.
Debug log: ~/.daari/cursor-requests.log (request shape, tier attempts, content_chunks).
Issues found & fixed (shipped 2026-06-23)¶
| Issue | Symptom | Fix | Status |
|---|---|---|---|
| Cursor blocks localhost | Access to private networks is forbidden |
HTTPS tunnel (scripts/tunnel.sh) |
[x] |
Array message content ([{"type":"text",...}]) |
422 Unprocessable Entity | content_to_text() in gateway |
[x] |
Cursor input_text content blocks |
Empty stream (~20ms), 0 content_chunks |
content_to_text() handles input_text, output_text, dict blocks |
[x] |
| Cursor sends 18 IDE tools on Ask | Ollama returns tool_calls, 0 text chunks |
Strip tools in _prepare_internal_request() + plain-text system hint |
[x] |
tool_calls left in message history |
Ollama 400, empty stream | sanitize_messages_for_ollama() when tools stripped |
[x] |
| Stream path missing L4→L3 fallback | L4 404 when llama3.1:8b not pulled; empty or slow retry |
stream_openai_chunks() tier chain + fallback (matches non-stream route()) |
[x] |
| Stream error JSON malformed | Cursor freeze / parse errors | json.dumps() for stream errors; initial role: assistant chunk |
[x] |
Missing /v1/models |
Cursor model picker issues | GET /v1/models, GET /v1/models/{id} |
[x] |
| Gateway request logging | Hard to debug Cursor payloads | ~/.daari/cursor-requests.log via log_gateway_event() |
[x] |
| Integration tests for above | — | tests/integration/test_gateway_flow.py, tests/unit/test_gateway_content.py |
[x] |
Verified E2E: Cursor user_agent: Cursor/1.0 → daari → Ollama llama3.2:3b (L3 fallback) or llama3.1:8b (L4 when pulled); content_chunks > 0. Release notes: RELEASE-v1.1.2-cursor-e2e.md.
Testing summary (2026-06-23)¶
| Layer | Result |
|---|---|
pytest (default, mocked) |
1475 passed (2026-09-03) |
| Manual Cursor Ask E2E | ✅ math question + follow-up |
| Log verification | ✅ tools_stripped, stream_fallback_ok, content_chunks > 0 |
Next steps (Cursor / BYOK) — migrated to GitHub issues (2026-07-10)¶
The open rows below moved to the auto-dev backlog worked by the autonomous dev loop (AUTOMATION.md):
| Task | Issue |
|---|---|
~~Commit Cursor compat fixes to main~~ |
done (commit 1d651c6) |
~~Document cursor-requests.log in setup/cursor.md~~ |
done |
| Tool hallucination after tools stripped | #1 (P1) |
| Ask vs Agent mode split (ADR-0004) | #2 (P1) |
| Cursor-specific tier policy | #3 (P2) |
| Pull L4 in install by default | #4 (P3) |
| Anthropic stream usage + fallback parity | #5 (P2) |
| Org L1 semantic matching depth | #6 (P2) |
| Browser extension E2E coverage | #7 (P3) |
| Tag v1.1.2 release prep | #8 (P3) |
| Automated Cursor E2E test | covered by local watchdog (scripts/autodev-local.sh, Cursor-shaped smoke every 2h) |
Autonomous dev loop (2026-07-10)¶
| Piece | Status | Notes |
|---|---|---|
Backlog seeded as auto-dev issues #1–#8 |
[x] | priorities P1–P3, acceptance criteria per issue |
| AGENTS.md agent contract | [x] | repo root |
Repo public + auto-merge + branch protection on main (CI test required) |
[x] | via gh api |
Local watchdog (scripts/autodev-local.sh + launchd) |
[x] | validated live: filed issue #9 on first cycle (caught real live-test regression), Cursor smoke PASS |
| Cloud automation drafts (dev-cycle / pr-review / scout) | [x] | docs/automations/ — create in Agents Window or enable Bugbot |
| CI fallback dev-cycle workflow | [x] | .github/workflows/autodev.yml; activates when CURSOR_API_KEY secret is set |
| Runbook | [x] | AUTOMATION.md |
Demo cycle (2026-07-10) — loop verified end-to-end¶
The first full autonomous cycle ran the same day the loop was built:
- Detect — local watchdog's first run caught a real regression on
main(live Ollama test asserting removeddaari_metadefault + a prompt failing the confidence heuristic) and filed #9 with logs, labelsauto-dev,regression. - Fix — agent picked up #9 per AGENTS.md: branch
autodev/9-live-test-meta-headers, fix + test runs (live: 1 passed; default: 161 passed), conventional commit. - Gate — PR #10 opened with
Closes #9, auto-merge armed; branch protection held it until CItestwent green. - Merge — auto-merged as
f12889a; branch auto-deleted; issue #9 auto-closed. - Validate — next watchdog cycle on updated
main: daemon healthy, integration tests PASS, Cursor smoke PASS (8 content chunks).cycle result: PASS.
Loop cycles 2–3: P1 backlog cleared (2026-07-10)¶
| Issue | Fix | PR | Merge |
|---|---|---|---|
| #1 tool hallucination | strengthened NO_TOOLS_HINT leads message list, idempotent |
#11 | 4b35cfa |
| #2 Ask vs Agent split | tool-history detection, X-Daari-Tools override, OpenAI tool_calls stream deltas |
#12 | f080150 |
Feature cycle: v1.2 candidate set (2026-07-10)¶
Scouted against LiteLLM/RouteLLM feature sets; filed as issues #13–#15, implemented TDD-style, all auto-merged the same evening:
| Issue | Feature | PR | Merge |
|---|---|---|---|
| #13 | Streaming L0 exact cache read/write + stream metrics (Cursor BYOK is stream-only; the cache never served it before) | #16 | d4fe1ee |
| #14 | Persistent usage ledger (~/.daari/usage/ledger.sqlite3) + GET /v1/daari/report + daari report CLI with estimated_saved_usd |
#17 | d9736f1 |
| #15 | Frontier daily budget guard (frontier.daily_budget_usd, warning frontier_budget_exceeded, spend surfaced in report) |
#18 | 2e7ec56 |
Default suite grew 162 → 180 tests across these five cycles.
Feature cycle: prompt intelligence & transparency (2026-07-10, PRD docs/prd/intelligence.md)¶
Filed as issues #19–#22 from the new PRD, implemented TDD-style, all auto-merged, then validated end-to-end by a full autodev-local.sh cycle (deploy ae3b01f, live Ollama integration tests PASS, Cursor-shaped streaming smoke PASS) plus a live trace fetch (daari trace showed profile → l0_lookup → l1_lookup → tier_attempt → served for a real daemon request):
| Issue | Feature | PR | Merge |
|---|---|---|---|
| #19 | Prompt profile (category/complexity/token est) + routing.category_policies action policies; category in daari_meta.task_type, new complexity |
#23 | ed43d21 |
| #20 | Per-request decision trace: TraceStore sqlite, daari_meta.trace_id, GET /v1/daari/traces[/id], daari trace CLI (client-facing audit trail) |
#24 | cb80d4a |
| #21 | Cached-draft injection: L1 near-misses (cache.l1.draft_threshold=0.75) seed local and L6 generation as reuse/reformat drafts |
#25 | e4126d4 |
| #22 | Context optimizer: system + last-N history trimming and whitespace squeeze for local models (context_optimizer.*), savings traced per prompt |
#26 | ae3b01f |
Default suite now at 226 tests (180 → 226).
Loop cycles: original backlog cleared (#3–#6) (2026-07-10)¶
The remaining seeded issues from the first backlog, implemented TDD-style, auto-merged, and each validated live by a full autodev-local.sh cycle (final deploy a8600cc, live Ollama integration tests PASS, Cursor-shaped streaming smoke PASS):
| Issue | Feature | PR | Merge |
|---|---|---|---|
| #3 | Tier cap for chat: routing.max_tier_for_chat + X-Daari-Tier-Cap header clamp initial tier, stream chain, and local escalation (latency recipe in docs/setup/cursor.md) |
#28 | 9da32c4 |
| #4 | daari setup cursor verifies/pulls the L4 model (--yes for non-interactive; tunnel.sh passes it); daari doctor upgrades model_l4 to required when Cursor is configured |
#29 | 79b3221 |
| #5 | Anthropic stream parity: tier fallback chain, message sanitization, chars/4 usage estimates in message_start/message_delta |
#30 | a956d33 |
| #6 | Org L1 semantic matching: embeddings stored with entries, POST /v1/org-cache/similar, client similarity fallback on key miss (paraphrases now hit L1-org) |
#31 | a8600cc |
Default suite now at 250 tests (226 → 250). Remaining open backlog: #7 (browser-extension E2E), #8 (v1.1.2 release prep — tagging stays human-approved).
Loop cycles: extension coverage + scouted improvements (#7, #34–#36) (2026-07-10)¶
After clearing the original backlog, the loop scouted and filed three fresh improvement issues (#34–#36), then implemented all of them plus #7. Each cycle E2E-validated by autodev-local.sh (final deploy 984bc99); markdown export and cache prune also verified live against the running daemon:
| Issue | Feature | PR | Merge |
|---|---|---|---|
| #7 | Browser extension DOM tests (jsdom + node --test, 12 tests: send flow, error UX, draft persistence, options); new extension CI job |
#33 | aa6602d |
| #34 | Frontier prompt slimming before L6: strip internal hints, collapse duplicate system prompts, trim history; ledger records actual chars sent (frontier.slim_prompts) |
#37 | e27e930 |
| #35 | Client-shareable markdown export: daari report/daari trace gain --format markdown + --out FILE |
#38 | 78a0066 |
| #36 | Cache TTLs (cache.l0/l1.ttl_seconds, category ttl_seconds overrides) + daari cache prune |
#39 | 984bc99 |
Default suite now at 272 pytest tests (250 → 272) + 12 extension tests. Open backlog: #8 (release prep, human-gated).
v1.1.2 released + CI hardening (2026-07-11)¶
- CI expanded to four required checks on
main:test,extension,lint(ruff),sanity(runtime-deps install, imports, settings load, CLI entrypoint, app build) — #41,97cb5e1. - v1.1.2 shipped (issue #8, user-approved tag): version bump (pyproject + stale
daari.__version__), consolidated release notes addendum,python -m build+twine checkPASSED (no PyPI upload) — #42, tagv1.1.2.
Loop cycles: streaming L1 + scouted improvements (#43–#46) (2026-07-11)¶
Scout pass filed four issues; all implemented TDD-style, auto-merged, and E2E-validated by autodev-local.sh on the final deploy 531e407 (live Ollama integration tests PASS, Cursor streaming smoke PASS; daemon restarted on the new build and /v1/daari/report verified live):
| Issue | Feature | PR | Merge |
|---|---|---|---|
| #43 | Streaming path L1 parity: semantic hits served as SSE, draft-band injection, post-[DONE] L1 write-back — Cursor (all-streaming) finally benefits from the semantic cache |
#47 | 1f3d963 |
| #44 | Gateway request log rotation: size-based with numbered backups (observability.request_log_max_bytes, default 5 MB / 3 backups) |
#48 | 121aa43 |
| #45 | Embedding memoization: in-process LRU in OllamaEmbedder keyed by (model, sha256) — repeat L1 lookups skip the Ollama HTTP call (cache.l1.embed_cache_size) |
#49 | b3e4596 |
| #46 | Web UI usage & savings dashboard: report totals, per-day tier table, recent traces with step-timeline click-through; jsdom DOM suite added to CI | #50 | 531e407 |
Default suite now at 291 pytest tests (272 → 291) + 12 extension tests + 7 web-ui tests. Open backlog: empty — scout refills it.
Phase D1 — personal feedback loop (2026-07-11, PRD docs/prd/learning.md)¶
daari starts learning from outcomes. Everything stays on-device
(~/.daari/feedback/feedback.sqlite3) and stores outcome metadata only —
never prompt or completion text. PRD merged as #52.
| Issue | Feature | PR | Merge |
|---|---|---|---|
| #53 | Outcome store + implicit capture (every model-tier response, stream + non-stream, not cache hits) + explicit POST /v1/daari/feedback / daari feedback <trace_id> --accept\|--reject |
#56 | b272153 |
| #54 | daari learn stats per-category × tier evidence + daari learn recommend (cheapest tier with escalation ≤ 15%, rejects ≤ 10%, min 20 samples) emitting a Settings-valid routing.category_policies YAML block; GET /v1/daari/learn/stats |
#57 | 62dbf24 |
| #55 | Routing tuner: per-category confidence thresholds ±0.05 from outcome evidence, bounded [0.5, 0.9], learning.tuner_min_samples gate, tuner trace step; off by default (learning.auto_tune) |
#58 | 518e8ae |
Live E2E validated 2026-07-11: daemon restarted on 518e8ae; live doc_qa
request through the gateway → daari feedback <trace_id> --accept →
outcome + accept visible in daari learn stats; daari learn recommend
emitted a valid policy block from live evidence. Default suite now at 329
pytest tests (291 → 329). Remaining Phase D scope (D2 local fine-tuning,
D3 opt-in collective stats) stays on the roadmap.
Phase D2 — local fine-tuning train (2026-07-12, PRD docs/prd/learning.md)¶
The models themselves can now improve. Accepted answers — especially L6
frontier answers — become local training data (distillation). Capture is
strictly opt-in (learning.capture_examples, default off) because unlike
the D1 outcome store it keeps full prompt/completion text; everything stays
under ~/.daari/training/ and is wipeable. PRD merged as #60.
| Issue | Feature | PR | Merge |
|---|---|---|---|
| #61 | ExampleStore + router capture (both paths, L3–L6, never cache hits/tool flows); accept promotes to training data, reject deletes; daari learn examples [--clear] |
#64 | 0144b17 |
| #62 | daari learn export-dataset: mlx-lm chat-format train/valid JSONL, deterministic trace_id-hash split, min-examples gate, --only-accepted |
#65 | eb7104d |
| #63 | daari learn finetune: plans + runs mlx_lm lora --train (LoRA on mlx-community/Llama-3.2-3B-Instruct-4bit), auditable run.json, --dry-run, clean optional-dep gating |
#66 | 1d4cdb5 |
Live E2E validated 2026-07-12 on an isolated temp instance (capture on,
throwaway stores — user config untouched): 10 live Ollama requests captured,
2 accepted + 1 rejected via /v1/daari/feedback (reject confirmed deleted),
export-dataset produced 8 train / 1 valid chat-format examples, and
plan_finetune emitted the exact mlx_lm lora command + run.json. Default
suite now at 359 pytest tests (329 → 359). Actual training runs are
user-invoked (daari learn finetune, needs pip install mlx-lm); serving
MLX adapters through Ollama (fuse/convert to GGUF) is the documented manual
follow-up. Remaining Phase D scope: D3 opt-in collective stats.
Trust & Efficiency trains (2026-07-12/13, PRD docs/prd/trust.md)¶
Competitive research (Portkey/LiteLLM/OpenRouter/Requesty/RouteLLM, semantic- cache postmortems, local-first routers) distilled into five trains. Headline: daari now measures semantic-cache false-hit rate — the metric none of the compared products ship. PRD merged as #68.
| Issue | Train | PR | Merge |
|---|---|---|---|
| #69 | Cache trust: embedding-input normalization (fences/JSON scaffolding stripped, cache.l1.normalize_inputs); per-category answer-diversity monitor (/v1/daari/cache/diversity + doctor warning); shadow sampling of L1 hits (cache.l1.shadow_sample_rate, default 5%) → per-category false-hit rate that auto-raises the L1 threshold; report/learn stats/dashboard panels |
#70 | 7721b25 |
| #71 | Token savings: Anthropic cache_control prompt-cache hint on L6 with byte-stable prefix pinned by test; context_optimizer.compact — over-limit history summarized by L3 into a pinned recap (memoized per prefix); frontier.compress_context — sentence-level relevance pruning before L6 |
#75 | da4fabf |
| #72 | Latency-aware routing: daari profile hardware benchmarks; routing.latency_budget_ms + category override + X-Daari-Latency-Budget with profiled step-down; warm-model preference via TTL-cached /api/ps |
#79 | 4c365d9 |
| #73 | Learned routing: daari learn train-router centroid classifier over captured prompts; routing.learned_router overrides heuristics when confident (200-sample floor + margin gate); trace learned_route |
#79 | 4c365d9 |
| #74 | Budget & client UX: monthly + soft budgets (frontier_budget_warning band before hard cap); per-client ledger attribution (X-Daari-Client-Id, Cursor auto-tagged) + daari report --by-client; opt-in pre-L6 PII scrub with typed placeholders |
#79 | 4c365d9 |
Trains 3–5 were consolidated into #79
after the stacked branches went stale post-squash (#76–#78 closed as superseded).
Live E2E validated 2026-07-13 on an isolated temp instance (port 11438, live
Ollama, throwaway stores): paraphrase served from L1 with normalization on,
shadow check ran in background (answer similarity 0.984 → agreed, false-hit
rate 0.0 in learn stats/report/diversity endpoints), per-client attribution
(e2e-test) and budget state in the report, and benchmark_model + warm
tracker measured the live 3B model (349 ms wall, 115 tok/s, warm set
detected). Default suite 430 pytest tests (359 → 430); web-ui at 9 DOM tests.
All new behaviors default-safe: normalization + shadow sampling on (read-only
additions), compaction/compression/learned-router/PII-scrub opt-in.
One-click client setup (2026-07-13, issue #81)¶
| Item | PR | Merge |
|---|---|---|
Claude Code one-click: daari setup claude-code merges ANTHROPIC_BASE_URL/ANTHROPIC_AUTH_TOKEN/ANTHROPIC_MODEL into the env block of ~/.claude/settings.json (existing keys preserved, backup + undo); Anthropic gateway now honors the top-level system field (string or blocks) that Claude Code sends. Ollama-compatible facade: /api/tags, /api/chat (stream NDJSON + non-stream), /api/version, /api/show, /api/ps — JetBrains AI Assistant, Zed, Continue etc. connect by pasting http://127.0.0.1:11435, full router semantics + per-client attribution (X-Daari-Client-Id). IntelliJ recipe/docs updated with exact AI Assistant steps; native IntelliJ plugin deferred in favor of the facade. |
#82 | bbe233e |
Live-verified 2026-07-13 on the running daemon: /api/tags lists daari +
tier models, non-stream /api/chat answered from L3 with daari_meta, and the
intellij client id landed in the ledger. The tool-passthrough limit noted at
the time was closed the same day by issue #84 (below). Suite: 442 pytest tests.
Claude Code agent E2E + tunnel hardening (2026-07-13, issues #84/#86/#88)¶
| Item | PR | Merge |
|---|---|---|
Anthropic tool passthrough: tools/tool_choice on /v1/messages, tool_use→tool_calls and tool_result→role:tool conversion, agent flows skip sanitization/context-optimizer, streamed tool_use content blocks with stop_reason: "tool_use"; X-Daari-Tools: strip forces plain chat; one-click uninstall fallback strips daari ANTHROPIC_* keys when no backup exists |
#85 | 1e2934c |
Gateway API-key auth: server.api_key setting + middleware (Bearer or x-api-key, health endpoints exempt); daari setup cursor --tunnel auto-generates + persists the key and configures Cursor with it; docs on named Cloudflare tunnels / Tailscale Funnel |
#87 | b594500 |
Claude Code live E2E fixes (first real session surfaced 3 gaps): OllamaRequestError preserves Ollama 400 bodies in logs; num_ctx sized from prompt chars (4096..32768) so 10-20k-token Claude Code system prompts stop yielding empty streams; tool-call arguments converted JSON-string→object and content-less non-tool messages dropped (both 400'd Ollama); anthropic_messages_request shape logging |
#89 | 8265b21 |
Loop cycle 2026-07-22 — full live E2E + per-project profiles (#91)¶
Regression issue #90 closed as transient: the working tree sat on a dev branch
Jul 13–22 so the watchdog skipped deploys; its own kickstart recovered the
daemon in the same run. Daemon redeployed on main @ 8265b21, then the full
live battery passed 11/11 against the running daemon: OpenAI fresh→L3,
repeat→L0, stream SSE+DONE, Anthropic non-stream with system, streamed
tool_use + stop_reason, facade /api/tags + /api/chat, stats/report/
traces/diversity endpoints. Full suite 474 pytest + ruff + extension/web-ui
npm suites green.
Per-project profiles (roadmap Phase C1's last unshipped item): commit a
.daari.yaml at the repo root (routing.max_tier_for_chat, no_frontier,
latency_budget_ms, client_id); clients opt in with X-Daari-Project:
/path/inside/repo on both OpenAI and Anthropic gateways (the Anthropic
gateway also gained X-Daari-Client-Id parity). Explicit headers always win;
profiles are mtime-cached and malformed files never break a request. New CLI:
daari project init / daari project show. Docs:
setup/project-profiles.md.
Release v1.2.0 + Claude Code live fix (2026-07-23)¶
| Item | PR | Merge |
|---|---|---|
| Release v1.2.0 — Learning, Trust & Clients: version bump + RELEASE-v1.2.0.md covering the 26 commits since v1.1.2 (Phase D learning, Trust trains, one-click clients, gateway auth, per-project profiles); tagged and published | #93 | 6bfc9e3 |
Trailing system message fix (issue #94): live claude -p printed nothing — claude-cli 2.1.215 sends messages=[user, system] (SessionStart-hook/plugin content as a 14k-char trailing system-role message) and llama chat templates emit zero tokens in that shape, so L4 and L3 both streamed empty. Captured via a recording proxy; Anthropic gateway now hoists out-of-order system messages ahead of the conversation (stable order, leading-system requests untouched) |
#95 | bab511c |
Suite: 493 pytest. The #94 regression test replays the captured Claude Code
request shape (top-level system + [user, system] + tools) and asserts
Ollama receives [system, system, user]. Live claude -p re-verification
runs on the next watchdog deploy cycle.
MLX backend + CI hardening (2026-07-23, issue #97)¶
| Item | PR | Merge |
|---|---|---|
MLX backend (roadmap C2 optional): MLXExecutor speaks OpenAI wire to mlx_lm.server and duck-types OllamaExecutor (SSE chunks converted to Ollama-style events), so tier loops/caching/escalation/budgets/traces are unchanged; mlx.models tier→model map routes only listed tiers to MLX (mixed setups fine, default off); optional daari doctor mlx check; setup/mlx.md |
#98 | 5270dd4 |
CI ruff pin: lint job installed unpinned ruff>=0.8; a new ruff release flagged 167 pre-existing issues repo-wide and failed every open PR — pinned to ruff==0.15.17 (local version, clean) so lint is deterministic and upgrades become deliberate PRs |
#99 | b1c2385 |
Suite: 508 pytest (493 → 508). MLX live smoke is hardware/download-gated
(pip install mlx-lm + model fetch) — doctor and docs cover the path.
Anthropic observability + Phase D3 + adapter deploy (2026-07-23, issue #101)¶
| Item | PR | Merge |
|---|---|---|
Anthropic stream observability & routing parity (issue #101): anthropic_stream_attempt_failed logs error_type (timeouts stringify to ""), new anthropic_stream_done event records winning tier/model/latency/chars, and the Anthropic path now builds the prompt profile so category policies, learned routing, and latency-budget step-down apply like the OpenAI path |
#102 | e4628f4 |
Phase D3 — opt-in collective stats (learning PRD): daari learn export-stats prints the exact metadata-only payload for review (category/tier aggregates, shadow false-hit rates, model IDs — never prompts/IDs); --upload gated on learning.collective_enabled + collective_url with a recursive sensitive-key guard; all defaults off |
#102 | e4628f4 |
daari learn deploy: bridges D2c fine-tune runs to executors — mlx backend prints the mlx_lm server --adapter-path command + config snippet (long-running, plan-and-print); ollama backend fuses to GGUF and ollama creates a named model with deploy.json status audit |
#102 | e4628f4 |
Suite: 535 pytest (508 → 535). Live E2E on the redeployed daemon:
/v1/messages stream answered via L3 in 2.2s and anthropic_stream_done
appeared in cursor-requests.log; learn export-stats rendered real
30-day aggregates (metadata only); learn deploy printed correct mlx and
ollama plans against a stub run dir.
Phase E2 — Org shared cache (tracer bullet)¶
| Task | Status | Notes |
|---|---|---|
Org cache HTTP service (/get, /put, /stats) |
[x] | daari/enterprise/service.py |
Router shared-cache lookup order (L0-org, L1-org) |
[x] | local L0 -> org L0 -> local L1 -> org L1 |
| Shared write-through from local model responses | [x] | pushes L0 + L1 keys to org cache when configured |
Config expansion (org.id, shared_cache_url, token, timeout) |
[x] | Settings.load maps org block into enterprise |
daari serve --org org-cache client wiring |
[x] | AppContext.from_settings instantiates OrgCacheClient when URL set |
| Doctor org-cache reachability check | [x] | optional org_cache check (/v1/org-cache/stats) |
| Tests (service/client/router/config/cli) | [x] | no real network required in CI |
| E3 collective learning | [x] | metadata-only feedback API + profile sync + CLI stats/export |
| Web UI serve CLI smoke test | [x] | tests/test_setup.py::test_web_ui_serve_mounts_static_assets |
Deferred / user-owned¶
- Cursor smoke test on personal device (
daari setup cursor+ chat through daari) — Ask E2E verified 2026-06-23; see Cursor E2E BYOK POC - L4 model pull/install still user-managed (falls back to L3 when unavailable; pull
llama3.1:8bto use L4 without retry) - L6 live frontier smoke depends on API key presence
- Cursor follow-up quality / tool hallucination when tools stripped (tracked in open todos above)
- PyPI upload remains user-gated (needs PyPI token in repo secrets)
Roadmap v2 (F1–F5) — 2026-07-23/24¶
Forward plan: ROADMAP-v2.md. Issues labeled auto-dev.
| Train | Status | Notes |
|---|---|---|
| F1 OSS launch | [x] | Docker/compose//ready (#105), MkDocs (#114), PyPI prep (#106, upload user-gated), CHANGELOG + community pack |
| F2 Gateway parity | [x]/[~] | Responses API (#108), Prometheus (#107), guardrails (#110), capability catalog (#113); L6 pool (#109), virtual keys (#111) in flight / merged via auto-dev PRs |
| F3 Ops | [x] | Prometheus + Grafana (#107); OTel export + structured stdout logs + GET/PATCH /v1/daari/config (#115) |
| F4 Enterprise scale | [x] | Redis L0 (#112); Postgres ledger/traces + observability.stateless (#116); Helm + capacity (#117); org pool + daari enterprise bootstrap (#118); SSO/RBAC/audit tracer (#119) |
| F5 Leftovers | [x] | Live sources (#120); MCP egress (#121); Phase B exit metrics recorded above + scripts/measure_phase_b_metrics.py (#122); Homebrew formula (#123, sha256 filled after release) |
Suite: 669 pytest (default markers).
Auto-mode deepeners (2026-07-24) — reviewed 2026-08-11¶
See HANDOFF-AUTO-2026-07.md. Built as tracer deepeners with review deferred; the frontier review pass landed as issue #137 (see below).
| Item | Status | Notes |
|---|---|---|
| Periodic org policy sync in daemon | [x] | policy_sync.py + learning sync loop |
| Config editor persist to disk | [x] | persist: true on PATCH; atomic 0600 write |
D4 propose-defaults |
[x] | YAML proposal only — never auto-promote |
| Web UI config card | [x] | Bearer field added in #141 |
| Strong-model review | [x] | #137 — see review pass below |
| PyPI upload / Homebrew sha256 | [ ] | User-gated (needs a release tarball) |
Redis L1 semantic (cache.backend=redis) |
[x] | #135 — RedisSemanticCache, tags fable-review/135-* |
| OIDC JWKS admin SSO | [x] | #136 — tags fable-review/136-*; HMAC stub retained |
| Web UI Bearer / API key field | [x] | #141 — tags fable-review/141-* |
| Live Redis+Postgres compose E2E | [x] | #142 — profile backends + scripts/smoke_backends.py / .sh |
| F6 Product boundaries (scope gate) | [x] | #145 — ADR-0015; B0–B3 + embed B0 (#172); tags fable-review/boundaries-* |
Frontier review pass (2026-08-11, issue #137)¶
Review of everything built while the deep-review pass was deferred. Defects fixed
in tests/unit/test_review_hardening_137.py (15 tests, all previously red):
| Defect | Severity | Fix |
|---|---|---|
| Daemon policy sync applied unsigned org config when no signing secret was set (CLI path already failed closed) | high | sync_policy_once requires a secret unless insecure=True |
Policy sync accepted plaintext http:// for config that can disable guardrails and raise budgets |
high | policy_url_is_secure() — https, or http to loopback only |
Blocking httpx fetch ran on the event loop, stalling in-flight requests for up to the 10s timeout |
medium | asyncio.to_thread in the sync loop |
Sync failures were swallowed by except Exception: pass — no way to see a broken policy feed |
medium | log_gateway_event("policy_sync_failed"/"policy_sync_skipped") |
daari learn propose-defaults read a {category: {accept_rate, n}} shape that build_collective_stats never emits, so every run wrote an empty proposal and reported success |
medium | _flatten_category() derives accept rate + sample count from the real nested categories payload; flat shape still supported |
~/.daari/config.yaml written non-atomically at umask default, next to provider API keys |
medium | write_config_atomically() — temp file + os.replace, mode 0600 (also used by enterprise bootstrap) |
| Cached JWKS locked admins out for the full TTL after an IdP key rotation | medium | refetch once with force=True on unknown kid |
PATCH /v1/daari/config returned 500 on malformed values and half-applied out-of-range ones; setattr bypassed pydantic validation |
medium | daari/config/validate.py validates the whole patch first → 400 |
validate_assignment on Settings closed in #152.
EC/ES256 JWKS keys closed in #151.
Redis L1 lost-update is #150.
Streaming policy parity (#154, #155) (2026-08-11)¶
A feature-gap audit found that boundaries, guardrails, and frontier escalation ran
only in Router.route(). Both streaming entry points were separate code paths that
called none of them — so for every supported IDE client, which streams by default,
those features were inert. Reproduced before the fix: the same prompt with
boundaries.mode = "block" returned tier='boundary' with zero upstream calls
through route(), but streamed model output with no refusal.
| Defect | Severity | Fix |
|---|---|---|
Boundary gate never ran on either stream path — a block-mode refusal was silently skipped for all IDE traffic |
high | _apply_input_policy() shared by route() and both stream paths |
| Input guardrails never ran on either stream path | high | same shared gate; refusals emitted as well-formed terminal streams (OpenAI [DONE], Anthropic message_stop) |
| Output guardrails never ran on streams, so a leaked secret reached the client and was written to L0/L1 | high | _apply_output_policy() runs over accumulated text before flush and before cache write-back |
_stream_tier_chain was local-model-only, so a streaming request could never reach the org pool or L6 |
high | streams call _maybe_escalate(..., local_ladder=False) — org pool and frontier now reachable |
local_ladder=False is deliberate: _stream_tier_chain has already chosen and
fallen back through the local tiers, so re-running L4/L5 non-streamed would only
duplicate work. Non-streaming keeps the full local ladder.
Covered by tests/unit/test_stream_policy_parity.py (11 tests, 5 previously red).
Streaming tier parity (#155) (2026-08-11)¶
Follow-up that closed the rest of the streaming gap.
| Defect | Severity | Fix |
|---|---|---|
Deterministic tiers (Lt shell tools, L2 rules, live fetch, integrations) were unreachable while streaming — the same prompt got a model answer instead of tool output |
high | _resolve_deterministic_tier() extracted from _route_impl and called from both stream paths |
| Escalated streams buffered the whole frontier answer before emitting, so first-token latency was non-streaming latency | medium | FrontierExecutor.stream() relays upstream SSE; the router relays it chunk-by-chunk |
_route_impl is now three named steps (deterministic tiers → generation →
escalation) rather than one long function, which is what let the stream paths
reuse the same logic instead of reimplementing a subset of it.
The relay is skipped, falling back to the buffered path, when the complete text
is needed first: output guardrails must see the whole answer to redact it, and
the org pool has to be tried before paying for frontier. Both are explicit
checks in _can_relay_frontier_stream().
Covered by tests/unit/test_stream_tier_parity.py (7 tests, 4 previously red),
including a MockTransport case proving the SSE decoder skips blank, malformed,
and [DONE] lines.
Real token accounting and per-model pricing (#156, #157) (2026-08-11)¶
Every token count in daari was len(chars) // 4, and every cost was that
estimate multiplied by one flat frontier.price_per_1k_tokens. Two compounding
approximations fed the savings report, the /v1/daari/report endpoint, the
Prometheus spend gauges, and the budget cutoff that decides whether to escalate.
| Defect | Severity | Fix |
|---|---|---|
Providers report real usage (Ollama prompt_eval_count/eval_count, OpenAI usage) and none of it was read |
high | daari/observability/tokens.py readers; captured into DaariMeta.input_tokens/output_tokens on both non-streaming and streaming paths |
Clients received chars/4 in the usage block with no way to tell it was a guess |
medium | real counts when reported; daari_meta.usage_estimated says which it was |
| One flat rate priced every model and both directions, so a gpt-4o output token and a gpt-4o-mini input token cost the same | high | daari/pricing.py with per-model, per-direction USD/1M rates; pricing.models in config |
| Ledger stored only chars, so spend could not be recomputed per model after the fact | medium | usage/client_usage gain model, provider, input_tokens, output_tokens |
Output tokens cost 4-5x input tokens at every major provider, so pricing both directions at one blended rate was the largest single source of error.
The ledger migration rebuilds usage and client_usage rather than using
ALTER TABLE ADD COLUMN: the upsert needs (day, tier, model) as its conflict
target and SQLite cannot alter a primary key in place. Existing rows are copied
across with model = '', which prices them at the fallback rate — historical
rows never had a model recorded, so no precision is lost that existed before.
Unpriced models still work: they fall back to the flat rate and daari doctor
prints a warning naming the model, so silently-wrong spend is visible rather
than discovered on a bill. The shipped price table is a dated convenience
default and pricing.models always overrides it.
Covered by tests/unit/test_token_accounting.py (15 tests) plus an
integration test asserting reported counts survive from executor to both the
API usage block and the ledger columns.
L1 hit verification before serving (#168) (2026-08-11)¶
daari measured its semantic-cache false-hit rate but still served any hit above a cosine threshold. Correct and incorrect similarity distributions overlap, so a threshold cannot separate the two cases: "what is 15% of 200" and "what is 15% of 300" are textually near-identical with different answers, while "how do I list files" and "how can I list files" are further apart with the same answer. Embedding distance ranks those two pairs in the wrong order.
cache.l1.verify (none | lexical | model, default lexical) adds a second
stage between the cosine match and the response. The lexical verifier rejects
differing numbers, differing or reordered units, flipped negations, swapped
opposites, and substituted content words; rejections fall through to generation
and increment daari_cache_false_hits_avoided_total.
Two deliberate choices worth knowing:
- Entries without stored prompt text are not served. Caches written before this change have no text to verify against, and serving them unverified would preserve exactly the false hits this exists to prevent. They are re-learned on the next miss, so the cost is a one-time warm-up.
- Additions pass, substitutions do not. Extra words are filler; a swapped word may change the answer.
The known limitation is honest and tracked rather than hidden: a lexical stage
cannot distinguish a harmless synonym substitution ("fix" for "resolve") from a
meaningful one ("staging" for "production"), because both are one-word swaps. It
errs toward regeneration and loses those hits. verify = "model" is the path to
recovering them.
evals/cache/verification.jsonl labels 36 pairs and runs in CI as a gate:
paraphrase retention and near-miss rejection both floor at 90%, currently 100%
each, with synonym retention reported but ungated at 0/6. Verification costs
0.007 ms per call against the hundreds of milliseconds a hit saves.
Covered by tests/unit/test_l1_verification.py (21 tests) and
tests/unit/test_l1_verification_corpus.py (4 gates).
Per-key frontier budgets (#158)¶
Virtual-key budgets were compared against global frontier spend. The middleware said so in its own comment: the ledger had no per-client frontier helper, so it used the global figure "conservatively". The effect was that one key's traffic exhausted every other key's allowance, and a key with a small cap could be blocked by spend it never caused — while per-key budgets were a headline feature of the virtual-keys work (#111).
UsageLedger.frontier_spend_usd_for_client() now answers the question directly,
over day or month, priced per model like the global figure. No migration was
needed: client_usage already gained the model and token columns in #156.
Two behaviors changed beyond the isolation fix:
- Monthly caps now do something. Only the daily window was ever checked, so
--monthly-budgetwas silently inert. - The 402 names what tripped. The body carries
window,budget_usd, andspend_usd, so a client can tell "I am out of budget" from "the org is out of budget" instead of inferring it.
The global caps still apply as an outer ceiling through the router's
_frontier_budget_state(), unchanged — a per-key allowance cannot be used to
exceed the org cap.
Covered by tests/unit/test_virtual_key_budgets.py (11 tests), including
two-key isolation and attribution for keys created without --client-id.
Upstream retries and per-tier timeouts (#159)¶
Every upstream call got exactly one attempt, so a single transient 429, connection reset, or 503 failed the request outright. Worse, the frontier pool advanced to the next provider on any exception, spending a provider slot on a blip that a 200 ms backoff would have cleared.
daari/router/retry.py adds bounded exponential backoff with equal jitter, wired
into Ollama, MLX, and frontier calls. Three properties matter more than the
backoff itself:
- Only transient failures retry. 408, 429, 5xx, connect and read timeouts. A 401 or malformed body fails immediately, because retrying it only delays an error the caller has to see.
- The retry budget never outlives the request timeout. A backoff that would land past the deadline is not attempted, so retries cannot turn a slow request into a hung one.
- Retries sit below the ledger write. One client request stays one ledger row, so a flaky upstream cannot inflate request counts or spend.
Retry-After is honored when present, in both seconds and HTTP-date form, and
overrides the computed backoff. Jitter spreads retries from requests that failed
together, which otherwise return in lockstep and rebuild the load that caused the
failure.
Timeouts are now per-tier rather than a hardcoded 120s: upstream.local_timeout_seconds
defaults to 120s (a cold local model is genuinely slow) and
upstream.frontier_timeout_seconds to 90s (a hosted API silent for 90s usually
stays silent).
Each retry becomes an upstream_retry trace step carrying the status and delay,
and increments daari_upstream_retries_total. A rising counter means backoff is
absorbing instability the client never saw.
Covered by tests/unit/test_upstream_retry.py (35 tests) and
tests/unit/test_upstream_retry_wiring.py (14 tests).
Test isolation: a flake and a hazard¶
tests/integration/test_redis_l1_gateway.py failed roughly one run in ten while
passing in isolation. Two independent causes, found by reproducing it rather than
by reading the test:
The flake was cache.l1.shadow_sample_rate. It ships at 0.05, so one in
twenty L1 hits spawns a background task that re-executes the request to audit the
cached answer. The test asserted its mock executor ran exactly once. Because the
audit runs as a task, whether it had incremented that counter by assertion time
was a race — the cache assertions always passed, which is why the failure surfaced
as a call count and looked unrelated to caching. The shared fixture now pins the
rate to 0; tests that exercise shadow sampling set it themselves, and the shipped
default is asserted separately so disabling it for tests cannot quietly disable it
for users.
The hazard was ~/.daari. Six settings defaulted there, including the
command-context store that a locally running daari serve writes, its L1 cache,
and — worst — the real virtual-keys database. The suite was reading and writing a
live daemon's state, and a developer's actual credential store. HOME is now
redirected per test, which fixes the whole class rather than the six paths found
today.
Verified by 25 consecutive full-suite runs with no failures, against two failures
in the preceding 22. Guarded by tests/unit/test_fixture_isolation.py (8 tests),
which walks the settings tree and fails on any path escaping to the real home.
Sampling parameters (#161)¶
ChatCompletionRequest declared six fields and set extra="ignore", so max_tokens,
top_p, stop, seed, and response_format were accepted with a 200 and dropped.
A client asking for a bounded or deterministic answer got neither, and no error. The
other three surfaces were worse: the Anthropic endpoint parsed the max_tokens that
API requires and never used it, the Responses endpoint did the same with
max_output_tokens, and the Ollama facade read temperature out of options and
discarded num_predict beside it.
daari/gateway/sampling.py now owns one SamplingParams model with a reader per
surface, and each executor maps it on the way out. Three rules shaped it:
- Unset means absent. Sending
Nonefor an omitted parameter would override the backend's own default, so unset keys never reach a payload. - What cannot be honored is reported.
presence_penalty,n > 1,logprobs, andtool_choice: requiredbecome adaari_meta.warning. Appended, not assigned: a low-confidence answer already sets a warning, and that is when a client most needs to know what else was dropped. - Only honored parameters split the cache. Sampling is in the cache key, so a 16-token answer cannot be served to a request asking for 500, while a parameter that never reached the model does not fragment the cache. Requests that set nothing keep hitting pre-#161 entries.
Two bugs the mocked tests could not have found, both caught by asserting on the wire:
max_completion_tokens is present-but-None in a parsed body, so get(key, default)
read the None and never fell back to max_tokens; and Ollama matches stop
case-sensitively, which broke a test rather than the mapping.
Covered by tests/unit/test_sampling_params.py (mapping), test_sampling_end_to_end.py
(values reach the wire, across all four surfaces), and tests/integration/test_sampling_live.py,
which confirms against a real llama3.2:3b that the cap truncates, a shared seed
reproduces, a stop sequence ends generation, and JSON mode parses.
PyPI and Homebrew (#160)¶
v1.1.1, v1.1.2, and v1.2.0 all built and then failed invalid-publisher because
no trusted publisher was registered. That is one form under a PyPI account — no
API, no token — so it waited on a human. On 2026-08-13 the pending publisher was
added with the OIDC claims publish.yml sends, and
python scripts/release_pypi.py publish --target pypi reran the failed job from
the v1.2.0 tag so the upload matched the tag rather than main.
verify --version 1.2.0 then installed into a throwaway venv and ran
daari --help. The Homebrew formula and tap were already filled from the earlier
prep; the formula job in publish.yml did not run on the rerun because it was
added after the tag, and had nothing left to write.
Multimodal images (#164)¶
content_to_text kept only text blocks, so an image_url part was accepted with
a 200 and the model answered as if no picture was sent. The capability check then
looked for the substring "image_url" in that already-flattened text, so vision
could never fire.
Images now live on Message.images. Ollama gets them as images: [b64]; OpenAI-
compatible backends get image_url content parts. A stack with no vision-capable
model returns 422 rather than a confident wrong answer. Cache keys hash the image
bytes; a request with no images keeps its pre-#164 key.
Embeddings endpoint (#163)¶
OllamaEmbedder already ran in-process for L1, but there was no POST /v1/embeddings,
so any app that wanted a vector had to point at a second host. The endpoint is
OpenAI-shaped (string or batch), served by cache.l1.embedding_model, cached in L0
under an __embed__: key, listed on /v1/models, and recorded as tier embed.
model: daari aliases the configured embedder; anything else is 400.
Native Anthropic L6 egress (#166)¶
provider: anthropic used to POST an OpenAI-shaped body at /chat/completions and
sprinkle cache_control on system strings. That is not the Anthropic API, so
Claude keys 400'd and prompt-cache billing never applied. FrontierExecutor now
selects a native Messages path when provider is anthropic/claude or
anthropic.com is in base_url: x-api-key + anthropic-version: 2023-06-01,
system text in the top-level system field, tool_use/tool_result blocks,
image sources, and cache_control: ephemeral on the last system block. Stream
deltas parse content_block_delta / text_delta. OpenAI-compatible providers
are unchanged.
Ingress gained POST /v1/messages/count_tokens — a local estimate_tokens of
system + messages + tools, not an L6 call. Claude-family clients use it for
context budgeting.
Covered by tests/unit/test_anthropic_egress.py and
tests/integration/test_anthropic_egress_live.py (skipped without
ANTHROPIC_API_KEY).
MCP JSON-RPC server (#162)¶
POST /v1/mcp/query was a bespoke body, not JSON-RPC, so no real MCP client could
connect even though the README claimed tools/list / tools/call. POST /mcp is
now a streamable-HTTP JSON-RPC 2.0 endpoint: initialize, notifications/* (202),
tools/list, tools/call, with parse/invalid/method-not-found codes. Tools are
route, stats, and registered integration: / mcp: providers, each with an
inputSchema. Auth is the existing master / virtual-key middleware. The old query
path stays as a deprecated alias (Deprecation: true, Link: </mcp>).
Covered by tests/unit/test_mcp_server.py. Client config:
MCP guide.
Responses API completeness (#165)¶
The adapter was text-only: function_call input items were skipped, tool-call
output items were never emitted, and there was no conversation state. Agent
frameworks moving off Assistants (sunset 2026-08-26) could not use this surface.
function_call / function_call_output now map to internal tool_calls /
role=tool. Non-stream output emits function_call items; streams emit
response.function_call_arguments.delta. previous_response_id prepends the
stored conversation (sqlite next to the trace store). store: false skips
persistence. background: true returns queued and GET /v1/responses/{id}
polls to completed. include is 400; metadata is echoed.
Covered by tests/integration/test_responses_api.py, including a call through
the official OpenAI AsyncOpenAI.responses client.
Distributed rate limiting (#169)¶
Virtual-key RPM used to be a SQLite INSERT + COUNT on every request — no TPM,
no global cap, no concurrency gate, and no shared counters across replicas. One
runaway agent loop could saturate the local GPU.
daari/auth/rate_limit.py now owns one limiter with Redis counters when
cache.backend: redis and SQLite otherwise. RPM and TPM apply per key and per
model (rate_limit.rpm / tpm / model_rpm / model_tpm; a virtual key's
--rpm / --tpm override the defaults). max_in_flight plus queue_size
bounds concurrency; overflow is 503 + Retry-After. Successful and rejected
responses carry X-RateLimit-Limit / Remaining / Reset. /metrics exposes
the configured limits and current in-flight / queued gauges. The Redis path
never opens SQLite.
Covered by tests/unit/test_rate_limit.py.
Local backend pool (#170)¶
Local tiers accepted one Ollama URL. A dead or overloaded host failed every request until restart — no health checks, no load balancing, and circuit breakers existed only for frontier providers.
routing.local_pool.backends is a per-tier host list (empty still means
ollama.base_url). A background loop probes /api/version (or MLX
/v1/models) without blocking requests. Pick is least_outstanding or
round_robin, with warm-model hosts preferred. Each host has a
CircuitBreaker. /ready is ready, degraded (200, some hosts down), or
not_ready (503). The chosen host is daari_meta.backend_id, a backend_pick
trace step, and daari_backend_* Prometheus series. All hosts down is a typed
backend_unavailable 503.
Covered by tests/unit/test_local_pool.py.
Redis L1 lost-update (#150)¶
RedisSemanticCache stored the whole entry list under one key and did an
unsynchronized GET/SET. Two replicas that loaded the same list each appended
and the later SET dropped the earlier write — the opposite of sharing
nearest-neighbor hits.
Puts (and TTL prune) now go through WATCH/MULTI optimistic locking with
five retries, then a last-snapshot write that never raises into the request
path. max_entries eviction still applies to the merged list. No new
dependency.
Covered by tests/unit/test_redis_semantic.py.
OTel GenAI semantic conventions (#167)¶
OTel export used to emit ad-hoc daari.* attributes with everything
stringified — no gen_ai.* names, so daari traces joined nothing in
Langfuse/Grafana/Datadog.
Root spans are now chat {model} with gen_ai.operation.name, provider.name,
request.model, response.model, response.finish_reasons, usage.* (real
provider counts only — estimates are flagged daari.usage_estimated, never
reported as measurements), and error.type. Metrics:
gen_ai.client.token.usage, gen_ai.client.operation.duration, and for
streams gen_ai.server.time_to_first_token / time_per_output_token.
daari facts stay under daari.*; numbers are numbers. Startup installs OTLP
providers when OTEL_EXPORTER_OTLP_ENDPOINT is set and nothing configured
OTel first. Conventions are Development status and may shift.
Covered by tests/unit/test_otel_genai.py; wire-verified by
scripts/smoke_otel_genai.py against an in-process OTLP collector.
Live product benchmark (#189)¶
scripts/bench.sh was a curl smoke with UUID prompts — it could not back a
routing or cache-trust claim. scripts/bench_live.py now runs the labeled
corpora (evals/routing/prompts.jsonl, evals/cache/verification.jsonl)
against a hermetic daari serve + real Ollama (fresh cold-cache instance per
phase), reports $0-tier rate, routing accuracy, L1 paraphrase retention /
near-miss rejection, p50/p95 per tier, and frontier USD avoided from
provider-reported tokens, and publishes
developer/resources/benchmarks.md with
commit, hardware, model IDs, and date. Never calls a paid API by default
(X-Daari-No-Frontier; expected-L6 rows excluded unless --allow-frontier).
Reproduce guide:
developer/guides/observability/live-benchmark.md.
First honest run found a real trust bug: the #168 lexical verifier is bypassed
on the serve path (router calls nearest(), never get()), so near-misses
serve from L1 — filed as
#206 (P1).
Pure logic covered by tests/unit/test_bench_live.py; the live path runs via
pytest -m benchmark or the script itself, skipping cleanly without Ollama.
L1 verifier restored on the serve path (#206)¶
The #168 lexical verifier only ran inside SemanticCache.get(), but the
draft-band refactor (779a146) had switched the router to nearest() — so no
live request was ever verified and near-misses served from cache ("15% of 300"
got the answer for "15% of 200"). nearest_with_source() now returns the
stored prompt text and both serve paths (non-streaming and streaming) apply
verify_for_serving() before serving; a vetoed hit falls back to the draft
band instead. Live bench: near-miss rejection 17% → 100%; retention dips to
61% because the verifier vetoed benign synonym substitutions — recovered in
#208 (SYN 6/6, near-miss
still 18/18).
Covered by tests/unit/test_l1_verify_serve_path.py (router-level, both
paths, veto + paraphrase + draft fallback + avoided-counter).
Competitive comparison bench (#190)¶
scripts/bench_compare.py answers "why daari" with the #189 corpus run three
ways on the same machine: raw Ollama, daari with caches (hermetic cold
daemon), daari with X-Daari-No-Cache. Publishes
developer/resources/benchmark-comparison.md
(linked from the benchmarks page and mkdocs nav) with per-prompt latency,
tier, implied frontier USD (gpt-4o rates priced onto recorded tokens — never
billed), and aggregates: median cache-hit speedup, total USD avoided, $0-tier
rate. Prompt IDs match #189 so rows join. Default run never calls a paid API;
--live-frontier exists but is not required to publish.
Pure logic covered by tests/unit/test_bench_compare.py; live path via
pytest -m benchmark or the script, skipping cleanly without Ollama.
Client-path live E2E pack (#191)¶
tests/integration/test_client_path_live.py exercises the surfaces an IDE
client actually sends against real Ollama: OpenAI streaming and Anthropic
/v1/messages streaming each yield more than one chunk; a vision request on
a text-only stack returns 422; POST /v1/embeddings returns a vector whose
length matches the embedder (nomic-embed-text). Sampling (max_tokens
binds, seed reproduces) stays in test_sampling_live.py. The color-conditioned
vision check skips unless a vision-capable model is pulled. Default suite
stays green (@pytest.mark.integration).
Note: stock capability defaults tag L5 as vision even when L5 is collapsed
onto a text-only 3B — the live 422 test declares the catalog explicitly.
daari vs LiteLLM (#214)¶
scripts/bench_vs_litellm.py runs the #189 routing corpus through a default
LiteLLM OpenAI-compat proxy (same Ollama, no paid providers) and a hermetic
daari serve. Publishes
developer/resources/benchmark-vs-litellm.md
joined by prompt ID. LiteLLM is not a runtime dependency: skip without it, or
--spawn into a throwaway venv. Covered by tests/unit/test_bench_vs_litellm.py.
Load harness (#215)¶
scripts/bench_load.py measures achieved RPS and p50/p95 on a hermetic
daari serve: a warmed L0 replay mix, a unique no-cache generate mix
(max_tokens capped), and a tool-bearing agent mix (G1 / #223) that
reports L0 hit rate and implied frontier input $ avoided. Publishes
developer/resources/benchmark-load.md
with commit, hardware, concurrency, RPS, p95, and errors. No vegeta/k6
dependency. Capacity guide now points at the measured page. Covered by
tests/unit/test_bench_load.py (reporter + skip).
OIDC ES256 JWKS (#151)¶
verify_oidc_token dispatches on JWK kty (RSA → RS256/384/512, EC →
ES256/384/512). Unsupported kty raises ValueError naming the type. JWKS
documents that mix use: enc and use: sig prefer the signing key. Covered
by tests/unit/test_oidc_jwks.py.
Discussions welcome GTM-6 (#233)¶
Templates under gtm/discussions/ (welcome, show-report,
setup-help) plus gh discussion create instructions. Welcome thread is
#238. README and
CONTRIBUTING link Discussions. Do not post a second welcome. Covered by
tests/unit/test_gtm_discussions.py.
OpenRouter gap roadmap (2026-08-26)¶
Stripe agreed to acquire OpenRouter. Forward plan is
prd/ROADMAP-v3.md: do not clone the 400-model marketplace;
win agent token economics (G1 prefix cache — exact L0 on identical tools +
history; L1 still off) and honor OpenRouter’s provider object on L6 (G2/G3).
OpenRouter L6 slot G3 (#225)¶
Documented id: openrouter slot (openrouter/auto, OPENROUTER_API_KEY).
Outbound calls send HTTP-Referer / X-Title. L6 meta has upstream
cost_usd vs daari_cost_usd: 0. Local :floor / :nitro aliases pick
the smallest capable or warmest local tier. Live chat test skips without
the key. Covered by tests/unit/test_openrouter_slot.py.
OpenRouter provider object G2 (#224)¶
OpenAI and Anthropic adapters parse the client provider object onto
InternalRequest. zdr: true with no frontier.providers[].zdr slot is
HTTP 400. OpenRouter L6 slots receive the object on the outbound body.
daari_meta records provider_prefs, cost_usd, and cached_tokens.
Covered by tests/unit/test_provider_object.py (mocked HTTP, no live key).
Browser extension content script (#171)¶
MV3 content script + site profiles intercept in-page chat widgets, route to
local daari with X-Daari-Boundary-Profile, and fall through when the daemon
is down or the boundary refuses. Popup shows session tier / estimated savings.
Shipped demos: ports 8765 (fintech) and 8766 (docs support). See
packages/browser-extension/README.md.
Cost-of-pass + agent $0-tier G4 (#243)¶
scripts/bench_live.py retries each routing row until the observed tier
matches (or --cost-of-pass-cap) and publishes a Cost of pass table
(attempts, ms, implied $). A separate tool-bearing corpus
(evals/routing/agent.jsonl, AG-01–AG-08) reports the share of agent
prompts that never left the device. Linked from
developer/guides/observability/live-benchmark.md.
Reporter tests live in tests/unit/test_bench_live.py (default suite).
First live run (2026-08-27, Apple M4 Pro, Ollama 0.18.2): agent $0-tier 100% of 8 tool-bearing prompts; routing 16/19; cost-of-pass missed GP-10 / GP-12 / GP-19 at cap 3. Page: developer/resources/benchmarks.md.
Availability G5 (#244)¶
A context-length error on L3 remaps to L4/L5 (then compress+retry L3) and
records context_length_failover on the request trace.
GET /v1/models (and retrieve) include capabilities for local models and
configured L6 slots (zdr when the provider is tagged). The live
max_tokens check uses usage.completion_tokens + finish_reason —
supersedes the brittle "twenty" not in text assertion that filed
#213 /
#217 /
#220. Covered by
tests/unit/test_context_length_failover.py,
tests/unit/test_models_catalog.py, tests/unit/test_max_tokens_bind.py.
Agent prefix cache G1 (#223)¶
ADR-0004 no longer skips L0 on tools / tool history. Identical agent
turns hit exact L0 (full messages + tools schema are already in the key);
changing only the last tool result misses. L1 and Lt stay off for agent
turns. Load harness agent mix publishes RPS / L0 hit rate / implied
frontier $ on
developer/resources/benchmark-load.md.
Settings validate_assignment (#152)¶
Runtime-mutated settings models (RoutingSettings, FrontierSettings,
CacheSettings + L0/L1, BoundariesSettings, ObservabilitySettings) use
validate_assignment and field constraints (ge/le, Literal modes).
setattr of an out-of-range value raises ValidationError. Config PATCH
still returns 400 via daari/config/validate.py.
Boundaries B2/B3 + embed B0 (#172)¶
B0 can cosine-score topics/examples with the L1 embedder and falls back to
lexical matching when embeddings fail. Ambiguous cases take an N-vote local
quorum (B2, quorum_votes) and an optional frontier judge (B3) that will not
spend past frontier_judge_daily_budget_usd. Enabling B3 without a frontier
warns at startup. Labeled fixtures in evals/boundaries/fixtures.jsonl gate
CI on false-refuse = 0. Covered by tests/unit/test_boundaries.py.
Routing eval harness (#173)¶
Labeled corpus is evals/routing/prompts.jsonl (plus agent.jsonl). Live
numbers live on
developer/resources/benchmarks.md.
CI gates $0-tier ≥30% and routing accuracy ≥80% via
daari.eval.routing_score (published headline + mocked GP-01–20).
ROADMAP-v2 Phase B “never measured” row is retired. Covered by
tests/unit/test_routing_score.py.
IdP-minted virtual keys (#176)¶
enterprise.sso.key_mappings maps an IdP claim (groups, department, …)
to budget, RPM, tier cap, team, and boundary profile. First verified SSO
session mints the key; later sessions resync. The key is revoked when the
mapped claim disappears. Unmapped claims use default_policy or 403
(deny_unmapped). Audit records include the claim. Documented next to
daari enterprise bootstrap in
developer/guides/configuration/auth-and-keys.md.
Covered by tests/unit/test_sso_key_mappings.py.
Multi-window budgets and teams (#174)¶
Virtual keys store budget_windows: [{duration, max_usd}] and optional team_id.
A teams table holds inherited caps; the tighter of key vs team wins per
duration. daari keys create --team / --window, daari keys team-create,
and daari report --by-team. 402s include reset_at and scope. Flat
daily_budget_usd / monthly_budget_usd rows migrate to day/month
windows. Covered by tests/unit/test_budget_windows.py,
tests/unit/test_virtual_key_budgets.py.
Lexical synonym allowlist (#208)¶
LexicalVerifier normalizes British/American spelling and a small curated
verb/adj allowlist (fix/resolve, start/launch, create/write, cause/trigger,
fast/quick) before the content-word substitution check. SYN-01–06 retain;
all 18 near-miss rows still veto. Covered by
tests/unit/test_l1_verification_corpus.py.
Stalled auto-merge watch (#200)¶
Conflicted auto-merge PRs never get CI (DIRTY / empty checks). AGENTS.md
now requires merging origin/main before open and checking
mergeStateStatus after --auto. scripts/autodev_pr_watch.py comments
on the PR and files auto-dev,regression once; autodev-cycle runs it on
the same 6h schedule (no CURSOR_API_KEY required). Covered by
tests/unit/test_autodev_pr_watch.py.
Local watchdog readiness poll (#181)¶
Issue #181 filed false positives: a single 5s /health probe marked the
daemon unreachable while the same cycle's Cursor smoke passed once serve
finished booting; a transient Ollama disconnect failed live integration tests
without a retry. scripts/autodev_local.py polls /ready (then /health) with
30s bounded backoff; autodev-local.sh kickstarts then waits, and retries
integration tests once after 3s. Covered by tests/unit/test_autodev_local.py.
daari onboard (#256)¶
daari onboard --yes is the pip/brew first-run: probe Ollama (print
https://ollama.com/download if down), pull L3 + embed when missing, run
doctor, print daari serve. daari install still runs scripts/install.sh
in a clone and falls back to onboard when that file is absent. Covered by
tests/unit/test_onboard.py. Remaining setup train: #257–#261.
Default model stack (#258)¶
scripts/install.sh pulls L3 + nomic-embed-text unless MINIMAL=1.
daari install --minimal / daari onboard --minimal skip embed. When L1 is
enabled, daari doctor fails if the embedding model is missing. Covered by
tests/test_doctor.py and tests/unit/test_install_default_stack.py.
Install CI gate (#257)¶
The install GitHub Actions job runs RUN_DOCTOR=0 ./scripts/install.sh on a
fresh checkout (no Ollama, no model pulls) and asserts .venv/bin/daari --help.
tests/unit/test_install_sh.py execs the script with a fake python3.12 /
ollama and requires the job to stay in ci.yml. Doctor required checks
remain in tests/test_doctor.py (already on the test job).
Cursor tunnel one-liner (#259)¶
daari setup cursor --tunnel --yes --daemonize starts daari serve if
/health is down, opens cloudflared, writes the public /v1 URL into
Cursor, and returns without waiting on the tunnel. Missing cloudflared
prints brew install cloudflared. scripts/tunnel.sh --setup-cursor
delegates to the same command. Covered by
tests/unit/test_cursor_oneclick.py.
User service install (#260)¶
daari service install writes a user systemd unit (~/.config/systemd/user/daari.service)
or launchd plist (~/Library/LaunchAgents/com.daari.gateway.plist). Logs go to
~/.daari/serve.log. status / uninstall operate on those files only — CI
does not start a real service. Native Windows is refused with a WSL pointer
(#261). Covered by tests/unit/test_service.py.
Windows / WSL first-run (#261)¶
Honest Windows path is WSL2. docs/developer/get-started/install-windows.md
and scripts/install.ps1 install via pip inside WSL and run
daari onboard --yes. They do not claim a native-Windows daemon.
Covered by tests/unit/test_install_windows.py.
Onboard --serve (#268)¶
daari onboard --yes --serve starts the daemon in the background after a
successful first-run (ensure_local_daemon) and prints the /v1 URL plus
GET /ready. Serve is skipped when Ollama is down. Default remains
--no-serve. Covered by tests/unit/test_onboard.py.
Stale agent:working sweep (#272)¶
A dev-cycle agent that dies mid-issue leaves agent:working behind, and the
picker skips the issue forever. scripts/autodev_pr_watch.py now sweeps the
label from open issues untouched for 24h with no open PR referencing them
(branch autodev/<n>- or #<n> in the PR title/body), leaving a marker
comment. Runs in the existing stall-watch job. Covered by
tests/unit/test_autodev_pr_watch.py.
Relicense to Apache 2.0 (#227)¶
Human decision: whole tree Apache 2.0, not the NC/fleet hybrid. Recorded in
ADR-0016. Public copy, PyPI classifier, and
Homebrew license match. v1.3.0 as tagged stays PolyForm NC.
Agent prefix L1 (#269)¶
G1b turns L1 back on for agent turns, but only over the stable prefix
(system + tools + history minus trailing tool results). The trailing tool
results are hashed into the L1 lookup context, so an identical prefix with a
different last tool result can never serve the previous answer; an identical
prefix and tool result can hit L0 or prefix-L1 (nearest_agent_prefix /
put_agent_prefix on SemanticCache). Turns that emit tool calls are
still never stored as answers. ADR-0004 amended. Covered by
tests/integration/test_agent_prefix_l1.py.
Service install --now (#270)¶
daari service install --now writes the same user unit as S5 and then enables
and starts it (systemctl --user daemon-reload + enable --now, or
launchctl load -w <plist>). daari service uninstall --now disables/unloads
before removing the file. Commands go through an injectable runner
(default_runner), so tests never touch a real service manager; a non-zero
exit raises ServiceCommandError and the CLI exits 1. Without --now
behavior is unchanged. Covered by tests/unit/test_service.py.
OpenAI-compat local backend kind (#275)¶
routing.local_pool.backends[].kind: openai binds an OpenAI-compat executor
(POST /v1/chat/completions, stream converted to Ollama events) and probes
GET /v1/models. The slot model is sent to the server; capability tags on
that name apply the same as Ollama models. Failures trip the existing circuit
breaker. Covered by tests/unit/test_openai_executor.py and
tests/unit/test_local_pool.py.
SSE keepalive heartbeat on streaming routes (#276)¶
server.sse_keepalive_seconds (default 10, 0 disables) drives a shared
stream_with_keepalive() helper that emits SSE comment frames (: keepalive)
while waiting for the first model chunk on /v1/chat/completions,
/v1/messages, /v1/responses, and the Ollama NDJSON facade (blank-line
no-op). Keepalives stop after the first upstream chunk. Covered by
tests/unit/test_streaming_keepalive.py and keepalive integration cases in
tests/integration/test_gateway_flow.py.
Backlog picker off the search index (#291)¶
scripts/autodev_backlog.py picks the next auto-dev issue (--pick) or
lists all eligible ones (--list) through the GraphQL repository
connection only — never search( — because GitHub's search index lagged the
repository on 2026-08-30 and the dev cycle silently exited on a "empty"
backlog. Pick rules match AGENTS.md: P1 > P2 > P3, oldest first, skipping
agent:working and issues with an open linked PR (shared matcher from
autodev_pr_watch.py). The autodev.yml prompt now calls the script (edit
authorized by #291), autodev-local.sh regression dedupe uses a
repository-connection query with exact-title matching, and the surviving
search-backed callsites in autodev_pr_watch.py carry audit comments
explaining why their failure modes are safe. Covered by
tests/unit/test_autodev_backlog.py.
MCP tool governance (#277)¶
daari/gateway/mcp_policy.py resolves a glob-style allow / deny tool
policy per caller: integrations.mcp_policy (global default, governs the
master key and open installs too) → integrations.mcp_team_policies[<team>]
→ the virtual key's metadata.mcp. Denies accumulate, the narrowest allow
wins, deny beats allow. POST /mcp returns JSON-RPC error -32003 for a
denied tools/call, the legacy /v1/mcp/query returns HTTP 403
(MCP_ERR_TOOL_DENIED), and tools/list on both routes is filtered to the
caller's allowed tools. Every ingress tools/call — allowed or denied —
writes an mcp.tools/call row to the enterprise audit log (key/team, tool,
decision, transport) and never the arguments. The egress client sends
Mcp-Method / Mcp-Name (MCP 2026-07-28) on outbound requests; the ingress
honours the same headers for policy when a client supplies them. Covered by
tests/unit/test_mcp_policy.py, tests/integration/test_mcp_governance.py
and an egress header case in tests/unit/test_mcp_egress.py.
Claude Desktop one-click setup recipe (#279)¶
daari setup claude-desktop [--dry-run] [--force] (daari/clients/claude_desktop/recipe.py)
writes a Claude Desktop third-party-inference configuration into the app's
saved-configuration library (~/Library/Application Support/Claude-3p/configLibrary/daari.json
on macOS; %LOCALAPPDATA% / ~/.config equivalents elsewhere) using the
documented flat keys: inferenceProvider=gateway, inferenceGatewayBaseUrl
(server root — the app appends /v1/messages), inferenceGatewayApiKey,
inferenceGatewayAuthScheme=x-api-key, and a pinned inferenceModels entry
for daari. Backup + daari setup --undo claude-desktop restore (or removal
when the recipe created the file), idempotent apply, registered in
default_registry() so daari setup all includes it and skips cleanly when
Claude Desktop is not installed. Docs: docs/developer/guides/clients/claude-desktop.md
(manual Import-configuration fallback + MDM note), cross-linked from the MCP
guide. Covered by tests/unit/test_claude_desktop_recipe.py.
Cost-split and savings response headers (#278)¶
daari/gateway/cost_headers.py puts x-daari-response-cost (USD spent; 0
for every local tier, provider usage.cost or pricing.models × tokens for
L6), x-daari-response-cost-avoided (frontier-implied USD for a $0 serve, on
the daari report chars/4 × usage.frontier_price_per_1k_tokens basis),
x-daari-tier and x-daari-cache (hit|miss|draft) on every non-streaming
/v1/chat/completions and /v1/messages response. DaariMeta.draft now
records L1 draft injection so the cache header can say draft. Streams get
tier/cache through a StreamOutcome the router fills before its first chunk
and a DeferredHeadersStreamingResponse that holds the HTTP start line until
that chunk (or the keepalive frame) is ready; cost headers are never sent on
streams. Contract documented in docs/developer/reference/headers.md.
Covered by tests/unit/test_cost_headers.py and
tests/integration/test_cost_headers.py.
Honor reasoning_effort (#297)¶
ChatCompletionRequest.reasoning_effort is declared (no longer dropped by
extra="ignore"). SamplingParams carries it to frontier via
openai_payload(), and maps it to Ollama's top-level think
(minimal → omit; low/medium/high → same) when the local model name
looks thinking-capable (gpt-oss, qwen3, thinking, deepseek-r1);
other models ignore it without error. Optional
routing.reasoning_effort_escalation (default false) lets high mark the
profile complex and floor tier selection at L4. daari_meta.reasoning_effort
and the profile trace step record the value. Covered by
tests/unit/test_reasoning_effort.py, tests/unit/test_sampling_params.py,
and cases in tests/integration/test_gateway_flow.py.
Stall classification for action_required / suppressed CI (#294)¶
scripts/autodev_pr_watch.py now inspects Actions workflow runs for the PR
head SHA when auto-merge is enabled and the check rollup is empty, and
classifies the stall as conflict (DIRTY / mergeable=CONFLICTING),
awaiting-approval (latest run conclusion=action_required),
no-ci-triggered (no runs for the SHA — typical of GITHUB_TOKEN-authored
PRs), or unknown. The filed issue/comment body states the classification
and the matching remedy (merge main; Approve and run with run URL;
close/reopen or different token). Marker <!-- autodev-pr-stall --> and
conflict-path merge-main guidance are unchanged. Covered by
tests/unit/test_autodev_pr_watch.py.
Upgrade and config migration guide (#287)¶
docs/developer/guides/operations/upgrade.md (nav: Operations) is the
operator runbook for moving a fleet N → N+1: pip / Homebrew / Docker / Helm
(helm upgrade --atomic) steps with the restart each needs, the config
compatibility policy, the per-store survival table for ~/.daari/
(diskcache/Redis L0+L1 disposable and version-agnostic; .sqlite3 ledgers,
virtual keys, audit durable with additive in-place migrations; Postgres
create-if-missing only), rollback, and fleet ordering (gateway first; the
HMAC-signed policy bundle has no schema version and laptops ignore unknown
keys, so skew is tolerated both ways). Config claims are pinned by three new
tests in tests/unit/test_settings.py: unknown top-level YAML section fails
Settings.load() (BaseSettings extra="forbid"), unknown nested key is
ignored, wrong type fails. Cross-linked from capacity-helm.md.
Secret references for provider and org keys (#288)¶
Secret-bearing config values accept secret:// URIs resolved once at daemon
startup: secret://env-file/<path>#<KEY>, secret://exec/<command> (covers
op/vault/aws CLIs), and secret://keychain/<service>/<account> (macOS
security, Linux secret-tool) — all shell-outs, no new runtime dependency.
Resolution lives in daari/security/secret_refs.py, is wired into
create_app() (fatal on failure, message names the ref and config path, never
the value), and registers resolved values so log_gateway_event() redacts
them. daari doctor gains a secret_refs check verifying every configured
ref resolves. Plain strings unchanged. Covered by
tests/unit/test_secret_refs.py and tests/test_doctor.py.
Signed ghcr images with SBOM and provenance (#295)¶
.github/workflows/docker.yml (edit authorized by #295) now signs every image
pushed on main/v* with cosign keyless (GitHub OIDC; id-token: write
scoped to the build job only, signing by digest so all tags are covered) and
attaches a Syft SBOM plus SLSA provenance via docker/build-push-action
(sbom: true, provenance: true). PR builds still build without pushing and
skip signing. Verify instructions (cosign verify with the expected identity
and issuer, SBOM/provenance inspection) live in the docker-compose operations
guide. Covered by tests/unit/test_docker_supply_chain.py.
MCP Tasks extension (#289)¶
POST /mcp advertises io.modelcontextprotocol/tasks on protocol
2026-07-28+. Clients that opt in via _meta get a taskId for eligible
long-running tools/calls (route by default, or all tools when
threshold_ms > 0); tasks/get / tasks/update / tasks/cancel poll and
cooperatively cancel. Non-opt-in callers keep blocking behavior. Task state
lives in-process with a diskcache directory (integrations.mcp_tasks.path).
Governance audits still run before task creation. Docs:
mcp.md. Covered by
tests/unit/test_mcp_tasks.py.
daari service restart + Cursor tunnel hint label fix (#323)¶
daari service restart (restart_service() in daari/setup/service.py) runs
systemctl --user restart daari.service on Linux and
launchctl kickstart -k gui/<uid>/com.daari.gateway on macOS, refuses native
Windows like its siblings, and raises ServiceNotInstalledError (CLI exit 1
with the install hint) when no unit/plist exists. The daari setup cursor
--secure post-key message used to name com.daari.serve — the dev-watchdog
plist in scripts/launchd/, not the user service — and now says
daari service restart via restart_hint(). Docs: install stay-up section,
upgrade guide restart steps, CLI reference row, AUTOMATION.md label note.
Covered by tests/unit/test_service.py (TestRestart, CLI cases) and
test_setup_cursor_secure_hint_points_at_service_restart.
Shadow evals for tier decisions (#318)¶
routing.shadow_sample_rate (default 0) samples responses served by a local
tier (not cache hits, not tool flows) and replays them in a background task at
routing.shadow_compare_tier — empty = highest configured local tier, L6
only while routing.shadow_daily_usd > 0 (in-memory daily estimate via
cost_usd, never the ledger). Answer similarity uses the L1 shadow-check
embedder/cosine path; FeedbackStore.record_tier_shadow / tier_shadow_stats
(new tier_shadow_checks table, additive) give per-category samples, agree
rate, divergence rate, compare/served tier counts. Replays are executor-only:
no cache write, no ledger row, no learned-routing outcome; served responses are
never delayed or altered. Surfaced in daari learn stats ("Tier divergence"),
daari report + /v1/daari/report (tier_divergence),
/v1/daari/learn/stats (tier_shadow), Prometheus
daari_tier_shadow_samples_total{agreed}. Docs:
routing-tiers.md. Covered by
tests/unit/test_tier_shadow.py and
test_shadow_sampled_tier_decision_records_divergence in the gateway flow suite.
MCP guardrails on tools/call arguments and results (#317)¶
integrations.mcp_guardrails (same GuardrailSettings shape as chat, off by
default) feeds a GuardrailEngine through daari/gateway/mcp_guardrails.py.
input_rules run over flattened tools/call arguments before execution;
output_rules run over every text item of the result after, including task
results before they are stored. An input trip is JSON-RPC -32003 with
data.{tool,rule,direction} on /mcp and HTTP 403 MCP_ERR_GUARDRAIL_BLOCKED
on /v1/mcp/query; an output block swaps the result for block_message
(isError: true), redact rewrites in place. The egress client checks outbound
arguments before posting and scrubs inbound results. Every trip writes an
mcp.guardrail audit row (tool, rule, direction, action, transport — never the
payload). GuardrailEngine gained check_input_text / check_output_text
so chat and MCP share one rule evaluator. Docs:
mcp.md. Covered by
tests/unit/test_mcp_guardrails.py and
tests/integration/test_mcp_guardrails.py.
Streaming usage accounting counts once (#320)¶
Status: Done (2026-09-02). Locked the "one ledger row per streamed request"
contract with tests/integration/test_streaming_usage.py across the OpenAI SSE
path, /v1/messages, the /api/chat NDJSON facade, and client cancellation,
for backends that report usage on a final chunk, as running totals, or not at
all. Defects the tests surfaced and fixed: streamed /v1/messages never wrote a
ledger row (or a metrics sample) and emitted chars/4 usage even when Ollama
reported real counts; OpenAICompatExecutor.stream dropped the usage block
(and never asked for it — now sends stream_options.include_usage); the Ollama
facade's final line hard-coded prompt_eval_count/eval_count to 0; and the
OpenAI stream's client-visible usage chunk ignored reported counts. Running
totals are handled by keeping the last report, never summing. Docs:
savings-report.md.
Budget-remaining response headers (#319)¶
daari/auth/budgets.py gained WindowStatus / budget_status /
tightest_window / reset_epoch / window_header_label
(first_exceeded_window now builds on them). The auth middleware evaluates
every effective key+team window once per request and, for virtual keys with at
least one budget, adds x-daari-budget-remaining / -limit / -window
(1d, 1mo, 7d, 12h) / -reset (epoch seconds = reset_at) / -scope
(key|team) for the window with the least USD left — on every 2xx, streams
included. The budget-exhausted 402 keeps its body and status (pinned since
174/#158) and now carries the same headers with remaining 0 plus¶
Retry-After until reset. No budget ⇒ no headers; master key unchanged.
Rendering lives in daari/gateway/budget_headers.py next to the #308 cost
headers (shared usd_string). Docs: headers reference "Budget headers",
budgets-frontier guide. Covered by tests/unit/test_budget_headers.py and the
test_budget_headers_* / test_no_budget_means_no_budget_headers cases in
the gateway flow suite.
secret://oauth resolver — client-credentials upstream auth (#321)¶
Status: Done (2026-09-02). New secret://oauth/<token-url>?client_id=…&client_secret=<secret-ref>
scheme in daari/security/secret_refs.py performs an RFC 6749
client-credentials grant (HTTP Basic by default, auth=post optional; scope,
audience, resource passthrough) and caches the access token until
expires_in minus refresh_margin (default 60s), serialising concurrent
refreshes behind one lock. The client secret must itself be an
env-file/exec/keychain ref — inline secrets are rejected. Tokens resolve to a
RefreshableSecret (str subclass carrying its ref) so the values written
into settings stay plain strings everywhere; current_secret() re-mints on
read and is wired into ProviderSlot.pick_key (every L6 attempt) and the org
cache/learning clients' bearer headers. A failed refresh raises
SecretRefError naming the endpoint (never the secret) and the frontier pool
fails over instead of sending a stale token. No new dependency (httpx already
in tree). Docs: auth-and-keys.md.
Covered by tests/unit/test_secret_refs_oauth.py.
Virtual key expiry (expires_at + SSO key_ttl) (#331)¶
Status: Done (2026-09-03). Virtual keys gain an additive expires_at column
(NULL = never; existing keys unaffected). daari keys create --expires 30d|12h|ISO
sets it; keys list shows expiry and active / expired / revoked. Auth
rejects expired keys with 401 key_expired (distinct from invalid/revoked) and
writes auth.key_expired. SSO key_policy.key_ttl mints a TTL; find_sso_key
treats an expired SSO key as absent so the next login remints. Docs:
auth-and-keys.md. Covered by
tests/unit/test_key_expiry.py.
Issue auto-labeler — intended labels applied at filing time (#330)¶
Status: Done (2026-09-03). scripts/apply_intended_labels.py parses the
**Intended labels:** first line of an issue body, intersects it with a hard
allowlist (auto-dev, P1–P3, bug, regression, documentation,
enhancement — never agent:working) and adds the missing ones via
gh api; it never creates labels and is idempotent. The explicitly authorized
.github/workflows/issue-labeler.yml runs it on issues: opened, edited with
issues: write as its only permission. Removes the last recurring human step
in the dev loop: prd-cycle refills are pickable by autodev_backlog.py the
moment they are filed. Docs: AUTOMATION.md.
Covered by tests/unit/test_apply_intended_labels.py (parser, allowlist,
idempotence, CLI, workflow shape).
Budget alert webhooks at soft thresholds (#333)¶
Status: Done (2026-09-03). alerts.budget_webhook_url + budget_thresholds
(default [0.8, 1.0]) POST a JSON payload when a request pushes a key/team
window across a ratio. Delivery is a background task; failures log
budget.alert_failed and never delay the response. In-memory dedupe per
(scope, id, window, threshold, reset_epoch) until reset (multi-replica
caveat documented). Audit budget.alert; Prometheus
daari_budget_alerts_total{scope,threshold}. Docs:
budgets-frontier.md.
Covered by tests/unit/test_budget_alerts.py.
Retention policy and prune for traces, ledger, audit, shadow, tasks (#332)¶
Status: Done (2026-09-03). observability.retention.{traces,ledger,audit,shadow,tasks}_days
defaults to 0 (keep forever). daari prune [--dry-run] and a daily
daari serve background sweep delete older rows on SQLite and Postgres
(traces + ledger). Audit prune writes a retention.prune summary row after
the delete. Sweep failures log retention.sweep_failed and never raise.
Docs: traces-stats.md,
upgrade.md. Covered by
tests/unit/test_retention.py.
Draft v1.4.0 release notes and version bump (#334)¶
Status: Done (2026-09-03). Prep-only release package: docs/RELEASE-v1.4.0.md
(Apache 2.0 + cosign/SBOM lead, human tag/release steps at top, upgrade notes
→ upgrade.md #316), pyproject.toml /
daari.__version__ / README / CHANGELOG bumped to 1.4.0, docs/RELEASING.md
verified against docker.yml cosign + SBOM + provenance. No git tag, GitHub
release, or PyPI/ghcr publish from the agent. Covered by
tests/unit/test_release_v140_prep.py.
Skip human-gated stall re-picks (#342)¶
Status: Done (2026-09-06). Stall findings comments carry
<!-- autodev-blocked: <classification> run=<id> --> (helpers in
scripts/autodev_pr_watch.py). scripts/autodev_backlog.py::pick skips
issues whose body has autodev-pr-stall when the latest blocked marker
matches the referenced PR's current fingerprint; a new CI run, cleared
block, or closed/merged PR re-eligibilizes the issue. At most one
findings comment per distinct fingerprint (post_blocked_findings_if_new).
Covered by tests/unit/test_autodev_backlog.py and
tests/unit/test_autodev_pr_watch.py.
Opt-in budget window rollover (#344)¶
Status: Done (2026-09-06). Per-window rollover: true (default off) carries
unused headroom into the next period's effective limit, capped at
rollover_cap_multiple (default 2×). Carry is persisted on
budget_window_state in the SQLite and Postgres usage ledgers. Headers, 402
bodies, and budget alert webhooks use the effective limit. Docs:
budgets-frontier.md.
Covered by tests/unit/test_budget_rollover.py.
Audit log list and JSONL export (#345)¶
Status: Done (2026-09-06). daari audit list (--limit/--actor/
--action prefix/--since/--json) and daari audit export --format jsonl
stream newest-first rows from enterprise.audit_path in seq batches. Docs:
auth-and-keys.md. Covered by
tests/unit/test_audit_cli.py.
Shipped pricing and capability tables for the September 2026 frontier lineup (#355)¶
Status: Done (2026-09-07). Default pricing.models now includes Claude
Fable 5.1 ($10/$50, cache $0.25), Opus 5, Sonnet 5, Haiku 4.5, GPT-6 Astra
($10/$50, cache $1), the GPT-5.6 family (Sol promo through 2026-11-21), and
Gemini 3.8 Flash intro rate ($0.75/$3.75, cache $0.075 through 2026-12-31).
Dated and vendor-prefixed ids (claude-fable-5-1-20260901,
anthropic.claude-fable-5-1) resolve via longest key. Known frontier
capabilities advertise tools/json/vision/long_context. GPT-6 Astra's >272K
input surcharge is documented as out of scope. Existing 2024 entries are
unchanged. Docs:
budgets-frontier.md.
Covered by tests/unit/test_token_accounting.py and
tests/unit/test_capabilities.py.
Session affinity across tool-turn continuations (#356)¶
Status: Done (2026-09-07). routing.session_affinity (default off) pins the
served tier for a session. Tool-result continuations and an unchanged user-turn
prefix replay the pin; a new human turn re-routes. Session key is
X-Daari-Session or OpenAI user, else a conversation-prefix hash, with
routing.session_affinity_ttl_seconds (default 1800). Tier cap, an open
circuit, a down model, confidence escalation, and context-length failover
override the pin (session_pin / session_pin_override). Docs:
routing-tiers.md.
Covered by tests/unit/test_session_affinity.py and
tests/integration/test_gateway_flow.py.
Stall escalation for a stuck agent loop (#357)¶
Status: Done (2026-09-07). routing.stall_escalation (default off; repeats
3, window 6) reads the request's tool history only. N identical calls (name +
normalized arguments) in the last window, or N consecutive error tool results,
bump the heuristic tier by one. X-Daari-Tier-Cap and
routing.max_tier_for_chat still win. Logged as stall_escalation with
pattern and count. Docs:
routing-tiers.md.
Covered by tests/unit/test_stall.py and
tests/integration/test_gateway_flow.py.
MCP egress follows tools/list nextCursor (#358)¶
Status: Done (2026-09-07). Egress tools/list follows nextCursor until
absent, aggregates tools in first-seen order, and de-duplicates by name.
A page cap (20) and a 15s listing timeout stop an infinite-cursor upstream.
Single-page servers are unchanged. Guardrail result checks see the aggregated
catalog. Covered by tests/unit/test_mcp_egress.py.
ChatGPT Desktop via Ollama facade generate/embed (#343)¶
Status: Done (2026-09-06). Ollama facade gains /api/generate
(stream NDJSON + non-stream, same router/ledger path as /api/chat) and
/api/embed + legacy /api/embeddings (shared embeddings_api helper with
/v1/embeddings). Unknown Ollama 0.34 fields are ignored (extra="ignore").
Recipe: chatgpt-desktop.md
(11435 vs 11434). Covered by tests/integration/test_ollama_compat.py.
PR-watch auto-drain of BEHIND auto-merge PRs (#368)¶
Status: Done (2026-09-08). scripts/autodev_pr_watch.py merges origin/main
into BEHIND auto-merge PRs (keep-both docs/TRACKING.md), skips a second
attempt after a failed push marker, and approves first-party
action_required workflow runs. autodev.yml uses AUTODEV_GH_TOKEN when
set so new PRs are not attributed to github-actions[bot] (GitHub's
2026-06-11 bot-PR approval gate has no repo-level opt-out). Covered by
tests/unit/test_autodev_pr_watch.py and tests/unit/test_autodev_backlog.py.
Pre-dispatch context-window escalation (#385)¶
Status: Done (2026-09-09). routing.context_window_escalation (default on)
compares prompt_tokens_est to per-tier routing.context_windows × buffer
0.95 and picks the cheapest higher local tier that fits before the first hop.
Unknown windows are left alone; X-Daari-Tier-Cap still wins. Trace/event
context_window_escalation. Docs:
routing-tiers.md.
Covered by tests/unit/test_context_window.py.
OpenRouter cost_tier body mapping (#388)¶
Status: Done (2026-09-09). OpenAI / Anthropic / Responses accept cost_tier
and plugins: [{id: "auto-router", cost_tier}]. Map low→L3, medium→L4,
high→L5, xhigh/max→L6 onto meta.tier_cap. X-Daari-Tier-Cap wins;
unknown values log cost_tier_ignored. Anthropic now also reads the header.
Docs: routing-tiers.md. Covered by
tests/unit/test_cost_tier.py and tests/integration/test_gateway_flow.py.
Vision hop for tool-result images (#397)¶
Status: Done (2026-09-09). Anthropic tool_result image blocks populate
Message.images (OpenAI role=tool parts already did). required_capabilities
then requires vision and the existing catalog filter hops off text-only L3.
Event modality_escalation. Docs:
routing-tiers.md. Covered by
tests/unit/test_multimodal.py and tests/integration/test_anthropic_tools.py.
Honor response_format json_schema (#398)¶
Status: Done (2026-09-09). type: json_schema is stored on SamplingParams
and passed to Ollama as format (the schema object) and to OpenAI-compat as
response_format. Requires the json capability like json_object.
Malformed schemas log json_schema_ignored and are dropped. Covered by
tests/unit/test_sampling_params.py.
Status: Done (2026-09-20). Chargeback guide documents daari spend export
--tier with asr / embed examples. Covered by
tests/unit/test_chargeback_docs.py.
Status: Done (2026-09-20). CLI help contract asserts daari cache invalidate
--help mentions --token for SSO daemon auth. Covered by
tests/unit/test_cache_invalidate_cli_help.py.
Docs: cache invalidate --token (#828)¶
Status: Done (2026-09-20). cli.md and org-cache guide document
cache invalidate --token for SSO/master Bearer. Covered by
tests/unit/test_cache_invalidate_docs.py.
Unit tests for ASR frontier_fallback findings (#834)¶
Status: Done (2026-09-20). Direct unit coverage for
asr_frontier_fallback_findings. Covered by
tests/unit/test_config_validate.py.
How to update; on conflict keep both -->¶
Budget alert fleet dedupe via Redis (#369)¶
Status: Done (2026-09-08). When cache.backend=redis, BudgetAlerter
claims each (scope, id, window, threshold, reset_epoch) crossing with
SET NX EX (TTL through window reset) before the webhook POST, so replicas
notify once. No Redis keeps the in-process _seen path. Redis errors still
deliver and log budget.alert_dedupe_degraded. Docs:
budgets-frontier.md.
Covered by tests/unit/test_budget_alerts.py.
Subtask/phase routing from tool history (#374)¶
Status: Done (2026-09-09). routing.phase_routing (default off; window 6)
classifies the last N tool-call names as explore / implement / verify and
applies a configurable tier delta (defaults explore: -1, floor L3;
implement/verify: 0). Stall escalation beats a phase downgrade; session
affinity pins the phase-adjusted served tier; X-Daari-Tier-Cap and
routing.max_tier_for_chat still cap. Trace/event phase_route. Non-agent
requests are unchanged. Docs:
routing-tiers.md.
Covered by tests/unit/test_phase_routing.py and
tests/integration/test_gateway_flow.py.
Incremental streaming output guardrails (#375)¶
Status: Done (2026-09-09). guardrails.stream_mode (buffered default |
incremental) with stream_holdback_chars (default 256). Incremental mode
scans SSE deltas through a holdback window so secrets spanning chunks never
leak, keeps L6 frontier relay eligible, records hits like the buffered path,
and caches only scanned text. Tool-call streams stay exempt. Docs:
guardrails.md. Covered by
tests/unit/test_stream_guardrails.py, tests/unit/test_stream_policy_parity.py,
and tests/integration/test_gateway_flow.py.
MCP semantic tool search (#376)¶
Status: Done (2026-09-09). integrations.mcp_tool_search (default off;
min_catalog_size 40, top_k 40) ranks aggregated egress tools/list
catalogs by local embedding similarity when over the threshold. Query is
trailing text after @mcp … tools/list, else recent user/tool message text.
Governance allow/deny runs before ranking; embed failures log
mcp_tool_search_degraded and return the unranked catalog. Tool embeddings
are cached per (server, name, description hash). Docs:
mcp.md. Covered by
tests/unit/test_mcp_egress.py.
Zero-downtime virtual key rotation (#377)¶
Status: Done (2026-09-09). daari keys rotate <key_id> [--grace 24h]
mints a new secret for the same key identity; budgets/team/policies/RPM/TPM
untouched. Old secret authenticates until grace ends (--grace 0 immediate),
then rejects as expired via the #331 path. Both secrets share one budget and
rate-limit bucket. keys.rotate audit row (no secret material). keys list
shows grace_until. Docs:
auth-and-keys.md. Covered by
tests/unit/test_key_rotate.py.
Tamper-evident audit hash chain (#378)¶
Status: Done (2026-09-09). Every new audit row stores prev_hash /
row_hash (SHA-256 over canonical seq/ts/actor/role/action/detail/prev_hash;
genesis 0×64). daari audit verify [--json] walks oldest-first; legacy
(null-hash) rows counted; exits non-zero on hash_mismatch / seq_gap.
Export JSONL includes both hash fields. Retention prune re-anchors the chain.
Docs: auth-and-keys.md.
Covered by tests/unit/test_audit_cli.py.
Streamed usage.cost on the final usage chunk (#386)¶
Status: Done (2026-09-09). The OpenAI chat.completion.chunk usage object
and Anthropic message_delta.usage include cost (USD float). Local / L0 / L1
are $0; L6 uses the same cost_usd() / provider-reported figure as
x-daari-response-cost on non-stream responses. Stream cost headers stay
absent (usage is unknown at the HTTP start line). Docs:
headers.md. Covered by
tests/unit/test_stream_usage_cost.py, tests/integration/test_streaming_usage.py,
and tests/integration/test_gateway_flow.py.
Scan chat tool-result messages (#387)¶
Status: Done (2026-09-09). Opt-in guardrails.scan_tool_results (default
off). When on, OpenAI role=tool and Anthropic-converted tool_result
contents run through input + output-style rules before the model hop: block
refuses with block_message (stream or JSON); redact rewrites the tool
message in place so execute and cache keys never see the secret.
System/user/assistant unchanged; MCP tools/call scanning unchanged. Docs:
guardrails.md. Covered by
tests/unit/test_guardrails.py and tests/unit/test_stream_policy_parity.py.
Skip re-profiling on tool-result continuations (#389)¶
Status: Done (2026-09-09). Opt-in routing.classify_user_turn (default
off). Tool-result continuations reuse the prior user-turn category/complexity
instead of re-running build_prompt_profile; prompt_tokens_est still
reflects full message size. Phase routing and stall escalation still inspect
tool history; a new user message re-profiles. Trace/event
classify_user_turn with reused: true. Docs:
routing-tiers.md.
Covered by tests/unit/test_session_affinity.py.
Streamed usage cached_tokens (#399)¶
Status: Done (2026-09-09). Final OpenAI stream usage always includes
prompt_tokens_details.cached_tokens (0 when unknown; L0/L1 equals
prompt_tokens; L6 from daari_meta.cached_tokens). Anthropic
message_delta.usage adds cache_read_input_tokens only when known and
non-zero. Docs: headers.md. Covered by
tests/unit/test_stream_usage_cost.py, tests/integration/test_streaming_usage.py,
and tests/integration/test_gateway_flow.py.
Advertise context_windows on GET /v1/models (#400)¶
Status: Done (2026-09-09). Local tier cards from openai_model_cards /
GET /v1/models include context_length from routing.context_windows
when known (omit if missing). Frontier/L6 cards are not overwritten.
Docs: routing-tiers.md.
Covered by tests/unit/test_models_catalog.py and
tests/integration/test_gateway_flow.py.
Derive length hops from context_windows only (#401)¶
Status: Done (2026-09-09). Removed the 24k-char long_context inference
from required_capabilities. Prompt length hops solely via
routing.context_windows × buffer (#385), so capability filter and
context-window escalation no longer disagree. Docs:
routing-tiers.md.
Covered by tests/unit/test_capabilities.py and
tests/unit/test_context_window.py.
Stall issues get Intended-labels + post-create label retry (#409)¶
Status: Done (2026-09-10). autodev_pr_watch._cli_create_issue prefixes
**Intended labels: \auto-dev`, `regression`**on stall issue bodies soissue-labeler(#330) applies them even when a PAT silently drops--labelat create time (#408). Also retriesgh issue edit --add-labelafter create
(failure logged, not fatal). Cleanup: mergedorigin/maininto PR #404 to
unpark #398. Covered bytests/unit/test_autodev_pr_watch.py`.
End-user spend attribution and per-user caps (#410)¶
Status: Done (2026-09-10). Usage ledger user_usage table records the
OpenAI user (or unknown) per virtual-key client_id. daari usage
--by-user / GET /v1/daari/report users array group spend. Optional
user_daily_usd_cap on a key returns the same 402 shape with scope: user
when that named user exceeds it; requests without user are never capped.
Docs: budgets-frontier.md,
savings-report.md.
Covered by tests/unit/test_user_usage.py and
tests/integration/test_gateway_flow.py.
Context-threshold pricing for gpt-6-astra (#411)¶
Status: Done (2026-09-10). ModelPrice optional
input_threshold_tokens / above_input_per_1m / above_output_per_1m;
cost_usd selects the tier from prompt token count. Default table encodes
gpt-6-astra at 272K → 2× input / 1.5× output. Cost headers and the budget
ledger share that path. Docs:
budgets-frontier.md.
Covered by tests/unit/test_threshold_pricing.py.
Redact secrets from gateway error details (#412)¶
Status: Done (2026-09-10). Client-facing HTTPException / error-envelope
text in gateway modules runs through safe_detail / routing_failure_detail
(daari/gateway/client_errors.py), which apply redact_secrets(). Upstream
httpx failures on the 503 path are summarized as host + status (no URLs).
Covered by tests/unit/test_gateway_error_redaction.py.
Harness-aware profiling ignores catalogs (#418)¶
Status: Done (2026-09-11). routing.harness_aware_profile (default on)
drops role=system text and recognized Codex/Claude Code blocks
(<environment_context>, <recommended_plugins>, <system-reminder>) from
category/complexity. prompt_tokens_est still counts the full request so
context-window escalation sees size. Event harness_profile with
stripped_chars. Docs:
routing-tiers.md.
Covered by tests/unit/test_prompt_profile.py.
Close stall issues when the PR merges (#419)¶
Status: Done (2026-09-11). scripts/autodev_pr_watch.py closes open
issues whose body contains autodev-pr-stall once the referenced PR is
MERGED or CLOSED, with a one-line comment. Open PRs are left alone.
Covered by tests/unit/test_autodev_pr_watch.py.
Honor Anthropic output_format json_schema (#420)¶
Status: Done (2026-09-11). /v1/messages output_format.type == json_schema
(with schema or json_schema.schema) is stored on SamplingParams.json_schema
and forwarded the same way as OpenAI #398. Malformed payloads log
json_schema_ignored and are dropped. Covered by
tests/unit/test_sampling_params.py.
Agent UA shortcut for classify_user_turn (#421)¶
Status: Done (2026-09-11). When routing.classify_user_turn is false
(default), User-Agents matching cursor / claude-code / claude code /
codex still reuse category/complexity on tool-result continuations via
routing.classify_user_turn_agents (default on). Set agents false to disable;
explicit classify_user_turn: true still applies to every client. Event
classify_user_turn includes source: ua vs source: config. Docs:
routing-tiers.md.
Covered by tests/unit/test_classify_user_turn.py.
Merge multiple SSO JWKS URLs (#422)¶
Status: Done (2026-09-11). enterprise.sso.jwks_urls (list) combines with
singular jwks_url; tokens validate against any configured JWKS. Unknown kid
on one document tries the next; all miss → existing 401. Docs:
auth-and-keys.md.
Covered by tests/unit/test_oidc_jwks.py.
Honor service_tier for pricing and L6 forward (#430)¶
Status: Done (2026-09-11). OpenAI/Anthropic service_tier is stored on
SamplingParams, forwarded on L6 OpenAI and Anthropic payloads, and
multiplies cost_usd (flex 0.5×, priority 2×, standard/default/auto 1×).
Unknown values log service_tier_ignored. Docs:
budgets-frontier.md.
Covered by tests/unit/test_service_tier.py.
Preserve Anthropic thinking blocks on L6 replay (#431)¶
Status: Done (2026-09-11). Inbound thinking / redacted_thinking blocks
with text, signature, or redacted data are stored on Message.thinking_blocks
and replayed on Anthropic L6 /v1/messages assistant turns. Empty blocks are
omitted (Kong parity). content_to_text and sanitize_messages_for_ollama
strip them so local tiers stay plain text. Covered by
tests/unit/test_anthropic_gateway.py.
Circuit-breaker state on /ready and stats (#432)¶
Status: Done (2026-09-11). LocalBackendPool.readiness() / snapshot()
include circuit (closed|open|half_open) per backend; /ready and
GET /v1/daari/stats expose it (backends: [] when no pool). Prometheus
backend series carry a circuit label. /health stays {status: ok}.
Covered by tests/unit/test_local_pool.py.
OpenAI Batch API local drain (#433)¶
Status: Done (2026-09-12). POST/GET /v1/batches and
POST /v1/batches/{id}/cancel — OpenAI-shaped jobs
(validating → in_progress → completed / failed / cancelled).
Inline requests (chat bodies or JSONL-line objects) drain sequentially
through the existing router; concurrent batch jobs capped at 1. Cancel
marks remaining items skipped. input_file_id accepted; file upload is a
follow-up (fails validation without inline requests). Covered by
tests/unit/test_batches.py and tests/integration/test_gateway_flow.py.
Anthropic cache_control TTL write rates (#434)¶
Status: Done (2026-09-12). System/tool cache_control.ttl (5m/1h)
is preserved on L6 Anthropic payloads; default auto-hint stays ephemeral
without TTL. ModelPrice.cache_write_1h_per_1m + cost_usd(...,
cache_write_tokens, cache_ttl) bill 1h writes distinctly; missing TTL keeps
today's input rate. Docs:
budgets-frontier.md.
Covered by tests/unit/test_cache_ttl_pricing.py.
Batch items carry creating-key governance (#441)¶
Status: Done (2026-09-12). POST /v1/batches snapshots key id, client_id,
tier cap, no_frontier, user, and boundary profile onto BatchJob.governance.
Items route with that meta (L5 cap / no_frontier never reach L6); per-key/team
and per-user budget checks fail frontier-capable items with a 402-shaped
error while siblings continue; ledger rows carry client_id/user; output
guardrails apply via the shared router path. Covered by
tests/unit/test_batches.py and tests/integration/test_gateway_flow.py.
OpenAI Files API for Batch JSONL (#442)¶
Status: Done (2026-09-12). POST/GET/DELETE /v1/files and
GET /v1/files/{id}/content — OpenAI-shaped file objects on disk under
files.path (default ~/.daari/files, files.max_bytes).
POST /v1/batches with input_file_id parses JSONL (bad lines report
errors.data[].line); completed jobs set output_file_id /
error_file_id while keeping inline results. Adds runtime dep
python-multipart for multipart upload. Covered by
tests/unit/test_files_api.py, tests/unit/test_batches.py, and
tests/integration/test_gateway_flow.py.
Durable batch jobs across restarts (#443)¶
Status: Done (2026-09-12). BatchStore persists jobs/items/results to
SQLite (batches.path, default ~/.daari/batches/jobs.sqlite3). Restart
reloads unfinished work and resumes pending items (mid-flight retried once);
past-expires_at jobs become expired with remaining items skipped.
Multi-replica caveat documented in
batches.md. Covered by
tests/unit/test_batches.py.
Batch drain yields to interactive traffic (#444)¶
Status: Done (2026-09-12). Batch worker waits while interactive HTTP
in-flight > 0 (batches.yield_to_interactive, default true;
batches.idle_poll_seconds). Emits batch.waiting_for_idle once per wait
episode; cancel still wins while waiting. Docs:
batches.md. Covered by
tests/unit/test_batches.py.
One-command client onboarding (#445)¶
Status: Done (2026-09-12). daari configure <client> writes the same
settings as daari setup (claude-code, vscode, claude-desktop, …), backs up
modified files, is idempotent / --dry-run safe, and prints a one-line verify
curl. Unknown clients list supported names and exit 1. Client docs link the
fast path. Covered by tests/unit/test_configure_cli.py.
Batch and Files reads scoped to creating key (#452)¶
Status: Done (2026-09-13). FileStore records owner_key_id (nullable for
master/no-auth) and persists it in index.json. Virtual-key auth scopes
GET/DELETE /v1/files and content downloads to the caller's files (404 for
cross-tenant / ownerless); master sees everything. GET /v1/batches and
get/cancel use BatchGovernance.key_id the same way. Batch
output_file_id / error_file_id inherit the job owner. Covered by
tests/unit/test_files_api.py, tests/unit/test_batches.py, and
tests/integration/test_gateway_flow.py.
Stored Responses scoped to creating key (#453)¶
Status: Done (2026-09-13). ResponseStore gains owner_key_id (migrates
legacy rows to master-only). Virtual-key GET /v1/responses/{id} and
previous_response_id reject cross-tenant ids with the same not-found shapes;
master/no-auth unrestricted. Background polls keep the create-time owner.
Covered by tests/unit/test_response_store.py,
tests/unit/test_responses_tenancy.py, and
tests/integration/test_gateway_flow.py.
Anthropic-native GET /v1/models (#454)¶
Status: Done (2026-09-13). GET /v1/models returns Anthropic list shape
(type/display_name/created_at, has_more/first_id/last_id) when the
request carries anthropic-version or bare x-api-key (Claude Code / Desktop
+ daari configure claude-desktop verify hint). Bearer / no Anthropic headers
keep the OpenAI card shape. Same catalog ids as openai_model_cards. Covered by
tests/unit/test_anthropic_gateway.py and
tests/integration/test_gateway_flow.py.
Forward client anthropic-beta / anthropic-version on L6 (#455)¶
Status: Done (2026-09-13). Anthropic /v1/messages captures inbound
anthropic-beta and anthropic-version onto RequestMeta. The L6 Anthropic
leg forwards beta verbatim when present and lets client anthropic-version
override the pinned 2023-06-01 default; absent beta is not injected. Local
tiers unaffected. Covered by tests/unit/test_anthropic_gateway.py and
tests/unit/test_anthropic_egress.py.
Files store retention, expires_after, and size cap (#456)¶
Status: Done (2026-09-13). POST /v1/files accepts OpenAI expires_after
(created_at + seconds); objects expose expires_at; expired files 404 and
are deleted on access or daari prune / retention sweep. files.retention_days
(default 0) sets fallback expiry including batch output/error files;
files.max_total_bytes (default 0) rejects over-cap uploads with 413 naming
cap and usage. Covered by tests/unit/test_files_api.py and
tests/unit/test_retention.py.
Redis outage resilience (#463)¶
Status: Done (2026-09-14). All Redis clients use
cache.redis_timeout_seconds (default 2s) for socket connect/command timeouts.
Rate-limit Redis errors log one rate_limit.degraded event and fall back to
per-replica SQLite (or rate_limit.fail_open); counting recovers when Redis
answers again. /ready probes Redis when cache.backend=redis (degraded-but-200).
Docs: auth-and-keys.md,
capacity-helm.md. Covered by
tests/unit/test_rate_limit.py and tests/integration/test_ready_probe.py.
Audit coverage: lifecycle, failed auth, tenancy (#464)¶
Status: Done (2026-09-14). CLI audits keys.create / keys.revoke /
teams.create / teams.update (new daari keys team-update). Invalid-key
401s record flood-safe auth.invalid_key (prefix + path). Cross-tenant
file/batch/response denials record tenancy.denied. Hash chain +
daari audit verify unchanged. Docs catalog in
auth-and-keys.md. Covered by
tests/unit/test_audit_coverage.py.
Cross-replica Batches and Files via Postgres (#465)¶
Status: Done (2026-09-14). batches.backend / files.backend default
sqlite; postgres reuses observability.postgres_url for shared jobs and
file BYTEA content. Drain uses claim + heartbeat (claim_ttl_seconds) so one
replica executes a job and crashed workers are reclaimed. Helm sets both
backends when postgres.enabled. Docs:
batches.md. Covered by
tests/unit/test_postgres_batches_files.py.
Region-pinned frontier fallback (#466)¶
Status: Done (2026-09-14). FrontierProviderConfig.region plus virtual-key /
team region_pin filter L6 slots fail-closed (RegionUnavailable). Escalation
soft-locals with region_pin_unavailable when possible; L6 responses expose
daari_meta.region and x-daari-region. CLI: --region-pin on keys/teams.
Docs: data-residency.md.
Covered by tests/unit/test_region_pin.py.
Request-count quotas per key/team window (#467)¶
Status: Done (2026-09-15). Budget windows accept max_requests alongside
USD (--window-requests 1d=5000, YAML/SSO max_requests). Exceeding returns
402 with quota: "requests" plus x-daari-quota-requests-* headers.
Counting is ledger-backed (requests − cache_hits); local tiers count, L0/L1
cache hits do not. Team inheritance matches USD (tighter wins per dimension).
daari keys list surfaces req used/cap next to USD. Docs:
budgets-frontier.md.
Covered by tests/unit/test_request_quotas.py.
Session-cumulative cost-avoided rollup (#475)¶
Status: Done (2026-09-15). x-daari-session-cost-avoided is the running
sum of per-response x-daari-response-cost-avoided for X-Daari-Session,
TTL matching session-affinity pins. Omitted without a session id. Streaming
final usage includes session_cost_avoided when usage is requested.
Covered by tests/unit/test_cost_headers.py and
tests/integration/test_cost_headers.py.
Never-empty PRD and autodev refill (#474)¶
Status: Done (2026-09-15). Empty backlog is a refill trigger, not a stop.
.github/workflows/prd-cycle.yml runs daily at 14:00 UTC. Autodev and
AGENTS.md must file 3–5 auto-dev issues (features or performance /
usability / testability / benchmarking / documentation / tooling) when
--pick is empty, then implement. docs/automations/prd-cycle.md forbids
no-delta filing. Covered by tests/unit/test_autodev_backlog.py.
Helm chart image pin + multi-replica SQLite warnings (#476)¶
Status: Done (2026-09-15). Chart appVersion / image.tag track
daari.__version__ (1.4.0). Defaults are replicaCount / HPA min 1 until
postgres.enabled; NOTES + DAARI_FLEET_REPLICAS / daari doctor
fleet_artifacts warn on multi-replica + SQLite artifacts. Docs:
capacity-helm.md,
batches.md. Covered by
tests/unit/test_helm_chart.py and tests/test_doctor.py.
Hermetic benchmark suite beyond L0 (#477)¶
Status: Done (2026-09-15). tests/benchmark/test_hermetic_paths.py adds
no-Ollama ceilings for ExactCache get, budget_status, SQLite
RateLimiter.check, and BatchStore.create (still @pytest.mark.benchmark).
Docs: benchmark-load.md,
live-benchmark.md.
Doctor flags fleet SQLite artifacts + stale Helm tag (#478)¶
Status: Done (2026-09-15). fleet_artifacts also trips on
cache.backend=redis or observability.backend=postgres with sqlite
batches/files; warning names the postgres settings to flip. Optional
helm_image_tag compares checkout chart image.tag to
daari.__version__. Docs: doctor-health.md.
Covered by tests/test_doctor.py.
Cross-replica Responses via Postgres (#481)¶
Status: Done (2026-09-15). responses.backend defaults to sqlite;
postgres reuses observability.postgres_url so store: true,
previous_response_id, and background polling work across replicas.
Owner scoping (#453) unchanged. Helm sets DAARI_RESPONSES__BACKEND with
batches/files. Docs: batches.md.
Covered by tests/unit/test_postgres_responses.py.
Fleet-shared session pins + cost-avoided via Redis (#482)¶
Status: Done (2026-09-15). When cache.backend: redis, session affinity
pins and the session cost-avoided accumulator use Redis (TTL =
session_affinity_ttl_seconds); Redis errors fail open to in-process and
log session_affinity.degraded. Docs: routing-tiers.md,
headers.md. Covered by
tests/unit/test_session_affinity_redis.py.
Fleet-shared audit log via Postgres (#483)¶
Status: Done (2026-09-15). enterprise.audit_backend: sqlite|postgres
(default sqlite); postgres reuses observability.postgres_url with one
serialized hash chain (daari audit verify is fleet-global). Helm sets
DAARI_ENTERPRISE__AUDIT_BACKEND. Docs:
auth-and-keys.md. Covered by
tests/unit/test_postgres_audit.py.
Signed budget alert webhooks (#484)¶
Status: Done (2026-09-15). alerts.budget_webhook_secret (supports
secret://) adds Stripe-style X-Daari-Timestamp + X-Daari-Signature
(HMAC-SHA256 over timestamp + "." + exact body bytes). Unset keeps
unsigned POSTs. Docs:
budgets-frontier.md.
Covered by tests/unit/test_budget_alerts.py.
W3C trace-context propagation (#485)¶
Status: Done (2026-09-15). When OTel is on, inbound traceparent /
tracestate parent the GenAI span tree; outbound Ollama / OpenAI-compat /
MLX / frontier calls inject the same context. Disabled / missing packages
stay no-op. Docs:
otel-genai.md.
Covered by tests/unit/test_otel_genai.py.
Fleet-shared profile pins via Redis (#495)¶
Status: Done (2026-09-15). ProfilePinStore uses the same Redis path as
session pins when cache.backend: redis (TTL =
session_affinity_ttl_seconds); Redis errors fail open and log
session_affinity.degraded. Docs:
routing-tiers.md.
Covered by tests/unit/test_session_affinity_redis.py.
Doctor warns on sqlite audit and unsigned budget webhooks (#496)¶
Status: Done (2026-09-15). fleet_artifacts also flags
enterprise.audit_backend=sqlite under fleet signals; new optional
budget_webhook_secret warns when a webhook URL lacks a signing secret.
Docs/NOTES mention responses + audit. Covered by tests/test_doctor.py.
Stored Responses retention (#497)¶
Status: Done (2026-09-15). responses.retention_days (default 0 =
forever) prunes SQLite and Postgres stored Responses via daari prune /
daily sweep. Docs: batches.md.
Covered by tests/unit/test_response_store.py,
tests/unit/test_retention.py, tests/unit/test_postgres_responses.py.
Request-quota soft warning and budget alerts (#498)¶
Status: Done (2026-09-16). Request-count quotas reuse
frontier.soft_budget_ratio for a soft band before the hard 402:
x-daari-quota-requests-warning: soft and
daari_meta.warning=request_quota_warning. BudgetAlerter fires on
request-quota threshold crossings with quota: "requests" payload fields.
Docs: budgets-frontier.md,
headers.md. Covered by
tests/unit/test_request_quotas.py and tests/unit/test_budget_alerts.py.
L0 exact-cache singleflight (#499)¶
Status: Done (2026-09-16). Concurrent identical L0 cold misses share one
upstream fill via in-process singleflight; waiters get the same body; cache
writes once. Upstream errors clear the in-flight slot so retries work.
Docs: routing-tiers.md. Covered by
tests/unit/test_l0_singleflight.py.
Stream L0 singleflight (#506)¶
Status: Done (2026-09-16). Streaming cold misses join the same
in-process singleflight map as non-stream fills; waiters replay the leader
body as SSE; cache writes once. Mixed stream + route() coalesce.
Docs: routing-tiers.md. Covered by
tests/unit/test_l0_singleflight.py.
Responses input_tokens endpoint (#507)¶
Status: Done (2026-09-16). POST /v1/responses/input_tokens returns a
local input_tokens estimate (instructions + input + tools) via
estimate_tokens — same pattern as Anthropic count_tokens, no L6 hop.
Docs: clients-and-gateways.md,
http-api.md. Covered by
tests/unit/test_responses_input_tokens.py.
Prometheus TTFT histogram (#508)¶
Status: Done (2026-09-16). Stream path records time-to-first-token into
daari_ttft_ms (by tier) on /metrics. Non-stream requests omit TTFT.
Docs: metrics-prometheus.md.
Covered by tests/unit/test_prometheus.py, tests/unit/test_ttft_metrics.py.
Request-quota soft warn on Anthropic/Responses (#509)¶
Status: Done (2026-09-16). Soft request-quota band sets
daari_meta.warning=request_quota_warning on Anthropic Messages and
Responses (with X-Daari-Meta), matching OpenAI chat. Header soft warn was
already middleware-wide. Docs:
budgets-frontier.md.
Covered by tests/unit/test_request_quotas.py.
Doctor fleet cache without Redis (#510)¶
Status: Done (2026-09-16). daari doctor warns (fleet_cache) when
DAARI_FLEET_REPLICAS>1 without cache.backend=redis (L0/session/singleflight
per-pod). Single-replica stays quiet. Docs:
doctor-health.md. Covered by
tests/test_doctor.py.
L1 semantic-cache cold-miss singleflight (#517)¶
Status: Done (2026-09-16). Concurrent Ask-path L1 lookups/fills for the
same normalized embed key share one nearest + upstream via in-process
singleflight; agent turns stay on exact-L0 coalescing only. Docs:
routing-tiers.md. Covered by
tests/unit/test_l1_singleflight.py.
Virtual-key RPM soft warning (#518)¶
Status: Done (2026-09-16). RPM/TPM usage at or above
frontier.soft_budget_ratio (still under the hard cap) sets
x-daari-ratelimit-warning: soft and optional daari_meta.warning=
rate_limit_warning. Hard 429 + existing rate-limit headers unchanged.
Docs: headers.md,
auth-and-keys.md.
Covered by tests/unit/test_rate_limit.py.
Prometheus TTFT Grafana panel (#516)¶
Status: Done (2026-09-16). Grafana overview dashboard charts stream TTFT
p50/p95 by tier via daari_ttft_ms. Docs:
metrics-prometheus.md.
Covered by tests/unit/test_grafana_dashboard.py.
Report request-quota soft-band (#519)¶
Status: Done (2026-09-16). GET /v1/daari/report and daari report
expose request_quotas (used/cap/remaining/soft); keys list marks soft
windows. Docs: savings-report.md,
budgets-frontier.md.
Covered by tests/unit/test_request_quotas.py.
L0 singleflight hermetic ceiling (#520)¶
Status: Done (2026-09-16). Hermetic @pytest.mark.benchmark burst:
N concurrent identical L0 cold misses → exactly one upstream under a wall
ceiling. Docs: benchmark-load.md.
Covered by tests/benchmark/test_hermetic_paths.py.
Soft-warning Prometheus counters (#526)¶
Status: Done (2026-09-16). /metrics exposes
daari_soft_warnings_total{kind="request_quota"|"rate_limit"} when soft
headers are set; hard 402/429 do not increment. Docs:
metrics-prometheus.md.
Covered by tests/unit/test_soft_warning_metrics.py.
Report CLI JSON format (#527)¶
Status: Done (2026-09-16). daari report --format json dumps the same
payload as GET /v1/daari/report (including request_quotas / frontier).
Docs: savings-report.md.
Covered by tests/unit/test_render_markdown.py.
L1 singleflight hermetic ceiling (#528)¶
Status: Done (2026-09-16). Hermetic @pytest.mark.benchmark burst: N
concurrent same-embed-key L1 cold misses (distinct L0 keys) → exactly one
embed before fill and one upstream under a wall ceiling. Docs:
benchmark-load.md. Covered by
tests/benchmark/test_hermetic_paths.py.
TTFT-aware local tier preference (#529)¶
Status: Done (2026-09-16). Opt-in routing.ttft_aware (default off)
prefers a faster local tier with better recent stream TTFT percentile when
ttft_min_samples are present. Docs: routing-tiers.md,
config.md. Covered by
tests/unit/test_ttft_aware_routing.py.
Doctor soft_budget_ratio=0 warning (#530)¶
Status: Done (2026-09-16). daari doctor warns (soft_budget_ratio) when
frontier.soft_budget_ratio is 0 while request quotas or rate_limit RPM/TPM
are configured. Quiet when ratio > 0 or no caps. Docs:
doctor-health.md. Covered by
tests/unit/test_doctor_soft_budget_ratio.py.
Soft-warning Grafana panel (#537)¶
Status: Done (2026-09-16). Grafana overview charts
daari_soft_warnings_total by kind. Docs:
metrics-prometheus.md.
Covered by tests/unit/test_grafana_dashboard.py.
Route preview dry-run (#538)¶
Status: Done (2026-09-16). POST/GET /v1/daari/route/preview and
daari route preview return the would-be initial local tier plus
heuristic / phase / latency_budget / ttft_preference reason fields, without
calling Ollama or frontier. Docs:
routing-tiers.md. Covered by
tests/unit/test_route_preview.py.
Hard-reject Prometheus counters (#551)¶
Status: Done (2026-09-16). /metrics exposes
daari_rejects_total{kind="budget"|"request_quota"|"rate_limit"} on hard
402/429 only; soft warning headers unchanged. Grafana hard-rejects panel.
Docs: metrics-prometheus.md.
Covered by tests/unit/test_reject_metrics.py.
Harness strip parity for Claude Code / Codex (#541)¶
Status: Done (2026-09-16). strip_harness_text matches LiteLLM Codex
reminder markers (user_instructions, environments_instructions, agents.md
envelope) plus existing Claude Code blocks; fixtures assert category/complexity
and router tier stay on the user task. Docs:
routing-tiers.md.
Covered by tests/unit/test_harness_strip_parity.py.
Postgres virtual keys / teams (#544)¶
Status: Done (2026-09-16). server.virtual_keys.backend=postgres shares
keys/teams via observability.postgres_url; factory wires app/SSO/CLI;
Helm + doctor fleet warnings list the auth store. Docs:
config.md. Covered by
tests/unit/test_postgres_virtual_keys.py.
Helm graceful rollout lifecycle (#545)¶
Status: Done (2026-09-16). Chart defaults:
terminationGracePeriodSeconds=60, lifecycle.preStopSleepSeconds=5
(omit when 0), rollingUpdate maxUnavailable: 0 / maxSurge: 1. Docs:
capacity-helm.md. Covered by
tests/unit/test_helm_chart.py.
Team-level aggregate RPM/TPM (#546)¶
Status: Done (2026-09-16). Team.rpm/tpm (0 = unlimited) with SQLite
migration + Postgres schema, daari keys team-create/update --rpm/--tpm, and
gateway checks of rpm:team:{id} / tpm:team:{id} after per-key counters
(same Redis/SQLite backend). Soft-warn header + Retry-After on 429 match
key limits. daari report --by-team shows configured limits. Docs:
auth-and-keys.md,
virtual-keys.md. Covered by
tests/unit/test_team_rate_limits.py.
Keys/teams export and import (#548)¶
Status: Done (2026-09-16). daari keys export --json [--out FILE] writes a
versioned document (teams + key hashes, never plaintext). daari keys import
FILE [--dry-run] upserts by team_id/key_id and refuses unknown schema
versions. Round-trip preserves rotated-key grace. Docs:
upgrade.md.
Covered by tests/unit/test_keys_export_import.py.
Package version discovery (#552)¶
Status: Done (2026-09-16). daari --version prints daari.__version__.
GET /health returns {"status":"ok","version":"…"} (status unchanged for
probes). Docs: upgrade.md,
doctor-health.md. Covered by
tests/unit/test_version_discovery.py.
Rate-limit Redis degraded gauge (#553)¶
Status: Done (2026-09-16). /metrics exposes
daari_rate_limit_degraded{mode="sqlite_fallback"|"fail_open"} (1 while Redis
counting is degraded, 0 after recover). Docs + Grafana panel:
metrics-prometheus.md.
Covered by tests/unit/test_rate_limit.py.
Helm securityContext and PDB (#554)¶
Status: Done (2026-09-16). Chart defaults: non-root uid 1000, drop ALL
caps, readOnlyRootFilesystem with emptyDir mounts for ~/.daari and /tmp.
Optional podDisruptionBudget (off by default). Docs:
capacity-helm.md. Covered by
tests/unit/test_helm_chart.py.
SSO RBAC: analyst read / admin mutate (#555)¶
Status: Done (2026-09-17). When SSO is on, mutating admin routes
(reload-caches, config PATCH, org-learning sync) require admin_min_role;
ops reads (stats / traces / report / audit) allow analyst+. Master API key
remains admin. Docs:
auth-and-keys.md. Covered by
tests/integration/test_oidc_sso_gateway.py.
TTFT preference Prometheus counter (#539)¶
Status: Done (2026-09-17). daari_ttft_preference_total{from,to} increments
when routing.ttft_aware rewrites the heuristic local tier. Docs:
metrics-prometheus.md.
Covered by tests/unit/test_ttft_aware_routing.py.
Soft rate-limit warn hermetic burst (#540)¶
Status: Done (2026-09-17). @pytest.mark.benchmark burst: N concurrent
soft-band RPM requests (memory limiter + frozen window) → all 200 with soft
header under a wall ceiling. Docs:
benchmark-load.md. Covered by
tests/benchmark/test_hermetic_paths.py.
Ollama facade capability advertisement (#547)¶
Status: Done (2026-09-17). /api/tags and /api/show advertise
Ollama-shaped capabilities (completion, plus tools / vision /
thinking from the catalog). Docs:
intellij.md. Covered by
tests/integration/test_ollama_compat.py.
Helm ServiceMonitor for /metrics (#570)¶
Status: Done (2026-09-17). Optional serviceMonitor.enabled (default
false) renders a Prometheus Operator ServiceMonitor scraping the chart
Service /metrics on port http. Docs:
capacity-helm.md. Covered by
tests/unit/test_helm_chart.py.
Grafana TTFT preference panel (#571)¶
Status: Done (2026-09-17). Overview dashboard adds TTFT preference by
from→to on rate(daari_ttft_preference_total[5m]). Docs:
metrics-prometheus.md.
Covered by tests/unit/test_grafana_dashboard.py.
SSO analyst GET config (#572)¶
Status: Done (2026-09-17). When SSO is on, GET /v1/daari/config allows
analyst+; PATCH still requires admin_min_role. Docs:
auth-and-keys.md. Covered by
tests/integration/test_oidc_sso_gateway.py.
ChatGPT Desktop capabilities note (#573)¶
Status: Done (2026-09-17). Recipe notes /api/tags and /api/show
advertise completion / tools / vision / thinking from the catalog.
Docs: chatgpt-desktop.md.
Hermetic TTFT preference burst (#574)¶
Status: Done (2026-09-17). @pytest.mark.benchmark burst: N concurrent
_choose_initial_tier preference rewrites (seeded TTFT) → all faster tier,
counter ≥ N, wall ceiling. Docs:
benchmark-load.md. Covered by
tests/benchmark/test_hermetic_paths.py.
Helm orgPool Deployment env (#581)¶
Status: Done (2026-09-17). orgPool.enabled wires
DAARI_ROUTING__ORG_POOL__ENABLED + BASE_URL into the Deployment. Docs:
capacity-helm.md. Covered by
tests/unit/test_helm_chart.py.
ServiceMonitor bearer auth for /metrics (#582)¶
Status: Done (2026-09-17). Optional serviceMonitor.bearerTokenSecret
(name + key) renders Prometheus Operator authorization Bearer
credentials on the ServiceMonitor endpoint so scrapes work when the master
API key protects /metrics. Docs:
capacity-helm.md. Covered by
tests/unit/test_helm_chart.py.
Grafana backend pool and concurrency panels (#583)¶
Status: Done (2026-09-17). Overview dashboard adds Backend pool health
& outstanding (daari_backend_up / _outstanding / request rate) and
Concurrency gate (daari_rate_limit_in_flight vs _max / _queued).
Docs: metrics-prometheus.md.
Covered by tests/unit/test_grafana_dashboard.py.
Doctor Redis PING and GET /ready (#584)¶
Status: Done (2026-09-17). Optional redis check PINGs cache.redis_url
when cache.backend=redis; optional ready check hits GET /ready when the
daemon is up and warns on degraded / not_ready. Docs:
doctor-health.md. Covered by
tests/unit/test_doctor_redis_ready.py.
Hermetic hard-reject burst ceiling (#585)¶
Status: Done (2026-09-17). @pytest.mark.benchmark burst: N concurrent
chat requests after RPM exhausted → all 429, daari_rejects_total ≥ N, wall
ceiling. Docs: benchmark-load.md.
Covered by tests/benchmark/test_hermetic_paths.py.
Stats + web-ui soft_warnings / rejects (#593)¶
Status: Done (2026-09-17). GET /v1/daari/stats returns soft_warnings
and rejects maps (empty {} when zero); web-ui tables render both by kind.
Docs: metrics-prometheus.md,
web-ui README. Covered by
tests/unit/test_stats_cliff_counters.py and web-ui DOM tests.
Optional Prometheus metrics scrape port (#594)¶
Status: Done (2026-09-17). observability.metrics_port (0 = off) binds a
scrape-only 127.0.0.1 listener with the same /metrics body and no API key;
main-port auth unchanged. Docs: config.md,
metrics-prometheus.md.
Covered by tests/unit/test_metrics_port.py.
Anthropic SSE L0 exact-cache parity (#600)¶
Status: Done (2026-09-17). stream_anthropic_events performs the same
exact L0 lookup (and org-L0 when enabled) as the OpenAI stream path before
the model tier chain; hits emit daari_meta.tier=L0 / L0-org, record
cache-hit metrics / usage ledger, and skip upstream. Successful Anthropic
streams write back exact L0. Agent turns keep exact-key semantics; no new
L1. ADR-0004 negative note updated. Covered by
tests/unit/test_anthropic_stream_l0.py.
Grafana panels for retries / false-hits / budget alerts (#601)¶
Status: Done (2026-09-17). Bundled Grafana dashboard adds rate panels for
daari_upstream_retries_total, daari_cache_false_hits_avoided_total, and
daari_budget_alerts_total. Docs note in
metrics-prometheus.md.
Covered by tests/unit/test_grafana_dashboard.py.
Helm Service port for metrics_port scrapes (#602)¶
Status: Done (2026-09-17). Chart observability.metricsPort (0 = off)
wires DAARI_OBSERVABILITY__METRICS_PORT, containerPort, and Service port
metrics; ServiceMonitor scrapes metrics (no bearer) when set, else API
http (+ optional bearer). Docs:
capacity-helm.md. Covered by
tests/unit/test_helm_chart.py.
Prometheus counters for MCP tool ingress (#603)¶
Status: Done (2026-09-17). daari_mcp_tool_calls_total{tool,outcome} for
MCP ingress (ok / deny / error / guardrail); no-op when
observability.prometheus=false. Docs:
metrics-prometheus.md.
Covered by tests/unit/test_mcp_prometheus.py.
Expose agent_turn on daari_meta (#604)¶
Status: Done (2026-09-17). DaariMeta.agent_turn is set true on agent
turns (tools / tool history); omitted for plain chat via exclude_none.
ADR-0004 checklist item checked. Covered by
tests/unit/test_agent_turn_meta.py.
Hermetic hard 402 budget/quota burst (#592)¶
Status: Done (2026-09-17). @pytest.mark.benchmark bursts: N concurrent
chat requests after USD budget or max_requests exhausted → all 402,
daari_rejects_total{kind="budget"|"request_quota"} ≥ N, wall ceiling,
upstream must not run. Docs:
benchmark-load.md. Covered by
tests/benchmark/test_hermetic_paths.py.
Helm NOTES ServiceMonitor bearer and orgPool (#595)¶
Status: Done (2026-09-17). Post-install NOTES.txt echoes ServiceMonitor
/metrics scrape + Bearer/bearerTokenSecret when enabled, and org pool env
(including baseUrl) when orgPool.enabled. Docs:
capacity-helm.md. Covered by
tests/unit/test_helm_chart.py.
Time-boxed autodev dev-cycle loop (#611)¶
Status: Done (2026-09-17). autodev-cycle exports
AUTODEV_DEADLINE_EPOCH (job start + 50 min) and raises timeout-minutes
to 60; the agent loops pick → PR → auto-merge and stops picking with under
10 min left, exiting 0 instead of being killed mid-wait. Documented in
docs/automations/dev-cycle.md and docs/AUTOMATION.md. Contract test
test_workflow_time_boxes_loop_before_job_timeout.
Doctor metrics_auth when API key protects /metrics (#596)¶
Status: Done (2026-09-17). Optional metrics_auth check: daemon up +
prometheus on + server.api_key set and unauthenticated GET /metrics → 401
warns with Bearer / serviceMonitor.bearerTokenSecret guidance; quiet when
prometheus off, no api_key, or scrape already open. Docs:
doctor-health.md. Covered by
tests/unit/test_doctor_metrics_auth.py.
Team budget remaining Prometheus gauges (#616)¶
Status: Done (2026-09-17). GET /metrics exposes
daari_team_budget_remaining_usd / _limit_usd / _remaining_hours per team
USD window (scrape-time snapshot via list_teams + ledger). Docs:
metrics-prometheus.md.
Covered by tests/unit/test_prometheus.py.
Team rate-limit scope headers and gauges (#617)¶
Status: Done (2026-09-17). X-RateLimit-Scope: key|team|model on
success and 429 when a counter is active; /metrics exposes
daari_team_rate_limit_remaining / _limit for teams with rpm/tpm.
Docs: headers.md,
auth-and-keys.md,
metrics-prometheus.md.
Covered by tests/unit/test_team_rate_limits.py and
tests/unit/test_prometheus.py.
RFC 7662 POST /introspect (#618)¶
Status: Done (2026-09-17). Authenticated POST /introspect (JSON or form
token) returns RFC 7662 active payloads for master/virtual keys without
leaking secrets; inactive tokens → {"active": false} / 200; no caller auth →
401. Docs: mcp.md,
auth-and-keys.md. Covered by
tests/unit/test_introspect.py.
Grafana MCP tool ingress panels (#619)¶
Status: Done (2026-09-17). Overview dashboard adds MCP tool ingress by
outcome (sum by (outcome) (rate(daari_mcp_tool_calls_total[5m]))). Docs:
metrics-prometheus.md.
Covered by tests/unit/test_grafana_dashboard.py.
Hermetic soft request-quota warn burst (#620)¶
Status: Done (2026-09-17). Hermetic @pytest.mark.benchmark guard: N
concurrent soft-band request-quota requests (VK ledger prefilled to
soft_budget_ratio) all return 200 with x-daari-quota-requests-warning:
soft, daari_soft_warnings_total{kind="request_quota"} ≥ N under a wall
ceiling, and no hard 402. Docs:
benchmark-load.md. Covered by
tests/benchmark/test_hermetic_paths.py.
Soft USD budget warn header + soft_warnings{kind=budget} (#626)¶
Status: Done (2026-09-17). Soft-band USD windows emit
x-daari-budget-warning: soft and daari_meta.warning=budget_warning;
daari_soft_warnings_total{kind="budget"} increments on 2xx (hard 402 does
not). Hermetic concurrent soft-band burst under a wall ceiling. Docs:
headers.md,
metrics-prometheus.md,
benchmark-load.md. Covered by
tests/unit/test_soft_warning_metrics.py and
tests/benchmark/test_hermetic_paths.py.
Grafana team budget and rate-limit remaining panels (#627)¶
Status: Done (2026-09-17). Overview dashboard adds Team budget remaining
(USD) (daari_team_budget_remaining_usd / _limit_usd) and Team
rate-limit remaining (daari_team_rate_limit_remaining / _limit). Docs:
metrics-prometheus.md.
Covered by tests/unit/test_grafana_dashboard.py.
Stats tier p50/p95 latency (#628)¶
Status: Done (2026-09-17). GET /v1/daari/stats tier objects include
p50_ms / p95_ms from latency histogram buckets (same helper as TTFT).
Web-ui tier table already rendered these fields; DOM test pins non-dash
values. Docs: web-ui README. Covered by
tests/unit/test_stats_cliff_counters.py and web-ui DOM tests.
Web-ui local pool backends (#629)¶
Status: Done (2026-09-17). Dashboard Local pool backends table renders
stats.backends (id, healthy, circuit, outstanding) with an empty-state
row when the pool is empty. Docs: web-ui README.
Covered by web-ui DOM tests.
OTel daari.agent_turn on GenAI root spans (#630)¶
Status: Done (2026-09-17). export_trace() stamps boolean
daari.agent_turn on the GenAI root span when daari_meta.agent_turn is
set (true/false); omitted when unset. Docs:
otel-genai.md. Covered by
tests/unit/test_otel_genai.py.
Grafana soft USD budget warnings panel (#636)¶
Status: Done (2026-09-17). Overview dashboard adds Soft USD budget
warnings (rate(daari_soft_warnings_total{kind="budget"}[5m])). Docs:
metrics-prometheus.md.
Covered by tests/unit/test_grafana_dashboard.py.
Doctor soft_budget_ratio covers USD windows (#637)¶
Status: Done (2026-09-17). daari doctor warns when
frontier.soft_budget_ratio=0 while any VK/team USD budget window is set
(same advisory as request-quota/RPM). Docs:
doctor-health.md. Covered by
tests/unit/test_doctor_soft_budget_ratio.py.
Document USD soft budget warning in budgets guide (#638)¶
Status: Done (2026-09-17). budgets-frontier.md documents soft USD band →
x-daari-budget-warning: soft, daari_meta.warning=budget_warning, and
daari_soft_warnings_total{kind="budget"}, parallel to request-quota soft
band. Docs: budgets-frontier.md.
Stats backend_summary counts (#639)¶
Status: Done (2026-09-17). GET /v1/daari/stats adds backend_summary
(total / healthy / unhealthy / open_circuit) derived from
backends. Docs: traces-stats.md,
web-ui README. Covered by
tests/unit/test_local_pool.py.
Assert backend_summary on empty stats cliff maps (#647)¶
Status: Done (2026-09-18). test_stats_includes_empty_cliff_maps asserts
backend_summary == {total:0, healthy:0, unhealthy:0, open_circuit:0} so the
shared stats contract cannot drop the field on non-pool installs.
Note backend_summary on architecture stats surface (#648)¶
Status: Done (2026-09-18). docs/ARCHITECTURE.md API table notes
soft_warnings / rejects and backend_summary on GET /v1/daari/stats.
Prune shipped ENTERPRISE.md gap rows after soft-budget drain (#649)¶
Status: Done (2026-09-18). docs/prd/ENTERPRISE.md drops shipped night-of-17
rows (Anthropic L0 → hermetic 402) plus soft-budget follow-ons; changelog
bullet; watch rows only. Docs-only.
Note agent_turn on daari_meta in architecture (#653)¶
Status: Done (2026-09-18). docs/ARCHITECTURE.md package map notes
DaariMeta.agent_turn (ADR-0004) on agent tool-history turns.
Web-ui backend_summary counts (#654)¶
Status: Done (2026-09-18). Dashboard renders backend_summary (total /
healthy / unhealthy / open_circuit) above the local pool backends table.
Covered by packages/web-ui/test/dashboard.test.js.
Expand package-map observability for stats cliff maps (#656)¶
Status: Done (2026-09-18). package-map.md observability/ row notes
stats soft_warnings / rejects and backend_summary (link to traces-stats).
Note stats tier p50/p95 in traces-stats (#655)¶
Status: Done (2026-09-18). traces-stats.md documents optional
tiers.*.p50_ms / p95_ms on GET /v1/daari/stats.
Note daari stats JSON includes backend_summary (#664)¶
Status: Done (2026-09-18). daari stats CLI docstring notes the payload
includes soft_warnings / rejects and backend_summary.
Document POST /introspect in architecture API table (#657)¶
Status: Done (2026-09-18). docs/ARCHITECTURE.md HTTP API table lists
POST /introspect (RFC 7662 virtual-key introspection).
Mention backend_summary in metrics-prometheus.md (#663)¶
Status: Done (2026-09-18). metrics-prometheus.md notes
backend_summary on GET /v1/daari/stats (link to traces-stats).
Assert POST /introspect in architecture API table (#665)¶
Status: Done (2026-09-18). tests/unit/test_architecture_introspect.py
asserts docs/ARCHITECTURE.md lists POST /introspect (RFC 7662).
Note agent_turn in traces-stats meta section (#666)¶
Status: Done (2026-09-18). traces-stats.md daari_meta list includes
agent_turn.
Cross-link backend_summary from savings-report (#673)¶
Status: Done (2026-09-18). savings-report.md links to traces-stats
backend_summary for local pool pressure beside spend.
Emphasize open_circuit count on web-ui when > 0 (#667)¶
Status: Done (2026-09-18). Dashboard open-circuit metric uses metric-warn
when backend_summary.open_circuit > 0. Covered by
packages/web-ui/test/dashboard.test.js.
Note soft_warnings/rejects on architecture metrics row (#674)¶
Status: Done (2026-09-18). docs/ARCHITECTURE.md metrics.py package row
mentions soft_warnings / rejects and backend_summary on stats.
Assert daari stats CLI help mentions backend_summary (#675)¶
Status: Done (2026-09-18). daari stats --help contract test asserts
backend_summary appears in Typer help. Covered by
tests/unit/test_stats_cli_help.py.
Repair metrics-prometheus series table after prose insert (#679)¶
Status: Done (2026-09-18). Moved mid-table soft_warnings / backend_summary
prose below the contiguous Series table in metrics-prometheus.md. Covered by
tests/unit/test_metrics_prometheus_docs.py.
Sync CLI reference with Typer catalog (#681)¶
Status: Done (2026-09-18). cli.md lists all top-level Typer commands;
learn stale names removed; route preview documented. Covered by
tests/unit/test_cli_reference_catalog.py.
Regenerate http-api.md to include POST /introspect (#680)¶
Status: Done (2026-09-18). Committed http-api.md includes
| POST | /introspect |; test_gen_reference asserts generator + on-disk.
Assert daari doctor --help mentions --suggest-models (#683)¶
Status: Done (2026-09-18). daari doctor --help contract test asserts
--suggest-models appears in Typer help. Covered by
tests/unit/test_doctor_cli_help.py.
Note soft_warnings/rejects cliff maps in traces-stats (#682)¶
Status: Done (2026-09-18). traces-stats.md documents soft_warnings /
rejects on GET /v1/daari/stats with a metrics-prometheus cross-link.
Covered by tests/unit/test_traces_stats_docs.py.
Grafana tier-shadow samples panel (#689)¶
Status: Done (2026-09-18). Overview dashboard adds Tier shadow
agree/disagree on daari_tier_shadow_samples_total; metrics-prometheus
intro mentions the panel. Covered by tests/unit/test_grafana_dashboard.py.
Contract-test boundary_decisions_total Prometheus export (#691)¶
Status: Done (2026-09-18). test_boundary_decisions_counter asserts
daari_boundary_decisions_total{stage,label} in render_prometheus(). Covered
by tests/unit/test_prometheus.py.
Grafana boundary decisions panel (#690)¶
Status: Done (2026-09-18). Overview dashboard adds Boundary decisions
by stage on daari_boundary_decisions_total. Covered by
tests/unit/test_grafana_dashboard.py.
Cross-link tier_shadow series in metrics-prometheus (#693)¶
Status: Done (2026-09-18). Series table row for
daari_tier_shadow_samples_total{agreed} with routing-tiers link; contract
test asserts presence. Covered by tests/unit/test_metrics_prometheus_docs.py.
Assert prune --help documents retention stores (#692)¶
Status: Done (2026-09-18). daari prune --help contract test asserts
traces, ledger, audit, shadow, and tasks appear in Typer help. Covered by
tests/unit/test_prune_cli_help.py.
Fix route preview stale --json in cli.md (#699)¶
Assert report --help documents format and breakdown flags (#701)¶
Status: Done (2026-09-18). daari report --help contract test asserts
format, by-client, by-team, and by-user. Covered by
tests/unit/test_report_cli_help.py.
Assert context clear --help documents L0/L1/CCS (#700)¶
Status: Done (2026-09-18). daari context clear --help contract test
asserts l0, l1, and ccs appear in Typer help. Covered by
tests/unit/test_context_clear_cli_help.py.
Fix route preview stale --json in cli.md (#699)¶
Status: Done (2026-09-18). cli.md documents --model /
--latency-budget-ms for route preview (not --json). Covered by
tests/unit/test_cli_reference_catalog.py.
Per-key and per-team model allowlists (#708)¶
Status: Done (2026-09-18). VirtualKey and Team take optional
allowed_models globs plus named model_groups from settings. Team and key
lists intersect. Denied gateway calls (chat, responses, embeddings, Anthropic
messages, Ollama facade) return 403 model_not_allowed and an
auth.model_denied audit row. Frontier fallback skips models outside the
list. Unset keeps the previous unrestricted behavior. Covered by
tests/unit/test_model_allowlists.py.
Config validate and strict nested keys (#710)¶
Status: Done (2026-09-18). daari config validate reports unknown keys
(top-level and nested), type errors, and out-of-range values. Settings.load
warns on unknown nested keys via daari.config and fails when
DAARI_STRICT_CONFIG=1 or daari serve --strict. daari doctor mentions
them on config_keys. Covered by tests/unit/test_config_validate.py.
Per-request spend rows and chargeback export (#709)¶
Status: Done (2026-09-18). Opt-in usage.spend writes one row per completed
request (SQLite by default, Postgres when observability.backend is postgres).
daari spend export --since --format csv|jsonl streams rows; spend_days
prunes them. Off by default, the day ledger is unchanged. Covered by
tests/unit/test_spend_export.py.
Master key overlap rotation (#711)¶
Status: Done (2026-09-18). server.api_key accepts a string or a list.
Every entry is checked with a constant-time compare. secret:// refs resolve
per entry. An auth.master_key_overlap audit row stores the count, never the
secrets. daari doctor warns when more than two keys are active. Covered by
tests/unit/test_master_key_overlap.py.
Responses cancel and delete (#713)¶
Status: Done (2026-09-18). POST /v1/responses/{id}/cancel is idempotent
(in-flight background jobs stay cancelled; terminal objects return 200).
DELETE /v1/responses/{id} removes the row so later GET is 404. Tenancy
matches GET (cross-key 404 + audit, master can act on any row). SQLite and
Postgres stores both implement delete. Covered by
tests/unit/test_responses_tenancy.py, tests/unit/test_response_store.py,
and tests/unit/test_postgres_responses.py.
Per-provider and per-tier retry/timeout (#712)¶
Status: Done (2026-09-18). Each frontier.providers entry accepts optional
timeout_s, retry_attempts, and retry_backoff_s; unset fields inherit
upstream.frontier_timeout_seconds / upstream.retry. models.timeout_s
overrides the local timeout per L3/L4/L5. Failover spends one hop's budget
then advances; one timeout does not shrink the next slot. Route preview
returns policy and chain. Covered by
tests/unit/test_provider_retry_timeout.py.
Local-first audio transcriptions (#715)¶
Status: Done (2026-09-18). POST /v1/audio/transcriptions forwards the
OpenAI multipart form to asr.base_url when set. With no local ASR the route
returns 501 unless asr.frontier_fallback is true, frontier is enabled, and a
key is present. Auth, request quotas, and rate limits apply; a successful
transcription counts as one request. Covered by
tests/unit/test_audio_transcriptions.py.
Optional KEDA request-rate autoscaling (#716)¶
Status: Done (2026-09-18). autoscaling.keda.enabled (default false) renders
a KEDA ScaledObject on sum(rate(daari_requests_total[1m])) with a configurable
threshold. CPU HPA is unchanged. minReplicaCount above 1 is refused while
postgres.enabled is false. Covered by tests/unit/test_helm_chart.py.
Daily request caps beside rpm (#717)¶
Status: Done (2026-09-19). VirtualKey.rpd and Team.rpd (0 = unlimited) use the
existing rate-limit backends on a 86400-second UTC day window, separate from rpm.
Key and team caps both apply; the tighter remaining counter wins. daari keys
create/update --rpd and team-create/update --rpd persist it, and export/import
round-trips it. A deny is 429 naming rpd. Covered by
tests/unit/test_daily_request_caps.py.
Cross-link boundary decisions in the boundaries guide (#702)¶
Status: Done (2026-09-19). The boundaries guide Observe subsection links
daari_boundary_decisions_total and metrics-prometheus.md. Covered by
tests/unit/test_boundaries_docs.py.
Document escalations and errors series (#703)¶
Status: Done (2026-09-19). metrics-prometheus intro names the Grafana
Escalations & errors panel, and the series table rows
daari_escalations_total with companion daari_errors_total. Covered by
tests/unit/test_metrics_prometheus_docs.py.
Surface rpd on introspect and keys list (#730)¶
Status: Done (2026-09-19). POST /introspect includes rpd when the key's
day cap is set, and daari keys list prints an rpd column. Zero stays
omitted, matching rpm. Covered by tests/unit/test_introspect.py and
tests/unit/test_virtual_keys.py.
Scrape team rpd remaining (#731)¶
Status: Done (2026-09-19). team_rate_gauges emits kind="rpd" on the
86400-second window for teams with a day cap. Prometheus renders it next to
rpm/tpm. Covered by tests/unit/test_rate_limit.py and
tests/unit/test_prometheus.py.
Doctor warns when rpm has no daily cap (#732)¶
Status: Done (2026-09-19). daari doctor warns when a key or team has
rpm > 0 and rpd == 0, naming both rpd and the key or team. A set day
cap, or no rpm, stays quiet. Covered by
tests/unit/test_doctor_soft_budget_ratio.py.
Document rpd on keys guides (#733)¶
Status: Done (2026-09-19). Virtual-keys examples include --rpd (0 =
unlimited) and the config reference notes the day cap is per key/team, not a
rate_limit field, with a link to the auth guide. Covered by
tests/unit/test_rpd_docs.py.
Transcription model allowlists (#739)¶
Status: Done (2026-09-19). POST /v1/audio/transcriptions returns 403 model_not_allowed when the resolved model (asr.model, else the form model) is outside the key or team allowlist, and does not call the ASR upstream. Unset allowlists stay unrestricted.
Daily-cap Retry-After (#738)¶
Status: Done (2026-09-19). An rpd 429 sets Retry-After to the seconds until the UTC day boundary. rpm/tpm still use rate_limit.retry_after_seconds.
Team rpd on stats and dashboard (#741)¶
Status: Done (2026-09-19). GET /v1/daari/stats includes team_rate_limits (team, kind, limit, remaining), including rpd. The web dashboard renders one row per entry. Covered by tests/unit/test_stats_team_rate_limits.py and packages/web-ui/test/dashboard.test.js.
Doctor ASR probe (#740)¶
Status: Done (2026-09-19). daari doctor warns when asr.base_url is unreachable and when asr.frontier_fallback is set without frontier or a resolvable key. Unconfigured ASR stays quiet. Covered by tests/unit/test_doctor_asr.py.
Per-key rpd scrape (#742)¶
Status: Done (2026-09-19). Prometheus emits daari_key_rate_limit_remaining / _limit for keys with rpd > 0, labeled by key name. A scrape does not consume the cap. Covered by tests/unit/test_prometheus.py.
Per-key rpd on stats and dashboard (#749)¶
Status: Done (2026-09-19). GET /v1/daari/stats includes key_rate_limits for keys with rpd > 0. A stats read does not consume the cap. The dashboard renders one row per entry. Covered by tests/unit/test_stats_key_rate_limits.py.
Per-key rpd Grafana panel (#750)¶
Status: Done (2026-09-19). Overview Grafana charts daari_key_rate_limit_remaining vs _limit under Key rate-limit remaining, legend by key name and kind. Covered by tests/unit/test_grafana_dashboard.py.
Transcription chargeback rows (#751)¶
Status: Done (2026-09-19). A successful transcription writes a spend row with tier asr or L6 and the caller's key and team, so chargeback export can tell local ASR from frontier fallback. Allowlist 403 and unconfigured 501 do not write a row. Covered by tests/unit/test_audio_transcriptions.py.
Chargeback guide names transcription tiers (#756)¶
Status: Done (2026-09-19). The chargeback guide states that a successful transcription exports as asr or L6, and that allowlist 403 / unconfigured 501 write no row. Covered by tests/unit/test_chargeback_docs.py.
Embedding chargeback rows (#755)¶
Status: Done (2026-09-19). POST /v1/embeddings writes a spend row with tier embed and the caller's key and team. An unknown model returns 400 and writes no row. Covered by tests/unit/test_embeddings.py.
Tenant cache scope (#768)¶
Status: Done (2026-09-19). Virtual keys and teams take an optional cache_scope (global default, team, key). Team and key scopes fold team_id or key_id into L0, Redis L0, and L1; global keeps today's cache hashes. Covered by tests/unit/test_cache_scope.py.
Embed and ASR latency (#762)¶
Status: Done (2026-09-19). Embedding cache misses and local or frontier transcriptions record a positive latency_ms on the embed, asr, and L6 histograms. Covered by tests/unit/test_embeddings.py and tests/unit/test_audio_transcriptions.py.
Helm Ollama base URL (#763)¶
Status: Done (2026-09-19). ollama.baseUrl sets DAARI_OLLAMA__BASE_URL for the embedder and L1. Empty leaves the image default. Distinct from orgPool. Covered by tests/unit/test_helm_chart.py.
Doctor embed probe (#764)¶
Status: Done (2026-09-19). When L1 is on, daari doctor posts one embed request and fails the check if the endpoint errors or returns no vector. L1 off skips the probe. Covered by tests/unit/test_doctor_embeddings.py.
Audio upload TPM (#765)¶
Status: Done (2026-09-19). Multipart POST /v1/audio/transcriptions charges TPM from len(file_bytes) // 4 instead of 1. JSON chat and embeddings still use the character estimate. A TPM denial is still 429 with Retry-After. Covered by tests/unit/test_rate_limit.py.
Client disconnect cancels upstream (#769)¶
Status: Done (2026-09-19). Non-streaming chat, Anthropic, and Responses calls cancel the router task when the client disconnects, so the executor sees CancelledError. Closing a stream stops the upstream generator and records daari_cancelled_requests_total{phase} plus a request_cancelled gateway event. Cancelled requests do not add a second ledger row. Covered by tests/unit/test_client_disconnect.py.
Selective cache invalidation (#770)¶
Status: Done (2026-09-19). ExactCache and RedisExactCache delete by served model or entry hash (Redis DELETE, not a no-op). L1 filters on context_key / answer_hash. POST /v1/daari/cache/invalidate and daari cache invalidate return removed counts and log cache_invalidate. Redis daari cache prune says expiry is TTL and does not scan. Covered by tests/unit/test_cache_invalidate.py.
Request log retention (#772)¶
Status: Done (2026-09-19). observability.retention.request_log_days (default 0) folds cursor-requests.log into prune_all. Rotated backups that are entirely old are deleted; the active file keeps newer lines. daari prune prints a request_log line. Covered by tests/unit/test_retention.py.
Request deadline across hops (#771)¶
Status: Done (2026-09-19). Optional X-Daari-Deadline-Ms (wins) and upstream.request_deadline_seconds bound the escalation chain. Each upstream call uses min(tier timeout, remaining); a spent budget returns 504 naming the deadline, records daari_request_deadline_exceeded_total, and does not call frontier. Streaming applies the budget to time-to-first-token only. Covered by tests/unit/test_request_deadline.py.
Helm local ASR base URL (#757)¶
Status: Done (2026-09-19). asr.baseUrl (empty by default) sets DAARI_ASR__BASE_URL on the Deployment when non-empty, so a cluster can point transcriptions at an on-box whisper/vLLM server. Covered by tests/unit/test_helm_chart.py.
Audio translations (#758)¶
Status: Done (2026-09-19). POST /v1/audio/translations forwards multipart audio to {asr.base_url}/audio/translations and returns JSON text. Unconfigured ASR is 501 with no upload; response_format other than json is 400. Covered by tests/unit/test_audio_translations.py.
Batch Ollama embeddings (#766)¶
Status: Done (2026-09-20). Cache-miss list inputs go out as one POST /api/embed with input string[]; L0 hits stay off the wire. Single-string embed is a batch of one. A 404 on /api/embed falls back to per-string /api/embeddings. Covered by tests/unit/test_embed_batch.py.
Helm request deadline and log retention (#786)¶
Status: Done (2026-09-20). upstream.requestDeadlineSeconds and observability.retention.requestLogDays set DAARI_UPSTREAM__REQUEST_DEADLINE_SECONDS and DAARI_OBSERVABILITY__RETENTION__REQUEST_LOG_DAYS when non-empty. Defaults omit both. Covered by tests/unit/test_helm_chart.py.
Cancel embed and ASR on disconnect (#787)¶
Status: Done (2026-09-20). Embeddings and audio transcription/translation wrap upstream work in await_unless_disconnected and return 499 with daari_cancelled_requests_total{phase=embed|asr|translation}. Covered by tests/unit/test_client_disconnect.py.
Grafana cancelled and deadline panels (#788)¶
Status: Done (2026-09-20). Overview dashboard charts daari_cancelled_requests_total by phase and daari_request_deadline_exceeded_total; metrics docs list the deadline series. Covered by tests/unit/test_grafana_dashboard.py.
Cache invalidate by team or key scope (#790)¶
Status: Done (2026-09-20). L0 put persists optional scope (team:<id> /
key:<id>); invalidate(team_id=…) / invalidate(key_id=…) drop matching L0
rows and L1 rows whose context_key contains that segment. CLI
--team / --key and admin team_id / key_id wired. Covered by
tests/unit/test_cache_invalidate.py.
Ollama /api/show thinking controls (#789)¶
Status: Done (2026-09-20). When facade capabilities include thinking,
POST /api/show also returns thinking: {values, default} with levels
low/medium/high (aligned with ollama_think; no max). Plain models
omit the object; /api/tags stays capability-string-only. Covered by
tests/unit/test_ollama_show_thinking.py.
Cancel Ollama facade non-stream on disconnect (#797)¶
Status: Done (2026-09-20). Non-stream /api/chat and /api/generate wrap
router.route in await_unless_disconnected (phase chat) and return 499 on
disconnect. Streaming NDJSON unchanged. Covered by
tests/unit/test_client_disconnect.py.
Cancel MCP tools/call on disconnect (#798)¶
Status: Done (2026-09-20). Router-backed MCP tools/call and legacy
/v1/mcp/query wrap work in await_unless_disconnected (phase mcp) and
return 499 on disconnect. Background MCP tasks stay unwrapped. Covered by
tests/unit/test_client_disconnect.py.
Audio translation TPM uses upload bytes (#796)¶
Status: Done (2026-09-20). Multipart POST /v1/audio/translations charges
TPM from len(file_bytes) // 4 like transcriptions; chat/embeddings stay on
the character estimate. Covered by tests/unit/test_rate_limit.py.
Document cache invalidate --team/--key and refresh http-api (#800)¶
Status: Done (2026-09-20). org-cache.md documents --team / --key and
admin team_id / key_id. Regenerated http-api.md lists
POST /v1/audio/translations and POST /v1/daari/cache/invalidate. Covered by
tests/unit/test_gen_reference.py.
Document request deadline header and config (#799)¶
Status: Done (2026-09-20). headers.md documents X-Daari-Deadline-Ms
(504 / request_deadline_exceeded; stream = TTFT-only). config.md lists
upstream.request_deadline_seconds. Covered by tests/unit/test_deadline_docs.py.
Distinct chargeback tier for audio translations (#807)¶
Status: Done (2026-09-20). Local /v1/audio/translations spend/ledger
rows use tier translation (frontier stays L6); transcriptions stay asr.
Chargeback guide updated. Covered by tests/unit/test_audio_translations.py.
Redis L0 age prune when TTL is off (#806)¶
Status: Done (2026-09-20). When cache.l0.ttl_seconds == 0, Redis L0
prune deletes keys whose stored t is older than 7 days; when TTL is on,
prune stays no-scan. CLI reports the count. Covered by
tests/unit/test_redis_cache.py.
Hermetic happy-path chat leaves cancel counter at zero (#808)¶
Status: Done (2026-09-20). Non-stream /v1/chat/completions with a fast
executor returns 200 and does not increment daari_cancelled_requests_total
for phase chat. Covered by tests/unit/test_client_disconnect.py.
Request deadline on audio and embeddings (#814)¶
Status: Done (2026-09-20). X-Daari-Deadline-Ms (and
upstream.request_deadline_seconds) binds on /v1/audio/transcriptions,
/v1/audio/translations, and /v1/embeddings; upstream httpx timeouts use
nonstream_timeout / remaining budget. Exhaustion returns 504
request_deadline_exceeded and increments daari_request_deadline_exceeded_total.
Covered by tests/unit/test_request_deadline.py.
Request deadline on Ollama facade (#815)¶
Status: Done (2026-09-20). /api/chat and /api/generate accept
X-Daari-Deadline-Ms into RequestMeta.deadline_ms so the router binds the
same wall-clock budget as OpenAI chat. Already-spent budgets return 504 before
NDJSON streaming starts. Covered by tests/unit/test_request_deadline.py.
Daemon cache invalidate Bearer under SSO (#816)¶
Status: Done (2026-09-20). daari cache invalidate sends
Authorization: Bearer (master key or --token) when SSO admin gate is
active; missing credentials yield a clear "SSO token required" error.
Covered by tests/unit/test_cache_invalidate.py.
Config validate ASR frontier_fallback (#817)¶
Status: Done (2026-09-20). daari config validate reports when
asr.frontier_fallback is true but frontier is disabled or no API key
resolves (empty asr.base_url — fallback-only path). Covered by
tests/unit/test_config_validate.py.
Request deadline on MCP tools/call (#827)¶
Status: Done (2026-09-20). MCP tools/call (JSON-RPC /mcp and legacy
query) binds X-Daari-Deadline-Ms before tool execution; exhaustion returns
504 request_deadline_exceeded. Covered by tests/unit/test_mcp_server.py.
Docs: deadline header on audio and embeddings (#824)¶
Status: Done (2026-09-20). docs/developer/reference/headers.md lists
audio transcriptions/translations and embeddings among
X-Daari-Deadline-Ms surfaces. Covered by tests/unit/test_deadline_docs.py.
Docs: spend export --tier filter (#825)¶
Status: Done (2026-09-20). Chargeback guide documents daari spend export
--tier with asr / embed examples. Covered by
tests/unit/test_chargeback_docs.py.
Spend export --tier filter (#818)¶
Status: Done (2026-09-20). daari spend export --tier filters chargeback
rows by the ledger tier column (combinable with --key / --team). Covered
by tests/unit/test_spend_export.py.
Client guides document /api/show thinking controls (#809)¶
Status: Done (2026-09-20). ChatGPT Desktop and IntelliJ guides note that
/api/show returns thinking controls (low/medium/high, default
medium) when the capability is advertised. Covered by
tests/unit/test_thinking_controls_docs.py.
Cache invalidate --help documents --token (#826)¶
Status: Done (2026-09-20). CLI help contract asserts daari cache invalidate
--help mentions --token for SSO daemon auth. Covered by
tests/unit/test_cache_invalidate_cli_help.py.
Docs: cache invalidate --token (#828)¶
Status: Done (2026-09-20). cli.md and org-cache guide document
cache invalidate --token for SSO/master Bearer. Covered by
tests/unit/test_cache_invalidate_docs.py.
Unit tests for ASR frontier_fallback findings (#834)¶
Status: Done (2026-09-20). Direct unit coverage for
asr_frontier_fallback_findings. Covered by
tests/unit/test_config_validate.py.
Batch RPM/TPM/RPD + tenant meta on drain (#840)¶
Status: Done (2026-09-20). Batch drain charges virtual-key RPM/TPM/RPD
(and team caps), copies key_id / team_id / cache_scope onto item
RequestMeta, and records structured 429 / 403 item failures for rate
limit and model allowlist denials. Master / no-auth batches stay open.
Covered by tests/unit/test_batches.py.
Helm master key, rate limits, frontier (#842)¶
Status: Done (2026-09-20). Chart values server.apiKeySecret,
rateLimit.*, and frontier.* wire DAARI_SERVER__API_KEY,
DAARI_RATE_LIMIT__*, and frontier enable/secret env; NOTES and
capacity-helm document multi-replica combos. Covered by
tests/unit/test_helm_chart.py.
L1 namespace includes embedding model (#845)¶
Status: Done (2026-09-20). L1 context_key folds in cache.l1.embedding_model
so switching embedders yields clean misses; trim prefers dropping stale-model
rows. upgrade.md updated. Covered by tests/unit/test_semantic_cache.py.
MCP virtual-key governance on tools/call (#839)¶
Status: Done (2026-09-20). MCP tools/call (route + provider execute)
applies apply_auth_claims_to_meta so virtual keys carry tier_cap,
cache_scope, key_id, and team_id; disallowed models return the same
403 model_not_allowed shape and audit rows as chat; spend and L0 cache
scope follow the key. Covered by tests/unit/test_mcp_vk_governance.py.
Embedding L0 honors cache_scope (#841)¶
Status: Done (2026-09-20). POST /v1/embeddings and Ollama /api/embed
apply virtual-key cache_scope / key_id / team_id to embed L0 keys so
scoped tenants do not share vectors; daari cache invalidate --key/--team
removes those entries. Covered by tests/unit/test_embeddings.py.
CLI models warm preload (#843)¶
Status: Done (2026-09-20). daari models warm loads configured L3–L5 and
the embed model via Ollama generate/embeddings; onboard --warm runs it after
pull; doctor optional warm_models hints when the daemon is up but /api/ps
is cold. Covered by tests/unit/test_models_warm.py.
Opt-in frontier failover when local pool is down (#846)¶
Status: Done (2026-09-20). routing.local_pool.frontier_fallback (default
false) escalates chat to L6 when every local backend for the chosen tier is
unavailable, respecting no_frontier, allowlists, budgets, and PII scrub.
Event local_pool_frontier_fallback. Covered by
tests/unit/test_local_pool.py.
Local-first POST /v1/audio/speech TTS (#847)¶
Status: Done (2026-09-20). tts.base_url proxies OpenAI-shaped
POST /v1/audio/speech to a local TTS server; unconfigured returns 501
tts_unavailable. Virtual-key allowlists, RPM, deadlines, and disconnect
cancel apply; spend ledger uses tier tts. Covered by
tests/unit/test_speech.py.
Per-key priority classes on the admission gate (#848)¶
Status: Done (2026-09-20). Virtual keys/teams accept priority
(high|normal|low); the global in-flight gate admits by priority then
FIFO. Batch drain acquires at low. Covered by
tests/unit/test_rate_limit.py.
Export gateway events as OTLP logs (#849)¶
Status: Done (2026-09-20). Opt-in observability.otlp_logs emits each
log_gateway_event as an OTel LogRecord (event name + attributes) on the
same OTLP endpoint as traces/metrics, correlated with the active span when
present; fail-open. Covered by tests/unit/test_otel_logs.py.
Docs: MCP tools/call honors X-Daari-Deadline-Ms (#835)¶
Status: Done (2026-09-20). MCP guide documents tools/call +
X-Daari-Deadline-Ms / 504 request_deadline_exceeded; headers contract
asserts the same. Covered by tests/unit/test_deadline_docs.py.
CLI reference mentions spend export --tier (#836)¶
Status: Done (2026-09-20). cli.md lists spend export with --tier and
links the chargeback guide. Covered by tests/unit/test_chargeback_docs.py.
Grafana deadline panel covers audio/embed/MCP (#837)¶
Status: Done (2026-09-20). Overview panel description and metrics docs note
deadlines are not chat-only (audio, embeddings, MCP tools/call); panel still
scrapes daari_request_deadline_exceeded_total. Covered by
tests/unit/test_grafana_dashboard.py.
Skip resolved stall issues in autodev --pick (#857)¶
Status: Done (2026-09-20). Stall tickets whose referenced PR is no longer
open (merged/closed) are skipped by autodev_backlog.py --pick so they cannot
block the backlog after conflict repair. Covered by
tests/unit/test_autodev_backlog.py.
CLI --priority for admission QoS (#868)¶
Status: Done (2026-09-21). daari keys create / team-create accept
--priority high|normal|low; list shows prio; Postgres VK schema stores
priority on keys and teams. Covered by tests/unit/test_keys_priority_cli.py.
Doctor warns when request deadline unset (#867)¶
Status: Done (2026-09-21). Optional doctor check request_deadline fails when
upstream.request_deadline_seconds is unset or <= 0, passes with a positive
value. Documented in doctor-health. Covered by
tests/unit/test_doctor_deadline.py.
Helm exposes tts.baseUrl (#870)¶
Status: Done (2026-09-21). Chart tts.baseUrl mounts DAARI_TTS__BASE_URL
when set; empty default omits the env. Documented in capacity-helm. Covered by
tests/unit/test_helm_chart.py.
Doctor probes TTS reachability (#869)¶
Status: Done (2026-09-21). Optional doctor check tts probes
{tts.base_url}/models when configured; unreachable fails optionally; empty
config stays quiet (501 path). Documented in doctor-health. Covered by
tests/unit/test_doctor_tts.py.
Docs: virtual-keys --priority (#877)¶
Status: Done (2026-09-21). Virtual-keys guide documents admission
--priority high|normal|low and the list prio column. Covered by
priority docs contract tests.
Docs: speech under X-Daari-Deadline-Ms (#871)¶
Status: Done (2026-09-21). headers.md lists POST /v1/audio/speech among
deadline-honoring routes; test_deadline_docs asserts it. Covered by
tests/unit/test_deadline_docs.py.
Doctor surfaces local_pool frontier failover (#879)¶
Status: Done (2026-09-21). Optional doctor check calls
local_pool_frontier_fallback_findings and fails when failover is on without
frontier.enabled. Documented in doctor-health. Covered by
tests/unit/test_doctor_local_pool_failover.py.
Doctor warns otlp_logs without OTEL endpoint (#878)¶
Status: Done (2026-09-21). Optional doctor check otlp_logs fails when
observability.otlp_logs is true without OTEL_EXPORTER_OTLP_ENDPOINT.
Documented in doctor-health. Covered by tests/unit/test_doctor_otlp_logs.py.
Docs: otlp_logs in otel-genai guide (#881)¶
Status: Done (2026-09-21). Contract test asserts otel-genai documents
otlp_logs and OTEL_EXPORTER_OTLP_ENDPOINT beside traces/metrics. Covered by
tests/unit/test_otel_docs.py.
Docs: capacity-helm documents tts.baseUrl (#880)¶
Status: Done (2026-09-21). Contract test asserts capacity-helm mentions
tts.baseUrl and DAARI_TTS__BASE_URL. Covered by
tests/unit/test_capacity_helm_docs.py.
Helm observability.otlpLogs + OTEL endpoint (#888)¶
Status: Done (2026-09-21). Chart observability.otlpLogs /
otlpEndpoint mount DAARI_OBSERVABILITY__OTLP_LOGS and
OTEL_EXPORTER_OTLP_ENDPOINT; defaults omit both. Documented in
capacity-helm. Covered by tests/unit/test_helm_chart.py.
Helm localPool.frontierFallback (#887)¶
Status: Done (2026-09-21). Chart localPool.frontierFallback mounts
DAARI_ROUTING__LOCAL_POOL__FRONTIER_FALLBACK when true; default omits env.
Documented in capacity-helm. Covered by tests/unit/test_helm_chart.py.
Docs: local text-to-speech backend guide (#890)¶
Status: Done (2026-09-21). New backends/tts.md; overview and
clients-and-gateways link it for /v1/audio/speech. Covered by
tests/unit/test_tts_docs.py.
TTS disconnect cancelled_requests phase=tts (#889)¶
Status: Done (2026-09-21). Unit coverage for speech disconnect 499 +
daari_cancelled_requests_total{phase="tts"}; metrics-prometheus lists tts.
Covered by tests/unit/test_client_disconnect.py and
tests/unit/test_metrics_prometheus_docs.py.
Helm tts.model and tts.voice (#900)¶
Status: Done (2026-09-21). Chart tts.model / tts.voice mount
DAARI_TTS__MODEL / DAARI_TTS__VOICE when set; empty defaults omit env.
Also wires tts.baseUrl so capacity-helm matches. Covered by
tests/unit/test_helm_chart.py.
Doctor scoped_cache_fleet check (#891)¶
Status: Done (2026-09-21). Optional doctor check fails when multi-replica
fleets use disk cache with non-global virtual-key/team cache_scope. Documented
in doctor-health. Covered by tests/unit/test_doctor_scoped_cache_fleet.py.
Docs: capacity-helm observability.otlpLogs (#898)¶
Status: Done (2026-09-21). Contract test asserts capacity-helm mentions
otlpLogs, DAARI_OBSERVABILITY__OTLP_LOGS, and
OTEL_EXPORTER_OTLP_ENDPOINT. Covered by
tests/unit/test_capacity_helm_docs.py.
Docs: capacity-helm localPool.frontierFallback (#897)¶
Status: Done (2026-09-21). Contract test asserts capacity-helm mentions
localPool.frontierFallback and
DAARI_ROUTING__LOCAL_POOL__FRONTIER_FALLBACK. Covered by
tests/unit/test_capacity_helm_docs.py.
Docs: scoped_cache_fleet in org-cache guide (#899)¶
Status: Done (2026-09-21). Org-cache guide documents Redis requirement for
multi-replica cache_scope and links doctor scoped_cache_fleet. Covered by
tests/unit/test_scoped_cache_fleet_docs.py.
Helm tts.baseUrl render contract (#906)¶
Status: Done (2026-09-21). Helm unit tests assert empty default omits
DAARI_TTS__BASE_URL and set tts.baseUrl renders it. Covered by
tests/unit/test_helm_chart.py.
Docs: doctor-health scoped_cache_fleet row (#908)¶
Status: Done (2026-09-21). Contract test asserts the doctor-health
scoped_cache_fleet row names cache.backend / Redis and cache_scope.
Covered by tests/unit/test_scoped_cache_fleet_docs.py.
Docs: mkdocs nav lists TTS guide (#905)¶
Status: Done (2026-09-21). Contract test asserts mkdocs.yml lists
developer/guides/backends/tts.md beside the ASR guide. Covered by
tests/unit/test_tts_docs.py.
Docs: TTS guide names Helm tts.model/voice (#907)¶
Status: Done (2026-09-21). backends/tts.md names Helm tts.model /
tts.voice and DAARI_TTS__MODEL / DAARI_TTS__VOICE. Covered by
tests/unit/test_tts_docs.py.
Helm asr.frontierFallback (#914)¶
Status: Done (2026-09-21). Chart asr.frontierFallback mounts
DAARI_ASR__FRONTIER_FALLBACK=true when enabled; default false omits env.
Documented in capacity-helm. Covered by tests/unit/test_helm_chart.py.
Helm asr.model (#913)¶
Status: Done (2026-09-21). Chart asr.model mounts DAARI_ASR__MODEL
when set; empty default omits env. Documented in capacity-helm. Covered by
tests/unit/test_helm_chart.py.
Docs: ASR guide names Helm asr.baseUrl (#916)¶
Status: Done (2026-09-21). backends/asr.md names Helm asr.baseUrl and
DAARI_ASR__BASE_URL. Covered by tests/unit/test_asr_docs.py.
Docs: cancelled_requests phase includes mcp (#915)¶
Status: Done (2026-09-21). metrics-prometheus lists mcp among
daari_cancelled_requests_total phases. Covered by
tests/unit/test_metrics_prometheus_docs.py.
Docs: doctor-health ASR frontier_fallback row (#917)¶
Status: Done (2026-09-21). Contract test asserts the doctor-health asr
row names asr.base_url and frontier_fallback. Covered by
tests/unit/test_doctor_health_asr_docs.py.
Docs: capacity-helm asr.model contract (#926)¶
Status: Done (2026-09-21). Contract test asserts capacity-helm mentions
asr.model and DAARI_ASR__MODEL. Covered by
tests/unit/test_capacity_helm_docs.py.
Docs: ASR guide names Helm asr.frontierFallback (#925)¶
Status: Done (2026-09-21). backends/asr.md names Helm
asr.frontierFallback and DAARI_ASR__FRONTIER_FALLBACK. Covered by
tests/unit/test_asr_docs.py.
Docs: ASR guide names Helm asr.model (#924)¶
Status: Done (2026-09-21). backends/asr.md names Helm asr.model and
DAARI_ASR__MODEL. Covered by tests/unit/test_asr_docs.py.
Request body size cap with early 413 (#933)¶
Status: Done (2026-09-21). server.max_body_bytes (default 10 MiB) rejects
oversized bodies with 413 before buffering; OpenAI/Anthropic error shapes;
upload routes use a higher floor; daari_rejects_total{kind="body_too_large"}.
Covered by tests/unit/test_body_limit.py and
tests/integration/test_gateway_flow.py.
Native TLS and optional mTLS for daari serve (#932)¶
Status: Done (2026-09-21). server.tls + --tls-cert/--tls-key/--tls-client-ca
wire uvicorn HTTPS/mTLS; secret:// key refs materialize to temp PEM files;
doctor warns on auth + non-loopback without TLS; Helm tls.enabled +
existingSecret; SECURITY.md documents native vs reverse-proxy TLS.
Covered by tests/unit/test_server_tls.py, test_doctor_tls.py,
test_helm_chart.py.
Auth throttle for invalid API keys (#935)¶
Status: Done (2026-09-21). Per-IP invalid-key counter (auth.max_failures /
window_seconds) returns 429 + Retry-After; Redis when cache.backend=redis,
in-process otherwise, fail-open; loopback exempt; auth.throttled audit +
daari_rejects_total{kind="auth_throttled"}. Covered by
tests/unit/test_auth_throttle.py.
Frontier tool parity — streamed tool-calls, tool_choice, output_format (#934)¶
Status: Done (2026-09-21). Frontier SSE relays delta.tool_calls / Anthropic
tool blocks; tool_choice and output_format/json_schema reach L6 payloads;
OpenAI + Anthropic gateways preserve escalated tool turns. Covered by
tests/unit/test_frontier_tool_parity.py and
tests/integration/test_gateway_flow.py.
CORS allowlist and default security headers (#938)¶
Status: Done (2026-09-22). server.cors_origins enablelist + OPTIONS 204;
baseline X-Content-Type-Options / X-Frame-Options / Referrer-Policy
(toggle security_headers); Helm corsOrigins / securityHeaders.enabled;
SECURITY.md + web-ui README. Covered by tests/unit/test_cors_security_headers.py.
Lifetime spend caps and temporary budget boosts (#936)¶
Status: Done (2026-09-22). BudgetWindow accepts lifetime/total (all-time
ledger sum, no rollover); daari keys budget-boost / team-budget-boost grant
audited auto-expiring increases; daari keys show lists active boosts; expiry
audited at enforcement. Covered by tests/unit/test_lifetime_budget_boosts.py.
Passthrough parallel_tool_calls, logit_bias, top_logprobs (#940)¶
Status: Done (2026-09-22). Declared on ChatCompletionRequest + SamplingParams;
forwarded in openai_payload() / cache fingerprint; local unsupported_locally
notes. Covered by tests/unit/test_sampling_params.py.
Skip rate-limit body buffer on safe HTTP methods (#939)¶
Status: Done (2026-09-22). GET/HEAD/OPTIONS skip request.body() (TPM=0);
POST/PUT/PATCH/DELETE unchanged. Covered by tests/unit/test_rate_limit.py.
Capacity-helm documents asr.frontierFallback (#927)¶
Status: Done (2026-09-22). Contract test locks asr.frontierFallback and
DAARI_ASR__FRONTIER_FALLBACK in capacity-helm.md. Covered by
tests/unit/test_capacity_helm_docs.py.
ASR guide links capacity-helm (#928)¶
Status: Done (2026-09-22). backends/asr.md Next links capacity-helm; contract
test locks the path. Covered by tests/unit/test_asr_docs.py.
MCP tool and task stats on /v1/daari/stats and web-ui (#941)¶
Status: Done (2026-09-22). Stats payload includes mcp_tool_calls +
mcp_tasks snapshot; web-ui panel; McpTaskStore.snapshot(). Covered by
tests/unit/test_stats_mcp.py and packages/web-ui/test/dashboard.test.js.
daari migrate dry-run and policy/store schema skew (#942)¶
Status: Done (2026-09-22). daari migrate / --dry-run inspects or opens
ledger, virtual-keys, audit, responses, and mcp-tasks via existing _migrate
paths; policy bundles gain integer schema (POLICY_SCHEMA=1) with unknown
majors refused; daari doctor warns on pending migrate (--strict required).
No Alembic. Docs: upgrade guide. Covered by
tests/unit/test_migrate_schema.py.
Doctor probes integrations.mcp_servers (#963)¶
Status: Done (2026-09-22). Optional mcp:<id> doctor rows POST a lightweight
JSON-RPC initialize to each configured egress URL (Bearer when token set);
empty list adds no rows. Covered by tests/unit/test_doctor_mcp_servers.py.
Reuse httpx.AsyncClient for MCP egress (#964)¶
Status: Done (2026-09-22). McpEgressProvider lazily reuses one
httpx.AsyncClient per instance (aclose() for teardown). Covered by
tests/unit/test_mcp_egress.py.
Honor and echo X-Request-ID (#965)¶
Status: Done (2026-09-22). Chat completions resolve sanitized inbound
X-Request-ID (or generate one), echo it on the response, and prefer it for
spend ledger request_id. Covered by tests/unit/test_request_id.py.
Docs: capacity-helm documents tts.model and tts.voice (#962)¶
Status: Done (2026-09-22). Doc contract asserts capacity-helm.md names
tts.model / tts.voice and DAARI_TTS__MODEL / DAARI_TTS__VOICE. Covered by
tests/unit/test_capacity_helm_docs.py.
Drain stuck PRs first; watcher survives label-permission errors (#983)¶
Status: Done (2026-09-22). apply_sweep logs and skips label edits the
PAT cannot perform instead of aborting stall-watch; the dev-cycle prompt
drains open autodev/* PRs (update-branch → wait → gh pr merge --squash;
resolve DIRTY conflicts) in priority order before picking new work. Covered
by test_apply_sweep_survives_label_edit_failure and
test_workflow_drains_stuck_prs_before_picking.
Reuse pooled httpx.AsyncClient across upstream hops (#971)¶
Status: Done (2026-09-22). Ollama, OpenAI-compat, MLX, frontier, embedder,
TTS, and ASR reuse a long-lived httpx.AsyncClient with configurable
upstream.pool_max_connections / upstream.pool_keepalive_connections;
per-request timeouts unchanged. Covered by tests/unit/test_http_pool.py.
Forward X-Request-ID on all upstream hops (#977)¶
Status: Done (2026-09-22). Middleware binds the resolved id; inject_trace_headers
forwards X-Request-ID on Ollama, OpenAI-compat, MLX, frontier, ASR, TTS,
embeddings, and MCP egress. Covered by tests/unit/test_request_id.py.
Honor Idempotency-Key on chat and Responses (#714)¶
Status: Done (2026-09-22). Idempotency-Key on POST /v1/chat/completions
and POST /v1/responses replays the original status/body for the same
principal + body hash within TTL (default 24h) without a second router call;
mismatched body returns 409 idempotency_conflict; in-flight duplicates wait.
SQLite default / postgres when configured; daari prune sweeps expired rows.
Covered by tests/unit/test_idempotency.py.
SSE keepalive for the entire stream lifetime (#972)¶
Status: Done (2026-09-22). stream_with_keepalive emits heartbeats between
chunks for the full stream (not only TTFT); optional
server.stream_idle_timeout_seconds ends hung streams with an in-band error.
Covered by tests/unit/test_streaming_keepalive.py.
In-band stream_incomplete + breaker on truncated streams (#973)¶
Status: Done (2026-09-23). OpenAI and Anthropic streams that die after
emitting deltas append a stream_incomplete in-band error before stream end,
log stream_incomplete via log_gateway_event, and trip the serving host's
circuit breaker; partial streams stay out of L0/L1. Covered by
tests/unit/test_stream_incomplete.py.
Pre-first-token host failover for streamed local requests (#974)¶
Status: Done (2026-09-23). Streaming local requests that fail before the
first chunk retry the next healthy host in the same tier (OpenAI and Anthropic
paths); post-first-token behavior unchanged; breakers record failure/success.
Covered by tests/unit/test_stream_host_failover.py.
Pooled Postgres connections for fleet stores (#975)¶
Status: Done (2026-09-23). Spend/batches/files/responses share a per-DSN
psycopg_pool.ConnectionPool (observability.postgres_pool_min/max, default
1/4); pools close on app shutdown. Covered by tests/unit/test_pg_pool.py.
Echo X-Request-ID on Anthropic, Responses, Ollama, embeddings, audio (#978)¶
Status: Done (2026-09-23). Middleware echoes X-Request-ID on every
response; Anthropic/Responses/Ollama bind the id on RequestMeta;
embeddings/ASR/TTS spend rows carry the same request_id. Covered by
tests/unit/test_request_id.py.
MCP tools/list ttlMs and cacheScope hints (#979)¶
Status: Done (2026-09-23). tools/list (JSON-RPC + legacy) emits _meta
with configurable ttlMs (integrations.mcp_list_cache.ttl_ms, default
60000) and cacheScope (public unfiltered / private when ACL-filtered).
Covered by tests/unit/test_mcp_server.py.
RetryPolicy on ASR, TTS, and embedding HTTP (#980)¶
Status: Done (2026-09-23). Transcriptions, speech, and Ollama embed HTTP
go through run_upstream with RetryPolicy.from_settings(upstream.retry);
transient 5xx/connect retry, non-retryable 4xx fail fast. Covered by
tests/unit/test_modality_retry.py.
OpenAI input_audio content blocks (#981)¶
Status: Done (2026-09-23). extract_audio keeps input_audio parts on
Message.audio; L6 OpenAI payloads rebuild them; when asr.base_url is set,
inline clips are transcribed into the local-tier prompt. Docs:
asr.md. Covered by
tests/unit/test_input_audio.py.
Docs: server.cors_origins and security_headers (#966)¶
Status: Done (2026-09-23). docs/developer/reference/config.md documents
server.cors_origins and server.security_headers. Covered by
tests/unit/test_cors_security_headers_docs.py.
Responses API input_audio (#997)¶
Status: Done (2026-09-23). Responses message content extracts input_audio
onto Message.audio and runs the same ASR inject path as chat. Docs:
asr.md. Covered by
tests/unit/test_responses_input_audio.py.
Accept service_tier on OpenAI and Anthropic HTTP ingress (#1005)¶
Status: Done (2026-09-23). ChatCompletionRequest and AnthropicRequest
declare service_tier so it survives validation and reaches
openai_payload() / to_anthropic_payload(). Covered by
tests/unit/test_service_tier.py.
Non-stream frontier OpenAI includes tools (#1006)¶
Status: Done (2026-09-23). _openai_payload forwards tools on both
stream and non-stream L6 OpenAI calls so agent loops with stream: false
get tool schemas. Covered by tests/unit/test_frontier_tool_parity.py and
tests/integration/test_gateway_flow.py.
Preserve json_schema name and strict (#1008)¶
Status: Done (2026-09-23). SamplingParams keeps client name/strict and
forwards them on openai-kind response_format; Ollama format stays
schema-only. Covered by tests/unit/test_sampling_params.py.
Ollama facade think/format/keep_alive (#1011)¶
Status: Done (2026-09-23). Facade chat/generate declare and forward
think, format, and keep_alive onto local Ollama payloads. Docs:
chatgpt-desktop.md. Covered by
tests/integration/test_ollama_compat.py.
Doctor CORS web-ui origin advisory (#999)¶
Status: Done (2026-09-23). Optional cors_origins doctor check tips empty
allowlists and warns when the web-ui origin is missing. Covered by
tests/unit/test_doctor_cors_origins.py.
Docs: server body/TLS and auth throttle rows (#998)¶
Status: Done (2026-09-23). config.md documents server.max_body_bytes,
server.tls.*, and auth.* throttle knobs without wiping the rpd footnote.
Covered by tests/unit/test_config_server_tls_auth_docs.py.
Forward remaining OpenAI chat params (#1007)¶
Status: Done (2026-09-23). ChatCompletionRequest / SamplingParams declare
store, metadata, prediction, modalities, audio, verbosity,
web_search_options; openai_payload() also emits logprobs / n; local
tiers warn via always-on x-daari-warning (no X-Daari-Meta required).
Covered by tests/unit/test_remaining_openai_chat_params.py.
Anthropic thinking, metadata, and top_k parity (#1009)¶
Status: Done (2026-09-23). /v1/messages accepts thinking / metadata;
to_anthropic_payload forwards them plus top_k; thinking budgets map to
Ollama think via reasoning_effort. Docs:
claude-code.md.
Covered by tests/unit/test_anthropic_thinking_parity.py.
Map Responses-native sampling shapes (#1012)¶
Status: Done (2026-09-23). from_responses_body maps reasoning.effort,
text.format (json_object / json_schema with name/strict), tool_choice,
parallel_tool_calls, service_tier; logs responses_truncation_ignored.
Docs: clients-and-gateways.md.
Covered by tests/unit/test_responses_sampling.py and
tests/integration/test_responses_api.py.
Default dropped-parameter visibility (#1013)¶
Status: Done (2026-09-23). Local tiers set daari_meta.dropped_params and
emit always-on x-daari-dropped-params (plus dropped_params log event);
stream sets the header via StreamOutcome when the served tier is not L6.
Docs: headers.md. Covered by
tests/unit/test_dropped_params.py.
MCP empty resources/list and prompts/list (#1014)¶
Status: Done (2026-09-23). initialize advertises empty resources /
prompts capabilities; list methods return []. Docs:
mcp.md. Covered by
tests/unit/test_mcp_server.py.
Hermetic stream host failover ceilings (#1015)¶
Status: Done (2026-09-23). Benchmark pins pre-first-token failover under
STREAM_FAILOVER_CEILING_S (0.500s) and happy-path under
STREAM_HAPPY_PATH_CEILING_S (0.200s). Covered by
tests/benchmark/test_hermetic_paths.py.
Integration coverage for chat input_audio L6 (#1000)¶
Status: Done (2026-09-24). Gateway integration pin: chat input_audio
escalates to mocked L6 with OpenAI payload preserving the block; optional
asr.base_url path injects the transcript into the local-tier prompt. Covered
by tests/integration/test_gateway_flow.py.
L1 semantic embed includes audio tokens (#1001)¶
Status: Done (2026-09-24). extract_embed_text appends
ContentAudio.cache_token() when messages carry audio so different clips with
the same caption cannot share an L1 hit; text-only embed strings unchanged.
Docs: caching-and-trust.md. Covered
by tests/unit/test_semantic_cache.py.
L1 semantic embed includes image tokens (#1029)¶
Status: Done (2026-09-24). extract_embed_text appends
ContentImage.cache_token() when messages carry images (parity with audio
1001); text-only and audio-only embed strings unchanged. Docs:¶
caching-and-trust.md. Covered by
tests/unit/test_semantic_cache.py.
Agent prefix L1 folds multimodal tokens (#1030)¶
Status: Done (2026-09-24). agent_prefix_text shares _message_embed_chunk
with extract_embed_text so image/audio cache tokens differentiate agent-turn
L1 prefixes; text-only prefixes unchanged. Covered by
tests/unit/test_semantic_cache.py.
Ollama facade logprobs / top_logprobs (#1031)¶
Status: Done (2026-09-24). Facade chat/generate accept top-level logprobs
and top_logprobs, map them onto SamplingParams (local dropped-params /
L6 OpenAI forward). Docs:
chatgpt-desktop.md. Covered by
tests/unit/test_sampling_params.py and
tests/integration/test_ollama_compat.py.
Integration coverage for x-daari-dropped-params (#1032)¶
Status: Done (2026-09-24). Gateway integration pin: local chat sets
x-daari-dropped-params for unsupported knobs; L6 omits the header. Covered by
tests/integration/test_gateway_flow.py.
Integration pin: distinct audio clips miss L1 (#1033)¶
Status: Done (2026-09-24). Router L1 integration (mocked embedder): same
caption with clip-B misses after clip-A is stored; caption+clip-A still hits.
Covered by tests/integration/test_l1_semantic_cache.py.
Integration pin: distinct images miss L1 (#1039)¶
Status: Done (2026-09-24). Router L1 integration (mocked embedder): same
caption with image-B misses after image-A is stored; caption+image-A still hits.
Covered by tests/integration/test_l1_semantic_cache.py.
ExactCache L0 multimodal key differentiation (#1040)¶
Status: Done (2026-09-24). normalize_messages keeps audio tokens (parity
with images) so cache_key differs for same caption with distinct clips;
text-only keys still match a no-media request. Covered by
tests/unit/test_exact_multimodal_keys.py.
Integration pin: facade generate logprobs (#1041)¶
Status: Done (2026-09-24). /api/generate maps logprobs / top_logprobs
onto SamplingParams and local dropped-params (parity with chat #1031);
malformed top_logprobs ignored. Covered by
tests/integration/test_ollama_compat.py.
Agent-prefix L1 misses when multimodal tokens differ (#1042)¶
Status: Done (2026-09-24). Router agent-prefix L1 integration: same tools
and suffix with image-B miss after image-A is stored; image-A replay (fresh
tool_call id) still hits. Covered by tests/integration/test_agent_prefix_l1.py.
Docs: agent-prefix multimodal fold in caching-and-trust (#1043)¶
Status: Done (2026-09-24). Trust concept doc states agent-prefix L1 shares
_message_embed_chunk with embed text for image/audio tokens. Covered by
tests/unit/test_caching_trust_docs.py.
Agent-prefix L1 misses when audio clips differ (#1049)¶
Status: Done (2026-09-24). Router agent-prefix L1 integration: same tools
and suffix with clip-B miss after clip-A is stored; clip-A replay (fresh
tool_call id) still hits (parity with image pin #1042). Covered by
tests/integration/test_agent_prefix_l1.py.
OpenAI POST /v1/moderations L6 passthrough (#1050)¶
Status: Done (2026-09-24). POST /v1/moderations forwards OpenAI-shaped
{input, model?} to the configured frontier when enabled+keyed; returns 501
when frontier is off (never invents scores). Docs: http-api.md. Covered by
tests/unit/test_moderations.py.
Cohere-compatible POST /v1/rerank (#1051)¶
Status: Done (2026-09-24). POST /v1/rerank accepts Cohere/LiteLLM-shaped
query + documents (+ optional top_n/model) and forwards to configured
frontier; 501 when frontier is off. Docs: http-api.md. Covered by
tests/unit/test_rerank.py.
Key governance + spend for moderations/rerank (#1058)¶
Status: Done (2026-09-24). /v1/moderations and /v1/rerank honor
no_frontier / local tier_cap (403 frontier_not_allowed), model allowlists
(reject_disallowed_model), and write usage + spend ledger rows attributed to
key/team. Covered by tests/unit/test_moderations.py and
tests/unit/test_rerank.py.
Guardrails on embeddings/audio/moderations/rerank (#1059)¶
Status: Done (2026-09-24). Shared apply_endpoint_input_policy /
apply_endpoint_output_policy apply chat guardrails to embeddings, TTS, ASR
(prompt + transcript; frontier fallback honors no_frontier), moderations, and
rerank. Covered by tests/unit/test_guardrails_endpoints.py.
Retry + slot failover + region_pin for moderations/rerank (#1060)¶
Status: Done (2026-09-24). Moderations and rerank use run_upstream with
provider retry policy, iterate frontier slots in chat order on failure, and
filter by virtual-key region_pin (400 when none match). Covered by
tests/unit/test_moderations.py and tests/unit/test_rerank.py.
OpenRouter region_pin → us/eu hosts (#1052)¶
Status: Done (2026-09-24). region_pin us/eu keeps global OpenRouter
slots eligible and rewrites L6 base URL to https://{us|eu}.openrouter.ai/api/v1
for the attempt (restored after). Docs: compare-openrouter.md. Covered by
tests/unit/test_openrouter_region.py.
ASR frontier fallback honors no_frontier and region_pin (#1061)¶
Status: Done (2026-09-24). ASR frontier fallback reuses resolve_l6_targets
so no_frontier keys never upload audio, region_pin filters slots (400 when
none match), and eligible slots fail over in pool order. Covered by
tests/unit/test_audio_transcriptions.py.
Response cost headers on embeddings and audio (#1062)¶
Status: Done (2026-09-24). Embeddings, speech, transcriptions/translations,
moderations, and rerank emit the same x-daari-response-cost* header set as
chat, matching spend_ledger amounts. Covered by tests/unit/test_cost_headers.py,
test_embeddings.py, test_speech.py, test_audio_transcriptions.py,
test_moderations.py, and test_rerank.py.
OpenAI images/generations L6 passthrough (#1064)¶
Status: Done (2026-09-24). POST /v1/images/generations is a governed L6
passthrough (no_frontier, model allowlist, region_pin, run_upstream
retry/slot failover, usage+spend rows). Covered by tests/unit/test_images.py.
Idempotency-Key on embeddings, audio, moderations, and rerank (#1065)¶
Status: Done (2026-09-25). Idempotency-Key replays embeddings, TTS
(binary), ASR/translations (multipart form+file digest), moderations, and
rerank without a second upstream call; same key + different body → 409
idempotency_conflict. Docs: headers.md, config.md. Covered by
tests/unit/test_idempotency.py.
Honor or declare Ollama 0.34 tool_search and response_compaction (#1066)¶
Status: Done (2026-09-25). Facade /api/chat and /api/generate forward
tool_search / response_compaction on local Ollama hops; cache and
non-Ollama tiers declare them via daari_meta.dropped_params. Docs:
chatgpt-desktop.md. Covered by tests/integration/test_ollama_compat.py.
Week/weekly/rpw budget duration aliases (#1067)¶
Status: Done (2026-09-25). normalize_duration accepts week / weekly /
rpw / 1w as ISO calendar weeks (period_id like 2026-W38); header label
1w; CLI --window week=10 persists. Docs: budgets-frontier.md. Covered by
tests/unit/test_budget_windows.py, test_budget_rollover.py,
test_virtual_keys.py.
ExactCache L0 router pin for distinct audio clips (#1053)¶
Status: Done (2026-09-25). Router integration: L1 off + ExactCache on;
caption+clip-A stores L0, caption+clip-B misses, clip-A hits L0. Covered by
tests/integration/test_l1_semantic_cache.py.
Integration pins for moderations and rerank (#1068)¶
Status: Done (2026-09-25). Hermetic ASGI coverage for POST /v1/moderations
and POST /v1/rerank: OpenAPI path registration, auth middleware on
(401 without key / 200 mocked happy path), and frontier-disabled / no-key
→ 501 not_implemented. Covered by
tests/integration/test_moderations_rerank.py.
Idempotency-Key on images/generations (#1082)¶
Status: Done (2026-09-25). Idempotency-Key replays
POST /v1/images/generations without a second upstream call; same key +
different body → 409 idempotency_conflict. Docs: headers.md, config.md.
Covered by tests/unit/test_idempotency.py.
Guardrails on images/generations (#1083)¶
Status: Done (2026-09-25). Input guardrails run on the images prompt
before upstream (block → 400 guardrail_blocked; redact mutates prompt).
Covered by tests/unit/test_guardrails_endpoints.py.
Response cost headers on images/generations (#1084)¶
Status: Done (2026-09-25). POST /v1/images/generations emits the same
x-daari-response-cost* / tier header set as other modality routes, matching
spend_ledger. Docs: headers.md. Covered by tests/unit/test_images.py.
Integration pins for images/generations (#1081)¶
Status: Done (2026-09-25). Hermetic ASGI coverage for
POST /v1/images/generations: OpenAPI path, auth middleware on
(401 without key / 200 mocked happy path), frontier-disabled / no-key → 501.
Covered by tests/integration/test_images.py.
Doctor probe for images/generations (#1092)¶
Status: Done (2026-09-25). When frontier.enabled and a key resolves,
daari doctor dry-checks OpenAPI for POST /v1/images/generations; frontier
on without a key warns that the route returns 501. Docs: doctor-health.md.
Covered by tests/unit/test_doctor_images_generations.py,
test_doctor_health_images_docs.py.
Images edits/variations surface honesty (#1090)¶
Status: Done (2026-09-25). OpenAPI audit: only
POST /v1/images/generations exists (no edits/variations routes). Docs state
edits/variations are not supported. Covered by
tests/unit/test_images_surface_docs.py.
Images cost headers in shared suite (#1091)¶
Status: Done (2026-09-25). Shared tests/unit/test_cost_headers.py
modality matrix pins tier=images / zero reported cost for
POST /v1/images/generations, matching the route helper.
Images edits L6 passthrough (#1097)¶
Status: Done (2026-09-25). POST /v1/images/edits is a governed multipart
L6 passthrough (frontier gate, allowlists, region_pin, guardrails on prompt,
spend/cost headers). Binary multipart bodies no longer crash rate-limit JSON
parse. Docs: clients-and-gateways.md. Covered by
tests/unit/test_images_edits.py, test_images_surface_docs.py.
SSO bearers control-plane only (#1103)¶
Status: Done (2026-09-25). Verified SSO access tokens are accepted only on
/v1/daari/* and /v1/org-learning/*; inference routes return
401 sso_key_required and audit auth.sso_key_required. Docs note in
auth-and-keys.md. Covered by
tests/integration/test_oidc_sso_gateway.py::test_sso_bearer_rejected_on_inference_routes.
Images variations L6 passthrough (#1098)¶
Status: Done (2026-09-25). POST /v1/images/variations is a governed
multipart L6 passthrough (frontier gate, allowlists, region_pin, spend/cost
headers). Docs: clients-and-gateways.md + regenerated http-api.md. Covered by
tests/unit/test_images_variations.py, test_images_surface_docs.py.
Endpoint-family RPM/TPM on virtual keys (#1099)¶
Status: Done (2026-09-25). Virtual-key metadata rate_families (and
daari keys create --rate-family) adds per-modality RPM/TPM counters so
images cannot starve chat on a shared key. Docs: auth-and-keys + config.md.
Covered by tests/unit/test_rate_families.py.
Coordinated graceful shutdown (#1104)¶
Status: Done (2026-09-25). SIGTERM/SIGINT flips /ready to
503 shutting_down (liveness /health stays 200), admission drain rejects
new in-flight arrivals with 503 + Retry-After while queued/admitted work
finishes, daari serve --graceful-timeout / Helm gracefulTimeoutSeconds
wire uvicorn timeout_graceful_shutdown, and lifespan awaits budget-alert
tasks before closing pools. Docs: upgrade.md + capacity-helm.md. Covered by
tests/unit/test_graceful_shutdown.py,
tests/integration/test_gateway_flow.py::test_shutdown_drain_ready_flip_inflight_and_reject.
Cache-read/cache-write token dimensions (#1105)¶
Status: Done (2026-09-26). Anthropic cache_read_input_tokens /
cache_creation_input_tokens land on DaariMeta and feed
cost_usd(cache_write_tokens=…). OTel emits gen_ai.token.type
cache_read / cache_creation plus span attrs. Usage + spend ledgers
persist both dims (sqlite+postgres migrate); spend export includes
cache_write_tokens. Covered by tests/unit/test_cache_token_dimensions.py,
tests/unit/test_otel_genai.py::test_cache_read_and_creation_token_dims.
Per-modality token/spend metrics (#1106)¶
Status: Done (2026-09-26). daari_tokens_total{modality,tier,direction}
plus additive modality labels on daari_requests_total; images /
moderations / rerank call metrics.record; OTel
gen_ai.operation.name follows modality; Grafana panels for tokens and
requests by modality. Covered by tests/unit/test_modality_metrics.py,
tests/unit/test_otel_genai.py::test_operation_name_reflects_non_chat_modality.
Request-time team selection with membership (#1107)¶
Status: Done (2026-09-26). Optional X-Daari-Team selects among teams the
authenticated subject is entitled to (team_members table, sqlite+postgres);
pinned keys require metadata.allow_team_override; non-members get 403 +
audit. SSO mint/refresh re-syncs entitlements; CLI
daari keys team-members. Covered by tests/unit/test_team_selection.py.
Shared USD budget per named model_group (#1109)¶
Status: Done (2026-09-26). Keys/teams attach model_group_budgets (metadata)
keyed by settings.model_groups; org-wide L6 spend for matching models shares
one window; gateway 402 / soft budget_warning before escalate; report
model_group_spend + spend export model_group column. Covered by
tests/unit/test_model_group_budgets.py.
Doctor probes for moderations and rerank (#1110)¶
Status: Done (2026-09-26). daari doctor dry-checks OpenAPI for
POST /v1/moderations and POST /v1/rerank when frontier can escalate
(skip/501 tip when disabled or missing key), mirroring images. Covered by
tests/unit/test_doctor_moderations_rerank.py.
Config live-vs-file ownership honesty (#1111)¶
Status: Done (2026-09-26). GET /v1/daari/config returns per-field
ownership (source/editable/diverged/file_value); PATCH without
persist returns a runtime-only warning + audit runtime_only; persist
still returns persisted_to. Covered by tests/unit/test_config_ownership.py.
Pre-auth request-header allow/block policy (#1112)¶
Status: Done (2026-09-26). server.header_policy (default off) screens
required headers, deny exact/regex rules, and optional allowlists before
require_api_key; open probes stay exempt; denies emit header_policy_error
+ log_gateway_event / audit. Doctor warns when enabled but empty/malformed.
Covered by tests/unit/test_header_policy.py and
tests/unit/test_doctor_header_policy.py.
Team-level model_max_budget with key overrides (#1113)¶
Status: Done (2026-09-26). Teams/keys accept model_max_budget
{model_or_pattern: usd|window} (bare float = daily); key overlays team map;
pre-check before L6 soft/hard via soft_budget_ratio; report exposes
team_model_spend. Covered by tests/unit/test_model_max_budget.py.
Anthropic-native moderations ingress (#1100)¶
Status: Done (2026-09-26). POST /v1/messages/moderations accepts OpenAI
input or Anthropic messages bodies and reuses L6 moderations governance
(501 when frontier off / no key). Rate family maps to moderations. Covered by
tests/unit/test_anthropic_moderations.py.
Integration pins for images edits (#1101)¶
Status: Done (2026-09-26). Hermetic ASGI pins for POST /v1/images/edits:
OpenAPI path, auth middleware (401 without key / 200 mocked), frontier-disabled
→ 501. Covered by tests/integration/test_images_edits.py.
Frontier model-aware parameter compatibility (#1129)¶
Status: Done (2026-09-26). Per-model frontier param-compat table (longest-prefix
lookup) strips unsupported sampler knobs for gpt-6-astra, floors
reasoning_effort=none → minimal, and warns + logs frontier.tools_transport
when tools hit chat completions. Covered by
tests/unit/test_frontier_param_compat.py.
Subject erasure across stores (#1130)¶
Status: Done (2026-09-26). daari erase --key|--team|--user [--dry-run|--yes]
sweeps spend, usage, request log, responses, files, batches, idempotency, and
scoped caches; audit appends compliance.erase (hash chain intact). Guide:
docs/developer/guides/operations/erasure.md. Covered by
tests/unit/test_erasure.py.
Full-state backup/restore CLI (#1131)¶
Status: Done (2026-09-26). daari backup create|restore archives durable
sqlite/JSONL/dir stores with a versioned manifest; postgres backends listed with
pg_dump commands; doctor hints when no recent backup; guide
docs/developer/guides/operations/backup-restore.md. Covered by
tests/unit/test_backup_restore.py.
Spend user_id + team-member chargeback (#1132)¶
Status: Done (2026-09-26). Spend rows gain nullable user_id (sqlite +
postgres migrate); daari spend export --user filters; daari spend report
--team T --by-user rolls member×spend. Guide:
docs/developer/guides/observability/chargeback.md. Covered by
tests/unit/test_spend_user_attribution.py.
Responses reasoning items across turns (#1133)¶
Status: Done (2026-09-26). /v1/responses accepts, stores, and replays
type: reasoning items on previous_response_id chains; output emits
reasoning when InternalResponse.reasoning_content is set;
include=[reasoning.encrypted_content] allowlisted. Covered by
tests/unit/test_responses_reasoning.py and
tests/integration/test_responses_api.py.
Responses reject hosted tool types (#1135)¶
Status: Done (2026-09-26). POST /v1/responses returns 400 for non-function
tools (web_search, mcp, …) with an explicit allowlist; docs note in
clients-and-gateways.md. Covered by tests/unit/test_responses_hosted_tools.py.
Batches + L0/L1 cache in prune_all (#1136)¶
Status: Done (2026-09-27). observability.retention.batches_days (default 0)
and cache_prune fold BatchStore / PostgresBatchStore and L0/L1 prune() into
prune_all. Dry-run counts without deleting. Guide:
docs/developer/guides/observability/traces-stats.md. Covered by
tests/unit/test_retention.py.
Responses prompt_cache_key / retention on L6 (#1137)¶
Status: Done (2026-09-27). ResponsesRequest declares prompt_cache_key,
prompt_cache_retention, and prompt_cache_options; they reach L6 via
SamplingParams.openai_payload() and are named in dropped_params on local
tiers. Docs note in clients-and-gateways.md. Covered by
tests/unit/test_responses_prompt_cache.py.
Policy-sync drift hash and policy-status (#1138)¶
Status: Done (2026-09-27). Successful policy apply records policy_hash,
applied_at, and redacted source_url (in-memory + ~/.daari/policy-sync-state.json).
daari enterprise policy-status prints them and exits 1 when configured but
never applied. Covered by tests/unit/test_policy_status.py.
Responses stream resume via starting_after (#1139)¶
Status: Done (2026-09-27). GET /v1/responses/{id}?stream=true replays
terminal stored output as Responses SSE with sequence_number;
starting_after skips earlier events; non-terminal background returns 409;
plain GET stays JSON. Covered by tests/unit/test_responses_stream_resume.py.
Responses create-stream sequence_number (#1151)¶
Status: Done (2026-09-27). POST /v1/responses stream events include
contiguous sequence_number (keepalives excluded); resume remains a
synthesized 0-based replay. Covered by
tests/unit/test_responses_stream_resume.py.
Responses Last-Event-ID stream resume (#1152)¶
Status: Done (2026-09-27). Resume SSE frames emit id: <seq>;
Last-Event-ID acts as starting_after when the query is omitted (query
wins; invalid/negative ignored). Covered by
tests/unit/test_responses_stream_resume.py.
Responses compact endpoint (#1153)¶
Status: Done (2026-09-27). POST /v1/responses/compact summarizes via
the router from previous_response_id / input, stores a shorter
conversation, and documents the route. Covered by
tests/unit/test_responses_compact.py.
Anthropic hoist only on local Ollama (#1154)¶
Status: Done (2026-09-27). Trailing system hoist moved from the Anthropic
gateway into OllamaExecutor._payload so Anthropic/frontier egress preserves
mid-conversation system turns. Covered by
tests/unit/test_anthropic_hoist.py.
Responses stream resume http-api docs (#1155)¶
Status: Done (2026-09-27). http-api.md documents GET
/v1/responses/{id} query params stream / starting_after and 409 for
non-terminal status. Covered by
tests/unit/test_responses_stream_resume_docs.py.
OpenAPI + doctor for Responses stream resume (#1161)¶
Status: Done (2026-09-27). OpenAPI documents stream /
starting_after descriptions and 409 on GET responses; doctor dry-check
pins them. Covered by tests/unit/test_doctor_responses_stream_resume.py.
Doctor tip for enterprise policy-status (#1163)¶
Status: Done (2026-09-27). Optional policy_status doctor check warns
when policy_sync_url is set but never applied; docs mention it. Covered by
tests/unit/test_doctor_policy_status.py.
Responses stream resume Last-Event-ID in http-api (#1162)¶
Status: Done (2026-09-27). http-api.md resume section documents
Last-Event-ID and sequence_number. Covered by
tests/unit/test_responses_stream_resume_docs.py.
Responses compact/create govern like chat (#1169)¶
Status: Done (2026-09-27). Compact and POST /v1/responses share
RequestMeta auth claims, model_max_budget 402, and rate-family chat
bucketing for /v1/responses/... subpaths. Covered by
tests/unit/test_responses_compact.py, tests/unit/test_model_max_budget.py,
tests/unit/test_rate_families.py, and
tests/integration/test_responses_api.py.
Erasure reaches Redis/Postgres/user_id/traces (#1170)¶
Status: Done (2026-09-27). Subject erase covers spend user_id (and
legacy client_id), Redis L0/L1, Postgres batches, and traces; cache
dry-run reports candidates or unknown. Covered by
tests/unit/test_erasure.py and docs/developer/guides/operations/erasure.md.
OTel gen_ai.conversation.id from session (#1175)¶
Status: Done (2026-09-27). export_trace stamps
gen_ai.conversation.id from RequestMeta.session_id (X-Daari-Session)
when set; never synthesizes. Covered by tests/unit/test_otel_genai.py and
tests/unit/test_otel_docs.py.
Backup catalog honesty + restore interlock (#1171)¶
Status: Done (2026-09-27). PG batches/files catalogued as external with
pg_dump hints; rotated request logs archived; restore refuses when
/health succeeds unless --i-know-server-is-stopped. Covered by
tests/unit/test_backup_restore.py.
Spend user_id on modalities + Responses (#1172)¶
Status: Done (2026-09-27). Images/embeddings/moderations/speech/
transcriptions/rerank binders pass request user into spend user_id;
ResponsesRequest/CompactRequest accept user on RequestMeta. Covered by
tests/unit/test_spend_user_attribution.py.
Config-driven frontier param compat + stream warnings (#1173)¶
Status: Done (2026-09-27). frontier.param_compat merges over the builtin
table; Anthropic egress applies the strip; streamed L6 attaches dropped-param
warnings to daari_meta / stream headers. Covered by
tests/unit/test_frontier_param_compat.py.
Optional encrypted backup archives (#1176)¶
Status: Done (2026-09-27). backup create --encrypt openssl|age wraps the
tarball via host CLI; restore decrypts with passphrase/identity env or flags;
doctor hints when recent backups are all plaintext. Covered by
tests/unit/test_backup_restore.py.
Embed Postgres dumps into backup archive (#1177)¶
Status: Done (2026-09-28). When pg_dump is on PATH, postgres-backed
stores are dumped into stores/pg/*.sql and marked backend=embedded with
size/hash; missing/failed dumps stay external unless --require-pg-dump.
Restore documents extract paths and applies dumps with --restore-pg +
psql. Covered by tests/unit/test_backup_restore.py.
Helm sessionAffinity + policySync knobs (#1178)¶
Status: Done (2026-09-28). Chart values expose routing.sessionAffinity
(+ TTL) and enterprise.policySync (url + signingSecret); Deployment emits
DAARI_ROUTING__SESSION_AFFINITY* / DAARI_ENTERPRISE__POLICY_SYNC_URL /
CONFIG_SIGNING_SECRET. NOTES + capacity-helm document them. Covered by
tests/unit/test_helm_chart.py and tests/unit/test_capacity_helm_docs.py.
Hermetic ceilings for Responses compact + stream resume (#1179)¶
Status: Done (2026-09-28). Benchmark suite pins SQLite ResponseStore
compact put+get and stream append + starting_after resume ceilings in
tests/benchmark/test_hermetic_paths.py (still skipped by default CI).
Responses stream resume starting_after gateway pin (#1164)¶
Status: Done (2026-09-28). Hermetic gateway ASGI pin in
tests/integration/test_responses_api.py for
GET ?stream=true&starting_after= skip semantics and 409 on non-terminal;
unit coverage also asserts 409 with a cursor. Covered by those tests.
Keep committed config.md in sync with Settings (#1165)¶
Status: Done (2026-09-28). Regenerated docs/developer/reference/config.md
from render_config_reference(); hermetic equality test fails on drift.
Non-Settings rpd / rate_families footnotes live in the generator after the
table. Covered by tests/unit/test_gen_reference.py.
Gateway ASGI pin for header_policy deny (#1197)¶
Status: Done (2026-09-28). Hermetic gateway pin asserts denied chat returns
header_policy_error and /health stays exempt when policy is enabled.
Covered by tests/integration/test_header_policy_gateway.py.
CLI docs for backup --encrypt and --restore-pg (#1193)¶
Status: Done (2026-09-28). cli.md documents backup create --encrypt
openssl/age, --require-pg-dump, restore decrypt env, and --restore-pg,
with a pointer to the backup-restore guide. Covered by
tests/unit/test_backup_cli_docs.py.
Docs: operator guide for server.header_policy (#1194)¶
Status: Done (2026-09-28). Auth-and-keys guide documents
server.header_policy knobs, header_policy_error deny shape, and open-probe
exemptions. Covered by tests/unit/test_header_policy_docs.py.
Stall-watch survives issue write failures (#1207)¶
Status: Done (2026-09-28). apply_resolved_stalls and apply_sweep route
every gh issue side effect through _issue_write, which logs a warning:
and continues when the token lacks Issues write instead of aborting the
stall-watch job. AUTOMATION.md lists Issues among the AUTODEV_GH_TOKEN
permissions. Covered by tests/unit/test_autodev_pr_watch.py.
Docs-site strict build green (#1208)¶
Status: Done (2026-09-28). mkdocs.yml uses pymdownx.slugs.slugify so
GitHub-style anchors (#providers--fallback) resolve on the site; links to
repo files outside docs/ (SECURITY.md, LICENSE, workflows, web-ui
README) and to bare directories now use GitHub URLs or a concrete page.
mkdocs build --strict passes with zero warnings.
Per-key/team MCP server allowlists (#1201)¶
Status: Done (2026-09-28). McpServerPolicy layers
integrations.mcp_policy.servers → team → metadata.mcp.servers (deny wins,
key over team). Denied egress servers are filtered from tools/list, blocked
on tools/call with data.server in the existing denial shape, audited with
the server id, and never POSTed by the egress client. CLI
--mcp-server-allow/--mcp-server-deny, keys show, and daari doctor
(mcp_server_policy) surface the grants. Covered by
tests/unit/test_mcp_policy.py, tests/unit/test_mcp_egress.py,
tests/integration/test_mcp_governance.py, tests/unit/test_virtual_keys.py.
MCP tools/call metering + mcp rate family (#1202)¶
Status: Done (2026-09-28). Non-route MCP tools/call (provider/egress
included) writes usage + spend rows attributed to key/team; /mcp maps to a
dedicated mcp rate-limit family so ceilings do not consume chat. Docs:
MCP guide metering section + auth-and-keys family list. Covered by
tests/unit/test_mcp_vk_governance.py, tests/unit/test_rate_families.py.
MCP egress resilience — spans, retry, circuit breaker (#1203)¶
Status: Done (2026-09-29). MCP egress POSTs emit an OTel client span
(mcp.tools/call / mcp.tools/list) with server id and tool name, retry
transient failures via run_upstream/RetryPolicy (upstream.retry), and trip
a per-server CircuitBreaker (integrations.mcp_egress.failure_threshold /
cooldown_seconds) with fail-fast + daari_mcp_egress_total circuit labels.
Covered by tests/unit/test_mcp_egress_resilience.py.
MCP server/discover + per-request protocol _meta (#1213)¶
Status: Done (2026-09-29). POST /mcp implements server/discover
(supportedVersions, capabilities, serverInfo) and honors
params._meta["io.modelcontextprotocol/protocolVersion"] without a prior
initialize. Unsupported versions return JSON-RPC -32022 with
data.supported / data.requested. Legacy initialize unchanged. Covered by
tests/unit/test_mcp_server.py.
MCP egress SSRF / private-network guards (#1214)¶
Status: Done (2026-09-29). Before MCP egress POST, URLs are resolved and
checked (http/https only; deny loopback, RFC1918, link-local, metadata).
integrations.mcp_egress.allow_private_networks opts in for lab/docker.
Doctor fails closed without probing blocked URLs. Covered by
tests/unit/test_egress_url.py, tests/unit/test_mcp_egress.py,
tests/unit/test_doctor_mcp_servers.py.
GTM: scoreboard refreshed, v1.4.0 note, honest copy (#1222)¶
Status: Done (2026-09-29). First scoreboard refresh since the 2026-08-26
seed: 3 stars, 4 unique viewers / 14d, PyPI 135 / 30d. DEFAULT_SHIPPED /
DEFAULT_WAITING now reflect Apache 2.0, v1.4.0, green docs site, and the
four launch drafts. docs/gtm/drafts/shipping-note.md regenerated for 1.4.0;
the generator no longer leaves a stray ** in the headline. Welcome
Discussion #238 retitled to Apache 2.0 with the Homebrew tap command. Covered
by tests/unit/test_gtm_shipping_note.py, tests/unit/test_gtm_scoreboard.py.
GTM measure on a schedule (#1223)¶
Status: Done (2026-09-29). .github/workflows/gtm-scoreboard.yml runs
weekly (Mon 14:00 UTC): refreshes docs/gtm/SCOREBOARD.md + REPORT.md,
opens an auto-merging docs(gtm): scoreboard <date> PR when they changed, and
files a gtm,regression issue on a 14-day viewer drought (deduped by title).
Shell only, AUTODEV_GH_TOKEN fallback to GITHUB_TOKEN. PLAN.md Measure row
and AUTOMATION.md components table point at it.
Scoreboard names the PAT permission on 403 (#1226)¶
Status: Done (2026-09-29). scripts/gtm_scoreboard.py catches a failed
gh api call and exits 1 with one line naming the endpoint and the fix
(Administration: read on the fine-grained PAT). AUTOMATION.md lists it next to
Issues write. Covered by tests/unit/test_gtm_scoreboard.py.
Refreshable secret:// resolution (#1204)¶
Status: Done (2026-09-29). Non-oauth secret:// refs
(env-file/exec/keychain) resolve to RefreshableSecret when
secrets.refresh_ttl_seconds > 0 (default 300); env-file also re-resolves on
source mtime change. TTL 0 keeps boot-only plain strings. Boot failures stay
fatal. Doctor hints when refresh is disabled without oauth refs. Docs:
auth-and-keys rotation runbook. Covered by
tests/unit/test_secret_refs_refresh.py, tests/test_doctor.py.
Helm header_policy knobs + MCP deny pin (#1205)¶
Status: Done (2026-09-29). Chart server.headerPolicy (disabled by
default) mounts DAARI_SERVER__HEADER_POLICY__* env; ASGI pin proves /mcp
is denied pre-auth while health/ready stay open. Docs cross-link capacity-helm
and auth-and-keys. Covered by tests/unit/test_helm_chart.py,
tests/integration/test_header_policy_gateway.py.
Honor Anthropic output_config.effort (#1230)¶
Status: Done (2026-09-29). AnthropicRequest / SamplingParams keep
output_config; L6 to_anthropic_payload forwards it; effort maps to local
reasoning_effort / Ollama think when adaptive or without budget_tokens
(budget still wins); Opus 4.5 injects effort-2025-11-24 beta. Docs:
claude-code.md.
Covered by tests/unit/test_anthropic_output_config.py.
Per-key/team MCP client allowlists (#1215)¶
Status: Done (2026-09-29). Nested mcp.clients allow/deny (global/team/key)
gates initialize / server/discover / tools/list / tools/call by
clientInfo / OAuth client_id / User-Agent; unset = passthrough. CLI
--mcp-client-allow/deny. Docs: mcp.md.
Covered by tests/unit/test_mcp_policy.py,
tests/integration/test_mcp_governance.py.
Scheduled encrypted backups via Helm CronJob (#1216)¶
Status: Done (2026-09-29). Optional chart backup.* CronJob (off by
default) runs daari backup create --encrypt with encrypt secret + retain
prune + PVC; values document schedule/encrypt/retain/secret keys. Docs add
systemd user-timer equivalent. Covered by tests/unit/test_helm_chart.py,
backup-restore.md.
Spend report --by-tool (#1217)¶
Status: Done (2026-09-29). Spend rows keep provider (MCP metering ids);
daari spend report --by-tool rolls USD/requests by provider-or-model.
--by-user unchanged. Docs: chargeback.md. Covered by
tests/unit/test_spend_report_by_tool.py.
MCP in-flight activity + admin force-abort (#1231)¶
Status: Done (2026-09-30). In-process registry tracks tools/call with
request_id, key/principal, method, tool, started_at; cleared on
completion/cancel. Admin GET /v1/daari/mcp/activity lists in-flight work;
POST .../activity/{request_id}/abort cancels the awaitable (idempotent when
finished; 404 unknown), writes mcp.activity.abort audit. Covered by
tests/unit/test_mcp_activity.py.
Reject Mcp-Method / Mcp-Name body mismatches (#1233)¶
Status: Done (2026-09-30). When Mcp-Method / Mcp-Name are present on
POST /mcp they must match JSON-RPC method / params.name; mismatch → HTTP
400 + JSON-RPC -32023 (HEADER_MISMATCH). Absent headers unchanged. Docs:
mcp.md. Covered by tests/unit/test_mcp_header_match.py.
Hermetic http-api.md equality (#1195)¶
Status: Done (2026-09-30). test_committed_http_api_md_matches_generator
fails if committed http-api.md drifts from render_api_reference(); regenerated
to match. Covered by tests/unit/test_gen_reference.py.
Doctor-health plaintext backup encrypt tip (#1196)¶
Status: Done (2026-09-30). doctor-health.md documents the optional
backup check and plaintext → --encrypt openssl|age tip with a link to
backup-restore. Covered by tests/unit/test_doctor_health_backup_docs.py.
Responses server-side MCP tools (#1232)¶
Status: Done (2026-09-30). Opt-in integrations.mcp_egress.server_side_responses
lets Responses accept type: mcp tools that resolve to configured
mcp_servers (server_label); daari lists tools, runs egress tools/call on
model tool rounds (SSRF/policy/metering unchanged), and continues the turn.
Default off keeps the #1135 honesty 400. Covered by
tests/unit/test_responses_server_side_mcp.py.
Docs: cli.md pins spend report --by-tool (#1245)¶
Status: Done (2026-09-30). CLI catalog spend row mentions report,
--by-tool, and --by-user with a link to chargeback.md. Covered by
tests/unit/test_chargeback_docs.py.
Hermetic pin for Claude Code output_config.effort (#1246)¶
Status: Done (2026-09-30). Unit test reads claude-code.md Effort section
and asserts output_config, effort levels, xhigh/max→high, and
anthropic-beta: effort-2025-11-24. Covered by
tests/unit/test_claude_code_effort_docs.py.
Docs: doctor header_policy empty-rules tip (#1247)¶
Status: Done (2026-09-30). doctor-health.md troubleshoot row covers
doctor header_policy when enabled with empty required/deny/allow, linking
auth-and-keys. Covered by tests/unit/test_doctor_health_header_policy_docs.py.
Hermetic pin for secrets.refresh_ttl_seconds runbook (#1248)¶
Status: Done (2026-09-30). Unit tests pin auth-and-keys Refresh/rotation
(default 300, boot-only 0 / env, mtime vs TTL, doctor secret_refs) and the
secrets.refresh_ttl_seconds config.md row. Covered by
tests/unit/test_secret_refresh_docs.py.
Hermetic pin for MCP activity + force-abort (#1253)¶
Status: Done (2026-09-30). Unit test pins mcp.md activity list/abort
(/v1/daari/mcp/activity, /abort, 404, mcp.activity.abort). Covered by
tests/unit/test_mcp_activity_docs.py.
Hermetic pin for Responses server_side_responses (#1254)¶
Status: Done (2026-09-30). Unit tests pin mcp.md
server_side_responses / type: mcp / server_label / honesty 400 and the
config.md knob. Covered by tests/unit/test_responses_server_side_mcp_docs.py.
Anthropic output_config.format structured outputs (#1260)¶
Status: Done (2026-09-30). SamplingParams.from_anthropic_body honors
output_config.format (type: json_schema) for local schema enforcement,
prefers it over legacy output_format, logs json_schema_ignored on
malformed format, and L6 still forwards full output_config. Covered by
tests/unit/test_anthropic_output_config.py and
tests/unit/test_claude_code_effort_docs.py.
Messages server-side MCP mcp_servers + mcp_toolset (#1261)¶
Status: Done (2026-09-30). Opt-in integrations.mcp_egress.server_side_messages
accepts Anthropic mcp_servers + mcp_toolset against configured
integrations.mcp_servers ids; lists/calls via egress; fails closed when
disabled; unknown labels → 400. Covered by
tests/unit/test_messages_server_side_mcp.py and
tests/unit/test_messages_server_side_mcp_docs.py.
MCP RFC 9728 oauth-protected-resource discovery (#1262)¶
Status: Done (2026-09-30). Opt-in integrations.mcp_oauth.protected_resource
serves /.well-known/oauth-protected-resource (+ /mcp scoped), shapes
unauthenticated /mcp 401 with WWW-Authenticate, doctor tip when auth is on
without metadata. Covered by tests/unit/test_mcp_oauth_protected_resource.py
and tests/unit/test_mcp_oauth_docs.py.
OpenAI-compatible POST /v1/ocr (#1263)¶
Status: Done (2026-10-01). POST /v1/ocr accepts LiteLLM/Mistral-shaped
document bodies (URL or base64), prefers ocr.base_url then
ocr.vision_model (Ollama multimodal) else L6 passthrough, returns structured
pages payloads, meters spend/usage, and uses the ocr rate-limit family.
Covered by tests/unit/test_ocr.py.
MCP OpenAPI /mcp/proxy schema discovery (#1264)¶
Status: Done (2026-10-01). Opt-in integrations.mcp_openapi_proxy exposes
POST /mcp/proxy for OpenAPI→MCP tools/list and proxied tools/call under
existing SSRF egress guards and the mcp rate family. Covered by
tests/unit/test_mcp_openapi_proxy.py.
Docs pin: Mcp-Method/Name HEADER_MISMATCH (#1255)¶
Status: Done (2026-10-01). Hermetic mcp.md pin for Mcp-Method /
Mcp-Name / HEADER_MISMATCH / -32023. Covered by
tests/unit/test_mcp_header_match_docs.py.
Docs pin: Helm scheduled encrypted backups (#1256)¶
Status: Done (2026-10-01). Hermetic backup-restore.md pin for Helm
CronJob / backup.enabled / backup.schedule / --encrypt openssl.
Covered by tests/unit/test_backup_cronjob_docs.py.
Docs pin: mcp_tool_calls / mcp_tasks stats (#1257)¶
Status: Done (2026-10-01). Hermetic mcp.md pin for /v1/daari/stats,
mcp_tool_calls, and mcp_tasks. Covered by
tests/unit/test_mcp_stats_docs.py.
Doctor tip when OCR knobs unset (#1276)¶
Status: Done (2026-10-01). Optional doctor ocr tip when both
ocr.base_url and ocr.vision_model are empty (L6 fallthrough). Covered by
tests/unit/test_doctor_ocr.py and test_doctor_health_ocr_docs.py.
MCP Registry GET /v1/mcp/registry.json (#1277)¶
Status: Done (2026-10-01). Opt-in integrations.mcp_registry.enabled
serves auth-agnostic GET /v1/mcp/registry.json listing built-in /mcp and
configured mcp_servers URLs. Covered by tests/unit/test_mcp_registry.py.
Docs pin: POST /v1/ocr modality (#1274)¶
Status: Done (2026-10-01). Hermetic clients-and-gateways.md pin for
POST /v1/ocr / document / local vision / L6. Covered by
tests/unit/test_ocr_docs.py.
Docs pin: OpenAPI /mcp/proxy cues (#1275)¶
Status: Done (2026-10-01). Hermetic mcp.md pin for /mcp/proxy,
mcp_openapi_proxy, tools/list, and SSRF / allow_private_networks.
Covered by tests/unit/test_mcp_openapi_proxy_docs.py.
Docs pin: http-api OCR and /mcp/proxy rows (#1278)¶
Status: Done (2026-10-01). Hermetic http-api.md pin for POST /v1/ocr
and POST /mcp/proxy catalog rows. Covered by
tests/unit/test_http_api_ocr_mcp_proxy_docs.py.
Docs pin: MCP registry.json cues (#1284)¶
Status: Done (2026-10-01). Hermetic mcp.md pin for mcp_registry,
/v1/mcp/registry.json, and mcp_servers. Covered by
tests/unit/test_mcp_registry_docs.py.
Docs pin: http-api MCP registry row (#1285)¶
Status: Done (2026-10-01). Hermetic http-api.md pin for
GET /v1/mcp/registry.json catalog row. Covered by
tests/unit/test_http_api_mcp_registry_docs.py.
POST /v1/systemone decision-model facade (#1291)¶
Status: Done (2026-10-01). Opt-in (default on) POST /v1/systemone
proxies Ollama 0.35+ TypeSafe/Jev decision models via ollama.base_url;
same /v1/* auth; preserves answers/usage and adds daari_meta;
meters usage/metrics; malformed → 400; Ollama down → 503
backend_unavailable; systemone.enabled=false → 501. Docs: ollama.md +
regenerated http-api.md. Covered by tests/unit/test_systemone.py.
Optional decision-model complexity classifier (#1292)¶
Status: Done (2026-10-01). Opt-in routing.decision_classifier
(default off) runs an Ollama /v1/systemone difficulty hop before the
heuristic local tier pick; maps choice/score → L3–L5; timeout/failure
falls back to heuristics with a degraded event; daari_meta.decision +
trace step record model/answer/tier. Covered by
tests/unit/test_decision_classifier.py.
MCP local OAuth authorize/token mint (#1293)¶
Status: Done (2026-10-02). Opt-in integrations.mcp_oauth.local_as
exposes AS metadata + POST /oauth/token (client_credentials), mints
short-lived HS256 Bearer JWTs bound to API/virtual keys, usable on /mcp;
bad/expired → 401 + existing challenge; discovery lists local issuer when
authorization_servers empty. Docs: mcp.md + regenerated config.md.
Covered by tests/unit/test_mcp_oauth_token_mint.py and
tests/unit/test_mcp_oauth_docs.py.
MCP aggregate egress tools/list on /mcp (#1294)¶
Status: Done (2026-10-02). Opt-in integrations.mcp_aggregate_egress.enabled
merges mcp_servers tools into /mcp tools/list as {server_id}__{tool};
tools/call routes via egress; server/tool policy filter; per-server list
failures degrade with mcp_aggregate_egress_list_failed. Docs: mcp.md +
regenerated config.md. Covered by tests/unit/test_mcp_aggregate_egress.py
and tests/unit/test_mcp_aggregate_egress_docs.py.
Escalate web_search_options to L6 (no silent local drop) (#1295)¶
Status: Done (2026-10-02). Chat web_search_options skips local tiers and
escalates to L6 (payload forwarded); no-frontier / frontier-off / budget →
clear 4xx/501 with web_search_unavailable; daari_meta.warning /
trace reason web_search_required. Docs: clients-and-gateways.md. Covered by
tests/unit/test_web_search_escalate.py.
Docs: hermetic pin for mcp_openapi_proxy and mcp_registry config (#1286)¶
Status: Done (2026-10-02). Hermetic config.md pin for
integrations.mcp_openapi_proxy.enabled, integrations.mcp_registry.enabled,
/mcp/proxy, and /v1/mcp/registry.json. Covered by
tests/unit/test_mcp_proxy_registry_config_docs.py.
Doctor tip when mcp_registry.enabled (#1287)¶
Status: Done (2026-10-02). Optional doctor tip when
integrations.mcp_registry.enabled advertises GET /v1/mcp/registry.json
(auth-agnostic) while /mcp still enforces keys; absent when disabled.
Docs: doctor-health.md. Covered by tests/unit/test_doctor_mcp_registry.py.
Docs: hermetic pin for web_search_options escalate note (#1305)¶
Status: Done (2026-10-02). Hermetic clients-and-gateways.md pin for
web_search_options escalate / fail-closed wording from #1295. Covered by
tests/unit/test_web_search_escalate_docs.py.
Docs: refresh ENTERPRISE.md after web_search escalate + MCP P2 wave (#1304)¶
Status: Done (2026-10-02). docs/prd/ENTERPRISE.md marks #1291–#1295
Shipped, keeps hot-reload (#6) Open, updates inward theme so web_search is
no longer a silent local drop, and adds a 2026-10-02 changelog line.
Doctor tip when mcp_aggregate_egress.enabled (#1306)¶
Status: Done (2026-10-02). Optional doctor tip when
integrations.mcp_aggregate_egress.enabled merges egress tools into /mcp
tools/list as {server_id}__{tool} with policy/SSRF still enforced; absent
when disabled. Docs: doctor-health.md. Covered by
tests/unit/test_doctor_mcp_aggregate_egress.py.
Unit pin WebSearchUnavailable HTTP status mapping (#1307)¶
Status: Done (2026-10-02). Direct unit coverage for
_web_search_unavailable_response status table (no_frontier→403,
frontier_disabled→501, frontier_budget_exceeded→402, tier_cap:L3→403)
and web_search_unavailable error type/code. Covered by
tests/unit/test_web_search_unavailable_status.py.
Doctor tip when mcp_oauth.local_as mints tokens (#1312)¶
Status: Done (2026-10-02). Optional doctor tip when
integrations.mcp_oauth.local_as mints via POST /oauth/token while /mcp
still requires a Bearer; absent when disabled. Docs: doctor-health.md.
Covered by tests/unit/test_doctor_mcp_oauth_local_as.py.
Doctor tip when decision_classifier.enabled (#1313)¶
Status: Done (2026-10-02). Optional doctor tip when
routing.decision_classifier.enabled runs an Ollama /v1/systemone
difficulty hop before heuristic tier pick (fallback on timeout/failure);
absent when disabled. Docs: doctor-health.md. Covered by
tests/unit/test_doctor_decision_classifier.py.
RFC 8693 OBO token exchange for egress mcp_servers (#1319)¶
Status: Done (2026-10-02). Opt-in per integrations.mcp_servers entry:
auth_type: oauth2_token_exchange with token_exchange_endpoint,
client_id, client_secret, optional audience / scopes /
subject_token_type. On tools/list and tools/call, exchanges the inbound
Authorization bearer (RFC 8693), caches until expiry, and attaches only the
exchanged token upstream. Fail closed: missing subject → 401; exchange error
or SSRF-blocked endpoint → 502 (log_gateway_event; secrets never logged).
Doctor tip mcp_token_exchange when any server uses OBO. Docs: mcp.md,
config.md, doctor-health.md. Covered by
tests/unit/test_mcp_token_exchange.py.
Refuse weak or unset master key at serve (#1320)¶
Status: Done (2026-10-02). daari serve refuses empty/whitespace or
denylisted master keys (sk-1234 / changeme / daari-local / …) before
bind unless server.dangerously_permit_weak_or_unset_api_key is true.
Doctor tip master_key_strength when the escape hatch is on or the key is
unset while virtual keys / mcp_oauth.local_as imply auth. Docs:
auth-and-keys.md, config.md, doctor-health.md. Covered by
tests/unit/test_refuse_weak_master_key.py.
Doctor tip when decision_classifier model missing from Ollama (#1321)¶
Status: Done (2026-10-02). When routing.decision_classifier.enabled is
true, doctor probes Ollama /api/tags for routing.decision_classifier.model
(default nimble); missing → optional tip with ollama pull <model>. Quiet
when the tag is present or Ollama is unreachable (existing ollama check).
Docs: doctor-health.md. Covered by
tests/unit/test_doctor_decision_classifier_model.py.
Extend /v1/daari/config ownership to decision_classifier + MCP knobs (#1322)¶
Status: Done (2026-10-03). Config editor ownership covers
routing.decision_classifier.* and MCP leaves (mcp_oauth.local_as /
protected_resource, mcp_aggregate_egress.enabled, mcp_registry.enabled)
with source/editable metadata; PATCH validates types, updates live Settings,
and deep-merges yaml on persist. Secrets stay non-editable. Docs: config.md,
http-api.md. Covered by tests/unit/test_config_ownership.py.
Hermetic pin for systemone facade docs (#1314)¶
Status: Done (2026-10-03). Unit pins assert ollama.md and http-api.md
mention POST /v1/systemone and systemone.enabled so the decision-model
facade cannot drift without CI failure. Covered by
tests/unit/test_systemone_docs.py.
Hermetic pin for decision_classifier config rows (#1315)¶
Status: Done (2026-10-03). Unit pin asserts config.md lists
routing.decision_classifier.enabled / .model / .timeout_seconds and
timeout fallback cues. Covered by tests/unit/test_decision_classifier_docs.py.
ASGI pin web_search_options when frontier disabled → 501 (#1316)¶
Status: Done (2026-10-03). ASGI chat with web_search_options and
frontier.enabled=false returns 501 web_search_unavailable /
frontier_disabled; absent-web_search local path unchanged. Covered by
tests/unit/test_web_search_escalate.py.
Hermetic pin doctor-health MCP/decision rows (#1323)¶
Status: Done (2026-10-03). Unit pins assert doctor-health.md rows for
mcp_aggregate_egress, mcp_oauth_local_as, and decision_classifier
include the tip config path and route cue. Covered by
tests/unit/test_doctor_health_mcp_aggregate_egress_docs.py,
tests/unit/test_doctor_health_mcp_oauth_local_as_docs.py, and
tests/unit/test_doctor_health_decision_classifier_docs.py.
Local watchdog serve after weak master-key gate (#1328)¶
Status: Done (2026-10-03). Watchdog com.daari.serve launchd unit sets
DAARI_SERVER__DANGEROUSLY_PERMIT_WEAK_OR_UNSET_API_KEY=true so #1320 does
not KeepAlive crash-loop sandbox serve. Cursor smoke is skipped when the
daemon is already unreachable; filed issues include serve.err.log tail.
Re-run scripts/autodev-local.sh --install to reload the plist. Covered by
tests/unit/test_autodev_local.py.
Watchdog dedupe local E2E issues by failure list (#1337)¶
Status: Done (2026-10-03). Filing skips when an open regression issue
has the same title or the same ## Failures bullets, so SHA-stamped
watchdog tickets no longer duplicate. Covered by
tests/unit/test_autodev_local.py.
Hermetic pin http-api config ownership classifier/MCP (#1338)¶
Status: Done (2026-10-03). Unit pin asserts http-api.md /v1/daari/config
ownership names decision_classifier and MCP knobs. Covered by
tests/unit/test_http_api_config_ownership_docs.py.
Watchdog skip live integration when daemon unreachable (#1339)¶
Status: Done (2026-10-03). Local watchdog skips pytest -m integration with
SKIP: live integration (daemon unreachable) when serve is already down, even
if Ollama /api/tags answers. Covered by tests/unit/test_autodev_local.py.
Opt-in compact-to-fit before L6 (#1340)¶
Status: Done (2026-10-03). routing.compact_to_fit (default off) trims
oldest unprotected chat turns before frontier escalate. System messages and
tool-call payloads are never dropped; if the remainder still exceeds the cap,
history stays oversized. Covered by tests/unit/test_compact_to_fit.py.
Watchdog sandbox master-key hatch docs (#1341)¶
Status: Done (2026-10-03). auth-and-keys.md documents the watchdog
DAARI_SERVER__DANGEROUSLY_PERMIT_WEAK_OR_UNSET_API_KEY plist hatch and
scripts/autodev-local.sh --install reload. Covered by
tests/unit/test_watchdog_sandbox_key_docs.py.
Extend /v1/daari/config ownership to compact_to_fit knobs (#1348)¶
Status: Done (2026-10-03). Config editor GET/PATCH expose
routing.compact_to_fit.enabled / .max_messages / .max_tokens with
source/editable metadata; PATCH validates bool / positive int / non-negative
int. Default remains off. Covered by tests/unit/test_config_ownership.py.
Doctor tip when compact_to_fit.enabled (#1349)¶
Status: Done (2026-10-03). Optional daari doctor row compact_to_fit
when routing.compact_to_fit.enabled is true (advisory, not a failure);
silent when off. Covered by tests/unit/test_doctor_compact_to_fit.py.
daari_meta and stats when compact_to_fit trims (#1351)¶
Status: Done (2026-10-03). Successful history trims before L6 set
daari_meta.compact_to_fit (messages_before / messages_after) and
increment /v1/daari/stats compact_to_fit_applied. Unchanged or
fail-closed (tool-protected) history does not claim a trim. Covered by
tests/unit/test_compact_to_fit.py.
Opt-in fail-closed MCP when key has no MCP grant (#1352)¶
Status: Done (2026-10-03). integrations.mcp_policy.require_key_access_defined
(default false) fail-closes virtual keys whose metadata.mcp lacks
allow / deny / servers.allow|deny: empty tools/list, tools/call
deny. Master key and flag-off unchanged. Doctor tip
mcp_require_key_access. Docs: mcp.md, config.md. Covered by
tests/unit/test_mcp_require_key_access.py,
tests/unit/test_doctor_mcp_require_key_access.py,
tests/integration/test_mcp_governance.py.
Hermetic pin compact_to_fit in config and routing-tiers (#1350)¶
Status: Done (2026-10-03). Unit pins assert config.md lists the three
routing.compact_to_fit.* keys with fail-closed / tool-payload wording, and
routing-tiers.md documents compact before L6 with the same rules. Covered by
tests/unit/test_compact_to_fit_docs.py.
Config ownership for mcp_policy.require_key_access_defined (#1359)¶
Status: Done (2026-10-03). GET/PATCH /v1/daari/config exposes
integrations.mcp_policy.require_key_access_defined with ownership metadata;
PATCH validates bool and persists. Covered by
tests/unit/test_config_ownership.py.
Hermetic pin require_key_access_defined in mcp and config (#1360)¶
Status: Done (2026-10-04). Unit pins assert config.md lists
integrations.mcp_policy.require_key_access_defined with empty tools/list
or tools/call deny / metadata.mcp grant wording, and mcp.md documents
fail-closed grants with master-key unchanged. Covered by
tests/unit/test_mcp_require_key_access_docs.py.
Hermetic pin compact_to_fit_applied in stats docs (#1361)¶
Status: Done (2026-10-04). traces-stats.md documents
compact_to_fit_applied incrementing only when a trim changes message
count. Covered by tests/unit/test_compact_to_fit_stats_docs.py.
ASGI pin servers-only MCP grant under require_key_access_defined (#1362)¶
Status: Done (2026-10-04). Flag on + metadata.mcp.servers.allow keeps a
non-empty first-party tools/list; clients alone still fail-closes.
Covered by tests/integration/test_mcp_governance.py.
Mark compact live-tune and MCP grant rows shipped in ENTERPRISE.md (#1363)¶
Status: Done (2026-10-04). ENTERPRISE.md rows 19–23 are Shipped; path retargeted to live config reload then remaining gaps. Changelog line for this pass.
Hermetic pin compact_to_fit_applied Prometheus series (#1369)¶
Status: Done (2026-10-04). metrics-prometheus.md lists
daari_compact_to_fit_applied_total for actual compact-to-fit trims.
Covered by tests/unit/test_metrics_prometheus_docs.py.
Hermetic pin ENTERPRISE compact and MCP grant rows shipped (#1370)¶
Status: Done (2026-10-04). Unit pin asserts ENTERPRISE.md gap rows for
compact ownership, doctor tip, docs pin, meta/stats, and MCP grant
fail-closed stay Shipped and do not say File this run. Covered by
tests/unit/test_enterprise_prd_docs.py.
Hermetic pin daari_meta.compact_to_fit on traces-stats (#1371)¶
Status: Done (2026-10-04). traces-stats.md documents
daari_meta.compact_to_fit (messages_before / messages_after) with
X-Daari-Meta: true. Covered by tests/unit/test_compact_to_fit_stats_docs.py.
Hermetic pin mcp.md servers-only grant vs clients-only (#1372)¶
Status: Done (2026-10-04). Unit pin asserts mcp.md treats
servers.allow/servers.deny as a grant under
require_key_access_defined and a clients block alone as not a tool
grant. Covered by tests/unit/test_mcp_require_key_access_docs.py.
Hermetic pin http-api compact_to_fit ownership leaves (#1373)¶
Status: Done (2026-10-04). Unit pin asserts http-api.md documents
routing.compact_to_fit on GET/PATCH /v1/daari/config ownership.
Covered by tests/unit/test_http_api_config_ownership_docs.py.
Prometheus pin compact_to_fit_applied series increment (#1379)¶
Status: Done (2026-10-04). render_prometheus emits
daari_compact_to_fit_applied_total 0 on empty metrics and 1 after
record_compact_to_fit_applied(). Covered by
tests/unit/test_prometheus.py.
MCP initialize fail-closed without key grant (#1389)¶
Status: Done (2026-10-04). When require_key_access_defined is true,
virtual keys with no MCP grant fail initialize (HTTP 403, JSON-RPC
-32003). Granted keys, master key, and flag-off still handshake.
tools/list empty catalog and tools/call deny unchanged. Covered by
tests/unit/test_mcp_require_key_access.py and
tests/integration/test_mcp_governance.py.
CLI pin daari stats --help for compact_to_fit_applied (#1380)¶
Status: Done (2026-10-04). daari stats --help names
compact_to_fit_applied alongside other stats JSON keys. Covered by
tests/unit/test_stats_cli_help.py.
Docs pin headers.md X-Daari-Meta compact_to_fit (#1381)¶
Status: Done (2026-10-04). headers.md documents compact_to_fit
(messages_before / messages_after) on X-Daari-Meta. Covered by
tests/unit/test_compact_to_fit_stats_docs.py.
HTTP pin require_key_access_defined ownership (#1382)¶
Status: Done (2026-10-04). Unit pin asserts http-api.md documents
require_key_access_defined on GET/PATCH /v1/daari/config ownership.
Covered by tests/unit/test_http_api_config_ownership_docs.py.
Docs pin cli.md stats compact_to_fit_applied (#1383)¶
Status: Done (2026-10-04). cli.md stats row names
compact_to_fit_applied on GET /v1/daari/stats JSON. Covered by
tests/unit/test_stats_cli_help.py.
MCP grant fail-closed dedicated counter (#1390)¶
Status: Done (2026-10-04). mcp_grant_denied / daari_mcp_grant_denied_total
increments on initialize, tools/list, and tools/call when
require_key_access_defined fail-closed applies. Ordinary tool-name deny
does not. Series present at 0 when unused. Covered by
tests/unit/test_prometheus.py and tests/unit/test_mcp_require_key_access.py.
Grafana compact_to_fit_applied overview panel (#1391)¶
Status: Done (2026-10-04). Overview dashboard charts
daari_compact_to_fit_applied_total under Compact-to-fit applied.
Covered by tests/unit/test_grafana_dashboard.py.
Stats for tokens dropped by compact_to_fit (#1392)¶
Status: Done (2026-10-04). When compact-to-fit actually trims, increment
compact_to_fit_tokens_dropped / daari_compact_to_fit_tokens_dropped_total
by tokens_before - tokens_after. Meta includes tokens_before /
tokens_after. Covered by tests/unit/test_compact_to_fit.py and
tests/unit/test_prometheus.py.
Refresh compare-litellm for LiteLLM 1.104 GA (#1393)¶
Status: Done (2026-10-04). compare-litellm.md notes 1.104 GA parity for
master-key refuse, compact-to-fit, and MCP grant fail-closed; stdio MCP
marked N/A/watch. Covered by tests/unit/test_compare_litellm_1104.py.
Grafana compact_to_fit_tokens_dropped overview panel (#1400)¶
Status: Done (2026-10-05). Overview dashboard charts
daari_compact_to_fit_tokens_dropped_total under Compact-to-fit tokens
dropped. Covered by tests/unit/test_grafana_dashboard.py.
Grafana mcp_grant_denied overview panel (#1401)¶
Status: Done (2026-10-05). Overview dashboard charts
daari_mcp_grant_denied_total under MCP grant denied. Covered by
tests/unit/test_grafana_dashboard.py.
CLI pin stats help for compact_to_fit_tokens_dropped (#1402)¶
Status: Done (2026-10-05). daari stats --help and cli.md mention
compact_to_fit_tokens_dropped. Covered by
tests/unit/test_stats_cli_help.py.
ENTERPRISE operate-gates rows marked shipped (#1403)¶
Status: Done (2026-10-05). PRD rows 24–28 (Grafana compact, grant counter,
initialize refuse, token-drop stats, compare-litellm 1.104) marked Shipped;
inward theme and path no longer treat those as open. Covered by
tests/unit/test_enterprise_prd_docs.py.
compact_to_fit trace step tokens_before/after (#1404)¶
Status: Done (2026-10-05). Successful compact-to-fit trims record
tokens_before / tokens_after on the compact_to_fit trace step; no-op
paths still omit the step. Covered by tests/unit/test_compact_to_fit.py.
routing-tiers compact_to_fit tokens_before/after (#1410)¶
Status: Done (2026-10-05). routing-tiers.md notes tokens_before /
tokens_after and compact_to_fit_tokens_dropped when a trim changes
message count. Covered by tests/unit/test_compact_to_fit_docs.py.
doctor compact_to_fit tip mentions token-drop stats (#1411)¶
Status: Done (2026-10-05). Optional compact_to_fit doctor tip names
tokens_before / tokens_after and compact_to_fit_tokens_dropped when
compact is enabled. Covered by tests/unit/test_doctor_compact_to_fit.py.
CLI pin trace compact_to_fit token fields (#1412)¶
Status: Done (2026-10-05). cli.md trace row documents compact_to_fit
tokens_before / tokens_after. Covered by
tests/unit/test_cli_reference_catalog.py.
HTTP API pin traces compact_to_fit token fields (#1413)¶
Status: Done (2026-10-05). http-api.md notes compact_to_fit trace step
tokens_before / tokens_after when a trim ran. Covered by
tests/unit/test_http_api_traces_compact_docs.py.
ARCHITECTURE pin compact_to_fit token fields (#1418)¶
Status: Done (2026-10-05). ARCHITECTURE.md routing overview names
routing.compact_to_fit and tokens_before / tokens_after. Covered by
tests/unit/test_architecture_introspect.py.
README pin compact-to-fit in feature snapshot (#1419)¶
Status: Done (2026-10-05). README routing snapshot mentions compact-to-fit
tokens_before / tokens_after and compact_to_fit_tokens_dropped. Covered by
tests/unit/test_gtm_public_copy.py.
Docs: compare-litellm compact-to-fit token telemetry (#1420)¶
Status: Done (2026-10-05). compare-litellm 1.104 compact row names
tokens_before / tokens_after and compact_to_fit_tokens_dropped. Covered by
tests/unit/test_compare_litellm_1104.py.
Web-ui shows compact_to_fit tokens on traces (#1421)¶
Status: Done (2026-10-06). Trace detail panel surfaces compact_to_fit
tokens_before / tokens_after when the step is present (plus full JSON).
Covered by packages/web-ui/test/dashboard.test.js.
Docs: config.md pin compact_to_fit FinOps token fields (#1422)¶
Status: Done (2026-10-06). routing.compact_to_fit.enabled description
documents tokens_before / tokens_after and compact_to_fit_tokens_dropped
(via settings Field + gen_reference). Covered by
tests/unit/test_compact_to_fit_docs.py.
Web-ui stats show compact_to_fit_tokens_dropped and mcp_grant_denied (#1429)¶
Status: Done (2026-10-06). Dashboard summary cards render
compact_to_fit_tokens_dropped and mcp_grant_denied from
/v1/daari/stats (zeros when absent). Covered by
packages/web-ui/test/dashboard.test.js.
Config ownership for observability.structured_json_logs (#1430)¶
Status: Done (2026-10-06). GET/PATCH /v1/daari/config exposes
observability.structured_json_logs with ownership metadata; PATCH
validates bool and updates live configure_request_log without restart.
Covered by tests/unit/test_config_ownership.py.
Doctor tip for observability.structured_json_logs (#1431)¶
Status: Done (2026-10-06). daari doctor emits an optional tip when
observability.structured_json_logs is on (stdout JSON); quiet when off.
Covered by tests/unit/test_doctor_structured_json_logs.py.
Docs: pin compact_to_fit_tokens_dropped in savings-report (#1432)¶
Status: Done (2026-10-06). Savings-report guide points operators at
compact_to_fit_tokens_dropped and trace tokens_before / tokens_after.
Covered by tests/unit/test_savings_report_compact_docs.py.
Docs: LiteLLM 1.105 RC watch in compare-litellm (#1433)¶
Status: Done (2026-10-06). compare-litellm notes LiteLLM 1.105 as RC/watch
(M365 MCP catalog, Straiker, Lens/agent traces) while keeping 1.104 GA as
the stable floor. Covered by tests/unit/test_compare_litellm_1104.py.
Require master key for config writes when SSO is off (#1441)¶
Status: Done (2026-10-06). With SSO off and a master key set, virtual keys
get 403 on PATCH /v1/daari/config (and other _require_admin_role surfaces);
master key still succeeds. GET stays viewer-style for virtual keys. Sandbox
(no master key) stays open; daari doctor warns config_editor_ungoverned.
Covered by tests/unit/test_config_admin_master_key.py.
Add gpt-6.1-sol pricing, capabilities, and param compat (#1442)¶
Status: Done (2026-10-06). Shipped $2/$10 (cached $0.10, cache write
$2.50) for gpt-6.1-sol with capabilities + GPT-6 family param compat; no
invented above-272K surcharge. Covered by
tests/unit/test_gpt_6_1_sol_pricing.py.
Forward systemone multimodal images + rate family (#1443)¶
Status: Done (2026-10-06). POST /v1/systemone accepts and forwards
Ollama 0.35.1+ images (base64 list); rate_limit_family maps the path to
systemone. Covered by tests/unit/test_systemone.py,
tests/unit/test_rate_families.py.
Add claude-sonnet-5-5 pricing, capabilities, and param compat (#1450)¶
Status: Done (2026-10-06). Shipped $2/$10 (cached $0.20, 1h cache write
$4.00) for claude-sonnet-5-5 with Claude 5.x capabilities and strip of
non-default temperature/top_p/top_k. Covered by
tests/unit/test_claude_sonnet_5_5_pricing.py.
Web-ui: escape DOM, CSP, session key storage (#1444)¶
Status: Done (2026-10-06). Dashboard renders API fields via textContent,
ships a restrictive CSP meta, and keeps the Bearer key in memory /
sessionStorage (opt-in) instead of localStorage. Covered by
packages/web-ui/test/dashboard.test.js.
OCR modality Prometheus label, guardrails, OTel span (#1445)¶
Status: Done (2026-10-06). ocr is a first-class Prometheus modality;
OCR page markdown runs through output guardrails; upstream calls emit a
daari.ocr CLIENT span. Covered by tests/unit/test_ocr.py,
tests/unit/test_modality_metrics.py.
Anthropic Models API line on native /v1/models (#1451)¶
Status: Done (2026-10-06). Anthropic-shaped model cards include nullable
line from an explicit Claude family map; local/unknown ids stay null.
Covered by tests/unit/test_anthropic_gateway.py.
Prometheus latency and outcome series for decision classifier (#1452)¶
Status: Done (2026-10-07). daari_decision_classifier_total{outcome} and
daari_decision_classifier_latency_ms for the optional /v1/systemone
difficulty hop (success / heuristic_fallback / error / skipped).
Covered by tests/unit/test_decision_classifier.py,
tests/unit/test_prometheus.py.
Classify fatal serve config and stop KeepAlive crash-loop spam (#1453)¶
Status: Done (2026-10-07). Watchdog classifies master_key / hard refuse
stderr, skips kickstart, unloads serve for the cycle, and includes the
reason in regression issue bodies. Covered by
tests/unit/test_autodev_local.py.
compact_to_fit counts images/audio and protects multimodal turns (#1454)¶
Status: Done (2026-10-07). estimate_tokens adds per-image / per-audio
heuristics; image-only and audio-only turns are protected like tool pairs.
Covered by tests/unit/test_compact_to_fit.py.
Watchdog cursor smoke overwrites stale success JSON (#1425)¶
Status: Done (2026-10-07). Cursor-shaped smoke runs via
autodev_local.py cursor-smoke and always rewrites smoke-latest.json,
including on ConnectError, so FAIL logs cannot show a prior cycle's
status_code: 200 / content_chunks > 0. Daemon crash-loop root cause
remains covered by #1453. Covered by tests/unit/test_autodev_local.py.
grok-4.7 pricing, capabilities, and param compat (#1466)¶
Status: Done (2026-10-07). Lab $2/$6 + $0.50 cache read for grok-4.7
(LiteLLM 1.105 RC day-one); capabilities match other frontier chat IDs; no
sampler strip (none published). Covered by tests/unit/test_grok_4_7_pricing.py.
claude-opus-5-5 pricing, capabilities, and param compat (#1467)¶
Status: Done (2026-10-07). Lab $4/$20 + cache rates for claude-opus-5-5
(LiteLLM 1.104); capabilities + Anthropic line: opus; sampler strip matches
Sonnet 5.5. Covered by tests/unit/test_claude_opus_5_5_pricing.py.
MCP OAuth mint/deny audit_log events (#1468)¶
Status: Done (2026-10-07). POST /oauth/token records
mcp_oauth.token_minted / mcp_oauth.token_denied via the enterprise audit
sink (kind, expires_in, scope, token fingerprint — never the raw token).
Covered by tests/unit/test_mcp_oauth_token_mint.py.
OpenAI-compatible /v1/decisions on local models + gpt-6-luna (#1474)¶
Status: Done (2026-10-07). POST /v1/decisions accepts the OpenAI
Decisions beta shape; local decision models (nimble/clef/…) run via the
systemone executor; gpt-6-luna passes through L6 with no_frontier /
region_pin fences. Pricing $0.10/1M input (no output), decisions rate
family, cost headers + usage ledger. Covered by tests/unit/test_decisions.py.
MCP proxy response cost headers (#1469)¶
Status: Done (2026-10-07). Successful /mcp/proxy tools/call responses
emit the standard x-daari-response-cost* set via modality_response_headers
(zero USD when no model tokens). tools/list omits cost headers. Covered by
tests/unit/test_mcp_openapi_proxy.py.
MCP OAuth scope enforcement + RFC 7009 revocation (#1475)¶
Status: Done (2026-10-08). Local AS mint rejects scopes outside
scopes_supported (invalid_scope + audit); /mcp requires the mcp scope
claim (403 + audit); POST /oauth/revoke denylists by jti until remaining
exp; AS metadata advertises revocation_endpoint. Covered by
tests/unit/test_mcp_oauth_token_mint.py.
OTel, guardrails, and retry on /mcp/proxy + /v1/systemone (+ OCR input) (#1476)¶
Status: Done (2026-10-08). /mcp/proxy and /v1/systemone emit OTel client
spans and wrap upstream HTTP in run_upstream; MCP guardrails screen proxy
tool args/results; systemone screens state/questions (+ answer output);
OCR applies input guardrails before dispatch. Covered by
tests/unit/test_mcp_openapi_proxy.py, tests/unit/test_systemone.py,
tests/unit/test_ocr.py.
Read-only keys/teams/spend admin plane (#1477)¶
Status: Done (2026-10-08). Admin-gated GET /v1/daari/keys and
GET /v1/daari/teams return redacted inventories (id/alias/team/budgets/tier
caps/expiry + current spend; never secrets or hashes). Web-ui adds Keys and
Teams sections plus spend breakdown from report.teams /
report.model_group_spend. Covered by tests/unit/test_admin_read_plane.py
and packages/web-ui/test/dashboard.test.js.
Responses multi-agent beta fields (#1478)¶
Status: Done (2026-10-08). ResponsesRequest.multi_agent is declared and
forwarded on the L6 OpenAI payload (plus OpenAI-Beta:
responses_multi_agent=v1); local / no-frontier paths return an honest 400.
unsupported_responses_tools is wired at ingress. Covered by
tests/unit/test_responses_multi_agent.py.
Anthropic capabilities.thinking.types.disabled (#1481)¶
Status: Done (2026-10-08). Anthropic-native /v1/models cards include
capabilities.thinking.types.disabled.supported (true by default, including
local ids). Covered by tests/unit/test_anthropic_gateway.py.
Age-encrypt backup create/restore round-trip (#1482)¶
Status: Done (2026-10-08). Hermetic age encrypt/restore covers virtual keys,
spend, and audit when age is on PATH, and skips when it is not. Missing
recipient and missing identity stay fail-closed with BackupError. Covered by
tests/unit/test_backup_restore.py.
Grafana panels for decision classifier series (#1483)¶
Status: Done (2026-10-08). Overview dashboard charts
daari_decision_classifier_total by outcome and
daari_decision_classifier_latency_ms p50/p95. Covered by
tests/unit/test_grafana_dashboard.py.
Decisions-path input-only rates for gpt-6-luna (#1484)¶
Status: Done (2026-10-08). Chat/Responses keep $0.10 / $0.50 for
gpt-6-luna. POST /v1/decisions bills that id input-only ($0.10, $0 output,
$0 cache read/write) via billing_path="decisions". Covered by
tests/unit/test_gpt_6_luna_decisions_pricing.py and
tests/unit/test_decisions.py.
Guardrails + OTel + local retry on /v1/decisions (#1497)¶
Status: Done (2026-10-08). /v1/decisions screens flattened input+questions
and answer text via endpoint guardrails, opens daari.decisions OTel spans,
routes the local Ollama hop through _post_systemone retry, and binds optional
OpenAI-shaped user onto spend context. Covered by tests/unit/test_decisions.py.
Normalize gpt-6-luna-decisions aliases on /v1/decisions (#1485)¶
Status: Done (2026-10-08). normalize_decisions_model maps
gpt-6-luna / gpt-6-luna-decisions / openai/gpt-6-luna-decisions to the
frontier Decisions target; unknown ids return 400. Covered by
tests/unit/test_decisions.py.
Add claude-haiku-5-5 pricing + reprice sonnet/opus-5-5 cache reads (#1505)¶
Status: Done (2026-10-09). claude-haiku-5-5 catalog with ≤100K rates and
100K-threshold scaling (cache read $0.05 / 1h write $1.00 above); Sonnet/Opus
5.5 cache reads halved to $0.10 / $0.20; capabilities, Anthropic line: haiku,
and sampler strip. Covered by tests/unit/test_claude_haiku_5_5_pricing.py.
Bill gpt-6-luna chat/responses cached input at $0.01/MTok (#1506)¶
Status: Done (2026-10-09). Chat/Responses gpt-6-luna sets
cached_input_per_1m: 0.01 so prompt-cache hits are not billed at the $0.10
input rate; Decisions overlay still zeros cache/output. Covered by
tests/unit/test_gpt_6_luna_decisions_pricing.py.
Fleet-durable MCP OAuth revoked-jti denylist (#1498)¶
Status: Done (2026-10-09). Revoked JTIs use Redis (cache.backend=redis) or
Postgres (observability.postgres_url, memory: in tests); single-pod keeps
in-process. Doctor warns on multi-replica local_as without a shared store;
denylist is TTL-bound and not backup-critical. Covered by
tests/unit/test_revoked_jti_store.py.
Paginate + audit GET /v1/daari/keys and /teams (#1499)¶
Status: Done (2026-10-09). Admin inventories accept limit/offset
(default cap 100, max 500) and return total/has_more; successful GETs
emit admin.keys.list / admin.teams.list audit events. Covered by
tests/unit/test_admin_read_plane.py.
Meter multi_agent subagent token usage on L6 (#1500)¶
Status: Done (2026-10-09). OpenAI aggregated top-level usage (includes
subagents) stays the metered total; when nested agent_usages /
usage_by_agent under-count relative to top-level, parent+nested tokens and
cost are summed before ledger write. Per-agent rows land on
daari_meta.agent_usage for attribution without double-billing. Covered by
tests/unit/test_responses_multi_agent.py.
Anthropic server_tools + lifecycle on /v1/models (#1507)¶
Status: Done (2026-10-09). Anthropic-native model cards expose
lifecycle / deprecated_at / retires_at and
capabilities.server_tools (Claude 5.x true; locals false). List honors
lifecycle query (default active+deprecated; retired only when requested).
Covered by tests/unit/test_anthropic_gateway.py.
Decisions model/user budget 402 + user_id ledger (#1518)¶
Status: Done (2026-10-09). POST /v1/decisions applies the same
model_group / model_max_budget / user_daily_usd_cap 402 pre-checks as
chat before any local or frontier call. usage_ledger.record carries
user_id from the request body so /v1/daari/report users attributes
decisions traffic. Covered by tests/unit/test_decisions.py.
Decisions L0 exact cache for typed judgments (#1508)¶
Status: Done (2026-10-09). Identical POST /v1/decisions requests
(normalized model + input + questions) hit the shared L0 exact cache with
daari_meta.tier=L0 / cache_hit=true. Default-on via
decisions.cache_enabled (requires cache.l0.enabled);
X-Daari-No-Cache: true bypasses. Guardrail blocks never write. Prom
records L0 hits with modality=decisions. Covered by
tests/unit/test_decisions.py and
tests/integration/test_gateway_flow.py.
Map OpenAI chat-latest rolling alias in catalog (#1509)¶
Status: Done (2026-10-09). chat-latest prices at the published ChatGPT
Instant rates ($5 / $0.50 cached / $30) as a rolling alias (snapshot moves;
prefer gpt-6-astra for pins). Capabilities + /v1/models when configured
as a frontier provider. Covered by tests/unit/test_chat_latest_pricing.py.
Alias service_tier fast to 2.0× (OpenAI Fast mode) (#1527)¶
Status: Done (2026-10-09). service_tier: "fast" (OpenAI Fast mode,
former Priority) multiplies cost_usd at 2.0× like priority; no
service_tier_ignored. Chat + Responses HTTP models forward fast
unchanged. Docs: budgets-frontier service-tier note. Covered by
tests/unit/test_service_tier.py.
Ultrafast service_tier (6×) with model + EU gating (#1519)¶
Status: Done (2026-10-09). service_tier: "ultrafast" bills at 6×
Standard for gpt-6.1-sol / gpt-6-astra; unsupported models and
EU-pinned Astra clear the tier with daari_meta.warning
(ultrafast_unsupported_model / ultrafast_eu_unsupported). Docs:
budgets-frontier. Covered by tests/unit/test_service_tier.py.
claude-haiku-5-5 fidelity (budget_tokens, 5m write, cards) (#1520)¶
Status: Done (2026-10-09). Haiku 5.5 egress maps budget_tokens
thinking to adaptive + output_config.effort with a daari_meta warning;
5m cache writes bill $0.125 / $0.625 (above 100K); Anthropic /v1/models
cards advertise 1M context, 128K max output, adaptive default medium.
Covered by tests/unit/test_claude_haiku_5_5_pricing.py.
OpenAI /v1/models lifecycle + server_tools and retired routing (#1521)¶
Status: Done (2026-10-10). OpenAI-shaped list/retrieve cards expose the
same lifecycle / server_tools data as the Anthropic facade; lifecycle
query filter works on both shapes. Catalog retired models fail closed with
HTTP 410 (models.reject_retired, default true); deprecated models still
serve with daari_meta.warning=model_deprecated. Covered by
tests/unit/test_openai_models_lifecycle.py.
Pooled Postgres revoked-jti denylist hot path (#1522)¶
Status: Done (2026-10-10). PostgresRevokedJtiStore uses the shared
psycopg_pool (#975); is_revoked / revoke prune on the same checkout
(interval-throttled DELETE) so MCP token validation no longer opens two raw
connections per lookup. Redis + in-process backends unchanged. Covered by
tests/unit/test_revoked_jti_store.py.
Honor Idempotency-Key on POST /v1/decisions (#1528)¶
Status: Done (2026-10-10). Idempotency-Key replays local and frontier
POST /v1/decisions responses from the on-box store without a second
route/upstream call; same key + different body → 409 idempotency_conflict;
missing key is a no-op. Docs: headers.md, config.md. Covered by
tests/unit/test_idempotency.py.
Reject stream=true on POST /v1/decisions (#1529)¶
Status: Done (2026-10-10). stream: true on Decisions returns HTTP 400
(invalid_request) stating streaming is unsupported; stream: false /
omitted continue to succeed. OpenAPI does not advertise a stream field.
Covered by tests/unit/test_decisions.py.
Paginate GET /v1/daari/audit with limit/offset (#1530)¶
Status: Done (2026-10-10). Audit list clamps limit/offset like keys/teams
and returns entries, total, limit, offset, has_more (default cap 100).
SQLite + Postgres audit stores gain count() and list(offset=). Covered by
tests/unit/test_admin_read_plane.py.
OpenAI regional-processing 10% uplift when region_pin set (#1531)¶
Status: Done (2026-10-10). cost_usd applies 1.10× for us/eu region_pin
on OpenAI models released on or after 2026-03-05 (chat, Responses headers,
Decisions input-only, spend ledger). Unset pin unchanged. Docs:
budgets-frontier, data-residency. Covered by
tests/unit/test_regional_pricing.py.
Refresh ENTERPRISE.md after Oct 7–10 drain (#1470)¶
Status: Done (2026-10-10). Living PRD marks rows 55–59 Shipped, rewrites
stand + next-5 for ops-docs / hot-reload / MCP allowlist, and drops watch
rows that already landed. Covered by tests/unit/test_enterprise_prd_docs.py.
Doctor + Helm + auth-and-keys coverage for /v1/decisions (#1501)¶
Status: Done (2026-10-10). Doctor decisions tip for systemone/frontier
gaps; Helm values comment for decisions/systemone via ollama+frontier;
auth-and-keys lists decisions rate family. Covered by
tests/unit/test_doctor_decisions.py, test_doctor_health_decisions_docs.py,
test_auth_and_keys_decisions_family.py.
Filter GET /v1/models by virtual-key allowlist (#1555)¶
Status: Done (2026-10-10). OpenAI and Anthropic /v1/models list payloads
honor key ∪ team allowed_models / model groups (master / unrestricted VK
unchanged). Retrieve returns 404 outside the allowlist; lifecycle filters
compose. Docs: virtual-keys. Covered by
tests/unit/test_models_catalog_allowlist.py.
Track last_used_at on virtual keys + status/idle filters (#1545)¶
Status: Done (2026-10-10). SQLite + Postgres VK stores gain nullable
last_used_at (throttled touch on successful auth). Surfaced on CLI list/show
and GET /v1/daari/keys; --status / ?status= and --idle-days /
?idle_days= filter inventory. Docs: virtual-keys. Covered by
tests/unit/test_vk_last_used.py.
Proactive key-expiry signals — doctor, Prometheus, webhook (#1546)¶
Status: Done (2026-10-10). daari doctor warns on keys expiring within
alerts.key_expiry_warn_days (default 14) and counts expired-but-not-revoked.
Prometheus daari_virtual_keys{status=...} gauge refreshes on scrape. Opt-in
signed key_expiry webhook (Redis-deduped, fail-open). Covered by
tests/unit/test_doctor_key_expiry.py and tests/unit/test_key_expiry_alerts.py.
Retired-model audit, reject metric, doctor lifecycle (#1549)¶
Status: Done (2026-10-10). Retired 410s write models.retired_denied audit
rows and increment daari_rejects_total{kind="retired"}. daari doctor
errors on configured retired tier/frontier/pool models and tips on deprecated.
Covered by tests/unit/test_model_lifecycle_signals.py.
Decisions L0 cache_scope tenancy isolation test (#1554)¶
Status: Done (2026-10-10). Hermetic ASGI test: two cache_scope=key VKs
miss each other's identical /v1/decisions bodies; same key hits L0. Optional
same-team hit / cross-team miss for cache_scope=team. Covered by
tests/unit/test_decisions_cache_scope.py.
Doctor tip for unsigned key_expiry webhooks (#1563)¶
Status: Done (2026-10-10). Optional doctor key_expiry_webhook_secret
warns when alerts.key_expiry_webhook_url is set without a signing secret.
Covered by tests/test_doctor.py.
Grafana panels for virtual_keys lifecycle and retired rejects (#1564)¶
Status: Done (2026-10-11). Stock Grafana overview adds Virtual keys by
status (daari_virtual_keys{status}) and Retired model rejects
(daari_rejects_total{kind="retired"}). Covered by
tests/unit/test_grafana_dashboard.py.
Hermetic scrape refreshes daari_virtual_keys gauge (#1566)¶
Status: Done (2026-10-11). ASGI/prometheus_text_for_app pin: mixed
active / expiring_soon / expired / revoked keys appear on GET /metrics;
enabled key_expiry alerter posts once on scrape. Covered by
tests/unit/test_metrics_virtual_keys.py.
Docs: key_expiry webhook + warn_days next to budget alerts (#1565)¶
Status: Done (2026-10-11). Operator guides document
key_expiry_webhook_url / _secret / warn_days, payload
event: key_expiry, X-Daari-Signature reuse, doctor key_expiry, and
daari_virtual_keys. Covered by tests/unit/test_key_expiry_docs.py.
Docs: keys list --status / --idle-days in cli.md (#1571)¶
Status: Done (2026-10-11). CLI reference documents keys list --status
and --idle-days plus last_used_at throttle. Covered by
tests/unit/test_keys_list_cli_docs.py.
Pin keys list --help for --status and --idle-days (#1572)¶
Status: Done (2026-10-11). Unit test asserts daari keys list --help
mentions --status and --idle-days. Covered by
tests/unit/test_keys_list_cli_help.py.
Stats virtual_keys lifecycle counts (#1573)¶
Status: Done (2026-10-11). GET /v1/daari/stats includes virtual_keys
(active / expired / expiring_soon / revoked) matching the Prom gauge.
Docs: metrics-prometheus / traces-stats. Covered by
tests/unit/test_stats_virtual_keys.py.
Pin Grafana dashboard panel ids unique (#1574)¶
Status: Done (2026-10-11). Unit pin asserts every panel id in
deploy/grafana/daari-dashboard.json is unique. Covered by
tests/unit/test_grafana_dashboard.py.
Doctor-health docs name key_expiry checks (#1575)¶
Status: Done (2026-10-11). Doc pin asserts doctor-health.md names
key_expiry and key_expiry_webhook_secret. Covered by
tests/unit/test_doctor_key_expiry_docs.py.
Keys export/import round-trips last_used_at, priority, cache_scope (#1581)¶
Status: Done (2026-10-11). daari keys export / import carry
last_used_at, priority, and cache_scope (additive on schema 1; older
docs default). Docs: virtual-keys.md backup section. Covered by
tests/unit/test_keys_export_import.py.
RBAC analyst floors on route preview / diversity / learn / feedback (#1588)¶
Status: Done (2026-10-11). GET/POST /v1/daari/route/preview,
GET /v1/daari/cache/diversity, GET /v1/daari/learn/stats, and
POST /v1/daari/feedback require analyst via _require_role (same SSO-off
viewer convention as stats). Docs: auth-and-keys role matrix. Covered by
tests/unit/test_daari_admin_role_floors.py and SSO RBAC matrix.
Doctor tip for empty VK allowlist∩catalog (#1582)¶
Status: Done (2026-10-11). daari doctor emits optional vk_empty_allowlist
when an active virtual key's non-empty allowed_models / model_groups match
zero effective catalog cards. Covered by
tests/unit/test_doctor_vk_empty_allowlist.py.
Local decision-model guardrail check (#1589)¶
Status: Done (2026-10-11). Guardrail kind: decision evaluates a local
/v1/systemone predicate (no frontier egress); on_error warn/block.
Docs: guardrails.md decision-model checks. Covered by
tests/unit/test_guardrails_decision.py.
Configurable local decisions catalog (#1590)¶
Status: Done (2026-10-11). Built-in local decisions catalog adds
drex-1.5 / microsoft-decision-1; decisions.local_models extends it
(PATCH/ownership). Doctor tip decisions_local_models. Docs: ollama.md.
Covered by tests/unit/test_decisions_local_catalog.py.
Helm NetworkPolicy + topology spread (#1591)¶
Status: Done (2026-10-12). Optional networkPolicy.enabled (default
off) renders ingress/egress NetworkPolicy; affinity /
topologySpreadConstraints pass through to the Deployment. Docs:
capacity-helm.md. Covered by tests/unit/test_helm_chart.py.
Per-key concurrency + named per-model rate map (#1592)¶
Status: Done (2026-10-12). VirtualKey/Team.max_in_flight and
rate_limit.models (exact/longest-prefix) with Redis/SQLite gauges;
rejects kinds concurrency vs key_concurrency/team_concurrency.
Docs: auth-and-keys.md. Covered by tests/unit/test_rate_limit.py.
Team cache_scope/priority in keys export/import (#1595)¶
Status: Done (2026-10-12). export_document / import_document round-trip
team cache_scope and priority (SQLite + Postgres); legacy docs default
to global/normal. Docs: virtual-keys.md Backup. Covered by
tests/unit/test_keys_export_import.py.
Web UI keys/teams last_used_at, priority, cache_scope (#1596)¶
Status: Done (2026-10-12). Admin keys table shows last_used_at (or
"never"), priority, and cache_scope; teams table shows priority and
cache_scope. XSS-safe text cells. Covered by
packages/web-ui/test/dashboard.test.js.
Web UI keys status and idle_days filters (#1597)¶
Status: Done (2026-10-12). Virtual keys section filters via
status / idle_days query params on GET /v1/daari/keys; status line
reflects filter and has_more. Covered by
packages/web-ui/test/dashboard.test.js.
Bench Decisions / System One vs LiteLLM (#1599)¶
Status: Done (2026-10-12). bench_vs_litellm corpus adds /v1/decisions
and /v1/systemone scenarios; skips LiteLLM when the peer lacks the route;
report section with ms + speedup. Covered by
tests/unit/test_bench_vs_litellm.py.
Guardrails decision-rule data plane (#1610)¶
Status: Done (2026-10-12). Decision predicates offload via
asyncio.to_thread / async client; incremental streams defer decision rules
to final flush; chat blocks write guardrail.blocked audit rows;
daari_guardrail_trips_total gains a kind label; optional per-rule
timeout_seconds. Covered by tests/unit/test_guardrails_decision.py.
Teams metadata export/import + Postgres parity (#1611)¶
Status: Done (2026-10-12). Team metadata_json (max_in_flight,
model_group_budgets, model_max_budget) round-trips through keys
export/import; Postgres create_team / export / import persist the same
fields. Covered by tests/unit/test_keys_export_import.py.
Audit budget / rate-limit / concurrency rejects (#1616)¶
Status: Done (2026-10-12). Hard 402/429/503 control-plane rejects write
budget.exceeded, rate_limit.exceeded, and concurrency.exceeded audit
rows (scope detail, no prompts/secrets). Covered by
tests/unit/test_reject_audit.py.
Operable concurrency caps — CLI/admin/Helm/gauges (#1612)¶
Status: Done (2026-10-13). --max-in-flight on keys create /
team-create; list/export --json + admin/web-ui expose the cap; Helm
rateLimit.maxInFlight / rateLimit.models; scrape
daari_in_flight{scope,id} / _max. Covered by
tests/unit/test_keys_max_in_flight_cli.py,
tests/unit/test_metrics_in_flight_scopes.py,
tests/unit/test_helm_chart.py.
Helm external netpol egress + scheduling knobs (#1613)¶
Status: Done (2026-10-13). NetworkPolicy Redis/Postgres egress when
rateLimit.redisUrl / postgres.url are set; extraEgress; Deployment
priorityClassName / nodeSelector / tolerations / initContainers /
extraEnv. Docs: capacity-helm.md. Covered by
tests/unit/test_helm_chart.py, tests/unit/test_capacity_helm_docs.py.
Hermetic RPM window clock for flake-free tests (#1614)¶
Status: Done (2026-10-13). Counter backends bucket via _wall_time()
(monkeypatchable); test_rpm_returns_429 freezes mid-window and at :59.9.
Covered by tests/unit/test_rate_limit_clock.py,
tests/unit/test_virtual_keys.py.
Soft rate-limit warning with concurrency gauge binding (#1617)¶
Status: Done (2026-10-13). Soft RPM/TPM warning header and metric stay
when a tighter per-key/team/model max_in_flight gauge wins
X-RateLimit-* headers. Docs: headers.md. Covered by
tests/unit/test_soft_warning_metrics.py.
Idempotency-Key on POST /v1/systemone (#1618)¶
Status: Done (2026-10-13). Idempotency-Key replays systemone with the
same semantics as /v1/decisions (identical body replay, 409 on mismatch,
in-flight wait). Docs: headers.md, http-api.md. Covered by
tests/unit/test_idempotency.py.
Helm PrometheusRule alert pack (#1630)¶
Status: Done (2026-10-13). Opt-in prometheusRules PrometheusRule for
backend/budget/errors/queue/guardrails/key-expiry; thresholds in values.
Docs: metrics-prometheus.md. Covered by tests/unit/test_helm_chart.py.
Local decisions catalog on /v1/models and /api/tags (#1619)¶
Status: Done (2026-10-13). When systemone.enabled, built-in +
decisions.local_models ids appear on OpenAI /v1/models and Ollama
/api/tags with capability decision; VK allowlists still filter.
Docs: ollama.md, config.md. Covered by tests/unit/test_models_catalog.py,
tests/unit/test_ollama_decisions_tags.py.
Per-server MCP rpm/tpm ceilings (#1620)¶
Status: Done (2026-10-14). Optional rpm / tpm on each
integrations.mcp_servers[] entry (0 = unlimited) enforced on egress
tools/call and tools/list with 429, Retry-After, and
X-RateLimit-Scope: mcp_server:<id>. Independent of key-family mcp
ceilings (both apply). Doctor tip mcp_server_rate_limits; config.md +
mcp.md. Covered by tests/unit/test_mcp_server_rate_limits.py.
Fleet-durable request/event log backend (#1631)¶
Status: Done (2026-10-14). observability.request_log.backend
jsonl|postgres (default jsonl); postgres writes land in
daari_request_log via observability.postgres_url, best-effort.
Retention prune, backup catalog, doctor fleet warnings, and Helm
postgres.enabled cover the shared backend. Covered by
tests/unit/test_postgres_request_log.py.
MCP proxy/task Prometheus parity (#1632)¶
Status: Done (2026-10-14). Register mcp modality so /mcp/proxy
spans use gen_ai.operation.name=mcp; proxy tools/call bumps
daari_mcp_tool_calls_total (proxy.<name>) and modality request
series; MCP tasks emit daari_mcp_tasks_total + OTel spans; Grafana
MCP panel includes proxy/task queries. Covered by
tests/unit/test_mcp_proxy_metrics.py.
Helm Grafana overview dashboard ConfigMap (#1636)¶
Status: Done (2026-10-14). Opt-in grafanaDashboard.enabled renders a
ConfigMap embedding files/grafana/daari-dashboard.json with
grafana_dashboard: "1" sidecar labels; capacity-helm + metrics docs.
Covered by tests/unit/test_helm_chart.py.
Helm optional image.digest pin (#1637)¶
Status: Done (2026-10-14). Optional image.digest (empty default); when set,
Deployment + backup CronJob use repository@sha256:… (digest wins over tag).
NOTES + capacity-helm document cosign verify via RELEASING.md. Covered by
tests/unit/test_helm_chart.py.
Grafana scoped in-flight panels (#1638)¶
Status: Done (2026-10-14). Overview dashboard panel for
daari_in_flight / daari_in_flight_max with legend by scope/id;
metrics-prometheus docs list the series next to the global gate. Covered by
tests/unit/test_grafana_dashboard.py.
Prometheus cache invalidate counters (#1639)¶
Status: Done (2026-10-14). daari_cache_invalidate_total{scope,result} plus
daari_cache_invalidate_entries_removed_total{layer} on admin invalidate;
metrics-prometheus.md. Covered by tests/unit/test_cache_invalidate_metrics.py.
Subject erasure for Postgres request/event log (#1651)¶
Status: Done (2026-10-14). daari erase deletes matching daari_request_log
rows when observability.request_log.backend=postgres (dry-run counts only);
JSONL path unchanged. Covered by tests/unit/test_erasure.py,
tests/unit/test_postgres_request_log.py.
Postgres outage fail-closed auth + budget degrade (#1652)¶
Status: Done (2026-10-14). Postgres VK unreachable → 503 /
postgres_unavailable (never open); usage-ledger spend errors soft-allow with
X-Daari-Budget-Degraded + daari_budget_degraded; /ready + doctor PG
probes; outage matrix in auth-and-keys.md. Covered by
tests/unit/test_postgres_outage.py.
Default OpenAI model lifecycle + shutdown_date (#1653)¶
Status: Done (2026-10-14). Shipped _DEFAULT_MODEL_LIFECYCLE for the
2026-10-23 and 2027-01-14 OpenAI waves (operator overrides win); OpenAI
/v1/models cards expose shutdown_date; stock frontier.model →
gpt-6-luna; doctor flags deprecated defaults; retired wave models 410
without operator config. Covered by tests/unit/test_default_model_lifecycle.py.
MCP per-server 429 audit + reject metrics (#1654)¶
Status: Done (2026-10-15). Per-server MCP rpm/tpm 429s (ingress HTTP and
chat soft path) bump daari_rejects_total{kind="rate_limit"} and write
rate_limit.exceeded audit with bucket=mcp_server:<id>; Retry-After /
X-RateLimit-Scope unchanged. Covered by tests/unit/test_mcp_server_rate_limits.py.
Postgres store schema visibility in migrate + doctor (#1655)¶
Status: Done (2026-10-15). daari migrate --dry-run lists
postgres.request-log / virtual-keys / audit with schema version status
(unreachable reported, not omitted); apply is additive/idempotent; doctor
store_migrate warns on PG lag; upgrade.md documents migrate-before-rollout.
Covered by tests/unit/test_migrate_schema.py.
Enterprise audit row for admin cache invalidate (#1657)¶
Status: Done (2026-10-15). Successful POST /v1/daari/cache/invalidate
writes cache.invalidate audit (actor, role, scope, l0/l1 removed); error
path bumps Prom result=error without a success audit row; auth-and-keys
catalog lists the action. Covered by tests/unit/test_cache_invalidate.py.
Anthropic /v1/models decision capability parity (#1658)¶
Status: Done (2026-10-15). Local decisions ids on Anthropic-shaped
GET /v1/models (and retrieve) expose capabilities.decision.supported;
OpenAI/Ollama tags unchanged; non-decision cards keep thinking/server_tools
only. Docs: ollama.md, config.md. Covered by
tests/unit/test_models_catalog.py.
MCP server rpm/tpm in config editor + Helm (#1659)¶
Status: Done (2026-10-15). Config editor ownership exposes
integrations.mcp_servers as {id, rpm, tpm} ceiling rows (secrets omitted);
PATCH merges ceilings by id; Helm integrations.mcpServers renders
DAARI_INTEGRATIONS__MCP_SERVERS. Docs: mcp.md, http-api.md, config.md.
Covered by tests/unit/test_config_ownership.py, tests/unit/test_helm_chart.py.
Grafana + PrometheusRule for cache_invalidate (#1660)¶
Status: Done (2026-10-15). Overview dashboard panels for
daari_cache_invalidate_total and entries-removed; opt-in
DaariCacheInvalidateErrors alert; metrics-prometheus.md alert-pack table.
Covered by tests/unit/test_grafana_dashboard.py, tests/unit/test_helm_chart.py.
Label gateway errors by HTTP status (#1661)¶
Status: Done (2026-10-15). record_error(status=…) buckets into
errors_by_status; Prometheus emits daari_errors_total{status}; stats +
web-ui Errors breakdown; DaariErrorRateHigh uses sum(rate(...)).
Covered by tests/unit/test_metrics.py, tests/unit/test_prometheus.py,
web-ui dashboard tests.
CLI: mention virtual_keys in daari stats --help (#1583)¶
Status: Done (2026-10-15). daari stats docstring lists virtual_keys
lifecycle counts alongside cliff maps / backend_summary / compact-to-fit.
Covered by tests/unit/test_stats_cli_help.py.
Docs: LiteLLM 1.105 RC Decisions vs daari (#1584)¶
Status: Done (2026-10-15). compare-litellm RC watch notes Decisions API
in 1.105 RC and on-box /v1/decisions + /v1/systemone; 1.104 remains GA
floor. Covered by tests/unit/test_compare_litellm_1104.py.
Docs pin: virtual-keys.md GET /v1/models allowlist (#1585)¶
Status: Done (2026-10-15). Hermetic pin that virtual-keys.md documents
filtered GET /v1/models against key/team allowlists. Covered by
tests/unit/test_virtual_keys_priority_docs.py.
Lifecycle 410 on every inference facade (#1676)¶
Status: Done (2026-10-15). reject_retired_model gates Ollama
/api/chat + /api/generate, /v1/decisions, /v1/embeddings,
/v1/ocr, and /v1/systemone with the existing retired-model 410 shape;
deprecated ids keep working. Covered by
tests/unit/test_lifecycle_facade_410.py.
W3C traceresponse reply header (#1682)¶
Status: Done (2026-10-15). OTel middleware emits W3C traceresponse
(00-<trace-id>-<child-id>-<flags>) when inbound/active span context
exists; absent when otel off or no context. Docs: otel-genai.md. Covered by
tests/unit/test_otel_genai.py, tests/unit/test_otel_docs.py.
Team-scoped admin reads (#1677)¶
Status: Done (2026-10-15). ?team_id= on /v1/daari/traces, /stats,
/report; VK/SSO analysts restricted to their team (cross-team → 403);
SQLite + Postgres trace stores filter by team_id. Docs: auth-and-keys.md.
Covered by tests/unit/test_team_scoped_admin_reads.py.
Consistent cross-store backup (#1678)¶
Status: Done (2026-10-15). daari backup create --consistent refuses a
live /health, checkpoints SQLite WAL, and embeds one shared pg_dump
(stores/pg/consistent.sql). Fast path unchanged. Docs: guarantees matrix in
backup-restore.md. Covered by tests/unit/test_backup_restore.py.
Doctor TLS for remote Postgres/Redis (#1679)¶
Status: Done (2026-10-16). daari doctor warns on non-localhost
observability.postgres_url without sslmode=require+ and on
redis:// remote cache.redis_url (prefer rediss://); localhost/unix
sockets stay quiet. Docs: config.md + Helm values TLS examples +
doctor-health. Covered by tests/unit/test_doctor_data_plane_tls.py.
Webhook signing-secret rotation (#1680)¶
Status: Done (2026-10-16). daari alerts rotate-secret moves current→
previous with audit row; dual digests on X-Daari-Signature during grace;
doctor tips stale previous (~7d). Docs: budgets-frontier + doctor-health.
Covered by tests/unit/test_webhook_secret_rotation.py.
Per-key invalid-auth lockout (#1684)¶
Status: Done (2026-10-16). auth.key_lockout counts invalid attempts by
key prefix (independent of the IP throttle), returns 429 auth_key_locked
with Retry-After, and writes auth.key_locked. Redis fail-open; loopback
follows auth.exempt_loopback. Docs: config.md, auth-and-keys.md. Covered by
tests/unit/test_key_lockout.py.
HTTP GET /v1/daari/events (#1683)¶
Status: Done (2026-10-16). Newest-first GET /v1/daari/events over the
postgres request log (limit/offset/cursor, optional team_id/key_id,
default 100, max 1000). JSONL-only returns 501. Docs: http-api.md,
traces-stats.md. Covered by tests/unit/test_request_log_events_http.py.
Grafana errors by HTTP status (#1685)¶
Status: Done (2026-10-16). Overview dashboard adds "Errors by HTTP status"
(sum by (status) (rate(daari_errors_total[5m]))). Helm copy stays identical
to deploy/grafana/daari-dashboard.json. Covered by
tests/unit/test_grafana_dashboard.py.
ChatGPT Desktop configure recipe (#1686)¶
Status: Done (2026-10-16). daari configure chatgpt-desktop and
daari setup chatgpt-desktop print the Ollama host (port 11435, no /v1),
the 11434 reminder, and X-Daari-Client-Id: chatgpt-desktop. App preferences
are not written. Docs: chatgpt-desktop.md. Covered by
tests/unit/test_configure_cli.py.
Per-member budget threshold webhooks (#1633)¶
Status: Done (2026-10-16). alerts.member_budget_thresholds (empty by
default) POSTs scope=member when a named user crosses a ratio of
user_daily_usd_cap on a team key. Redis dedupe fail-open matches team
alerts; signing reuses X-Daari-Signature. Docs: budgets-frontier, config.md.
Covered by tests/unit/test_budget_alerts.py.
LiteLLM 1.106 decision-model guardrail watch (#1598)¶
Status: Done (2026-10-16). compare-litellm.md watches LiteLLM 1.106's
decision-model guardrail and states daari does not ship one yet (heuristic
guardrails + /v1/decisions). Comparison floor stays 1.104.0 GA. Covered by
tests/unit/test_compare_litellm_1104.py.
How to update¶
- Mark tasks
[x]when merged tomain; add commit hash in Notes when helpful. - Refresh Last updated and pytest count after test changes.
- Do not mark done without implementation — check
daari/cli/,tests/, andgit log. - Keep Phase B+ as preview; detail stays in ROADMAP and phase-a.md. Forward work: ROADMAP-v2.
- Append a new
###section above `### Docs: spend export --tier filter (#825)
Status: Done (2026-09-20). Chargeback guide documents daari spend export
--tier with asr / embed examples. Covered by
tests/unit/test_chargeback_docs.py.
- Append a new
###section above `### Cache invalidate --help documents --token (#826)
Status: Done (2026-09-20). CLI help contract asserts daari cache invalidate
--help mentions --token for SSO daemon auth. Covered by
tests/unit/test_cache_invalidate_cli_help.py.
Docs: cache invalidate --token (#828)¶
Status: Done (2026-09-20). cli.md and org-cache guide document
cache invalidate --token for SSO/master Bearer. Covered by
tests/unit/test_cache_invalidate_docs.py.
Unit tests for ASR frontier_fallback findings (#834)¶
Status: Done (2026-09-20). Direct unit coverage for
asr_frontier_fallback_findings. Covered by
tests/unit/test_config_validate.py.