[NOTICKET][AI] feat(chat): wire Orchestrator -> slow path at the endpoint (env-gated)
Rifqi Hafizuddin+1: Claude Opus 4.8
636290e
The slow path (Planner -> TaskRunner -> Assembler) was reachable in ChatHandler but
never enabled from the live endpoint. Wire it so the team can test end-to-end from
/chat/stream, behind an env flag so it stays opt-in until the lead's real
BusinessContext lands (it's still a stub).
- settings.enable_slow_path (env ENABLE_SLOW_PATH, default False).
- api/v1/chat.py passes it to the shared ChatHandler.
- ENABLE_SLOW_PATH=true → structured intents route through the slow path and stream
status progress + the assembled answer; default keeps the single-query QueryService
path. Fast/unstructured paths unchanged.
Verified: toggle flips handler._enable_slow_path; wiring tests pass; live run through
ChatHandler.handle confirmed earlier this session.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comments are not a sync mechanism. Remove the two duplicated definitions on the
agent side.
- PLACEHOLDER_RE: defined once in planner/schemas.py (it's part of the ToolCall
${t<id>} placeholder convention); planner/validator.py and slow_path/task_runner.py
now import it instead of each compiling their own copy "kept in sync" by comment.
- planner/registry.py still holds local data-access spec BODIES (stub pending
KM-465 #4) but derives/validates their NAMES against the canonical
src.tools.data_access.DATA_ACCESS_TOOLS: _data_access_slice() raises at
default_registry() if a tool is added/renamed/removed upstream, instead of
drifting silently. Registry output unchanged (same 12 tools, same order).
Tests: 51 passed (planner + slow_path); ruff clean; registry builds.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Log the TAB-owned review work in the fix tracker:
- R11 [ ]->[~]: canonical DATA_ACCESS_TOOLS now lives once in tools/data_access.py, invoker.py imports it (90e80f9). Agent remainder (planner registry derive + _PLACEHOLDER_RE dedup) noted.
- T1 [x]: input_schema clarified as presence-only, not type-checked (90e80f9).
- T2 [blocked]: Python embed path likely redundant (verified Go is a full writer to langchain_pg_embedding for all 5 file types) but blocked on frontend routing fact; do not delete a live ingestion path.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
DB3 (DB owner): DbExecutor.prewarm(catalog, user_id) warms the pooled engine for
schema sources, fired (best-effort, never raises) at slow-path entry so the cold
first-query TCP+TLS+auth handshake overlaps the ~4s Planner LLM call. Gated to the
default path (skipped when a coordinator factory is injected, so tests stay
hermetic). Reuses DbExecutor's existing client resolution + the DB2 engine cache.
R4 (agent): SlowPathCoordinator.run gained an optional `progress` callback;
ChatHandler bridges it to SSE `status` events via an asyncio queue, and chat.py
forwards them. The slow path no longer streams ~13s of silence — live timeline now
shows Planning -> Running N steps -> Composing, max wire gap ~4.6s, fixing proxy
idle-timeout + UX. Status events only appear when the coordinator calls back, so the
existing wiring test's event sequence is unchanged (fake coordinator just accepts
**kwargs). Token-streaming the Assembler answer is deferred: it would require
splitting the Assembler into a …
Merge
Rifqi Hafizuddin
d7dad60
[NOTICKET][DB] perf(query): DB2 - pooled engine cache for user DBs
Rifqi Hafizuddin+1: Claude Opus 4.8
4c911cb
DbExecutor built a fresh engine and disposed it on every query, paying a full
TCP+TLS+auth handshake per call (~6-8s measured, dominating slow-path latency).
engine_scope's connect-once-dispose is right for ingestion, wrong for the query
path.
- New UserEngineCache (src/database_client/engine.py): process-wide cache of pooled
engines keyed by client_id + a hash of the decrypted creds (rotation auto-
invalidates the key). Bounded LRU (50) + 600s idle TTL; pool_pre_ping +
pool_recycle=300; small pool (1 + 2 overflow). invalidate(client_id) disposes
eagerly. Scope: postgres/supabase only; other db_types keep the legacy per-call
path, so nothing regresses.
- DbExecutor._run_sync reuses the warm connection (no per-query SET, no dispose).
- Read-only + statement_timeout moved from libpq startup `options` to a per-
connection `connect` event: Neon's transaction pooler rejects
default_transaction_read_only as a startup parameter (caught in a live run) but
accepts it as a SET. …
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
[NOTICKET][DB] fix(query): R2 - always compile a bounded LIMIT
Rifqi Hafizuddin+1: Claude Opus 4.8
6974c5b
An IR with no limit compiled to a LIMIT-less SELECT, and DbExecutor materialized
the entire result set before capping at 10k rows - an unbounded fetch that can OOM
on a large user table.
- SqlCompiler now bounds every query: an explicit IR limit is honored (clamped to
MAX_RESULT_ROWS=10000); an unbounded query gets LIMIT cap+1 so the executor can
distinguish exactly-the-cap from more-rows-existed and flag truncation.
- CompiledSql.row_cap carries the effective cap; DbExecutor caps + derives truncated
from it and drops its own _ROW_HARD_CAP constant.
- test_sql.py: no-limit golden strings get the safety bound; +3 cases (no-limit
bounded, explicit-limit clamp, row_cap). Restored S608 to tests/** ruff ignore
(documented in PR3-DB but missing from pyproject) - golden tests assert literal SQL.
Also logs R13: a pre-existing, unrelated test_prompt failure found while running R2.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Cut redundant catalog-DB round-trips in the slow path and stop rebuilding the
Azure LLM clients per request.
- MemoizingCatalogReader (catalog/reader.py): request-scoped wrapper that caches
each read by source_hint. ChatHandler builds one per request and threads it into
the slow-path DataAccessToolInvoker, so the planner's catalog load is reused by
describe_source / query_structured instead of re-reading from the catalog DB.
Measured live: describe_source 3.5s -> ~2.0s; catalog reads/request ~5 -> ~2.
- chat.py: one module-level ChatHandler singleton instead of per-request, keeping
the Orchestrator/Chatbot Azure clients (and TLS pools) warm across requests.
Does not touch the external query_structured handshake (that is the engine-cache
work, DB2). Also logs the principal-review fix tracker (R1-R12, DB1-DB3) in PROGRESS.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
[KM-626] docs: record slow-path agent layer in PROGRESS
Rifqi Hafizuddin+1: Claude Opus 4.8
726faa2
PROGRESS.md: 2026-06-08 entry for the slow-path agent (TaskRunner + Assembler +
Coordinator under agents/slow_path/), the Orchestrator-vs-slow_path naming, and
the open follow-ups (live wiring, real invoker swap, GPT-mini deployment).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
[KM-624] docs: record tool taxonomy + ownership revision in PROGRESS
Rifqi Hafizuddin+1: Claude Opus 4.8
f346114
PROGRESS.md: 2026-06-08 entry — composite analyze_* tools adopted (atomic
compute_* dropped), tool team owns ALL tools, Planner stub realigned, Pattern
A/B left open, stubs still to retire on integration.
.gitignore: exclude personal/local working docs (AGENT_ARCHITECTURE_CONTEXT_new.md,
PROJECT_SUMMARY.md) from the shared repo.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
[KM-567] docs: record Phase 3 Planner agent in PROGRESS.md
Rifqi Hafizuddin+1: Claude Opus 4.8
ba2fa88
Add "What just shipped (2026-06-05 — Phase 3: Planner agent)" section: files
added under src/agents/planner/, the stub contracts pending reconciliation with
the lead (BusinessContext) and tool team (KM-608), and the next steps
(Orchestrator expansion + TaskRunner + Assembler).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>