phase-1: read-only NL analyst over live MSSQL (Gemini)#19
Open
singhalvivek wants to merge 6 commits into
Open
Conversation
* Spec filled in: roadmap, architecture, agent graph, api, ui, data, 3 capabilities.
* scaffold: src/mssql_analyst/ + frontend/ + alembic/ + pyproject.toml on this branch.
* src/ does not exist on main (boilerplate-only) — first appearance per
SKILL.md pitfall. PR base is main, this commit is also src/'s first.
* Backend: FastAPI + LangGraph (ReAct single-shot ReAct). Audit-SQLite via
SQLAlchemy 2.0. pyodbc integrated-auth connector to local master.
Gemini via google-genai (verified model on live whitelist).
* Read-only enforcement at the SQL validator + system prompt — DROP/UPDATE/
INSERT/DELETE/ALTER/CREATE/TRUNCATE/GRANT/REVOKE/EXEC all rejected.
* Frontend: Next.js 15 + Tailwind v4 static export at /app/. Question form,
result table, 'Show SQL' toggle, 'tokens used' badge, last-50 stub,
charts/export/multi-db/follow-up stubs visibly disabled.
* Tests: 31 unit + integration passing (incl. validator, graph topology,
/api/ask envelope, /api/usage, audit-row write, data-locality prompt-spy).
* Obs: structured JSON logging via structlog to stdout.
* Existing pattern from feature/cctns-analyst-v0.1 lifted and adapted;
mock mirror intentionally excluded — Phase 1 is live-only by spec.
* node_modules and frontend/out are still gitignored (do not commit). * Bare npm scripts work on Windows cmd; inline NODE_OPTIONS= prefix on bash-on-Windows confuses POSIX env-var parsing within cmd. * Confirmed: npm install + npm run build → frontend/out/index.html + Tailwind v4.3.3 CSS bundle (12.9 kB, full utility classes, no @tailwind survives).
…ghting
* Alembic 0002 adds answer_runs.result_columns_json / result_rows_json / day;
applied cleanly (alembic current → 0002 head).
* /api/history?limit=50&offset=0 (paged, newest-first).
* /api/usage/by-day?days=14 (descending day buckets; '1970-01-01' excluded).
* /api/ask/{id}/csv (text/csv RFC 4180 quoting; 404 on unknown / failed).
* /api/ask/{id}/anomalies?threshold=2.0 (sample-std z-score; min_samples=4;
constant columns are not flagged; inf/NaN/skipped).
* /api/ask response shape gains run_id so the UI can drive CSV/anomalies
without a second GET.
* Frontend rewires the Phase-1 keyboard-stub into:
- real history list + tokens/day sparkline,
- Download CSV button per result card,
- anomaly highlight (red row + count chip).
Phase-3 stubs (multi-db, follow-up chat) remain clearly labelled.
* 60/60 tests pass (31 phase-1 regression + 29 new: 16 unit, 13 integration).
* Frontend build green: npm run build → Tailwind v4.3.3 CSS bundle,
no @tailwind survives, HTML testids render server-side.
* state: add max_sql_attempts, row_cap_effective, timelines. * tools/structural_validator.validate_sql_structure: pure (clean, complaints) pair; flags unbounded SELECT * (no TOP, no WHERE) with retry-friendly hints. * tools/row_cap.shrink_row_cap: pure base × shrink_factor with floor; safe with negative / zero tokens. * tests: 14 new unit cases for the helpers above (74/74 pass).
* state: add max_sql_attempts / validation_complaints /
validation_retry_pending / row_cap_effective / timelines.
* nodes: validate_sql node (rejects unbounded SELECT *, no WHERE,
no TOP). Cycle edge validate_sql -> nl_to_sql when validator
rejects AND sql_attempts < max_sql_attempts. execute_sql shrinks
row_cap once tokens_used >= 30k (high_water_mark).
* graph: build_request_graph wires 5 nodes
(nl_to_sql, validate_sql, execute_sql, finalize, handle_error)
with the cycle. max_sql_attempts defaults to 2.
* alembic 0003 adds answer_runs.timeline_json (default '[]').
Migration chain 0001 -> 0002 -> 0003 applied.
* /api/runs/{run_id}/timeline serves the persisted per-node
timing list; happy + 404 + corrupt-JSON-recovery cases.
* tests/integration/test_phase3_retry.py: end-to-end retry path.
Stub LLM returns bad SQL on attempt 1, clean bounded SQL on
attempt 2; assert sql_attempts=2, 2x validate_sql entries with
clean=False then clean=True, row_count=1 execute, and
row_cap_effective=1000 (well below token threshold).
* tests/integration/test_phase3_retry.py: token-aware shrink —
scripted 82 000 input tokens fires shrink_row_cap(base=1000) to
500; row_cap_effective lands at 500.
* 79/79 tests pass (8 phase-1, 13 phase-2, 5 phase-3 api + 2 retry,
50 unit). Boot clean, 14 routes including the new
/api/runs/{run_id}/timeline.
* Phase-1/2/3 regression unbroken.
madhyamakist
added a commit
that referenced
this pull request
Jul 19, 2026
…ards) Mined from ~/.hermes/logs of the Jul 20 data-analyst build. New durable guards: - #17 branch reuse imports a prior build's stack (live run inherited an old ASP.NET+MSSQL data-analyst branch and tried dotnet/Docker on Python). Fix: enforce unique date-time branch slug + ls-remote collision check + refuse to build into an existing feature branch; clean-baseline precheck before scaffold. - #18 long-lived servers must use terminal(background=true); Hermes hard-blocks &/nohup/setsid. Agent had punted run-commands to the user instead of a URL. - #19 browser tools are often unavailable; curl/httpx content assertion is the smoke-test of record, browser only when its check passes. - #20 shell cwd doesn't persist between terminal calls (use abs paths / cd&&cmd); no claude/dotnet/docker CLI unless the stack installed it; don't checkout over a dirty tree. Wired the two git guards into rules/git.md + SKILL Stage 2 scaffold; the background-flag + curl-smoke rule into SKILL Stage 4 and phases.md boot gate. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
phase-1: read-only natural-language analyst over a live Microsoft SQL Server
What this PR does
Builds Phase 1 of the NL→SQL→answer agent intake from
/zero-shot-build:SELECTagainst MSSQL→ executes via
pyodbc(Windows Integrated Auth) → returns the bounded result + SQL + latency + tokensINFORMATION_SCHEMA.COLUMNSintrospected once at startup, per request: only one Gemini + one MSSQL call/api/askwrites oneanswer_runsrow to SQLite via SQLAlchemy 2.0/app/— question form + result table + collapsible SQL + "tokens used" badge, with clearly-labelled stubs for last-50 sidebar / charts / CSV export / multi-DB / follow-up chatalembic currentreports0001 (head)PR base — exception documented
This PR targets
main. Perharness/rules/git.mdmainis normally boilerplate-only — that rule still holds; onmainno build output is present after this PR is merged (this PR opens the door). The agent-runtime contents (src/,frontend/,alembic/, etc.) are introduced by this branch's first commit. Seeharness/skills/zero-shot-build/SKILL.mdStage 2 pitfall ("src/ skeleton may not exist on the working branch").Verified
uv syncclean (Resolved 68 packages, mssql-analyst 0.1.0 installed).alembic upgrade head+alembic current→0001 (head).pytest tests/ -v→ 31 passed (validator: 21 cases · graph topology: 2 · integration: 8 incl./api/askenvelope, audit-row persistence,/api/usagetotals, validator rejection, data-locality prompt-spy)..venv/bin/python -m src;/health→ 200, mssql_mode=live; live MSSQL integrated auth confirmed (schema cache message:table_count=6).empty_question,unsafe_sql,pipeline_error,llm_unavailable,mssql_unavailable,no_db_configured).Gemini runtime caveat — surfaced, not hidden
The default
gemini-3.1-promodel listed in the boilerplate.env.exampleis not in this account's live whitelist (verified viaclient.models.list()). Defaults switched togemini-3.1-pro-preview. In this turn the live Gemini account also returned429 RESOURCE_EXHAUSTED("monthly spending cap reached"). The wrapper handles this honestly: end-users get HTTP 502 with{"code":"llm_unavailable","message":"gemini_request_failed: ClientError"}, audit-log row recorded (status=failed), JSON-log event emitted. The agent is spec-correct — the user needs to either lift the AI Studio project cap or supply a different model viaAGENT_LLM_MODEL=...in.env.What's clearly-labelled and stubbed in Phase 1
These are by design, will become real in Phase 2/3:
data-stub="history-stub")data-stub="charts")data-stub="export")data-stub="multi-db")data-stub="followup")Run it (from repo root)
Phase 2 plan (in
spec/roadmap.md): last-50 sidebar with real paging, token/cost charts, CSV export, anomaly highlighting.