Skip to content

Turn the README into a landing page, add a browser analyzer prototype - #17

Merged
alexandrefimov merged 6 commits into
mainfrom
browser-analyzer-spike
Aug 11, 2026
Merged

Turn the README into a landing page, add a browser analyzer prototype#17
alexandrefimov merged 6 commits into
mainfrom
browser-analyzer-spike

Conversation

@alexandrefimov

@alexandrefimov alexandrefimov commented Aug 11, 2026

Copy link
Copy Markdown
Owner

Summary

Two independent pieces of work on one branch; they can be split if you want the
README landing merged first.

README becomes a landing page (2979d5f). It was 843 lines, and its first
Quickstart command pointed at ./exported-impala-profile.txt, a file the reader
does not have — so the first thing a visitor tried failed, while the path that
works in ten seconds (query-doctor-web --public-demo) sat on line 597. That
path now leads. README drops to 196 lines, README.ru.md to 200. The Support
Boundary table, What Is / Is Not, Trino and Spark detail, direct-Impala history
depth, and supported deployment shape move to docs/support-boundary.md with a
Russian companion; the three entry paths move to docs/first-path.md with setup
and troubleshooting intact. The Container/Kubernetes section was already
duplicated in deploy/ and docs/recent-history-store.md, so it collapses to a
run command and links.

Eight tests asserted specific sentences against README.md, which is the
mechanism that grew it to 843 lines: every claim got pinned to the landing page.
They now assert against the document that carries the claim. Reviewers should
look at the tests/ and scripts/audit_spark_support_boundary.py diffs first —
that is the contract change, not the prose.

Also fixed: the stale claim that the 0.11.0 image will be published after the
release is approved (it has been live since 2026-08-10), and a package
description that led with four qualifiers instead of what the tool does.
Development Status moves Alpha → Beta; the version stays 0.11.0, since 1.0
touches seven files plus release metadata and belongs to the release checklist.

Browser analyzer prototype (857e783, 47263c8) under web/. A static page
that runs the deterministic analyzer in the visitor's browser through Pyodide,
so an exported profile can be diagnosed without leaving the machine. The most
common reason an operator cannot try Query Doctor is that the profile must not
go anywhere; here it does not, and DevTools shows it in ten seconds.

The analyzer core needed no changes — it imports no third-party package and no
networking or process stdlib, so it loads into Pyodide as built. Measured
against native CPython on synthetic Impala-shaped profiles: 1.9–2.2× slower,
linear at ~1.1 s per megabyte, identical output. Full page in headless Chromium:
boot 917 ms, 103 KiB profile in 116 ms, no external requests. Pyodide is fetched
at build time rather than vendored, keeping ~13 MB of binaries out of git while
the built site still serves every asset from its own origin.

Not deployed and not linked from the README. Hosting and wheel trimming are
recorded as open decisions in web/README.md.

Type

  • Documentation
  • Feature or workflow change
  • Bug fix
  • Safety or trust-boundary change
  • CI, packaging, or release
  • Tests only

Safety Checklist

  • No raw production SQL, profiles, metadata, local paths, hostnames, secrets, subprocess output, model/runtime internals, or raw artifact filenames are introduced.
  • Browser-visible UI and trusted reports remain sanitized.
  • External collection changes, if any, are explicit, bounded, read-only, and redacted by default. (None; the new page performs no collection and no network access after load.)
  • Query Optimizer changes, if any, do not execute user SQL or optimizer draft SQL. (None.)
  • LLM-facing changes, if any, keep Python/analyzer facts as the source of truth. (None.)
  • Unsupported evidence remains unknown, not_observed, or explicitly unsupported.
  • No screenshots, logs, generated reports, or browser captures from real clusters are added.
  • Fixtures, examples, and test names use synthetic placeholders only. web/bench/make_profile.py generates profiles with example.invalid hosts and synthetic_* identifiers.

No support claim changes. The moved text is verbatim; 50 of the 52 body lines in
docs/i18n/ru/support-boundary.md are byte-identical to the already-public
README.ru.md on main, the other two being relative-link fixes.

Validation

  • Full suite: 4697 passed, 1 skipped
  • Docs CI: check_active_docs, audit_public_docs, audit_public_distribution_boundary, check_markdown_links
  • scripts/check_staged_public_safety.py --changed over the whole branch diff: OK
  • python3 scripts/agent_preflight.py
  • ruff check query_doctor scripts tests web/bench
  • git diff --check
  • scripts/installed_readme_quickstart_smoke.py against a clean wheel install, exercising the three commands the README now shows
  • query-doctor-demo-preflight --public-release: READY_WITH_WARNINGS
  • pre-commit run --all-files — not run locally; CI covers it
  • Release gate — not release-facing

The preflight gained four warnings on docs/first-path.md for naming
analysis.json, analysis_facts.md, profile.txt, and CM_PASSWORD. That is
moved text, the scan only inspects changed files, and docs/local-smoke.md and
docs/credentials.md already carry the same tokens on main.

Documentation

  • Public docs are updated.
  • Documentation drift was checked for changed behavior, contracts, commands, routes, and safety wording.
  • docs/changelog.md is updated.
  • New examples and fixtures are synthetic and sanitized.

Branch Hygiene

  • This PR targets a review branch, not direct pushes to main.
  • Branch is based on current main.

Assisted-by: Claude Opus 5 via Claude Code

Runs the deterministic analyzer in the visitor's browser through Pyodide, so
an exported Impala profile can be diagnosed without leaving the machine. That
removes the objection that blocks most first tries: the profile never reaches
a server, and DevTools shows it.

The analyzer core needed no changes. It imports no third-party package and no
networking or process stdlib, so it loads into Pyodide as built; only the CLI
wrapper pulls sockets and subprocesses. The page calls analyzer.service.analyze
directly with the seven threshold values it reads.

Measured against native CPython on synthetic Impala-shaped profiles: 1.9-2.2x
slower, linear at roughly 1.1 s per megabyte, with identical output. Full page
in headless Chromium boots in ~1.0 s and diagnoses a 103 KiB profile in 126 ms
with no external requests.

Pyodide is fetched at build time rather than vendored, keeping ~13 MB of
binaries out of git while the built site still serves every asset from its own
origin. Not deployed and not linked from the README yet; hosting and output
language are still open.
The README was 843 lines, and its first Quickstart command pointed at
./exported-impala-profile.txt, a file the reader does not have. The first thing
a visitor tried therefore failed, while the path that works in ten seconds --
query-doctor-web --public-demo -- sat on line 597. It now leads.

README drops to 196 lines and README.ru.md to 200. The Support Boundary table,
the What Is / Is Not lists, the Trino and Spark detail, the direct-Impala
history depth note, and the supported deployment shape move to
docs/support-boundary.md, with a Russian companion under docs/i18n/ru/. The
three entry paths move to docs/first-path.md with their setup options and
troubleshooting intact. The Container and Kubernetes section was already
duplicated in deploy/kubernetes/README.md, deploy/helm/query-doctor/README.md,
and docs/recent-history-store.md, so it collapses to a run command and links.

Several tests asserted specific sentences against README.md, which is the
mechanism that grew it to 843 lines in the first place. They now assert against
the document that carries the claim -- support-boundary for the SSO proxy,
Spark, and Trino boundary text, deploy/kubernetes/README.md for the auth
front-door smoke -- so the guarantee survives without pinning prose to the
landing page. The Trino test keeps a weaker invariant on both READMEs: they must
name the engines, say bounded local production support, and link the boundary
doc.

Also fixes the stale claim that the 0.11.0 image will be published after the
release is approved; ghcr.io/alexandrefimov/query-doctor:0.11.0 has been live
since 2026-08-10. The package description now leads with what the tool does
instead of four qualifiers, and Development Status moves from Alpha to Beta.
The version itself stays at 0.11.0: 1.0 touches seven files plus release
metadata and belongs to the release checklist, not a metadata edit.

Full suite green (4697 passed), docs CI checks green, ruff clean, and the
installed README Quickstart smoke passes against the three commands the README
now shows.
The analyzer output from render_md is English, so Russian page chrome read
badly against it. English is the canonical documentation language here; a
Russian layer belongs under docs/i18n/ru/ if it is ever wanted, not as a
second page.

Also fixes link labels in the Russian support-boundary document, which kept
repo-root paths after the text moved into docs/i18n/ru/.

Rebuilt and rechecked in headless Chromium: boot 917 ms, 103 KiB sample profile
analyzed in 116 ms, 8 requests, no external hosts, no JS errors.
The rename to ./your-profile.txt reached the quickstart smoke and its test but
missed installed_user_paths_smoke.py, which asserts the same literal on the
summary it gets back. That is what turned the wheel job red. Door 1 in
docs/first-path.md still showed the old name too, so the copy-paste command no
longer matched the README.

The occurrences left alone are deliberate: docs/changelog.md records what the
README said at the time, and SELF_TEST_PROFILE_NAME is the self-test's own
synthetic fixture, unrelated to README copy.

Reproduced the failing job locally against a clean wheel install: installed
user paths ok, full suite 4697 passed.
The page called analyzer.service.analyze on the raw text. That skips the CLI's
case staging, which is where query-doctor-analyze applies redaction, so users,
pools, emails, secrets, local paths, and host identifiers reached the rendered
facts. Nothing was transmitted either way, but rendered output is a browser
surface and screenshots of it travel, which is exactly the boundary the safety
contract draws.

The page now calls safety.redaction.redact_profile_text with the CLI defaults
before analyzing. It is pure regex over stdlib, so it loads in Pyodide like the
rest of the core. Verified on the sample profile: synthetic_user, synthetic_pool,
and 15 host references become <user>, <pool>, and host_NN aliases.

Costs about 90 ms on a 103 KiB profile; the page still boots in ~1.0 s and
diagnoses the sample in 212 ms with no external requests.
@alexandrefimov
alexandrefimov merged commit 20144a5 into main Aug 11, 2026
10 checks passed
@alexandrefimov
alexandrefimov deleted the browser-analyzer-spike branch August 11, 2026 10:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant