diff --git a/.editorconfig b/.editorconfig index 9dd1efb..500711b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,7 +8,7 @@ indent_size = 4 insert_final_newline = true trim_trailing_whitespace = true -[*.{rs,hs,py}] +[*.{rs,hs,py,js}] max_line_length = 100 [*.md] @@ -20,3 +20,6 @@ indent_size = 2 [*.{yaml,yml,json}] indent_size = 2 + +[*.{css,html}] +indent_size = 2 diff --git a/AGENTS.md b/AGENTS.md index ea29a6c..e3efb88 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -54,6 +54,9 @@ The shape today is: - `tools/exporter/`: Haskell tool that consumes hand-authored `.scenario.json` files in `tools/exporter/examples/` and emits the runner-IR JSON consumed by `crates/plan-runner`. See [`tools/exporter/README.md`](tools/exporter/README.md). +- `tools/plan-viewer/`: static HTML viewer for `plan-runner` fixtures. + It evaluates a fixture in the browser and renders the plan DAG, per-node relations, input facts, and oracle comparison. + See [`tools/plan-viewer/README.md`](tools/plan-viewer/README.md). - `external/`: git submodules. `external/geolog` provides the Haskell query planner used by the exporter; `external/geomerge` is the Rust CRDT crate consumed by `storage::adapters::geomerge`. diff --git a/tools/plan-viewer/README.md b/tools/plan-viewer/README.md new file mode 100644 index 0000000..b1e2d51 --- /dev/null +++ b/tools/plan-viewer/README.md @@ -0,0 +1,37 @@ +# Plan Viewer + +A static HTML viewer for `plan-runner` fixtures. +It renders the plan DAG, the input facts, and the relation computed at every plan node, so a fixture can be inspected without reading raw JSON. + +## Usage + +Open [`index.html`](index.html) in a browser, then drop a fixture from `crates/plan-runner/fixtures/` onto the page or pick one with the file input. + +When the repository is served over HTTP, a fixture can also be loaded through a query parameter: + +```sh +python3 -m http.server 8000 +# then open: +# http://localhost:8000/tools/plan-viewer/index.html?fixture=../../crates/plan-runner/fixtures/two_atom_join.json +``` + +## What It Shows + +- **Plan DAG**: one box per plan node, laid out left to right by join depth, with `L` and `R` labels on join inputs and the root node marked. + Each box shows the node's output columns and row count. +- **Selected Node**: a plain-language description of the operator, its output columns, and the full relation computed at that node. + Wildcard columns (names starting with an underscore) are dimmed. +- **Result Bindings**: the root relation projected to the fixture's `expected_bindings` columns, with a per-row check against the oracle and a list of any expected rows the plan did not produce. +- **Input Facts**: one table per relation in the fixture's schema. + +The header badge reports whether the evaluated bindings match the fixture's `expected_bindings` as a multiset, mirroring `plan_runner::verify`. + +## Scope + +The viewer re-implements the scan, semijoin, and natural join semantics of `crates/query-ops` and the execution order of `crates/plan-runner` in JavaScript, for display only. +The Rust crates and their tests remain the correctness oracle; if the two ever disagree, the Rust behavior wins and the viewer has a bug. + +Unsupported cases: + +- plan node actions other than `scan` and `join` +- fixtures without a `query` and `schema` block diff --git a/tools/plan-viewer/index.html b/tools/plan-viewer/index.html new file mode 100644 index 0000000..de59322 Binary files /dev/null and b/tools/plan-viewer/index.html differ