diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index f3c0f304..890c0431 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -9,7 +9,7 @@ What does this PR change, and why? - [ ] Refactor / maintenance - [ ] Documentation - [ ] CI / tooling -- [ ] RFC (adds/updates `docs/rfcs/*`) +- [ ] RFC (adds/updates `docs/rfcs/**`) ## Area(s) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 00000000..7d763d2b --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,94 @@ +name: Docs + +on: + push: + branches: [main] + paths: + - "docs/**" + - "examples/tutorial_book/**" + - "tests/docs/**" + - "utils/**" + - "Makefile" + - "mkdocs_hooks.py" + - "mkdocs.yml" + - "requirements-docs.txt" + - ".github/workflows/docs.yml" + pull_request: + branches: [main] + paths: + - "docs/**" + - "examples/tutorial_book/**" + - "tests/docs/**" + - "utils/**" + - "Makefile" + - "mkdocs_hooks.py" + - "mkdocs.yml" + - "requirements-docs.txt" + - ".github/workflows/docs.yml" + workflow_dispatch: + +concurrency: + group: docs-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + mkdocs: + name: MkDocs strict build + runs-on: ubuntu-latest + + steps: + - name: Check out IncQL + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + cache: pip + cache-dependency-path: requirements-docs.txt + + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version: "22" + + - name: Install docs dependencies + run: python -m pip install -r requirements-docs.txt + + - name: Build docs + run: make docs-build + + deploy-pages: + name: Deploy docs to GitHub Pages + needs: mkdocs + if: github.event_name != 'pull_request' + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - name: Check out IncQL + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + cache: pip + cache-dependency-path: requirements-docs.txt + + - name: Install docs dependencies + run: python -m pip install -r requirements-docs.txt + + - name: Configure deploy identity + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + - name: Publish docs + run: mkdocs gh-deploy --force --strict --remote-branch gh-pages --message "chore - deploy docs site" diff --git a/AGENTS.md b/AGENTS.md index f555742c..9a9c546e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -69,7 +69,7 @@ Normative behavior is defined in **`docs/rfcs/`**. If package code and an RFC di ## General workflow 1. **Branch from `main`**: Prefer `/-` (e.g. `feature/8-rfc-table-automation`, `docs/9-mkdocs-ci`), matching team practice. -2. **Follow RFCs**: Behavior changes should be reflected in the right `docs/rfcs/*.md` (or a new RFC) before or alongside code in the appropriate repository. +2. **Follow RFCs**: Behavior changes should be reflected in the right RFC under `docs/rfcs/` (or a new RFC) before or alongside code in the appropriate repository. 3. **Run the local gate**: `make ci` (or at least `make fmt-check`, `make build`, `make test`) before considering work done for **this** repo. 4. **Version sync**: If you bump the package version, update **both** [incan.toml][incan-toml] (`[project] version`) and [src/metadata.incn][metadata-incn] (`incql_version()`) in the same commit (see [CONTRIBUTING.md][contributing]). 5. **Documentation**: User-facing or spec changes should update `README.md`, relevant `docs/*`, or RFCs as appropriate. Keep prose markdown **without hard wrapping** (natural paragraphs). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index aab03c0b..2754d99c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,6 +14,7 @@ Thank you for your interest in IncQL — the typed relational layer for [Incan][ | [Incan `CONTRIBUTING.md`][incan-contributing] | Compiler, tooling, and Rust workspace workflow | | [Incan docs-site contributor loop][incan-docsite-loop] | Divio layout, snippets, checklist for Incan’s MkDocs site — mirror these patterns when shaping IncQL `docs/` | | [Incan AGENTS — Docs-site workflow][incan-agents-docs-workflow] | Prose **without hard wrapping**, `mkdocs build --strict`, Material-friendly Markdown | +| [Prismplane docs theme][prismplane-theme] | IncQL-specific docs visual rules adapted from the local Prismplane prototype | **Compiler and language implementation** (lexer, parser, typechecker, lowering, codegen for `query {}` and related surfaces) lives in the **Incan** repository. Use that project’s docs and gates when you change the toolchain. Use **this** repo for the IncQL **library source** (`.incn`) and **IncQL RFCs** that specify the relational surface. @@ -48,6 +49,21 @@ Thank you for your interest in IncQL — the typed relational layer for [Incan][ With `incan` on your `PATH` you can call `incan build --lib` and `incan test tests` directly (use the `tests/` path so a sibling Incan checkout under `./incan/` is not collected). Override the binary with `make build INCAN=/path/to/incan` if needed. +4. **Build the documentation site** + + Install the pinned docs dependencies and run a strict build before opening documentation-heavy PRs: + + ```bash + python -m pip install -r requirements-docs.txt + mkdocs build --strict + ``` + + Use `mkdocs serve` for local preview while editing. The GitHub Actions docs workflow runs the same strict build for changes under `docs/`, `mkdocs.yml`, `requirements-docs.txt`, or the workflow itself. + +5. **Preview or publish the documentation site** + + The public documentation site is published at [encero-systems.github.io/IncQL/][docs-site]. The docs workflow publishes to the `gh-pages` branch after a successful strict build on `main` or manual dispatch. Repository Pages settings should use **Deploy from a branch**, branch `gh-pages`, folder `/ (root)`. + ## Project structure See [docs/architecture.md][architecture] for a concise map. In short: @@ -56,6 +72,8 @@ See [docs/architecture.md][architecture] for a concise map. In short: - `src/*.incn` — library modules; `lib.incn` re-exports the public surface - `tests/` — Incan tests for the package - `docs/rfcs/` — design specifications (numbered separately from Incan’s RFC index) +- `mkdocs.yml` — documentation site navigation and strict-build configuration +- `requirements-docs.txt` — pinned Python dependencies for local and CI docs builds ## Changing behavior @@ -163,3 +181,5 @@ Open an issue on this repository for IncQL-specific design or package questions; [issue-templates]: .github/ISSUE_TEMPLATE/ [incan-docsite-loop]: https://github.com/encero-systems/incan/blob/main/workspaces/docs-site/docs/contributing/tutorials/book/08_docsite_contributor_loop.md [incan-agents-docs-workflow]: https://github.com/encero-systems/incan/blob/main/AGENTS.md#docs-site-workflow-mkdocs-material +[prismplane-theme]: docs/contributing/prismplane_docs_theme.md +[docs-site]: https://encero-systems.github.io/IncQL/ diff --git a/Makefile b/Makefile index 495bd542..30c86dd3 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,23 @@ INCAN ?= incan export INCAN_NO_BANNER ?= 1 +TUTORIAL_BOOK_DIR := examples/tutorial_book +TUTORIAL_BOOK_CHAPTERS := \ + src/chapter_01.incn \ + src/chapter_02.incn \ + src/chapter_03.incn \ + src/chapter_04.incn \ + src/chapter_05.incn \ + src/chapter_06.incn \ + src/chapter_07.incn \ + src/chapter_08.incn \ + src/chapter_09.incn + +TUTORIAL_BOOK_RUNNABLES := \ + src/main.incn \ + src/chapter_08.incn \ + src/chapter_09.incn + .DEFAULT_GOAL := help .PHONY: help @@ -66,6 +83,42 @@ test-locked: ## Run tests with `--locked` @echo "\033[1mRunning IncQL tests (locked)...\033[0m" @$(INCAN) test $(INCQL_TEST_DIR) --locked +# ============================================================================= +# Documentation +# ============================================================================= + +.PHONY: rfc-index +rfc-index: ## Validate RFC metadata and regenerate the RFC catalog + @python3 utils/rfc_catalog.py --tags docs/rfcs/catalog.json --write + +.PHONY: rfc-index-check +rfc-index-check: ## Validate RFC metadata and fail when the catalog is stale + @python3 utils/rfc_catalog.py --tags docs/rfcs/catalog.json --check + +.PHONY: docs-test +docs-test: ## Run documentation tooling unit tests + @python3 -m unittest discover -s tests/docs -p 'test_*.py' + @node --test tests/docs/test_rfc_reader.cjs + +.PHONY: docs-build +docs-build: rfc-index-check docs-test ## Validate and build the documentation site strictly + @mkdocs build --strict + +.PHONY: tutorial-book +tutorial-book: ## Type-check every tutorial chapter and run each complete learning path + @echo "\033[1mChecking IncQL tutorial-book chapters...\033[0m" + @cd $(TUTORIAL_BOOK_DIR) && $(INCAN) lock >/dev/null + @for script in $(TUTORIAL_BOOK_CHAPTERS); do \ + echo "\033[1m -> $$script\033[0m"; \ + cd $(TUTORIAL_BOOK_DIR) && $(INCAN) --check "$$script" || exit $$?; \ + cd ../..; \ + done + @for script in $(TUTORIAL_BOOK_RUNNABLES); do \ + echo "\033[1m -> run $$script\033[0m"; \ + cd $(TUTORIAL_BOOK_DIR) && $(INCAN) run "$$script" --locked || exit $$?; \ + cd ../..; \ + done + # ============================================================================= # Formatting (Incan source) # ============================================================================= @@ -76,7 +129,7 @@ test-locked: ## Run tests with `--locked` # packages are listed by source directory so generated `target/` output stays # outside the formatting walk. -INCQL_FMT_DIRS := src tests examples/advanced_retail_query_blocks/src +INCQL_FMT_DIRS := src tests examples/advanced_retail_query_blocks/src examples/tutorial_book/src INCQL_FMT_FILES := examples/*.incn .PHONY: fmt diff --git a/README.md b/README.md index 35be75c5..99780915 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ # IncQL -**IncQL** is the **typed data logic plane** for [Incan](https://github.com/encero-systems/incan): the place where you express **relational queries**, **schema-aware table transformations**, and **streaming-shaped relational work** with compile-time checks, without folding orchestration, catalogs, or engine-specific runtime into the authoring model. Row shapes come from Incan `model` types; column, join, and alias rules are part of one semantic core whether you use `query { }` blocks, method chains on `DataSet[T]` carriers, or (later) optional pipe-forward (`|>`). +**IncQL** is the **typed data logic plane** for governed relational work, implemented as an [Incan](https://github.com/encero-systems/incan) library package. It is where you express **relational queries**, **schema-aware table transformations**, and **streaming-shaped relational work** with compile-time checks, without folding orchestration, catalogs, or engine-specific runtime into the authoring model. Row shapes come from Incan `model` types; column, join, and alias rules are part of one semantic core whether you use `query { }` blocks, method chains on `DataSet[T]` carriers, or (later) optional pipe-forward (`|>`). **What IncQL is not:** It is not a pipeline or workflow framework, not a semantic catalog, and not a catch-all that swallows execution concerns. It owns **data logic**: query authoring, relational plan shape, resolution and schema flow, typed carrier semantics, and **backend-neutral logical intent**. Execution, binding, and operational semantics live in the layer below (session, adapters, runners). -**Why it matters:** Raw SQL strings and untyped rows defer mistakes to runtime. IncQL keeps relational work **in the language**: schemas are ordinary models, invalid references and many aggregation mistakes are caught where Incan promises checking, and plans are intended to lower to **Apache Substrait** so logical intent stays portable while credentials and physical reads are resolved outside the normative plan story. +**Why it matters:** Raw SQL strings and untyped rows defer mistakes to runtime. IncQL keeps relational work **in source**: schemas are ordinary models, invalid references and many aggregation mistakes are caught by the typechecked authoring surface, and plans are intended to lower to **Apache Substrait** so logical intent stays portable while credentials and physical reads are resolved outside the normative plan story. -**Bottom line:** IncQL is where you write **checked relational logic** in Incan: the same naming and schema rules apply to `query { }` blocks and `DataSet[T]` APIs, and **execution stays downstream** (sessions, adapters, runners) and consistent. +**Bottom line:** IncQL provides **checked relational logic** for data systems: the same naming and schema rules apply to `query { }` blocks and `DataSet[T]` APIs, and **execution stays downstream** (sessions, adapters, runners) and consistent. ## What you get @@ -19,6 +19,8 @@ Design is **RFC-driven**; **[docs/rfcs/](docs/rfcs/README.md)** is the source of truth. +Documentation site: **https://encero-systems.github.io/IncQL/** + ## Contributing See [CONTRIBUTING.md](CONTRIBUTING.md) for workflow and [architecture.md](docs/architecture.md) for how this repo relates to the Incan compiler. @@ -46,7 +48,8 @@ Normative proposals live under **[docs/rfcs/](docs/rfcs/README.md)**. IncQL’s - `src/lib.incn` — public exports - `src/` — library modules - `tests/` — tests -- `.github/workflows/` — CI (builds the pinned Incan release branch, then runs IncQL checks) +- `mkdocs.yml` — documentation site configuration for the `docs/` tree +- `.github/workflows/` — CI for package checks, strict docs builds, and GitHub Pages deployment Build and test from this repo root (with `incan` on your `PATH`): @@ -61,4 +64,4 @@ incan build --lib incan test tests ``` -See `make help` for other targets (`fmt`, `fmt-check`, `registry-metadata`, `build-locked`, …). Continuous integration builds **Incan from source** from the workflow's pinned `INCAN_REF` release branch or tag, then runs `fmt-check`, `test-style`, function registry metadata validation, `build`, `test`, and the pub-consumer smoke check (see [.github/workflows/ci.yml](.github/workflows/ci.yml)). +See `make help` for other targets (`fmt`, `fmt-check`, `registry-metadata`, `build-locked`, …). Continuous integration builds **Incan from source** from the workflow's pinned `INCAN_REF` release branch or tag, then runs `fmt-check`, `test-style`, function registry metadata validation, `build`, `test`, and the pub-consumer smoke check (see [.github/workflows/ci.yml](.github/workflows/ci.yml)). The docs workflow runs `mkdocs build --strict` for documentation changes and publishes the site to GitHub Pages from `main` or manual dispatch; see [CONTRIBUTING.md](CONTRIBUTING.md) for the local docs build loop. diff --git a/docs/architecture.md b/docs/architecture.md index 4c3c717b..daf69a6f 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -1,248 +1,418 @@ -# IncQL architecture - -This document describes the architectural model of **IncQL**. It is scoped to the IncQL repository and its relationship to the Incan compiler, not to product orchestration or engine-specific operational concerns. - -## What IncQL is - -IncQL is two things that evolve together: - -1. **A specification** under [docs/rfcs/][incql-rfcs]: naming and core semantics, dataset carriers, Substrait emission, query authoring, the execution boundary, and the internal planning substrate. -2. **An Incan library package**: `.incn` modules built with `incan build --lib`, consumed by Incan programs as a typed relational package. - -The Incan compiler remains responsible for parsing, typechecking, lowering, and Rust/code generation. The IncQL repo holds the author-facing package, its documentation, and the RFCs that define what that package is supposed to mean. - -## Architectural model - -IncQL is organized around three layers: - -- **Prism internally** — the immutable planning and optimization engine over persistent authored plan state and derived optimized views -- **Substrait at the boundary** — the normative emitted logical interchange contract -- **Session for execution** — the execution and binding layer that consumes plans but does not define them - -That gives each major concept one job: - -- **Prism** thinks about the plan -- **Substrait** communicates the plan -- **Session** executes the plan - -Per [RFC 008][rfc-008], that split is intentionally minimal at this stage: - -- Prism owns logical plan shaping before execution -- Session owns backend binding, physical planning, runtime metrics, and adaptive behavior -- richer statistics transport and optimizer mechanics remain follow-on work - -This separation keeps internal planning concerns, portable interchange semantics, and runtime execution concerns from collapsing into one another. - -## Conceptual pipeline - -IncQL follows this shape: - -```text -Incan models / model-derived schema - │ - ▼ - DataSet[T] carriers - │ - ├──► method chains - ├──► query { } blocks - └──► future pipe-forward / other authoring surfaces - │ - ▼ - Prism logical planning substrate - │ - ├──► authored plan state - ├──► lineage-preserving optimization - └──► derived optimized views - │ - ▼ - Substrait Plan / Rel emission - │ - ▼ - Session / backend execution -``` - -The core rule is: - -- authoring surfaces build or manipulate Prism-managed logical work -- Prism prepares that work for boundary emission -- RFC 002 owns the Substrait contract -- RFC 004 owns execution and binding - -## Layer responsibilities - -### Carriers - -The author-facing carrier family is rooted in `DataSet[T]` and includes `LazyFrame[T]`, `DataFrame[T]`, and `DataStream[T]`. - -Carriers are expected to be: - -- typed by model-derived schema information -- immutable from the author’s point of view -- cheap to branch -- execution-neutral on their own - -They should be understood as experiences over planning state, not as independent semantic systems. - -For current package behavior, see [Dataset carriers (Reference)][dataset-ref] and [Dataset carriers (Explanation)][dataset-expl]. - -### Prism - -Per [RFC 007][rfc-007], Prism is IncQL’s internal logical planning and optimization engine. - -Prism is responsible for: - -- persistent authored logical plan storage -- cheap branching through structural sharing -- lineage preservation -- logical rewrites and derived optimized views before boundary emission or execution - -Prism is not the normative interchange format and not the execution engine. - -### Substrait - -Per [RFC 002][rfc-002], Apache Substrait is the normative logical interchange boundary for IncQL. - -That means: - -- portable relational work must be expressible as Substrait `Plan` / `Rel` -- logical reads remain logical at the boundary -- extension and gap handling are documented at the Substrait boundary -- internal planning freedom is allowed, but emitted plans must follow RFC 002 - -Substrait-facing package code lives primarily under [substrait/](../src/substrait/). The current implementation is intentionally split into focused modules for relation building, plan assembly, schema registry, extension bookkeeping, expression lowering, and inspection. For current boundary docs, start with [Substrait read-root and binding contract][substrait-read-root]. - -### Session - -Per [RFC 004][rfc-004], `Session` owns binding and execution. - -Session is responsible for: - -- resolving logical reads to physical resources -- applying backend-specific execution behavior -- owning physical planning and runtime adaptation policy -- collecting or materializing results -- writing to sinks where appropriate - -The public Session surface is sync-first for common author workflows, but the execution substrate underneath it remains async-capable. That keeps local and batch usage ergonomic without collapsing the backend seam into a permanently blocking design. - -Session is intentionally outside RFC 002’s normative emitted contract. It consumes plans; it does not define plan semantics. - -For current package behavior, see [Execution context (Reference)][execution-ref] and [Execution context (Explanation)][execution-expl]. - -## Package implementation shape - -The package uses the following implementation shape: - -- author-facing carrier types live in [mod.incn](../src/dataset/mod.incn) -- canonical relational operator helpers live in [ops.incn](../src/dataset/ops.incn) -- Substrait emission lives under [substrait/](../src/substrait/) -- Prism internals live under [prism/](../src/prism/) -- `LazyFrame[T]` routes through a backend-native `PrismCursor[T]` -- `DataFrame[T]` and `DataStream[T]` keep their carrier-specific backing shapes while sharing the public dataset surface - -This keeps package architecture in this document while detailed API behavior lives in language docs and future surface expansion stays in RFCs, issues, and release notes. - -## Repository layout - -| Path | Role | -| ------------------------------------ | -------------------------------------------------- | -| `incan.toml` | Package metadata and Rust dependency declarations | -| `src/lib.incn` | Public package exports | -| `src/dataset/mod.incn` | Carrier types and trait surface | -| `src/dataset/ops.incn` | Canonical relational operator helpers | -| `src/prism/mod.incn` | Internal Prism graph, cursor, and lowering logic | -| `src/substrait/relations.incn` | Concrete `Rel` builders and relation lowering | -| `src/substrait/plans.incn` | Top-level `Plan` assembly helpers | -| `src/substrait/inspect.incn` | Relation/plan inspection and output-column helpers | -| `src/substrait/schema_registry.incn` | Named-table schema registration and lookup | -| `src/substrait/extensions.incn` | Extension anchors, URIs, and declaration helpers | -| `src/substrait/expr_lowering.incn` | Builder-to-Substrait expression lowering | -| `src/substrait/conformance.incn` | Typed conformance facade over catalog + validators | -| `src/substrait/schema.incn` | Model/schema to Substrait type bridging | -| `tests/` | Package tests run through `incan test` | -| `docs/language/` | Current package docs | -| `docs/rfcs/` | Normative RFC series | -| `docs/release_notes/` | Release-facing notes | - -Normative behavior lives in the RFC series first. Current package behavior and usage belong in the language docs. If code and RFCs disagree, treat that as a bug or transition state to resolve explicitly. - -## Repository vs compiler - -The IncQL repository and the Incan compiler have different responsibilities. - -```text -┌─────────────────────────────────────────────────────────────────────────────┐ -│ IncQL repo │ -├─────────────────────────────────────────────────────────────────────────────┤ -│ RFCs, package modules, tests, docs, architecture, conformance corpus │ -│ Defines the relational package surface and its normative contracts │ -└─────────────────────────────────────────────────────────────────────────────┘ - │ - │ implemented through - ▼ -┌─────────────────────────────────────────────────────────────────────────────┐ -│ Incan compiler │ -├─────────────────────────────────────────────────────────────────────────────┤ -│ Parsing, typechecking, lowering, Rust emission, LSP, test runner, builds │ -│ Makes IncQL package code executable and supports language surfaces │ -└─────────────────────────────────────────────────────────────────────────────┘ +
+ + +
+
+
+

From intent to execution

+

One typed path through the system

+

This is the Prism-backed LazyFrame path from checked intent to a materialized DataFusion result. Each boundary has one job, so changing a surface or backend cannot silently change the plan’s meaning.

+
+ +
+
    +
  1. 01 · AuthorAuthoring surfacesChecked query {} and LazyFrame intent.
  2. +
  3. 02 · PlanPrismSemantic planning and canonical rewrites.
  4. +
  5. 03 · ExchangeSubstraitPortable logical plan; no runtime bindings.
  6. +
  7. 04 · Bind + dispatchSessionValidate bindings and dispatch the selected adapter.
  8. +
  9. 05 · Plan + executeBackend adapterDataFusion planning, execution, and materialization.
  10. +
+ + + +
+
+Evidence rail +One local plan_target +
+
+
SchemaFields and types
+
Lineage + originAuthored meaning
+
RewriteCanonical derivation
+
RequirementsAdapter boundary
+
ObservationConcrete attempt
+
+Local inspection artifacts and the runtime observation meet through the same local target; they are not serialized through the Substrait plan. +
+ +
The representation path is not the literal public call stack: callers hand a Prism-backed LazyFrame to Session; Session triggers the Prism-to-Substrait handoff, validates logical reads, supplies registrations, and dispatches its configured adapter.
+
+ +
+

The separation is deliberate. Authoring surfaces express relational work. Prism is IncQL’s internal logical planning engine: it stores the authored plan, derives narrow semantics-preserving views, and keeps origin information intact. It is not the IncQL brand mark, the interchange format, or the execution engine.

+

Substrait carries portable logical meaning across the boundary. Session then supplies the runtime context that the logical plan intentionally lacks: registered sources, its configured backend selection, execution, materialization, and writes. The adapter may plan for its engine, but it does not become the semantic owner of the query.

+
+ + +
+ +
+
+

Correlated locally

+

Plan evidence and execution evidence stay distinct

+

For a Prism-backed LazyFrame, local plan inspection and the later execution observation expose the same plan_target. That correlation does not add evidence to the Substrait payload.

+
+ +
+ + +
+
+Before execution · local plan lane +

Inspect authored meaning

+

inspect_plan(...) reads Prism-backed state without binding a physical source or running a backend. It returns a structured PlanInspection.

+
+
    +
  • SchemaOutput fields + typed shape
  • +
  • Lineage + originValue, control, grouping + authored mappings
  • +
  • Plan + rewritesNodes, applied rules + origin map
  • +
  • RequirementsAdapter needs, diagnostics + unsupported markers
  • +
+

Plan anchorplan_target

+
+ +
+
+During and after · runtime lane +

Record one concrete attempt

+

Session derives the same plan target before lowering, records it on the ExecutionObservation, and gives the concrete run its own distinct attempt_target.

+
+
+
Attempt
execute, collect, or write; terminal status
+
Runtime
Context targets, backend, and adapter or profile identity
+
Timing
Start, end + monotonic duration
+
Outcome
Diagnostics; optional counts, traces + evidence references
+
+

Plan anchorplan_target

+
+ +

Substrait remains the logical-plan boundary. It carries the portable Plan / Rel. Neither the PlanInspection, its origin map, nor the correlation key is serialized into that plan. Adapter coverage is checked separately and is not automatically attached by collect_observed.

+
+ +

Quality checks stay explicit and policy-neutral. A quality block declares typed assertions; it does not filter, quarantine, or mutate a relation on its own. A caller evaluates those declarations through Session.observe_quality(...) or observe_quality_pair(...), which return quality observations. Enforcement remains the responsibility of policy, CI, or orchestration code.

+

A governance checkpoint is evidence, not a hidden policy engine. It records an observation or decision against a semantic target so downstream tooling can explain and verify what happened.

+ +
+ +
+
+

One concrete execution trace

+

From query block to observed result

+

This trace follows one Prism-backed LazyFrame[Order]. It shows the representation at each boundary, the transformation performed there, and the evidence that connects a concrete execution back to the authored Prism plan produced by the query.

+
+ +
    +
  1. Authoringquery { } + LazyFrame
  2. +
  3. PrismAuthored nodes + canonical view
  4. +
  5. SubstraitPlan / Rel
  6. +
  7. SessionPlan + registrations
  8. +
  9. Adaptermaterialization
  10. +
+ +
+
+
+Authoring · Surface input +

A query over orders

+
+ +```incan +from pub::incql import ( + LazyFrame, + Session, +) +from models import ( + Order, + OrderSummary, +) + +session = Session.default() +orders: LazyFrame[Order] = session.read_csv( + "orders", + "orders.csv", +)? + +paid_by_region: LazyFrame[OrderSummary] = ( + query { + FROM orders + WHERE .status == "paid" + GROUP BY .region + SELECT + .region as region, + sum(.amount) as total + } +) + +observed = session.collect_observed( + paid_by_region, +) ``` -That distinction matters because package design and compiler implementation move at different speeds. The repo owns the package and its design records; the compiler owns the language and tooling machinery that makes those surfaces executable. - -## Build and test - -From the repo root, with `incan` on `PATH`: - -```text -incan build --lib -incan test tests +
+
Input carrier
LazyFrame[Order]
+
Logical source
orders
+
Output intent
LazyFrame[OrderSummary]
+
Output fields
region, total
+
+
+ +
+
+Authoring · Desugar + typecheck +

The surface becomes ordinary carrier calls

+
+ +```incan +paid_by_region = orders + .filter(eq(col("status"), "paid")) + .group_by([col("region")]) + .agg([ + aggregate_as( + sum(col("amount")), + "total", + ), + ]) + .select([ + with_column_assignment( + "region", + col("region"), + ), + with_column_assignment( + "total", + col("total"), + ), + ]) ``` -In practice: - -- `incan build --lib` parses, typechecks, lowers, and emits a Rust crate for the IncQL library -- `incan test tests` discovers and runs package tests under `tests/` - -CI builds `incan` first, then runs the IncQL package checks against that compiler. - -## Reading order - -If you want the clearest current story, read in this order: - -1. [Language overview][language-root] -2. [Dataset carriers (Explanation)][dataset-expl] -3. [Execution context (Explanation)][execution-expl] -4. [Dataset carriers (Reference)][dataset-ref] -5. [Execution context (Reference)][execution-ref] -6. RFCs for normative and historical design context - -## Where to read more - -| Topic | Location | -| --------------------------- | ---------------------------------------------------------- | -| Docs landing page | [docs/README.md][docs-root] | -| Language overview | [docs/language/README.md][language-root] | -| Dataset carriers | [Reference][dataset-ref] · [Explanation][dataset-expl] | -| Execution context | [Reference][execution-ref] · [Explanation][execution-expl] | -| Substrait integration | [Reference docs][substrait-read-root] · [RFC 002][rfc-002] | -| Prism planning engine | [RFC 007][rfc-007] | -| IncQL RFC index | [docs/rfcs/README.md][incql-rfcs] | -| Incan compiler architecture | [Incan architecture docs][incan-architecture] | -| Contributing | [CONTRIBUTING.md][incql-contributing] | - - -[incan-architecture]: https://github.com/encero-systems/incan/blob/main/workspaces/docs-site/docs/contributing/explanation/architecture.md -[docs-root]: README.md -[language-root]: language/README.md -[incql-rfcs]: rfcs/README.md -[incql-contributing]: ../CONTRIBUTING.md -[dataset-ref]: language/reference/dataset_carriers.md -[dataset-expl]: language/explanation/dataset_carriers.md -[execution-ref]: language/reference/execution_context.md -[execution-expl]: language/explanation/execution_context.md -[substrait-read-root]: language/reference/substrait/read_root_binding_contract.md -[rfc-002]: rfcs/002_apache_substrait_integration.md -[rfc-004]: rfcs/004_incql_execution_context.md -[rfc-007]: rfcs/007_prism_planning_engine.md -[rfc-008]: rfcs/008_optimizer_boundary_stats_cbo_aqe.md +

The generated calls follow the normal Incan typechecker. In this CSV-backed trace, the registered orders source supplies the starting logical schema; later references resolve against each stage’s current logical schema during lowering. OrderSummary documents the intended output row model; full field/type compatibility validation against that annotation remains follow-up work.

+
+
+ +
+
+
Prism → Substrait · Logical handoff

See the concrete artifact at every step

+

The authored Prism graph is immutable. A narrow canonical rewrite fuses grouping and aggregation for lowering while retaining an authored-origin map.

+
+ +
+
Authored view
5 nodes
+
Applied rule
fuse_group_by_aggregate
+
Rewritten view
4 nodes
+
Explainability
Origin map retained
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Clause-by-clause transformation from the authored query into the portable logical plan
Query clause and carrier callPrism stateSubstrait relationInspectable evidence
FROM ordersSource carrier: ordersExisting carrier root n0ReadNamedTable("orders")FROM selects the existing orders carrier; it appends no node. The root is unchanged in the rewritten view.ReadRelNamedTable(["orders"])Read rootLogical source and input-field targets
WHERE .status == "paid".filter(eq(col("status"), "paid"))Authored n1FilterOrigin n1 after rewriteFilterRelequal(status, "paid")Control lineageorders.status controls which rows survive
GROUP BY .region.group_by(...).agg(sum(amount))Authored n2 + n3GroupBy then AggregateRewritten as one Aggregate(group, measures), origin n3AggregateRelGrouping: region
Measure: sum(amount) as total
Grouping + value lineageregion groups; amount contributes to total
SELECT region, total.select(...)Authored n4SelectProjectOrigin n4 after rewriteProjectRelRelRoot names region, totalOutput shapeSchema and output-field targets
+
+

How to read the plan: the Plan root wraps ProjectRel, which nests down through AggregateRel, FilterRel, and ReadRel. The tree is displayed root-to-source; rows later move source-to-root. The labels n0…n4 illustrate a fresh cursor and are not stable global IDs.

+
+ +
+
+
Session → adapter · Runtime handoff

The portable plan meets concrete runtime state

+

Substrait carries the logical read, not its concrete TableSource or backend selection. Session maps orders to TableSource { source_kind, uri } and dispatches the selected adapter.

+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
Runtime ownership and the artifact produced by each handoff
Stage and ownerReceivesPerformsProduces or preserves
Session · BindRuntime contextPlanRegistration for logical table orders and selected backendValidate + dispatchChecks that orders is registered, then passes backend registrations and plan to the adapterBackend dispatch inputsValidated Plan + BackendRegistration[]. Session separately carries the cursor-derived plan_target into the later observation.
Adapter · ExecuteDataFusionPlan + registrationsorders resolves to a concrete TableSourceBackend planning + executionRegisters the source, consumes the Substrait plan, builds a DataFusion logical plan, executes, and collects record batchesDataFrameMaterializationresolved_columns, runtime row_count, and rendered preview text
Session · Observe returnRuntime evidenceCollected DataFrame[OrderSummary] or SessionErrorThe result of this exact collect attemptNormalize public result + evidenceWraps data or error and records attempt status, backend, duration, diagnostics, and row count when availableObservedDataFrame[OrderSummary]data, observation, and error
+
+ + +
+ +
+
Evidence spine

Inspection and execution meet at plan_target

+
+
Before executioninspect_plan(paid_by_region)

Reports authored and rewritten nodes, the applied rewrite rule, output fields, lineage, adapter requirements, diagnostics, and unsupported-evidence markers—without binding a source or running a backend.

+
Shared correlation keyplan_targetPrism store + tip derived identity
+
After collectExecutionObservation

Reports Collect, success or failure, datafusion, runtime duration, diagnostics, and a row count when materialization succeeds.

+
+
+ +
+

What changes at each boundary. The query block desugars into carrier calls; those calls append immutable Prism nodes; a canonical view fuses GroupBy and Aggregate; lowering emits one Substrait AggregateRel; Session binds the logical read; and DataFusion performs backend-specific planning and execution.

+

What stays traceable. Prism retains rewritten-to-authored origin mappings. Local inspection explains that status provides control lineage, region provides grouping lineage, and amount provides value lineage to total. The later observation correlates to that local evidence through the same plan_target; the lineage itself is not runtime telemetry.

+
+ +

Boundary rule: Prism’s origin map and semantic targets remain local IncQL evidence; they are not smuggled into the Substrait payload. Substrait carries relational meaning. Session adds runtime binding and reconnects the concrete attempt to the inspected plan.

+ +
+ +
+
+

Clear seams

+

Each layer owns one kind of decision

+

Portability only works when language mechanics, relational meaning, interchange, and execution do not collapse into one another.

+
+ +
+
+
Authoring

Language host and semantic package

The compiler hosts the extension point; IncQL supplies the vocabulary and public relational contracts.

+
+
Generic language mechanics

Incan compiler

Decides: generic vocabulary hosting, parsing and typechecking generated Incan, language lowering and Rust emission, and language tooling.

Must not decide: IncQL vocabulary semantics, carrier contracts, relational meaning, or execution behavior.

+
IncQL vocabulary + package

IncQL

Decides: query: and quality: desugaring, public carriers and functions, relational semantics, and evidence contracts.

Must not decide: generic compiler semantics, workflow orchestration, credentials, or engine-specific behavior.

+
+

Handoffgeneric vocabulary ASTIncQL desugaringchecked carrier calls

+
+ +
+
Planning + interchange

Semantic graph and portable representation

Prism owns local planning state; Substrait is the representation exchanged with runtime.

+
+
Semantic planning

Prism

Decides: immutable authored graph state, narrow canonical rewrites, rewritten-to-authored origin mapping, and the state from which schema and lineage are derived.

Must not decide: syntax or desugaring, interchange representation, runtime binding, or physical execution.

+
Portable interchange

Substrait

Carries: portable Plan / Rel, schemas and expressions, extension declarations, and conformance-relevant plan shape.

Must not carry: source bindings, backend selection, local Prism evidence, or physical execution policy.

+
+

HandoffPrism rewritten viewloweringSubstrait Plan / Rel

+
+ +
+
Runtime

Binding context and engine integration

Session owns the attempt; the adapter owns the concrete engine bridge.

+
+
Runtime context

Session

Decides: registrations, selected backend, binding validation, dispatch, public result wrapping, execution observations, and explicit coverage evaluation when requested.

Must not decide: semantic rewrites or backend-specific physical planning.

+
Engine integration

Backend adapter

Decides: concrete source registration, backend bridging and planning, engine calls, collection or writes, and typed backend errors.

Must not decide: IncQL semantics, Session policy, or execution-observation creation.

+
+

HandoffPlan + BackendRegistration[]adapter dispatchmaterialization or typed error

+
+
+ +
+

What crosses the seams: IncQL vocabulary desugaring produces checked carrier calls. A Prism-backed carrier can derive a rewritten view and lower it to a Substrait Plan or Rel. Session validates registered logical reads, supplies BackendRegistration[], and dispatches the configured adapter, which returns a materialization or typed error.

+

What does not cross them: Prism’s origin map and inspection artifacts remain local. Session creates the runtime observation and correlates it with local plan evidence through plan_target; the adapter neither receives that evidence nor creates the observation.

+
+

The cross-cutting invariant: local plan evidence and runtime observations remain correlatable without turning Substrait or the adapter into an evidence transport.

+ +
+ +
+
+

Inside the package

+

The repository follows the same seams

+

Most boundaries map to focused modules. Two cross-cuts matter: IncQL-specific syntax lives in vocab_companion/, while shared evidence records connect Prism inspection to Session observations.

+
+ +
+
+Implementation cutaway +

Two carrier paths converge at Substrait

+

Only LazyFrame is Prism-backed today. Concrete DataFrame and DataStream carriers lower directly to the portable boundary.

+
+ +
+ + + + + + + + + +
BoundaryPrism-backed LazyFrameDirect DataFrame + DataStream
AuthoringIncQL surfacevocab_companion/IncQL vocabulary registration and desugaringsrc/lib.incn + src/dataset/ + src/functions/Public exports, carrier types, and relational callssrc/lib.incn + src/dataset/ + src/functions/Concrete carriers and relational calls; query vocabulary may produce the same calls
PlanningLocal semantic statesrc/prism/Authored graph, narrow rewrites, origin mapping, and planning stateNo Prism storeThis carrier path converges during Substrait lowering.
ExchangePortable logical plansrc/substrait/Schema and expression lowering, relation construction, extensions, plan assembly, and conformancesrc/substrait/Direct carrier lowering into the same portable Plan / Rel boundary
RuntimeBind + dispatchsrc/session/ + src/backends.incnPublic Session API, source and sink domain, registration validation, adapter selection, and dispatchsrc/session/ + src/backends.incnThe same runtime context receives the portable plan and registrations
AdapterConcrete enginesrc/session/datafusion_backend.incnCurrent DataFusion bridge, execution, collection, writes, and typed backend errorssrc/session/datafusion_backend.incnThe same current adapter consumes the direct path’s portable plan
+
+ + +
+ +
+
IncQL repositoryPackage code, tests, documentation, conformance material, and normative RFCs.
+
Incan repositoryCompiler implementation for syntax, typechecking, lowering, Rust emission, language tooling, and the test runner.
+
+ +
+
+

Go deeper

+

Contributor architecture

+

Browse the implementation map, build and test path, repository/compiler boundary, and source-level reading order without crowding the conceptual architecture.

+
+

Open contributor architecture

+
+
+
+
diff --git a/docs/contributing/architecture.md b/docs/contributing/architecture.md new file mode 100644 index 00000000..f00f3851 --- /dev/null +++ b/docs/contributing/architecture.md @@ -0,0 +1,92 @@ +# Contributor architecture + +This page is the source-level companion to the [conceptual architecture][architecture]. Start with the conceptual page if you want to understand the system boundaries; use this page when you need to find the module that implements one of those boundaries or verify how the package is built. + +## Responsibility map + +| Layer | Owns | Does not own | +| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | +| Incan compiler | Generic vocabulary hosting, parsing and typechecking generated Incan, language lowering, Rust emission, language tooling, and the test runner | IncQL vocabulary semantics, package contracts, or backend execution policy | +| IncQL package | `query:` and `quality:` vocabulary registration and desugaring, author-facing carriers, relational semantics, inspection, evidence contracts, and normative RFCs | Generic compiler semantics, workflow orchestration, credentials, or engine-specific semantics | +| Prism | IncQL's immutable internal logical plan state, schema facts, canonical rewrites, lineage, and authored-origin mappings | Public authoring syntax, portable interchange, binding, or execution | +| Substrait | The portable logical `Plan` / `Rel` boundary | Credentials, backend selection, or IncQL's complete local evidence model | +| Session | Source registration, logical-name binding, adapter selection, execute/collect/write, runtime observations, and adapter coverage | Redefining authored relational meaning | +| Backend adapter | Backend-specific planning, lowering, pushdown, execution, and runtime behavior | Becoming the semantic owner of the plan | + +Prism is an internal IncQL component rather than a public authoring surface. `LazyFrame[T]` carries a Prism-native `PrismCursor[T]`; the cursor is not backend-native and Prism is not represented by the IncQL brand mark. + +## Package implementation map + +| Path | Responsibility | +| ------------------------------------- | ------------------------------------------------------------------------------------------------- | +| `vocab_companion/src/lib.rs` | IncQL-specific `query:` and `quality:` vocabulary registration and desugaring | +| `src/lib.incn` | Public package exports | +| `src/dataset/mod.incn` | `DataSet`, `DataFrame`, `LazyFrame`, and `DataStream` carrier types | +| `src/dataset/ops.incn` | Canonical relational operator methods | +| `src/prism/types.incn` | Internal authored-node, rewritten-view, and rewrite evidence models | +| `src/prism/store.incn` | Append-only Prism store and structural sharing | +| `src/prism/rewrite.incn` | Narrow semantics-preserving canonical rewrites and origin mappings | +| `src/prism/lower.incn` | Lowering a Prism view toward the Substrait boundary | +| `src/substrait/` | Schema and expression lowering, relation construction, plan assembly, extensions, and conformance | +| `src/session/types.incn` | Public Session and SessionBuilder API plus execution and observation flow | +| `src/session/backend_dispatch.incn` | Portable adapter dispatch | +| `src/session/datafusion_backend.incn` | Reference DataFusion adapter implementation | +| `src/inspect.incn` | Structured plan inspection, schema flow, lineage, requirements, and artifact summaries | +| `src/evidence.incn` | Semantic targets, lineage, requirements, observations, and evidence records | +| `src/governance.incn` | Governed attributes and policy checkpoint evidence | +| `src/quality.incn` | Typed quality assertions and observations | +| `src/functions/` | Declared scalar, aggregate, generator, and window function surface | +| `tests/` | Package tests run through `incan test` | + +## End-to-end execution path + +1. An author uses a query block or dataset method to extend typed carrier state. +2. A Prism-backed carrier appends immutable authored nodes and retains schema and origin information. +3. Prism may derive a narrow canonical view. The current rewrite set is semantics-preserving and does not introduce cost-based optimization. +4. The selected view lowers to the Substrait logical boundary. +5. Session validates logical reads against registered sources, supplies backend registrations, and dispatches the requested operation to its selected adapter. +6. The adapter performs backend-specific bridging, planning, and execution, then returns a materialization or typed error. +7. Session returns the public result and creates structured execution evidence when the observed API is used. + +Plan inspection is a separate read-only path. It can describe plan structure, schema, lineage, metadata, governed attributes, and adapter requirements without registering physical sources or executing a backend. Adapter coverage evaluation is also explicit: callers use `check_plan_coverage(...)`, `check_inspection_coverage(...)`, or `check_coverage(...)` when they need it; observed execution methods do not infer or attach coverage automatically. + +## Repository and compiler boundary + +The IncQL repository owns the library package, its tests, public documentation, conformance material, and normative RFCs. It deliberately does not contain the Rust compiler implementation. + +The [Incan repository][incan] owns the generic vocabulary host, parsing, typechecking, language lowering, Rust emission, language tooling, and the test runner that make IncQL source executable. IncQL-specific `query:` or `quality:` registration and desugaring belong in this repository's `vocab_companion/`; changes to the generic vocabulary mechanism or compiler behavior belong in Incan. Package APIs and relational contracts remain here. + +## Build and test + +With `incan` on `PATH`, the repository gate is: + +```text +make ci +``` + +The underlying package commands are: + +```text +incan build --lib +incan test tests +``` + +`incan build --lib` is the important compile boundary: it parses, checks, lowers, and emits the Rust crate for the IncQL library. `incan test tests` discovers and runs the package tests under `tests/`. + +## Suggested source reading order + +1. `vocab_companion/src/lib.rs` for IncQL-specific query and quality desugaring. +2. `src/dataset/mod.incn` for the public carrier model. +3. `src/prism/mod.incn`, `types.incn`, `store.incn`, and `rewrite.incn` for internal planning state. +4. `src/substrait/mod.incn`, `relations.incn`, `plans.incn`, and `expr_lowering.incn` for the portable boundary. +5. `src/session/mod.incn`, `types.incn`, and `backend_dispatch.incn` for runtime ownership. +6. `src/inspect.incn` and `src/evidence.incn` for the cross-cutting evidence model. +7. `src/governance.incn` and `src/quality.incn` for governed and quality evidence. + +For API-level behavior, prefer the [Reference][reference] pages. For normative design history and end-state contracts, use the [RFC index][rfcs]. + + +[architecture]: ../architecture.md +[incan]: https://github.com/encero-systems/incan +[reference]: ../language/reference/README.md +[rfcs]: ../rfcs/README.md diff --git a/docs/contributing/prismplane_docs_theme.md b/docs/contributing/prismplane_docs_theme.md new file mode 100644 index 00000000..85ffa0a1 --- /dev/null +++ b/docs/contributing/prismplane_docs_theme.md @@ -0,0 +1,70 @@ +# Prismplane docs theme + +Prismplane is IncQL's documentation visual language: optical planning surfaces, transparent schema panes, refracted rails, and structure-first decoration. The source inspiration is the local prototype at `/Users/danny/Development/encero/prototypes/incql-prismplane-v6/`; that prototype is a design handoff, not production site code. + +IncQL is also the reference implementation for a wider Encero documentation system. Reuse should happen through stable interaction patterns, content contracts, and design tokens; product-specific names, colors, illustrations, and information architecture remain brand layers. Extract a shared package only after more than one site has proved which seams are genuinely common. + +## Design rules + +- Tables, code blocks, schema examples, and query snippets should carry more visual authority than decorative panels. +- Primary visuals should show IncQL product reality: source code, typed schemas, Prism plan shape, lineage, Substrait boundaries, adapter coverage, quality evidence, or similarly inspectable state. +- Avoid vague generated hero art. If a visual does not help a reader understand IncQL's data logic surface, replace it with a grounded product visual. +- Color belongs in rails, edge behavior, focus states, and signal accents; long-form prose should stay calm and readable. +- Glass effects should support inspectability. If a treatment makes text, navigation, or code harder to read, simplify it. +- IncQL should feel analytical and typed, not ceremonial. Do not copy Incan's Incapunk forged-metal tone directly. +- The landing page can be more expressive than reference pages, but ordinary Markdown pages must still look complete without local wrappers. + +## Implementation model + +The production theme is a light MkDocs Material adapter in `docs/stylesheets/prismplane.css`, with one shared light syntax system across ordinary docs and custom pages. Keep the implementation in Material-friendly CSS and normal Markdown first: + +- use Material navigation, search, code blocks, tables, admonitions, and task lists +- keep custom landing-page classes limited to `docs/index.md` +- avoid page-specific HTML when a normal Markdown heading, table, list, or admonition would work +- prefer editing existing CSS sections over appending late override piles +- use `mkdocs build --strict` before publishing docs changes + +Interactive documentation components follow the same split: + +- source Markdown and repository metadata remain the source of truth +- a build-time, standard-library generator validates and serializes portable data +- ordinary Markdown remains visible as the no-JavaScript and failed-enhancement fallback +- JavaScript enhances that data with search, filters, keyboard behavior, and URL state +- component CSS uses local `--pp-*` tokens, so another product can substitute its brand layer without rewriting behavior + +The RFC Context Reader is the first component built to this contract. Its filtering and master-detail behavior are product-neutral; IncQL supplies the RFC metadata schema, controlled tag vocabulary, assignments, and Prismplane appearance. + +For a second site, start by supplying its project label, intentional RFC-number gaps, controlled tag configuration, and `--pp-docs-*` color tokens. Keep the generated-data shape and reader interaction contract unchanged first. Only extract shared files after that port demonstrates which configuration points are real rather than IncQL-specific assumptions. + +The source tag catalog has a deliberately small, portable schema: + +```json +{ + "definitions": { + "planning": "Planning", + "evidence": "Evidence" + }, + "records": { + "030": ["planning", "evidence"] + } +} +``` + +The generator resolves those keys into each reader record as `"tags": [{"key": "planning", "label": "Planning"}]`. Keys are stable URL and data identifiers; labels are presentation text. A record must have one to three tags, with four reserved for genuinely cross-cutting program or platform RFCs. + +The reader treats multiple tags as an intersection: selecting Planning and Evidence shows records that carry both. URL state uses one sorted, repeated parameter per selection (`?tag=evidence&tag=planning`), so filtering can be bookmarked, shared, and restored through browser history. Clicking a tag in the detail pane toggles that same filter; clearing tags removes every `tag` parameter. Keep this data and interaction contract stable when porting the component even if the vocabulary, typography, and colors change. + +Reader controls belong to the surface they affect. Free-text search and tag facets share one stable-height search shell; selected tags become compact tokens inside that shell. Status filtering and status sorting live in the Status column header on desktop, with the same disclosure moved into the toolbar when that column is hidden on mobile. Query, status, sort, and repeated tag parameters remain bookmarkable, while open popovers stay transient and never enter history. + +The desktop documentation navigation can collapse to a narrow restore rail. That preference persists across ordinary documentation pages, but it does not replace or alter Material's mobile drawer. Custom full-width pages can continue hiding the primary sidebar entirely. + +## User experience target + +The docs should answer a new user's first questions quickly: + +1. What is IncQL? +2. Which task should I start with? +3. Where is the current API reference? +4. How do query blocks, dataset carriers, Prism, Substrait, sessions, and governance evidence fit together? + +If a page cannot answer one of those questions directly, it should still make the next useful page obvious through links and navigation. diff --git a/docs/contributing/writing_rfcs.md b/docs/contributing/writing_rfcs.md index d668ca61..ae8126b0 100644 --- a/docs/contributing/writing_rfcs.md +++ b/docs/contributing/writing_rfcs.md @@ -36,6 +36,7 @@ Purely internal Incan compiler refactors with **no** IncQL-visible meaning usual - Copy [TEMPLATE.md](../rfcs/TEMPLATE.md). - Add `docs/rfcs/NNN_short_slug.md` (example: `006_window_semantics.md`). - Pick the next `NNN` from the [RFC index](../rfcs/README.md) and open issues, avoiding collisions. + - Assign the RFC one to three controlled tags in `docs/rfcs/catalog.json`. Use only keys declared under `definitions`; add a new definition only when the existing vocabulary cannot describe a recurring concern. Four tags are reserved for genuinely cross-cutting program or platform RFCs. 3. **Fill in the RFC** One coherent proposal per RFC. Cover at least: @@ -52,21 +53,22 @@ Purely internal Incan compiler refactors with **no** IncQL-visible meaning usual 5. **Discuss** Use the PR (and the linked issue, if any) to converge. +6. **Regenerate the catalog** + Run `make rfc-index`, review the generated reader data and fallback table, then run `make docs-build`. The docs build validates the RFC number sequence, required metadata, related RFCs, lifecycle folder, controlled tag assignments, and generated index before rendering the site. + ## After acceptance -- **Implemented:** Set **Shipped in** to the first IncQL **package** release that contains the change. When you adopt a `closed/implemented/` layout (optional), move the file there and keep the index accurate. +- **Implemented:** Set **Shipped in** to the first IncQL **package** release that contains the change, move the RFC to `docs/rfcs/closed/implemented/`, and keep the index accurate. - **Deferred:** Update **Status** to `Deferred` and record why. -## Closed RFCs (optional layout) - -If you introduce subfolders under `docs/rfcs/`, a common pattern is: +## Lifecycle layout -- `docs/rfcs/` — active RFCs (Draft, Planned, In Progress, …) +- `docs/rfcs/` — active RFCs (Draft, Planned, In Progress, Blocked, or Deferred) - `docs/rfcs/closed/implemented/` — shipped - `docs/rfcs/closed/superseded/` — replaced by a newer IncQL RFC - `docs/rfcs/closed/rejected/` — withdrawn or rejected -When superseding or rejecting, update the status line (for example `Superseded by IncQL RFC NNN`) and move the file if you use `closed/`. +When implementing, superseding, or rejecting an RFC, update its status and release or replacement metadata, move it to the matching lifecycle folder, and repair incoming links in the same change. Keep the filename and RFC number unchanged, then run `make rfc-index` so the searchable catalog follows the source records. ## Tips for a good RFC diff --git a/docs/design_records.md b/docs/design_records.md new file mode 100644 index 00000000..147e2b8b --- /dev/null +++ b/docs/design_records.md @@ -0,0 +1,25 @@ +# Design records + +IncQL keeps normative design decisions and longer-form explorations separate from current API documentation. + +## RFCs + +[Requests for Comments][rfcs] define IncQL's stable design intent and record why important boundaries exist. They cover the language and carrier foundations, Prism and Substrait, Session execution, the function catalog, evidence and governance, external ingress, and backend modes. + +RFCs are moment-in-time design records. Use the [Reference][reference] pages for current package behavior and the RFC series when you need the normative contract, rationale, alternatives, or design history. + +## Whitepapers + +[Whitepapers][whitepapers] explore product and architectural directions that benefit from a cohesive narrative but are not themselves the public API contract. The current collection includes [IncQL-DB][incql-db], the local embedded analytical store direction. + +## Contributing a design + +Read [Writing RFCs][writing-rfcs] before proposing a normative change. New RFCs start from the [RFC template][template] and should make the north-star end state explicit before describing implementation stages. + + +[incql-db]: whitepapers/incql_db.md +[reference]: language/reference/README.md +[rfcs]: rfcs/README.md +[template]: rfcs/TEMPLATE.md +[whitepapers]: whitepapers/README.md +[writing-rfcs]: contributing/writing_rfcs.md diff --git a/docs/README.md b/docs/docs_map.md similarity index 83% rename from docs/README.md rename to docs/docs_map.md index 93eac12f..5ee829fa 100644 --- a/docs/README.md +++ b/docs/docs_map.md @@ -1,6 +1,6 @@ -# IncQL documentation +# IncQL documentation map -This directory holds the public documentation for the IncQL project. +This page maps the public documentation for the IncQL project. Use the docs tree like this: @@ -11,7 +11,7 @@ Use the docs tree like this: - **RFCs:** design records and normative proposals in [rfcs/][rfcs] - **Whitepapers:** broader non-normative design papers in [whitepapers/][whitepapers] - **Release notes:** shipped and user-visible changes in [release_notes/][release-notes] -- **Contributing:** contributor workflow, especially RFC process, in [contributing/][contributing] +- **Contributing:** contributor workflow, especially RFC process, in [contributing/writing_rfcs.md][contributing] ## Recommended reading paths @@ -44,17 +44,17 @@ Use the docs tree like this: 1. [RFC index][rfcs-index] 2. [How to write an RFC][writing-rfcs] -> Note: When a standalone docs site is added, `docs/` remains the content root. The structure here should already follow the same content model used in Incan: reference, how-to guides, explanation, architecture/contributing, RFCs, and release notes. +The standalone MkDocs site uses `docs/` as the content root. The structure follows the same content model used in Incan: reference, how-to guides, explanation, architecture/contributing, RFCs, whitepapers, and release notes. -[language-reference]: language/reference/ -[language-how-to]: language/how-to/ -[language-explanation]: language/explanation/ +[language-reference]: language/reference/dataset_carriers.md +[language-how-to]: language/how-to/README.md +[language-explanation]: language/explanation/dataset_carriers.md [architecture]: architecture.md [rfcs]: rfcs/README.md [whitepapers]: whitepapers/README.md -[release-notes]: release_notes/ -[contributing]: contributing/ +[release-notes]: release_notes/v0_1.md +[contributing]: contributing/writing_rfcs.md [language-overview]: language/README.md [window-columns-how-to]: language/how-to/window_columns.md [dataset-explanation]: language/explanation/dataset_carriers.md diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..f78947b7 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,430 @@ +
+
+
+

One semantic model. Multiple ways to think.

+ +

Write data logic once.
Run it anywhere.
Inspect everything.

+ +

IncQL unifies query blocks, DataFrame-style chains, LazyFrames, and pipelines into one typed relational model. Prism lets you see exactly what the compiler sees before execution.

+ + + +
+Typed & safe Catch issues early with static typing +Inspectable See the plan before execution +Portable Compile to Substrait +Governed Observe and verify behavior +
+
+ +
+A glass prism refracting data logic into layered plan stages. +
+
+ +
+
+## The problem with data logic today + +Teams lose time to expression, semantics drift, and opaque pipelines that lock them to engines. + +
+
+01 +
+

Too many ways to express logic

+

SQL, DataFrames, pipelines, and notebooks each carry their own shape.

+
+
+ +
+02 +
+

Different semantics and behaviors

+

Small rewrites can change meaning before anyone sees the plan.

+
+
+ +
+03 +
+

Hard to inspect and debug

+

You see results, but not the logic, lineage, or rechecking behavior.

+
+
+ +
+04 +
+

Tied to specific engines

+

Porting usually means rewriting logic and revalidating behavior.

+
+
+
+
+ +
+
+

IncQL is the unifying layer

+

Different surfaces. One compiler. Multiple execution targets.

+
+ +
+
+
+SQL +
SQL

Query blocks

+
+
+DF +
DataFrames

Python, Rust, JS

+
+
+Lazy +
LazyFrames

Polars, Ibis, DataFusion

+
+
+Pipe +
Pipelines

Airflow, dbt, custom

+
+
+ +
+ +IncQL +

Typed relational model

+
    +
  • Schema flow
  • +
  • Lineage
  • +
  • Optimizer choices
  • +
  • Type-checked
  • +
+
+ +
One semanticmodel
+ +
+
+ +DuckDBIn-process +
+
+ +DataFusionRust +
+
+ +SparkJVM +
+
+ +Substrait consumersAny engine +
+
+
+
+
+ +
+
+## How IncQL works + +Author in the surface that fits the task. IncQL keeps the semantics attached as the work moves from intent to execution. +
+ +
+
+
+
+01 +

Author

+
+

Write in the interface that matches your workflow and context.

+
    +
  • Query blocks
  • +
  • DataFrames
  • +
  • LazyFrames
  • +
  • Pipelines
  • +
+
+ +
+
+02 +

Compile

+
+

Lower authoring intent into a typed relational model and Substrait plan.

+
+ +```incan +model Order: + id: int + status: str + region: str +``` + +
+

Substrait

+
+ +
+
+03 +

Prism

+
+

IncQL's semantic core makes the compiler's decisions visible.

+
    +
  • Schema flow
  • +
  • Lineage
  • +
  • Projections
  • +
  • Filters
  • +
  • Optimizer choices
  • +
+
+ +
+
+04 +

Optimize

+
+

Apply rule- and cost-based optimizations for the best execution plan.

+
    +
  • Aggregate
  • +
  • Filter
  • +
  • Project
  • +
  • Scan
  • +
+

Smart optimizer

+
+ +
+
+05 +

Execute

+
+

Run on the engine that fits: local, in-process, or distributed.

+
    +
  • DataFusion
  • +
  • DuckDB
  • +
  • Spark
  • +
  • Substrait
  • +
+
+
+
+
+ +
+
+

Inspect before execution

+ +## Prism makes the compiler visible + +IncQL plans are not opaque strings handed to a backend engine. Prism exposes the typed model and evidence before execution. +
+ +
+
+
+01 +
Your queryAuthoring intent
+
+ +```incan +query { + FROM orders + WHERE .status == "paid" + GROUP BY .region + SELECT + .region as region, + sum(.amount) as total +} +``` +
+ +
+
+02 +
Prism inspectionTyped relational model
+Inspectable +
+ +```text +Source(orders) + Filter(status == paid) + Aggregate(group: region) + Project(region, total) +``` + +
+Schema flow +Lineage attached +Choices visible +
+
+ +
+
+03 +
Execution targetsPortable boundary
+
+ +
    +
  • DataFusion
  • +
  • DuckDB
  • +
  • Spark
  • +
  • Other Substrait consumers
  • +
+
+
+
+ +
+
+

Semantic convergence

+ +## One language. Many surfaces. + +Different ways to write. Same plan. Same result. +
+ +
+
+
+ + + + + +
+ +
+ +```incan +query { + FROM orders + WHERE .status == "paid" + ORDER BY desc(.amount) + LIMIT 20 +} +``` + +
+ +
+ +```sql +SELECT * +FROM orders +WHERE status = 'paid' +ORDER BY amount DESC +LIMIT 20; +``` + +
+ +
+ +```incan +preview = ( + orders + .where(orders["status"] == "paid") + .sort_values("amount", ascending=false) + .head(20) +) +``` + +
+ +
+ +```incan +preview = orders + .filter(eq(col("status"), "paid")) + .order_by([desc(col("amount"))]) + .limit(20) +``` + +
+ +
+ +```incan +preview = orders + |> where .status == "paid" + |> order_by .amount desc + |> limit 20 +``` + +
+
+
+ + + +
+
+Prism +

Same Prism plan

+
+ +
    +
  1. SourceRead the same typed relation.
  2. +
  3. FilterApply the same predicate semantics.
  4. +
  5. OrderKeep descending amount order attached.
  6. +
  7. LimitReturn the same preview window.
  8. +
+
+
+ +
+
+ +

Confidence by construction

+ +## Built for trust + +- Static typing and schema flow for confidence +- Prism for deep visibility before execution +- Deterministic, backend-neutral semantics +- Reproducible, testable, observable evidence +
+ +
+ +

A surface developers can own

+ +## Engineered for developers + +- Familiar when you know SQL or DataFrames +- Powerful when governance matters +- Extensible, open, and community-driven +- Designed for humans and AI agents +
+
+ +
+ + +
+

Start with inspectable data logic

+ +## Ready to experience IncQL? + +Start with a quickstart, explore examples, or jump straight into the reference. +
+ + +
+
diff --git a/docs/javascripts/prismplane.js b/docs/javascripts/prismplane.js new file mode 100644 index 00000000..86b8a99a --- /dev/null +++ b/docs/javascripts/prismplane.js @@ -0,0 +1,1270 @@ +(() => { + const rfcReaderContract = { + adjacentRecordId(records, currentId, offset) { + if (records.length === 0) { + return ""; + } + const currentIndex = records.findIndex((record) => record.id === currentId); + if (currentIndex < 0) { + return records[0].id; + } + const nextIndex = Math.max(0, Math.min(records.length - 1, currentIndex + offset)); + return records[nextIndex].id; + }, + + resolvedRecordId(records, selectedId) { + return records.some((record) => record.id === selectedId) + ? selectedId + : records[0]?.id ?? ""; + }, + + setTag(tags, key, selected) { + const next = selected + ? [...new Set([...tags, key])] + : tags.filter((tag) => tag !== key); + return next.sort(); + }, + + sortRecords(records, sort) { + const direction = sort === "status-desc" ? -1 : 1; + const byId = (left, right) => left.id.localeCompare(right.id, undefined, { numeric: true }); + if (!sort) { + return [...records].sort(byId); + } + return [...records].sort((left, right) => { + const byStatus = left.status.localeCompare(right.status, undefined, { sensitivity: "base" }); + return byStatus === 0 ? byId(left, right) : byStatus * direction; + }); + }, + + matches(record, state, normalizeSearch) { + const queryTokens = normalizeSearch(state.query).split(/\s+/).filter(Boolean); + const recordTags = new Set(record.tags.map((tag) => tag.key)); + return queryTokens.every((token) => record.searchText.includes(token)) + && (state.scope === "all" || record.lifecycle === state.scope) + && (!state.status || record.status_key === state.status) + && state.tags.every((tag) => recordTags.has(tag)); + }, + + readParams(params, defaults, known) { + const next = { ...defaults, tags: [] }; + next.query = params.get("q") ?? ""; + next.scope = ["active", "implemented"].includes(params.get("scope")) + ? params.get("scope") + : "all"; + next.status = known.statuses.has(params.get("status")) ? params.get("status") : ""; + next.sort = ["status-asc", "status-desc"].includes(params.get("sort")) + ? params.get("sort") + : ""; + if (next.status) { + next.scope = "all"; + } + next.tags = [...new Set(params.getAll("tag").filter((tag) => known.tags.has(tag)))].sort(); + const selected = params.get("rfc"); + if (selected && known.records.has(selected)) { + next.selectedId = selected; + next.selectedExplicitly = true; + } + return next; + }, + + writeParams(params, state) { + const setOrDelete = (key, value) => { + if (value) { + params.set(key, value); + } else { + params.delete(key); + } + }; + setOrDelete("q", state.query.trim()); + setOrDelete("scope", state.scope === "all" ? "" : state.scope); + setOrDelete("status", state.status); + setOrDelete("sort", state.sort); + params.delete("topic"); + params.delete("tag"); + [...state.tags].sort().forEach((tag) => params.append("tag", tag)); + setOrDelete("rfc", state.selectedExplicitly ? state.selectedId : ""); + return params; + }, + }; + + if (typeof module !== "undefined" && module.exports) { + module.exports = { rfcReaderContract }; + } + if (typeof document === "undefined") { + return; + } + + const initializeSurfaceTabs = (root = document) => { + root.querySelectorAll("[data-incql-surface-tabs]").forEach((group) => { + if (group.dataset.tabsReady === "true") { + return; + } + + const tabs = [...group.querySelectorAll('[role="tab"]')]; + const panels = [...group.querySelectorAll('[role="tabpanel"]')]; + + if (tabs.length === 0 || panels.length === 0) { + return; + } + + const activateTab = (nextTab, moveFocus = false) => { + tabs.forEach((tab) => { + const isActive = tab === nextTab; + tab.setAttribute("aria-selected", String(isActive)); + tab.tabIndex = isActive ? 0 : -1; + }); + + panels.forEach((panel) => { + panel.hidden = panel.id !== nextTab.getAttribute("aria-controls"); + panel.tabIndex = 0; + }); + + const tabList = nextTab.closest('[role="tablist"]'); + if (tabList && tabList.scrollWidth > tabList.clientWidth) { + const listStyle = getComputedStyle(tabList); + const startPadding = Number.parseFloat(listStyle.paddingInlineStart) || 0; + const activeRight = nextTab.offsetLeft + nextTab.offsetWidth; + const availableWidth = tabList.clientWidth - startPadding * 2; + const activeIndex = tabs.indexOf(nextTab); + const leftAnchor = tabs + .slice(0, activeIndex + 1) + .find((tab) => activeRight - tab.offsetLeft <= availableWidth) ?? nextTab; + const alignedLeft = leftAnchor.offsetLeft - startPadding; + const maximumLeft = tabList.scrollWidth - tabList.clientWidth; + tabList.scrollTo({ + left: Math.max(0, Math.min(alignedLeft, maximumLeft)), + behavior: "auto", + }); + } + + if (moveFocus) { + nextTab.focus(); + } + }; + + tabs.forEach((tab, index) => { + tab.addEventListener("click", () => activateTab(tab)); + tab.addEventListener("keydown", (event) => { + let nextIndex = null; + + if (event.key === "ArrowRight") { + nextIndex = (index + 1) % tabs.length; + } else if (event.key === "ArrowLeft") { + nextIndex = (index - 1 + tabs.length) % tabs.length; + } else if (event.key === "Home") { + nextIndex = 0; + } else if (event.key === "End") { + nextIndex = tabs.length - 1; + } + + if (nextIndex !== null) { + event.preventDefault(); + activateTab(tabs[nextIndex], true); + } + }); + }); + + const selectedTab = tabs.find((tab) => tab.getAttribute("aria-selected") === "true") ?? tabs[0]; + activateTab(selectedTab); + group.dataset.tabsReady = "true"; + }); + }; + + let disposeBookParts = () => {}; + + const initializeBookParts = (root = document) => { + disposeBookParts(); + const controller = new AbortController(); + const { signal } = controller; + + root.querySelectorAll("[data-book-parts]").forEach((book) => { + const parts = [...book.querySelectorAll(".pp-book-part")]; + if (parts.length === 0) { + return; + } + + const openPart = (target) => { + parts.forEach((part) => { + if (part !== target && part.open) { + part.open = false; + } + }); + }; + + parts.forEach((part) => { + part.addEventListener("toggle", () => { + if (part.open) { + openPart(part); + } + }, { signal }); + }); + + const requestedPart = parts.find((part) => `#${part.id}` === window.location.hash); + if (requestedPart) { + requestedPart.open = true; + openPart(requestedPart); + } else if (!parts.some((part) => part.open)) { + parts[0].open = true; + } + }); + + disposeBookParts = () => controller.abort(); + }; + + let disposeArchitectureNav = () => {}; + + const initializeArchitectureNav = (root = document) => { + disposeArchitectureNav(); + disposeArchitectureNav = () => {}; + + const nav = root.querySelector(".incql-architecture-rail"); + if (!nav) { + return; + } + + const links = [...nav.querySelectorAll("[data-architecture-link]")]; + const sections = [...root.querySelectorAll("[data-architecture-section]")]; + if (links.length === 0 || sections.length === 0) { + return; + } + + const setActive = (sectionId) => { + links.forEach((link) => { + const isActive = link.dataset.architectureLink === sectionId; + link.classList.toggle("is-active", isActive); + if (isActive) { + link.setAttribute("aria-current", "location"); + } else { + link.removeAttribute("aria-current"); + } + }); + + const activeLink = links.find((link) => link.dataset.architectureLink === sectionId); + if (activeLink && nav.scrollWidth > nav.clientWidth) { + const maximumLeft = nav.scrollWidth - nav.clientWidth; + const centeredLeft = activeLink.offsetLeft - (nav.clientWidth - activeLink.offsetWidth) / 2; + nav.scrollTo({ left: Math.max(0, Math.min(centeredLeft, maximumLeft)), behavior: "auto" }); + } + }; + + const linkHandlers = links.map((link) => { + const handleClick = (event) => { + const sectionId = link.dataset.architectureLink; + const section = root.getElementById(sectionId); + if (!section) { + return; + } + + event.preventDefault(); + const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches; + section.scrollIntoView({ behavior: reducedMotion ? "auto" : "smooth", block: "start" }); + window.history.replaceState(null, "", `#${sectionId}`); + setActive(sectionId); + }; + link.addEventListener("click", handleClick); + return { link, handleClick }; + }); + + let scrollFrame = null; + const updateActiveFromScroll = () => { + scrollFrame = null; + const readingLine = window.innerHeight * 0.28; + const currentSection = sections.find((section) => { + const bounds = section.getBoundingClientRect(); + return bounds.top <= readingLine && bounds.bottom > readingLine; + }); + + if (currentSection) { + setActive(currentSection.id); + return; + } + + const nearestSection = sections + .map((section) => ({ section, distance: Math.abs(section.getBoundingClientRect().top - readingLine) })) + .sort((left, right) => left.distance - right.distance)[0]?.section; + if (nearestSection) { + setActive(nearestSection.id); + } + }; + + const scheduleActiveUpdate = () => { + if (scrollFrame === null) { + scrollFrame = window.requestAnimationFrame(updateActiveFromScroll); + } + }; + + window.addEventListener("scroll", scheduleActiveUpdate, { passive: true }); + window.addEventListener("resize", scheduleActiveUpdate); + updateActiveFromScroll(); + disposeArchitectureNav = () => { + linkHandlers.forEach(({ link, handleClick }) => link.removeEventListener("click", handleClick)); + window.removeEventListener("scroll", scheduleActiveUpdate); + window.removeEventListener("resize", scheduleActiveUpdate); + if (scrollFrame !== null) { + window.cancelAnimationFrame(scrollFrame); + } + }; + nav.dataset.architectureNavReady = "true"; + }; + + let disposePrimaryNavCollapse = () => {}; + + const initializePrimaryNavCollapse = (root = document) => { + disposePrimaryNavCollapse(); + disposePrimaryNavCollapse = () => {}; + + const sidebar = root.querySelector(".md-sidebar--primary"); + if (!sidebar) { + return; + } + + const desktop = window.matchMedia("(min-width: 76.25em)"); + const storageKey = "incql-docs-primary-nav-collapsed"; + const navRegion = sidebar.querySelector(".md-sidebar__scrollwrap"); + if (navRegion && !navRegion.id) { + navRegion.id = "pp-primary-navigation"; + } + let toggle = sidebar.querySelector(".pp-primary-nav-toggle"); + if (!toggle) { + toggle = document.createElement("button"); + toggle.type = "button"; + toggle.className = "pp-primary-nav-toggle md-icon"; + const icon = root.querySelector(".md-search__icon svg:nth-of-type(2)") + ?? root.querySelector('.md-header__button[for="__drawer"] svg'); + if (icon) { + const clonedIcon = icon.cloneNode(true); + clonedIcon.setAttribute("aria-hidden", "true"); + toggle.append(clonedIcon); + } + sidebar.prepend(toggle); + } + if (navRegion) { + toggle.setAttribute("aria-controls", navRegion.id); + } + + let collapsed = false; + try { + collapsed = window.localStorage.getItem(storageKey) === "true"; + } catch { + collapsed = false; + } + + const apply = () => { + const isCollapsed = desktop.matches && collapsed; + document.body.classList.toggle("pp-primary-nav-collapsed", isCollapsed); + toggle.hidden = !desktop.matches; + toggle.setAttribute("aria-expanded", String(!isCollapsed)); + toggle.setAttribute("aria-label", isCollapsed ? "Expand navigation" : "Collapse navigation"); + toggle.title = isCollapsed ? "Expand navigation" : "Collapse navigation"; + navRegion?.setAttribute("aria-hidden", String(isCollapsed)); + }; + + const handleToggle = () => { + collapsed = !collapsed; + try { + window.localStorage.setItem(storageKey, String(collapsed)); + } catch { + // The control still works for the current page when storage is unavailable. + } + apply(); + }; + const handleMediaChange = () => apply(); + toggle.addEventListener("click", handleToggle); + desktop.addEventListener("change", handleMediaChange); + apply(); + + disposePrimaryNavCollapse = () => { + toggle.removeEventListener("click", handleToggle); + desktop.removeEventListener("change", handleMediaChange); + }; + }; + + let disposeRfcReader = () => {}; + + const initializeRfcReader = (root = document) => { + disposeRfcReader(); + disposeRfcReader = () => {}; + + const host = root.querySelector("[data-rfc-reader]"); + const dataNode = root.querySelector("script[data-rfc-catalog]"); + const fallback = root.querySelector("[data-rfc-fallback]"); + if (!host || !dataNode || !fallback) { + return; + } + + let records; + try { + records = JSON.parse(dataNode.textContent ?? ""); + if (!Array.isArray(records) || records.length === 0) { + return; + } + const requiredFields = ["id", "title", "status", "status_key", "lifecycle", "created", "summary", "motivation", "href"]; + const hasInvalidRecord = records.some((record) => { + const hasInvalidField = requiredFields.some((field) => typeof record[field] !== "string" || record[field].length === 0); + const hasInvalidTags = !Array.isArray(record.tags) || record.tags.some((tag) => ( + !tag || typeof tag.key !== "string" || tag.key.length === 0 || typeof tag.label !== "string" || tag.label.length === 0 + )); + return hasInvalidField || hasInvalidTags; + }); + if (hasInvalidRecord) { + return; + } + } catch { + return; + } + + const controller = new AbortController(); + const { signal } = controller; + const createElement = (tag, className = "", text = "") => { + const element = document.createElement(tag); + if (className) { + element.className = className; + } + if (text) { + element.textContent = text; + } + return element; + }; + const searchable = (value) => value.toLowerCase().normalize("NFKD").replace(/[\u0300-\u036f]/g, " ").replace(/[^a-z0-9]+/g, " ").trim(); + const excerpt = (value, limit) => { + if (value.length <= limit) { + return value; + } + const candidate = value.slice(0, limit + 1); + const boundary = candidate.lastIndexOf(" "); + return `${candidate.slice(0, boundary > limit * 0.72 ? boundary : limit).trim()}…`; + }; + const recordById = new Map(records.map((record) => [record.id, record])); + const tagsByKey = new Map(); + const tagCounts = new Map(); + records.forEach((record) => { + record.tags.forEach((tag) => { + tagsByKey.set(tag.key, tag.label); + tagCounts.set(tag.key, (tagCounts.get(tag.key) ?? 0) + 1); + }); + }); + const sortedTags = [...tagsByKey].sort((left, right) => left[1].localeCompare(right[1])); + const statusByKey = new Map(records.map((record) => [record.status_key, record.status])); + const media = window.matchMedia("(max-width: 56em)"); + const compactMedia = window.matchMedia("(max-width: 38em)"); + const defaultState = () => ({ + query: "", + scope: "all", + status: "", + sort: "", + tags: [], + selectedId: records[0].id, + selectedExplicitly: false, + }); + let state = defaultState(); + let visibleRecords = [...records]; + let urlTimer = null; + let lastFocusedId = state.selectedId; + + records.forEach((record) => { + record.searchText = searchable([ + `RFC ${record.id}`, + record.title, + record.status, + record.tags.map((tag) => tag.label).join(" "), + excerpt(record.summary, 380), + excerpt(record.motivation, 460), + ].join(" ")); + }); + + const reader = createElement("section", "pp-rfc-reader"); + reader.setAttribute("aria-label", "RFC catalog"); + reader.dataset.mobileView = "results"; + + const toolbar = createElement("div", "pp-rfc-reader__toolbar"); + const searchForm = createElement("form", "pp-rfc-reader__search"); + searchForm.setAttribute("role", "search"); + const searchLabel = createElement("label", "pp-sr-only", "Search RFCs"); + const searchId = "pp-rfc-search"; + searchLabel.htmlFor = searchId; + const searchShell = createElement("div", "pp-rfc-reader__search-shell"); + const activeTags = createElement("div", "pp-rfc-reader__active-tags"); + activeTags.hidden = true; + const searchInput = createElement("input"); + searchInput.id = searchId; + searchInput.type = "search"; + searchInput.autocomplete = "off"; + searchInput.placeholder = "Search RFCs or choose tags"; + searchInput.setAttribute("aria-controls", "pp-rfc-records"); + searchInput.setAttribute("aria-expanded", "false"); + const tagMenuButton = createElement("button", "pp-rfc-reader__tag-menu-button"); + tagMenuButton.type = "button"; + tagMenuButton.setAttribute("aria-label", "Choose tag filters"); + tagMenuButton.setAttribute("aria-expanded", "false"); + const tagMenuLabel = createElement("span", "", "Tags"); + const tagMenuCount = createElement("span", "pp-rfc-reader__tag-menu-count"); + tagMenuCount.hidden = true; + tagMenuButton.append(tagMenuLabel, tagMenuCount); + const shortcut = createElement("kbd", "", "/"); + shortcut.setAttribute("aria-hidden", "true"); + searchShell.append(activeTags, searchInput, tagMenuButton, shortcut); + + const facets = createElement("div", "pp-rfc-reader__facets"); + const scopeFieldset = createElement("fieldset", "pp-rfc-segments"); + const scopeLegend = createElement("legend", "pp-sr-only", "RFC lifecycle scope"); + scopeFieldset.append(scopeLegend); + const scopeInputs = new Map(); + const scopeDefinitions = [ + ["all", "All", records.length], + ["active", "Active", records.filter((record) => record.lifecycle === "active").length], + ["implemented", "Implemented", records.filter((record) => record.lifecycle === "implemented").length], + ]; + scopeDefinitions.forEach(([value, label, count]) => { + const item = createElement("div", "pp-rfc-segments__item"); + const input = createElement("input"); + input.type = "radio"; + input.name = "pp-rfc-scope"; + input.id = `pp-rfc-scope-${value}`; + input.value = value; + const inputLabel = createElement("label", "", label); + inputLabel.htmlFor = input.id; + inputLabel.append(createElement("span", "", String(count))); + item.append(input, inputLabel); + scopeFieldset.append(item); + scopeInputs.set(value, input); + }); + + const tagInputs = new Map(); + const tagPanel = createElement("div", "pp-rfc-reader__tag-panel"); + tagPanel.id = "pp-rfc-tag-panel"; + tagPanel.hidden = true; + const tagFieldset = createElement("fieldset"); + const tagLegend = createElement("legend", "", "Filter by tags"); + tagFieldset.append(tagLegend); + sortedTags.forEach(([key, label]) => { + const item = createElement("div", "pp-rfc-reader__tag-option"); + const input = createElement("input"); + input.type = "checkbox"; + input.id = `pp-rfc-tag-${key}`; + input.value = key; + const inputLabel = createElement("label"); + inputLabel.htmlFor = input.id; + inputLabel.append(createElement("span", "", label), createElement("span", "", String(tagCounts.get(key)))); + item.append(input, inputLabel); + tagFieldset.append(item); + tagInputs.set(key, input); + }); + const clearTagsButton = createElement("button", "pp-rfc-reader__clear-tags", "Clear tags"); + clearTagsButton.type = "button"; + tagPanel.append(tagFieldset, clearTagsButton); + searchInput.setAttribute("aria-controls", `${searchInput.getAttribute("aria-controls")} ${tagPanel.id}`); + tagMenuButton.setAttribute("aria-controls", tagPanel.id); + searchForm.append(searchLabel, searchShell, tagPanel); + + const resetButton = createElement("button", "pp-rfc-reader__reset", "Reset filters"); + resetButton.type = "button"; + resetButton.hidden = true; + facets.append(scopeFieldset); + toolbar.append(searchForm, facets); + + const statusbar = createElement("div", "pp-rfc-reader__statusbar"); + const resultCount = createElement("p", "pp-rfc-reader__count"); + resultCount.setAttribute("role", "status"); + resultCount.setAttribute("aria-live", "polite"); + resultCount.setAttribute("aria-atomic", "true"); + const keyboardHint = createElement("p", "pp-rfc-reader__hint", "↑↓ browse · Enter open"); + keyboardHint.setAttribute("aria-hidden", "true"); + statusbar.append(resultCount, keyboardHint, resetButton); + + const index = createElement("div", "pp-rfc-reader__index"); + const master = createElement("section", "pp-rfc-reader__master"); + master.setAttribute("aria-label", "RFC results"); + const listHeader = createElement("div", "pp-rfc-reader__list-header"); + listHeader.id = "pp-rfc-list-label"; + const statusSlot = createElement("span", "pp-rfc-reader__status-slot"); + const statusFilter = createElement("details", "pp-rfc-reader__status-filter"); + const statusSummary = createElement("summary"); + statusSummary.setAttribute("role", "button"); + statusSummary.setAttribute("aria-expanded", "false"); + const statusSummaryLabel = createElement("span", "", "Status"); + const statusSummaryState = createElement("span", "pp-rfc-reader__status-summary-state"); + statusSummaryState.hidden = true; + statusSummary.append(statusSummaryLabel, statusSummaryState); + const statusPanel = createElement("div", "pp-rfc-reader__status-panel"); + statusPanel.id = "pp-rfc-status-panel"; + statusSummary.setAttribute("aria-controls", statusPanel.id); + + const statusInputs = new Map(); + const statusFieldset = createElement("fieldset"); + statusFieldset.append(createElement("legend", "", "Filter")); + [["", "All statuses"], ...[...statusByKey].sort((left, right) => left[1].localeCompare(right[1]))] + .forEach(([value, label]) => { + const option = createElement("label", "pp-rfc-reader__status-option"); + const input = createElement("input"); + input.type = "radio"; + input.name = "pp-rfc-status"; + input.value = value; + option.append(input, createElement("span", "", label)); + statusFieldset.append(option); + statusInputs.set(value, input); + }); + + const sortInputs = new Map(); + const sortFieldset = createElement("fieldset"); + sortFieldset.append(createElement("legend", "", "Sort")); + [ + ["", "RFC number"], + ["status-asc", "Status A–Z"], + ["status-desc", "Status Z–A"], + ].forEach(([value, label]) => { + const option = createElement("label", "pp-rfc-reader__status-option"); + const input = createElement("input"); + input.type = "radio"; + input.name = "pp-rfc-sort"; + input.value = value; + option.append(input, createElement("span", "", label)); + sortFieldset.append(option); + sortInputs.set(value, input); + }); + + statusPanel.append(statusFieldset, sortFieldset); + statusFilter.append(statusSummary, statusPanel); + statusSlot.append(statusFilter); + listHeader.append( + createElement("span", "", "RFC"), + statusSlot, + createElement("span", "", "Title"), + ); + const list = createElement("div", "pp-rfc-reader__records"); + list.id = "pp-rfc-records"; + list.tabIndex = 0; + list.setAttribute("role", "listbox"); + list.setAttribute("aria-label", "Matching RFCs"); + list.setAttribute("aria-controls", "pp-rfc-detail"); + const emptyMessage = createElement("p", "pp-rfc-reader__empty", "No RFCs match these filters."); + emptyMessage.hidden = true; + const detail = createElement("article", "pp-rfc-reader__detail"); + detail.id = "pp-rfc-detail"; + detail.tabIndex = 0; + const rowById = new Map(); + + records.forEach((record) => { + const row = createElement("div", "pp-rfc-row"); + row.dataset.recordId = record.id; + row.id = `pp-rfc-record-${record.id}`; + row.setAttribute("role", "option"); + row.setAttribute("aria-selected", "false"); + row.append( + createElement("span", "pp-rfc-row__number", record.id), + createElement("span", "pp-rfc-row__status", record.status), + createElement("span", "pp-rfc-row__title", record.title), + ); + list.append(row); + rowById.set(record.id, row); + }); + master.append(listHeader, list, emptyMessage); + index.append(toolbar, statusbar, master); + reader.append(index, detail); + + const placeStatusFilter = () => { + if (compactMedia.matches) { + facets.append(statusFilter); + } else { + statusSlot.append(statusFilter); + } + }; + + let suppressTagPanelFocus = false; + const setTagPanelOpen = (open, { restoreFocus = false } = {}) => { + if (sortedTags.length === 0) { + return; + } + tagPanel.hidden = !open; + searchForm.dataset.tagsOpen = String(open); + searchInput.setAttribute("aria-expanded", String(open)); + tagMenuButton.setAttribute("aria-expanded", String(open)); + if (open) { + statusFilter.open = false; + } else if (restoreFocus) { + suppressTagPanelFocus = true; + searchInput.focus(); + suppressTagPanelFocus = false; + } + }; + + if (sortedTags.length === 0) { + tagMenuButton.hidden = true; + } + placeStatusFilter(); + + const appendMetadata = (listNode, label, value) => { + if (!value) { + return; + } + const group = createElement("div"); + group.append(createElement("dt", "", label)); + const description = createElement("dd"); + if (value instanceof Node) { + description.append(value); + } else { + description.textContent = value; + } + group.append(description); + listNode.append(group); + }; + + const sourceLink = (link) => { + if (!link?.url || !link?.label) { + return null; + } + const anchor = createElement("a", "", link.label); + anchor.href = link.url; + return anchor; + }; + + const sourceLinks = (links) => { + const anchors = (links ?? []).map(sourceLink).filter(Boolean); + if (anchors.length === 0) { + return null; + } + if (anchors.length === 1) { + return anchors[0]; + } + const group = createElement("span", "pp-rfc-reader__source-links"); + anchors.forEach((anchor, index) => { + if (index > 0) { + group.append(createElement("span", "", "·")); + } + group.append(anchor); + }); + return group; + }; + + const writeUrl = (mode = "replace", { mobileDetail = false } = {}) => { + const url = new URL(window.location.href); + rfcReaderContract.writeParams(url.searchParams, state); + const existingHistoryState = window.history.state; + const historyState = existingHistoryState && typeof existingHistoryState === "object" + ? { ...existingHistoryState, ppRfcDetail: mobileDetail } + : { ppRfcDetail: mobileDetail }; + window.history[mode === "push" ? "pushState" : "replaceState"](historyState, "", url); + }; + + const detailHeader = createElement("header", "pp-rfc-reader__detail-header"); + const detailBody = createElement("div", "pp-rfc-reader__detail-body"); + detail.append(detailHeader, detailBody); + + const renderDetail = (record, { focusHeading = false, resetScroll = true } = {}) => { + detailHeader.replaceChildren(); + detailBody.replaceChildren(); + if (!record) { + detail.hidden = true; + return; + } + detail.hidden = false; + const backButton = createElement("button", "pp-rfc-reader__back", `Back to ${visibleRecords.length} results`); + backButton.type = "button"; + backButton.addEventListener("click", () => { + if (window.history.state?.ppRfcDetail) { + window.history.back(); + return; + } + state.selectedExplicitly = false; + reader.dataset.mobileView = "results"; + writeUrl("replace"); + window.requestAnimationFrame(() => focusList(state.selectedId)); + }, { signal }); + + const eyebrow = createElement("p", "pp-rfc-reader__eyebrow", `RFC ${record.id}`); + const heading = createElement("h2", "", record.title); + heading.id = "pp-rfc-detail-title"; + heading.tabIndex = -1; + detail.setAttribute("aria-labelledby", heading.id); + const metadata = createElement("dl", "pp-rfc-reader__metadata"); + appendMetadata(metadata, "Status", createElement("span", "pp-rfc-reader__status-pill", record.status)); + if (record.tags.length > 0) { + const tags = createElement("span", "pp-rfc-reader__detail-tags"); + record.tags.forEach((tag) => { + const tagButton = createElement("button", "pp-rfc-reader__tag-chip", tag.label); + tagButton.type = "button"; + tagButton.dataset.tagKey = tag.key; + const isActive = state.tags.includes(tag.key); + tagButton.setAttribute("aria-pressed", String(isActive)); + tagButton.setAttribute("aria-label", `${isActive ? "Remove" : "Add"} ${tag.label} tag filter`); + tagButton.addEventListener("click", () => { + state.tags = rfcReaderContract.setTag(state.tags, tag.key, !state.tags.includes(tag.key)); + prepareFilterChange(); + applyControls(); + applyFilters(); + writeUrl("push"); + window.requestAnimationFrame(() => { + detail.querySelector(`[data-tag-key="${tag.key}"]`)?.focus({ preventScroll: true }); + }); + }, { signal }); + tags.append(tagButton); + }); + appendMetadata(metadata, "Tags", tags); + } + appendMetadata(metadata, "Created", record.created); + appendMetadata(metadata, "Issue", sourceLinks(record.issue_links)); + appendMetadata(metadata, "RFC PR", sourceLinks(record.rfc_pr_links)); + + const openLink = createElement("a", "pp-rfc-reader__open", "Open full RFC →"); + openLink.href = record.href; + + const summarySection = createElement("section", "pp-rfc-reader__section"); + summarySection.append( + createElement("h3", "", "Summary"), + createElement("p", "", excerpt(record.summary, 430)), + ); + const motivationSection = createElement("section", "pp-rfc-reader__section"); + motivationSection.append( + createElement("h3", "", "Why it matters"), + createElement("p", "", excerpt(record.motivation, 540)), + ); + const headingGroup = createElement("div", "pp-rfc-reader__detail-heading"); + headingGroup.append(eyebrow, heading); + detailHeader.append(backButton, headingGroup, openLink); + detailBody.append(metadata, summarySection, motivationSection); + + const related = (record.related_ids ?? []).map((id) => recordById.get(id)).filter(Boolean); + if (related.length > 0) { + const relatedSection = createElement("section", "pp-rfc-reader__section"); + const relatedLinks = createElement("div", "pp-rfc-reader__related"); + related.forEach((relatedRecord) => { + const link = createElement("a", "", `RFC ${relatedRecord.id}`); + link.href = relatedRecord.href; + relatedLinks.append(link); + }); + relatedSection.append(createElement("h3", "", "Related records"), relatedLinks); + detailBody.append(relatedSection); + } + + if (resetScroll) { + detail.scrollTop = 0; + } + + if (focusHeading) { + window.requestAnimationFrame(() => heading.focus()); + } + }; + + const matchesRecord = (record) => { + return rfcReaderContract.matches(record, state, searchable); + }; + + const syncListSelection = () => { + const selectedRow = rowById.get(state.selectedId); + rowById.forEach((row, id) => { + row.setAttribute("aria-selected", String(id === state.selectedId && !row.hidden)); + }); + if (selectedRow && !selectedRow.hidden) { + list.setAttribute("aria-activedescendant", selectedRow.id); + } else { + list.removeAttribute("aria-activedescendant"); + } + }; + + const applyFilters = ({ focusDetail = false } = {}) => { + const previousSelectedId = state.selectedId; + const orderedRecords = rfcReaderContract.sortRecords(records, state.sort); + visibleRecords = orderedRecords.filter(matchesRecord); + const visibleIds = new Set(visibleRecords.map((record) => record.id)); + orderedRecords.forEach((record) => { + const row = rowById.get(record.id); + const visible = visibleIds.has(record.id); + row.hidden = !visible; + list.append(row); + }); + + if (!visibleIds.has(state.selectedId)) { + state.selectedId = rfcReaderContract.resolvedRecordId(visibleRecords, state.selectedId); + state.selectedExplicitly = false; + } + syncListSelection(); + + const count = visibleRecords.length; + resultCount.textContent = count === 1 ? "1 matching design record" : `${count} matching design records`; + emptyMessage.hidden = count !== 0; + list.hidden = count === 0; + list.tabIndex = count === 0 ? -1 : 0; + listHeader.hidden = count === 0; + resetButton.hidden = !state.query + && state.scope === "all" + && !state.status + && !state.sort + && state.tags.length === 0; + renderDetail(recordById.get(state.selectedId), { + focusHeading: focusDetail, + resetScroll: previousSelectedId !== state.selectedId, + }); + const selectedId = state.selectedId; + ensureRowVisible(selectedId); + window.requestAnimationFrame(() => { + if (state.selectedId === selectedId) { + ensureRowVisible(selectedId); + } + }); + }; + + const prepareFilterChange = () => { + if (media.matches) { + state.selectedExplicitly = false; + reader.dataset.mobileView = "results"; + } + }; + + const applyControls = () => { + searchInput.value = state.query; + (scopeInputs.get(state.scope) ?? scopeInputs.get("all")).checked = true; + (statusInputs.get(state.status) ?? statusInputs.get("")).checked = true; + (sortInputs.get(state.sort) ?? sortInputs.get("")).checked = true; + tagInputs.forEach((input, key) => { + input.checked = state.tags.includes(key); + }); + const fullStatusLabel = state.status ? statusByKey.get(state.status) : ""; + const compactStatusLabel = state.sort + ? ({ Implemented: "Impl.", "In Progress": "In prog." }[fullStatusLabel] ?? fullStatusLabel) + : fullStatusLabel; + const summaryState = [ + compactStatusLabel, + state.sort ? (state.sort === "status-asc" ? "A–Z" : "Z–A") : "", + ].filter(Boolean).join(" · "); + statusSummaryLabel.hidden = Boolean(summaryState); + statusSummaryState.textContent = summaryState; + statusSummaryState.hidden = !summaryState; + statusFilter.dataset.filtered = String(Boolean(state.status)); + statusSummary.setAttribute("aria-label", [ + "Status options", + state.status ? `filter ${statusByKey.get(state.status)}` : "all statuses", + state.sort === "status-asc" + ? "sorted A to Z" + : state.sort === "status-desc" ? "sorted Z to A" : "sorted by RFC number", + ].join(", ")); + clearTagsButton.hidden = state.tags.length === 0; + activeTags.replaceChildren(); + state.tags.slice(0, 1).forEach((key) => { + const chip = createElement("button", "pp-rfc-reader__active-tag", `${tagsByKey.get(key)} ×`); + chip.type = "button"; + chip.setAttribute("aria-label", `Remove ${tagsByKey.get(key)} tag filter`); + chip.addEventListener("click", () => { + state.tags = state.tags.filter((tag) => tag !== key); + applyControls(); + applyFilters(); + writeUrl("push"); + tagMenuButton.focus({ preventScroll: true }); + }, { signal }); + activeTags.append(chip); + }); + if (state.tags.length > 1) { + const overflow = createElement("button", "pp-rfc-reader__active-tag", `+${state.tags.length - 1}`); + overflow.type = "button"; + overflow.setAttribute("aria-label", `Show all ${state.tags.length} selected tag filters`); + overflow.addEventListener("click", () => { + setTagPanelOpen(true); + tagMenuButton.focus({ preventScroll: true }); + }, { signal }); + activeTags.append(overflow); + } + activeTags.hidden = state.tags.length === 0; + tagMenuCount.textContent = String(state.tags.length); + tagMenuCount.hidden = state.tags.length === 0; + }; + + const readUrl = ({ preserveSelectedId = "" } = {}) => { + const params = new URLSearchParams(window.location.search); + const hadRedundantScope = Boolean(params.get("status") && params.get("scope")); + state = rfcReaderContract.readParams(params, defaultState(), { + statuses: new Set(statusByKey.keys()), + tags: new Set(tagsByKey.keys()), + records: new Set(recordById.keys()), + }); + if (!state.selectedExplicitly && recordById.has(preserveSelectedId)) { + state.selectedId = preserveSelectedId; + } + const requestedSelectedId = state.selectedId; + const requestedExplicitSelection = state.selectedExplicitly; + applyControls(); + applyFilters(); + lastFocusedId = state.selectedId; + reader.dataset.mobileView = media.matches && state.selectedExplicitly ? "detail" : "results"; + if (hadRedundantScope || (requestedExplicitSelection && ( + !state.selectedExplicitly || state.selectedId !== requestedSelectedId + ))) { + writeUrl("replace"); + } + }; + + scopeInputs.forEach((input, value) => { + input.addEventListener("change", () => { + if (!input.checked) { + return; + } + state.scope = value; + if (value !== "all") { + state.status = ""; + } + prepareFilterChange(); + applyControls(); + applyFilters(); + writeUrl("push"); + }, { signal }); + }); + statusInputs.forEach((input, value) => { + input.addEventListener("change", () => { + if (!input.checked) { + return; + } + state.status = value; + if (value) { + state.scope = "all"; + } + prepareFilterChange(); + applyControls(); + applyFilters(); + writeUrl("push"); + }, { signal }); + }); + sortInputs.forEach((input, value) => { + input.addEventListener("change", () => { + if (!input.checked) { + return; + } + state.sort = value; + prepareFilterChange(); + applyControls(); + applyFilters(); + writeUrl("push"); + }, { signal }); + }); + statusFilter.addEventListener("toggle", () => { + statusSummary.setAttribute("aria-expanded", String(statusFilter.open)); + if (statusFilter.open) { + setTagPanelOpen(false); + } + }, { signal }); + tagInputs.forEach((input, key) => { + input.addEventListener("change", () => { + state.tags = rfcReaderContract.setTag(state.tags, key, input.checked); + prepareFilterChange(); + applyControls(); + applyFilters(); + writeUrl("push"); + }, { signal }); + }); + clearTagsButton.addEventListener("click", () => { + state.tags = []; + prepareFilterChange(); + applyControls(); + applyFilters(); + writeUrl("push"); + searchInput.focus({ preventScroll: true }); + }, { signal }); + searchInput.addEventListener("input", () => { + state.query = searchInput.value; + prepareFilterChange(); + applyFilters(); + if (urlTimer !== null) { + window.clearTimeout(urlTimer); + } + urlTimer = window.setTimeout(() => writeUrl("replace"), 150); + }, { signal }); + searchInput.addEventListener("focus", () => { + if (!suppressTagPanelFocus) { + setTagPanelOpen(true); + } + }, { signal }); + tagMenuButton.addEventListener("click", () => { + setTagPanelOpen(tagPanel.hidden); + if (!tagPanel.hidden) { + searchInput.focus({ preventScroll: true }); + } + }, { signal }); + searchShell.addEventListener("click", (event) => { + if (event.target === searchShell) { + searchInput.focus(); + } + }, { signal }); + searchInput.addEventListener("keydown", (event) => { + if (event.key === "ArrowDown" && visibleRecords.length > 0) { + event.preventDefault(); + setTagPanelOpen(false); + focusList(state.selectedId); + } + }, { signal }); + searchForm.addEventListener("submit", (event) => event.preventDefault(), { signal }); + resetButton.addEventListener("click", () => { + const selectedId = state.selectedId; + state = { ...defaultState(), selectedId }; + applyControls(); + reader.dataset.mobileView = "results"; + applyFilters(); + writeUrl("push"); + suppressTagPanelFocus = true; + searchInput.focus(); + suppressTagPanelFocus = false; + }, { signal }); + + const ensureRowVisible = (targetId) => { + const row = rowById.get(targetId); + if (!row || row.hidden) { + return; + } + + const listRect = list.getBoundingClientRect(); + const rowRect = row.getBoundingClientRect(); + if (rowRect.top < listRect.top) { + list.scrollTop -= Math.ceil(listRect.top - rowRect.top); + } else if (rowRect.bottom > listRect.bottom) { + list.scrollTop += Math.ceil(rowRect.bottom - listRect.bottom); + } + }; + + const focusList = (targetId = state.selectedId) => { + const row = rowById.get(targetId); + if (!row || row.hidden) { + return; + } + list.focus({ preventScroll: true }); + ensureRowVisible(targetId); + }; + + const selectRecord = (targetId, { + focusHeading = media.matches, + showDetail = media.matches, + } = {}) => { + const row = rowById.get(targetId); + const record = recordById.get(targetId); + if (!row || row.hidden || !record) { + return; + } + + const previousSelectedId = state.selectedId; + state.selectedId = targetId; + state.selectedExplicitly = !media.matches || showDetail; + lastFocusedId = targetId; + syncListSelection(); + renderDetail(record, { + focusHeading, + resetScroll: previousSelectedId !== targetId, + }); + + if (media.matches && showDetail) { + reader.dataset.mobileView = "detail"; + } else { + focusList(targetId); + } + writeUrl( + media.matches && showDetail ? "push" : "replace", + { mobileDetail: media.matches && showDetail }, + ); + }; + + rowById.forEach((row, id) => { + row.addEventListener("click", () => selectRecord(id), { signal }); + }); + + list.addEventListener("focus", () => { + reader.dataset.listFocus = "true"; + ensureRowVisible(state.selectedId); + }, { signal }); + list.addEventListener("blur", () => { + reader.dataset.listFocus = "false"; + }, { signal }); + list.addEventListener("keydown", (event) => { + if (event.key === "Enter") { + event.preventDefault(); + const record = recordById.get(state.selectedId); + if (record && media.matches) { + selectRecord(record.id, { focusHeading: true, showDetail: true }); + } else if (record) { + window.location.assign(record.href); + } + } else if (event.key === "ArrowDown" || event.key === "ArrowUp") { + event.preventDefault(); + const offset = event.key === "ArrowDown" ? 1 : -1; + const targetId = rfcReaderContract.adjacentRecordId(visibleRecords, state.selectedId, offset); + selectRecord(targetId, { focusHeading: false, showDetail: false }); + } else if (event.key === "Home" || event.key === "End") { + event.preventDefault(); + const target = event.key === "Home" ? visibleRecords[0] : visibleRecords.at(-1); + selectRecord(target?.id, { focusHeading: false, showDetail: false }); + } + }, { signal }); + + const handleGlobalKeydown = (event) => { + const target = event.target; + const isEditable = target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement || target instanceof HTMLSelectElement || target?.isContentEditable; + if (event.key === "/" && !isEditable && !event.metaKey && !event.ctrlKey && !event.altKey) { + event.preventDefault(); + event.stopImmediatePropagation(); + searchInput.focus(); + } else if (event.key === "Escape" && !tagPanel.hidden && target instanceof Node && searchForm.contains(target)) { + event.preventDefault(); + setTagPanelOpen(false, { restoreFocus: true }); + } else if (event.key === "Escape" && statusFilter.open && target instanceof Node && statusFilter.contains(target)) { + event.preventDefault(); + statusFilter.open = false; + statusSummary.focus(); + } + }; + const handleDocumentPointerDown = (event) => { + const target = event.target; + if (!(target instanceof Node)) { + return; + } + if (!searchForm.contains(target)) { + setTagPanelOpen(false); + } + if (!statusFilter.contains(target)) { + statusFilter.open = false; + } + }; + window.addEventListener("keydown", handleGlobalKeydown, { signal, capture: true }); + document.addEventListener("pointerdown", handleDocumentPointerDown, { signal }); + window.addEventListener("popstate", () => { + setTagPanelOpen(false); + statusFilter.open = false; + const selectedBeforeNavigation = state.selectedId; + const wasMobileDetail = media.matches && reader.dataset.mobileView === "detail"; + readUrl({ preserveSelectedId: wasMobileDetail ? selectedBeforeNavigation : "" }); + if (media.matches) { + window.requestAnimationFrame(() => { + if (reader.dataset.mobileView === "detail") { + detailHeader.querySelector("h2")?.focus(); + } else { + focusList(state.selectedId); + } + }); + } + }, { signal }); + media.addEventListener("change", () => { + reader.dataset.mobileView = media.matches && state.selectedExplicitly ? "detail" : "results"; + }, { signal }); + compactMedia.addEventListener("change", placeStatusFilter, { signal }); + + readUrl(); + host.replaceChildren(reader); + host.hidden = false; + fallback.hidden = true; + host.dataset.rfcReaderReady = "true"; + disposeRfcReader = () => { + controller.abort(); + if (urlTimer !== null) { + window.clearTimeout(urlTimer); + } + }; + }; + + const initializePage = () => { + initializeSurfaceTabs(); + initializeBookParts(); + initializeArchitectureNav(); + initializePrimaryNavCollapse(); + initializeRfcReader(); + }; + + if (window.document$?.subscribe) { + window.document$.subscribe(initializePage); + } else if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", initializePage, { once: true }); + } else { + initializePage(); + } +})(); diff --git a/docs/language/README.md b/docs/language/README.md index 6a0bd6a0..73cb8ca3 100644 --- a/docs/language/README.md +++ b/docs/language/README.md @@ -1,79 +1,79 @@ -# IncQL language docs - -This section documents the current IncQL package surface. - -- Use [reference/][reference] for API shape, signatures, and current behavior contracts. -- Use [how-to/][how-to] for concrete task workflows. -- Use [explanation/][explanation] for mental models, usage framing, and tradeoffs. - -## Current entry points - -### Core carriers - -- [Build deferred dataset transformations (How-to)][dataset-transformations-how-to] -- [Expand rows with generators (How-to)][generator-rows-how-to] -- [Normalize semi-structured fields (How-to)][normalize-semistructured-fields-how-to] -- [Work with nested row values (How-to)][nested-row-values-how-to] -- [Dataset carriers (Reference)][dataset-reference] -- [Dataset carriers (Explanation)][dataset-explanation] -- [Dataset methods (Reference)][dataset-methods-reference] -- [Query blocks (Reference)][query-blocks-reference] - -### Execution and materialization - -- [Capture execution observations and adapter coverage (How-to)][execution-observations-how-to] -- [Inspect governed evidence (How-to)][governed-evidence-how-to] -- [Observe data quality checks (How-to)][quality-observations-how-to] -- [Execution context (Reference)][execution-reference] -- [Execution context (Explanation)][execution-explanation] - -### Analytical functions - -- [Add window columns (How-to)][window-columns-how-to] -- [Estimate approximate metrics (How-to)][approximate-metrics-how-to] -- [Build typed HyperLogLog sketches (How-to)][typed-hll-sketches-how-to] -- [Inspect typed variant payloads (How-to)][variant-payloads-how-to] - -### Substrait boundary - -- [Substrait read-root and binding contract][substrait-read-root] -- [Substrait conformance][substrait-conformance] -- [Substrait operator catalog][substrait-operator-catalog] -- [Substrait revision and extension policy][substrait-revision-policy] - -### Local evidence - -- [Inspect a plan and lineage graph (How-to)][inspect-plan-lineage-how-to] -- [Local inspection][inspection-reference] -- [Governed attributes and policy checkpoints][governance-reference] -- [Quality assertions and observations][quality-reference] - - -[reference]: reference/ -[how-to]: how-to/ -[explanation]: explanation/ -[approximate-metrics-how-to]: how-to/approximate_metrics.md -[dataset-reference]: reference/dataset_carriers.md -[dataset-explanation]: explanation/dataset_carriers.md -[dataset-methods-reference]: reference/dataset_methods.md -[dataset-transformations-how-to]: how-to/dataset_transformations.md -[generator-rows-how-to]: how-to/generator_rows.md -[nested-row-values-how-to]: how-to/nested_row_values.md -[normalize-semistructured-fields-how-to]: how-to/normalize_semistructured_fields.md -[query-blocks-reference]: reference/query_blocks.md -[typed-hll-sketches-how-to]: how-to/typed_hll_sketches.md -[variant-payloads-how-to]: how-to/variant_payloads.md -[window-columns-how-to]: how-to/window_columns.md -[inspection-reference]: reference/inspection.md -[execution-reference]: reference/execution_context.md -[execution-explanation]: explanation/execution_context.md -[execution-observations-how-to]: how-to/execution_observations.md -[governed-evidence-how-to]: how-to/governed_evidence.md -[governance-reference]: reference/governance.md -[quality-observations-how-to]: how-to/quality_observations.md -[inspect-plan-lineage-how-to]: how-to/inspect_plan_lineage.md -[quality-reference]: reference/quality.md -[substrait-read-root]: reference/substrait/read_root_binding_contract.md -[substrait-conformance]: reference/substrait/conformance.md -[substrait-operator-catalog]: reference/substrait/operator_catalog.md -[substrait-revision-policy]: reference/substrait/revision_and_extension_policy.md +# Learn IncQL + +

One project. The complete system path.

+ +IncQL makes more sense when you follow one piece of data logic all the way through the system. Start with the book for that guided path, or choose a focused route when you already know what you need. + +
+
+ +## From typed input to evidence you can use {#learn-book-title} + +Build a small order-analysis project, keep its plan deferred, inspect what Prism understands, run it through DataFusion, and retain structured evidence about the attempt. + +The core chapters accumulate into one runnable example; the query-block Part is an independent direct-entry path. Every chapter ends with a result you can verify before moving on. + +Start the IncQL Book + +

Prefer SQL-familiar clauses? Start directly with query blocks →

+ +
+ +
+

The path you will trace

+
    +
  1. InputCSV + intended row model
  2. +
  3. PlanPrism-backed LazyFrame
  4. +
  5. RunSession + DataFusion
  6. +
  7. EvidenceInspection, coverage, quality
  8. +
  9. DecisionCaller-owned write
  10. +
+
+
+ +## Choose the route that matches your task + + + +## Build the right mental model + +These explanations answer the two questions that recur throughout the book. + +
+
+ +### [Dataset carriers](explanation/dataset_carriers.md) + +Understand the difference between deferred, materialized, bounded, and unbounded data—and why only `LazyFrame[T]` owns Prism state today. + +
+
+ +### [Execution context](explanation/execution_context.md) + +Understand what a `Session` owns, when a plan reaches a backend, and where observations enter the flow. + +
+
+ +!!! note "What the book deliberately does not hide" + + The included project uses IncQL as a local path dependency and DataFusion as the implemented backend. Its model parameter records the intended row shape, while the Session discovers the CSV schema; full CSV-to-model compatibility validation is not implemented yet. The tutorial calls out boundaries like these where they matter. diff --git a/docs/language/explanation/dataset_carriers.md b/docs/language/explanation/dataset_carriers.md index df2b07f8..03f8f937 100644 --- a/docs/language/explanation/dataset_carriers.md +++ b/docs/language/explanation/dataset_carriers.md @@ -193,7 +193,7 @@ def summarize_orders(orders: LazyFrame[Order]) -> LazyFrame[Order]: ### Runnable Session example -The runnable example at [session_grouped_aggregate_csv.incn](../../../examples/session_grouped_aggregate_csv.incn) uses the real fixture in `tests/fixtures/aggregate_orders.csv`: +The runnable example at [`examples/session_grouped_aggregate_csv.incn`][session-grouped-aggregate-csv] uses the real fixture in `tests/fixtures/aggregate_orders.csv`: ```text customer_id,amount @@ -219,6 +219,10 @@ Run it from the repository root: incan run examples/session_grouped_aggregate_csv.incn ``` + + +[session-grouped-aggregate-csv]: https://github.com/encero-systems/IncQL/blob/main/examples/session_grouped_aggregate_csv.incn + It will: 1. read the fixture through `Session.read_csv(...)` diff --git a/docs/language/reference/README.md b/docs/language/reference/README.md new file mode 100644 index 00000000..f84339d9 --- /dev/null +++ b/docs/language/reference/README.md @@ -0,0 +1,40 @@ +# Reference + +Reference pages describe IncQL's current public contracts: the carrier model, query and method surfaces, inspection and evidence types, execution context, function catalog, and Substrait boundary. Use these pages when you need an exact API shape or behavioral rule. + +## Relational authoring + +- [Dataset carriers][dataset-carriers] explains the `DataSet`, `DataFrame`, `LazyFrame`, and `DataStream` type family. +- [Dataset methods][dataset-methods] lists the relational operations shared across authoring styles. +- [Query blocks][query-blocks] defines clauses, expression resolution, aliases, and lowering behavior. +- [Filter builders][filter-builders], [projection builders][projection-builders], and [aggregate builders][aggregate-builders] cover expression construction for each relational operation. + +## Inspection and evidence + +- [Inspection][inspection] documents structured plan, schema, lineage, requirement, and artifact records available before execution. +- [Quality][quality] defines typed assertions and quality observations. +- [Governance][governance] defines governed attributes and policy checkpoint evidence. + +## Execution and interchange + +- [Execution context][execution] documents Session construction, registration, execution, collection, writes, and observations. +- [Substrait conformance][substrait] is the entry point for IncQL's portable logical boundary and operator coverage. +- [Function reference][functions] lists declared scalar, aggregate, generator, sketch, formatting, nested, variant, and window functions. + +If you are learning the concepts rather than looking up a contract, start with [Learn][learn]. For a task-oriented walkthrough, use the [Guides][guides]. + + +[aggregate-builders]: builders/aggregates.md +[dataset-carriers]: dataset_carriers.md +[dataset-methods]: dataset_methods.md +[execution]: execution_context.md +[filter-builders]: builders/filters.md +[functions]: functions/index.md +[governance]: governance.md +[guides]: ../how-to/README.md +[inspection]: inspection.md +[learn]: ../README.md +[quality]: quality.md +[projection-builders]: builders/projections.md +[query-blocks]: query_blocks.md +[substrait]: substrait/conformance.md diff --git a/docs/language/reference/builders/aggregates.md b/docs/language/reference/builders/aggregates.md index 37637069..f86cbd7a 100644 --- a/docs/language/reference/builders/aggregates.md +++ b/docs/language/reference/builders/aggregates.md @@ -4,31 +4,31 @@ Current aggregate authoring is explicit and scalar-expression-based. ## Functions -| Builder | Signature | Meaning | -| ------- | ----------------------------------------------------------- | ---------------------------------------------------------------------- | -| `col` | `def col(name: str) -> ColumnExpr` | Column reference builder used by aggregates, filters, and projections. | -| `lit` | `def lit(value: int \| float \| str \| bool) -> ColumnExpr` | Canonical scalar literal helper. | -| `sum` | `def sum(expr: ColumnExpr) -> AggregateMeasure` | Sum one scalar expression. | -| `count` | `def count() -> AggregateMeasure`; `def count(expr: ColumnExpr) -> AggregateMeasure` | Count rows with no argument, or count non-null expression values with one argument. | -| `count_expr` | `def count_expr(expr: ColumnExpr) -> AggregateMeasure` | Compatibility spelling for `count(expr)`. | -| `count_distinct` | `def count_distinct(expr: ColumnExpr) -> AggregateMeasure` | Count distinct non-null expression values. | -| `count_if` | `def count_if(predicate: ColumnExpr) -> AggregateMeasure` | Count rows where the predicate is true. | -| `avg` | `def avg(expr: ColumnExpr) -> AggregateMeasure` | Average one numeric scalar expression. | -| `min` | `def min(expr: ColumnExpr) -> AggregateMeasure` | Return the minimum non-null value for one orderable scalar expression. | -| `max` | `def max(expr: ColumnExpr) -> AggregateMeasure` | Return the maximum non-null value for one orderable scalar expression. | -| `approx_count_distinct` | `def approx_count_distinct(expr: ColumnExpr) -> AggregateMeasure` | Estimate distinct non-null expression values. | -| `approx_percentile` | `def approx_percentile(expr: ColumnExpr, percentile: float, accuracy: int = 10000) -> AggregateMeasure` | Estimate one percentile over numeric non-null values. | -| `hll_sketch` | `def hll_sketch(expr: ColumnExpr, value_domain: SketchValueDomain = SketchValueDomain.StringIdentifier, precision: int = 14) -> AggregateMeasure` | Aggregate source values into typed HyperLogLog sketch state. | -| `hll_merge` | `def hll_merge(sketch: SketchExpr) -> AggregateMeasure` | Merge compatible typed HyperLogLog sketch values. | +| Builder | Signature | Meaning | +| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | +| `col` | `def col(name: str) -> ColumnExpr` | Column reference builder used by aggregates, filters, and projections. | +| `lit` | `def lit(value: int \| float \| str \| bool) -> ColumnExpr` | Canonical scalar literal helper. | +| `sum` | `def sum(expr: ColumnExpr) -> AggregateMeasure` | Sum one scalar expression. | +| `count` | `def count() -> AggregateMeasure`; `def count(expr: ColumnExpr) -> AggregateMeasure` | Count rows with no argument, or count non-null expression values with one argument. | +| `count_expr` | `def count_expr(expr: ColumnExpr) -> AggregateMeasure` | Compatibility spelling for `count(expr)`. | +| `count_distinct` | `def count_distinct(expr: ColumnExpr) -> AggregateMeasure` | Count distinct non-null expression values. | +| `count_if` | `def count_if(predicate: ColumnExpr) -> AggregateMeasure` | Count rows where the predicate is true. | +| `avg` | `def avg(expr: ColumnExpr) -> AggregateMeasure` | Average one numeric scalar expression. | +| `min` | `def min(expr: ColumnExpr) -> AggregateMeasure` | Return the minimum non-null value for one orderable scalar expression. | +| `max` | `def max(expr: ColumnExpr) -> AggregateMeasure` | Return the maximum non-null value for one orderable scalar expression. | +| `approx_count_distinct` | `def approx_count_distinct(expr: ColumnExpr) -> AggregateMeasure` | Estimate distinct non-null expression values. | +| `approx_percentile` | `def approx_percentile(expr: ColumnExpr, percentile: float, accuracy: int = 10000) -> AggregateMeasure` | Estimate one percentile over numeric non-null values. | +| `hll_sketch` | `def hll_sketch(expr: ColumnExpr, value_domain: SketchValueDomain = SketchValueDomain.StringIdentifier, precision: int = 14) -> AggregateMeasure` | Aggregate source values into typed HyperLogLog sketch state. | +| `hll_merge` | `def hll_merge(sketch: SketchExpr) -> AggregateMeasure` | Merge compatible typed HyperLogLog sketch values. | ## Modifiers Aggregate measures support method-style modifiers: -| Modifier | Signature | Meaning | -| --- | --- | --- | -| `distinct` | `measure.distinct() -> AggregateMeasure` | Apply SQL-style `DISTINCT` to aggregate input values. | -| `filter` | `measure.filter(predicate: ColumnExpr) -> AggregateMeasure` | Apply an aggregate-local boolean predicate before aggregation. | +| Modifier | Signature | Meaning | +| ---------- | ------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------- | +| `distinct` | `measure.distinct() -> AggregateMeasure` | Apply SQL-style `DISTINCT` to aggregate input values. | +| `filter` | `measure.filter(predicate: ColumnExpr) -> AggregateMeasure` | Apply an aggregate-local boolean predicate before aggregation. | | `order_by` | `measure.order_by(ordering: list[ColumnExpr]) -> AggregateMeasure` | Record ordered aggregate input. Core aggregates reject ordered input until an order-sensitive aggregate lands. | ## Notes diff --git a/docs/language/reference/builders/filters.md b/docs/language/reference/builders/filters.md index c9497a9a..702de997 100644 --- a/docs/language/reference/builders/filters.md +++ b/docs/language/reference/builders/filters.md @@ -4,16 +4,16 @@ Current filter authoring uses the shared scalar-expression builder model. ## Functions -| Builder | Signature | Meaning | -| -------------- | ------------------------------------------------------------------------- | ---------------------------------------------------------------------- | -| `always_true` | `def always_true() -> BoolLiteralExpr` | Trivial boolean scalar expression; canonical rewrite can eliminate it. | -| `always_false` | `def always_false() -> BoolLiteralExpr` | Boolean scalar expression that rejects every row. | -| `eq` | `def eq(left: ScalarValueOrColumn, right: ScalarValueOrColumn) -> BoolColumnExpr` | Equality predicate scalar expression. | -| `gt` | `def gt(left: ScalarValueOrColumn, right: ScalarValueOrColumn) -> BoolColumnExpr` | Greater-than predicate scalar expression. | -| `lit` | `def lit(value: int \| float \| str \| bool) -> ColumnExpr` | Canonical scalar literal helper. | -| `int_lit` | `def int_lit(value: int) -> IntLiteralExpr` | Typed integer literal helper. | -| `str_lit` | `def str_lit(value: str) -> StringLiteralExpr` | Typed string literal helper. | -| `bool_lit` | `def bool_lit(value: bool) -> BoolLiteralExpr` | Typed boolean literal helper. | +| Builder | Signature | Meaning | +| -------------- | --------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | +| `always_true` | `def always_true() -> BoolLiteralExpr` | Trivial boolean scalar expression; canonical rewrite can eliminate it. | +| `always_false` | `def always_false() -> BoolLiteralExpr` | Boolean scalar expression that rejects every row. | +| `eq` | `def eq(left: ScalarValueOrColumn, right: ScalarValueOrColumn) -> BoolColumnExpr` | Equality predicate scalar expression. | +| `gt` | `def gt(left: ScalarValueOrColumn, right: ScalarValueOrColumn) -> BoolColumnExpr` | Greater-than predicate scalar expression. | +| `lit` | `def lit(value: int \| float \| str \| bool) -> ColumnExpr` | Canonical scalar literal helper. | +| `int_lit` | `def int_lit(value: int) -> IntLiteralExpr` | Typed integer literal helper. | +| `str_lit` | `def str_lit(value: str) -> StringLiteralExpr` | Typed string literal helper. | +| `bool_lit` | `def bool_lit(value: bool) -> BoolLiteralExpr` | Typed boolean literal helper. | ## Notes diff --git a/docs/language/reference/builders/projections.md b/docs/language/reference/builders/projections.md index c975ba76..cd513422 100644 --- a/docs/language/reference/builders/projections.md +++ b/docs/language/reference/builders/projections.md @@ -4,18 +4,18 @@ Projection builders are the current semantic target for scalar expressions in co ## Functions -| Builder | Signature | Meaning | -| ------------ | ------------------------------------------------------------------------ | --------------------------- | -| `col` | `def col(name: str) -> ColumnRefExpr` | Named column reference. | -| `lit` | `def lit(value: int \| float \| str \| bool) -> ColumnExpr` | Canonical scalar literal. | -| `int_expr` | `def int_expr(value: int) -> IntLiteralExpr` | Integer literal expression. | -| `float_expr` | `def float_expr(value: float) -> FloatLiteralExpr` | Float literal expression. | -| `str_expr` | `def str_expr(value: str) -> StringLiteralExpr` | String literal expression. | -| `bool_expr` | `def bool_expr(value: bool) -> BoolLiteralExpr` | Boolean literal expression. | -| `add` | `def add(left: NumberValueOrColumn, right: NumberValueOrColumn) -> NumberColumnExpr` | Binary addition. | -| `mul` | `def mul(left: NumberValueOrColumn, right: NumberValueOrColumn) -> NumberColumnExpr` | Binary multiplication. | -| `eq` | `def eq(left: ScalarValueOrColumn, right: ScalarValueOrColumn) -> BoolColumnExpr` | Equality predicate. | -| `gt` | `def gt(left: ScalarValueOrColumn, right: ScalarValueOrColumn) -> BoolColumnExpr` | Greater-than predicate. | +| Builder | Signature | Meaning | +| ------------ | ------------------------------------------------------------------------------------ | --------------------------- | +| `col` | `def col(name: str) -> ColumnRefExpr` | Named column reference. | +| `lit` | `def lit(value: int \| float \| str \| bool) -> ColumnExpr` | Canonical scalar literal. | +| `int_expr` | `def int_expr(value: int) -> IntLiteralExpr` | Integer literal expression. | +| `float_expr` | `def float_expr(value: float) -> FloatLiteralExpr` | Float literal expression. | +| `str_expr` | `def str_expr(value: str) -> StringLiteralExpr` | String literal expression. | +| `bool_expr` | `def bool_expr(value: bool) -> BoolLiteralExpr` | Boolean literal expression. | +| `add` | `def add(left: NumberValueOrColumn, right: NumberValueOrColumn) -> NumberColumnExpr` | Binary addition. | +| `mul` | `def mul(left: NumberValueOrColumn, right: NumberValueOrColumn) -> NumberColumnExpr` | Binary multiplication. | +| `eq` | `def eq(left: ScalarValueOrColumn, right: ScalarValueOrColumn) -> BoolColumnExpr` | Equality predicate. | +| `gt` | `def gt(left: ScalarValueOrColumn, right: ScalarValueOrColumn) -> BoolColumnExpr` | Greater-than predicate. | ## Dataset entrypoint diff --git a/docs/language/reference/dataset_methods.md b/docs/language/reference/dataset_methods.md index c71c93d7..64db26f8 100644 --- a/docs/language/reference/dataset_methods.md +++ b/docs/language/reference/dataset_methods.md @@ -4,47 +4,47 @@ This page documents the current carrier method surface. Builder-function details ## Carrier method surface -| Method | Signature | Returns | Contract | -| -------------------- | ------------------------------------------------------------------------------- | ---------------- | -------- | -| `filter` | `def filter(self, predicate: ColumnExpr) -> Self` | Same carrier | Restrict rows by one boolean scalar expression. | -| `join` | `def join(self, other: Self, on: ColumnExpr, relation_name: str = "") -> Self` | Same carrier | Inner join with another same-carrier relation using a scalar predicate. | -| `left_join` | `def left_join(self, other: Self, on: ColumnExpr, relation_name: str = "") -> Self` | Same carrier | Left join with another same-carrier relation using a scalar predicate. | -| `select` | `def select[U](self, assignments: list[ProjectionAssignment] = []) -> SameCarrier[U]` | Same carrier kind with row type `U` | Project an output row shape while preserving carrier kind. | -| `with_column` | `def with_column(self, name: str, expr: ColumnExpr) -> Self` | Same carrier | Add or replace one projected column. | -| `group_by` | `def group_by(self, columns: list[ColumnExpr]) -> Self` | Same carrier | Define grouping keys for a following aggregate. | -| `agg` | `def agg(self, measures: list[AggregateMeasure]) -> Self` | Same carrier | Apply aggregate measures over the current relation or current grouping. | -| `generate` | `def generate(self, generator: GeneratorApplication) -> Self` | Same carrier | Apply a relation-shaping generator with explicit output aliases. | -| `with_window_column` | `def with_window_column(self, name: str, application: WindowFunctionApplication) -> Self` | Same carrier | Add or replace one projected column using a placed window function. | -| `order_by` | `def order_by(self, columns: list[ColumnExpr]) -> Self` | Same carrier | Sort rows by scalar expressions or ordering helpers. | -| `limit` | `def limit(self, n: int) -> Self` | Same carrier | Cap row count. | -| `to_substrait_plan` | `def to_substrait_plan(self) -> Plan` | `Plan` | Lower the carrier to a Substrait plan or raise on invalid lowering. | -| `try_to_substrait_plan` | `def try_to_substrait_plan(self) -> Result[Plan, SubstraitLoweringError]` | `Result[Plan, SubstraitLoweringError]` | Lower the carrier to a Substrait plan through a structured error envelope. | +| Method | Signature | Returns | Contract | +| ----------------------- | ----------------------------------------------------------------------------------------- | -------------------------------------- | -------------------------------------------------------------------------- | +| `filter` | `def filter(self, predicate: ColumnExpr) -> Self` | Same carrier | Restrict rows by one boolean scalar expression. | +| `join` | `def join(self, other: Self, on: ColumnExpr, relation_name: str = "") -> Self` | Same carrier | Inner join with another same-carrier relation using a scalar predicate. | +| `left_join` | `def left_join(self, other: Self, on: ColumnExpr, relation_name: str = "") -> Self` | Same carrier | Left join with another same-carrier relation using a scalar predicate. | +| `select` | `def select[U](self, assignments: list[ProjectionAssignment] = []) -> SameCarrier[U]` | Same carrier kind with row type `U` | Project an output row shape while preserving carrier kind. | +| `with_column` | `def with_column(self, name: str, expr: ColumnExpr) -> Self` | Same carrier | Add or replace one projected column. | +| `group_by` | `def group_by(self, columns: list[ColumnExpr]) -> Self` | Same carrier | Define grouping keys for a following aggregate. | +| `agg` | `def agg(self, measures: list[AggregateMeasure]) -> Self` | Same carrier | Apply aggregate measures over the current relation or current grouping. | +| `generate` | `def generate(self, generator: GeneratorApplication) -> Self` | Same carrier | Apply a relation-shaping generator with explicit output aliases. | +| `with_window_column` | `def with_window_column(self, name: str, application: WindowFunctionApplication) -> Self` | Same carrier | Add or replace one projected column using a placed window function. | +| `order_by` | `def order_by(self, columns: list[ColumnExpr]) -> Self` | Same carrier | Sort rows by scalar expressions or ordering helpers. | +| `limit` | `def limit(self, n: int) -> Self` | Same carrier | Cap row count. | +| `to_substrait_plan` | `def to_substrait_plan(self) -> Plan` | `Plan` | Lower the carrier to a Substrait plan or raise on invalid lowering. | +| `try_to_substrait_plan` | `def try_to_substrait_plan(self) -> Result[Plan, SubstraitLoweringError]` | `Result[Plan, SubstraitLoweringError]` | Lower the carrier to a Substrait plan through a structured error envelope. | `SameCarrier[U]` means `DataFrame[U]` for `DataFrame[T]`, `LazyFrame[U]` for `LazyFrame[T]`, and `DataStream[U]` for `DataStream[T]`. The root `DataSet[T]` trait remains the common plan/schema contract; schema-changing projection is expressed on concrete carriers until Incan grows native trait type-family support. ## Method semantics -| Method | Schema behavior | -| -------------------- | --------------- | -| `filter` | Preserves input columns. | -| `join` | Combines left and right output columns using the current join output-column contract. | -| `left_join` | Combines left and right output columns using the current left-join output-column contract. | +| Method | Schema behavior | +| -------------------- | -------------------------------------------------------------------------------------------------------------------------------- | +| `filter` | Preserves input columns. | +| `join` | Combines left and right output columns using the current join output-column contract. | +| `left_join` | Combines left and right output columns using the current left-join output-column contract. | | `select` | Identity `select()` preserves the current planned columns; explicit assignments replace the output schema with assignment names. | -| `with_column` | Appends a missing name at the end; replaces an existing name in place while preserving ordinal position. | -| `group_by` | Produces grouped relation state; grouped output columns are finalized by `agg(...)`. | -| `agg` | Emits grouping keys plus aggregate measure outputs for grouped input, or aggregate measure outputs for global input. | -| `generate` | Preserves all input columns and appends generated output aliases. Alias collisions are rejected during planning or lowering. | -| `with_window_column` | Appends or replaces the named output column using the same add-or-replace projection semantics as `with_column(...)`. | -| `order_by` | Preserves input columns. | -| `limit` | Preserves input columns. | +| `with_column` | Appends a missing name at the end; replaces an existing name in place while preserving ordinal position. | +| `group_by` | Produces grouped relation state; grouped output columns are finalized by `agg(...)`. | +| `agg` | Emits grouping keys plus aggregate measure outputs for grouped input, or aggregate measure outputs for global input. | +| `generate` | Preserves all input columns and appends generated output aliases. Alias collisions are rejected during planning or lowering. | +| `with_window_column` | Appends or replaces the named output column using the same add-or-replace projection semantics as `with_column(...)`. | +| `order_by` | Preserves input columns. | +| `limit` | Preserves input columns. | ## Carrier-specific notes -| Carrier | Notes | -| -------------- | ----- | -| `LazyFrame[T]` | Prism-backed deferred carrier. Transform methods append Prism nodes and preserve immutable branching. | -| `DataFrame[T]` | Materialized local carrier. Transform methods invalidate stale materialization and rebuild from the stored relation tree. | -| `DataStream[T]` | Streaming carrier surface is present in the type hierarchy; streaming-specific execution semantics remain future work. | +| Carrier | Notes | +| --------------- | ------------------------------------------------------------------------------------------------------------------------- | +| `LazyFrame[T]` | Prism-backed deferred carrier. Transform methods append Prism nodes and preserve immutable branching. | +| `DataFrame[T]` | Materialized local carrier. Transform methods invalidate stale materialization and rebuild from the stored relation tree. | +| `DataStream[T]` | Streaming carrier surface is present in the type hierarchy; streaming-specific execution semantics remain future work. | ## Expression inputs diff --git a/docs/language/reference/execution_context.md b/docs/language/reference/execution_context.md index 57c63085..c65cb2ba 100644 --- a/docs/language/reference/execution_context.md +++ b/docs/language/reference/execution_context.md @@ -17,14 +17,14 @@ This page documents the public execution surface in the IncQL package. Normative | ------------------------------------------------------------------ | ------------------------------------------------------------------- | | `Session.default()` | Create a session with the default backend and default configuration | | `Session.builder()` | Create a builder for backend selection and configuration | -| `Session.builder().with_backend(selection).build()` | Build a session from a portable backend-selection envelope | +| `Session.builder().with_backend(selection).build()` | Build a session from a portable backend-selection envelope | | `Session.builder().with_datafusion(backends.DataFusion()).build()` | Build an explicit DataFusion-backed session | ## Read and registration surface -| API | Returns | Notes | -| ------------------------------------ | ------------------------------------ | -------------------------------------------------------- | -| `session.register(name, source)` | `Result[None, SessionError]` | Bind a logical relation name to a source definition | +| API | Returns | Notes | +| --------------------------------- | ------------------------------------ | -------------------------------------------------------- | +| `session.register(name, source)` | `Result[None, SessionError]` | Bind a logical relation name to a source definition | | `session.table(name)` | `Result[LazyFrame[T], SessionError]` | Resolve a registered logical relation by name | | `session.read_csv(name, uri)` | `Result[LazyFrame[T], SessionError]` | Register and return a deferred CSV-backed relation | | `session.read_parquet(name, uri)` | `Result[LazyFrame[T], SessionError]` | Register and return a deferred Parquet-backed relation | @@ -49,72 +49,72 @@ All read APIs return `LazyFrame[T]`. They create deferred logical work; they do Observed execution methods preserve the ordinary session contracts while also returning runtime evidence. The ordinary `execute`, `collect`, and `write` methods use the same execution path internally and keep returning `Result[...]` values for compact application code. -| API | Input | Returns | Success data | Failure data | -| ---------------------------------------- | ------------------- | ---------------------- | ---------------------------------- | --------------------------------- | -| `session.execute_observed(data)` | `LazyFrame[T]` | `ObservedLazyFrame[T]` | `data=Some(LazyFrame[T])` | `data=None`, `error=Some(...)` | -| `session.collect_observed(data)` | `LazyFrame[T]` | `ObservedDataFrame[T]` | `data=Some(DataFrame[T])` | `data=None`, `error=Some(...)` | -| `session.write_observed(data, target)` | `BoundedDataSet[T]` | `ObservedWrite` | `error=None` | `error=Some(...)` | +| API | Input | Returns | Success data | Failure data | +| -------------------------------------- | ------------------- | ---------------------- | ------------------------- | ------------------------------ | +| `session.execute_observed(data)` | `LazyFrame[T]` | `ObservedLazyFrame[T]` | `data=Some(LazyFrame[T])` | `data=None`, `error=Some(...)` | +| `session.collect_observed(data)` | `LazyFrame[T]` | `ObservedDataFrame[T]` | `data=Some(DataFrame[T])` | `data=None`, `error=Some(...)` | +| `session.write_observed(data, target)` | `BoundedDataSet[T]` | `ObservedWrite` | `error=None` | `error=Some(...)` | ### Observed result records -| Record | Fields | -| ---------------------- | ------------------------------------------- | +| Record | Fields | +| ---------------------- | ------------------------------------------------------------------------------------------------ | | `ObservedLazyFrame[T]` | `data: Option[LazyFrame[T]]`, `observation: ExecutionObservation`, `error: Option[SessionError]` | | `ObservedDataFrame[T]` | `data: Option[DataFrame[T]]`, `observation: ExecutionObservation`, `error: Option[SessionError]` | -| `ObservedWrite` | `observation: ExecutionObservation`, `error: Option[SessionError]` | +| `ObservedWrite` | `observation: ExecutionObservation`, `error: Option[SessionError]` | ### `ExecutionObservation` -| Field | Type | Meaning | -| --------------------------------------- | ----------------------------- | -------------------------------------------------------------- | -| `observation_id` | `str` | Stable local identifier for this observation attempt | -| `attempt_target` | `SemanticTarget` | Semantic target for the concrete execution attempt | -| `plan_target` | `SemanticTarget` | Semantic target for the plan being attempted | -| `context_targets` | `list[SemanticTarget]` | Session or binding context targets attached to the attempt | -| `operation` | `ExecutionOperationKind` | Operation family: `execute`, `collect`, or `write` | -| `status` | `ExecutionObservationStatus` | Terminal status | -| `backend_name` | `str` | Selected backend name, currently `datafusion` by default | -| `adapter_version` | `Option[str]` | Adapter version when reported by the backend | -| `requested_semantic_profile_id` | `Option[str]` | Requested semantic profile identity when one is bound | -| `observed_semantic_profile_id` | `Option[str]` | Observed semantic profile identity when the adapter reports one | -| `started_at_unix_nanoseconds` | `int` | Wall-clock start timestamp from `std.datetime.runtime.SystemTime` | -| `ended_at_unix_nanoseconds` | `int` | Wall-clock end timestamp from `std.datetime.runtime.SystemTime` | -| `duration_nanoseconds` | `int` | Monotonic elapsed duration from `std.datetime.runtime.Instant` | -| `row_count` | `Option[int]` | Materialized row count when the operation supplies one | -| `byte_count` | `Option[int]` | Byte count when the operation supplies one | -| `trace_ids` | `list[str]` | Optional external trace or telemetry correlation IDs | -| `diagnostics` | `list[ExecutionDiagnostic]` | Structured diagnostics attached to the attempt | -| `coverage_records` | `list[AdapterCoverageRecord]` | Adapter coverage records linked to the attempt | -| `evidence_refs` | `list[str]` | Additional evidence artifact references | +| Field | Type | Meaning | +| ------------------------------- | ----------------------------- | ----------------------------------------------------------------- | +| `observation_id` | `str` | Stable local identifier for this observation attempt | +| `attempt_target` | `SemanticTarget` | Semantic target for the concrete execution attempt | +| `plan_target` | `SemanticTarget` | Semantic target for the plan being attempted | +| `context_targets` | `list[SemanticTarget]` | Session or binding context targets attached to the attempt | +| `operation` | `ExecutionOperationKind` | Operation family: `execute`, `collect`, or `write` | +| `status` | `ExecutionObservationStatus` | Terminal status | +| `backend_name` | `str` | Selected backend name, currently `datafusion` by default | +| `adapter_version` | `Option[str]` | Adapter version when reported by the backend | +| `requested_semantic_profile_id` | `Option[str]` | Requested semantic profile identity when one is bound | +| `observed_semantic_profile_id` | `Option[str]` | Observed semantic profile identity when the adapter reports one | +| `started_at_unix_nanoseconds` | `int` | Wall-clock start timestamp from `std.datetime.runtime.SystemTime` | +| `ended_at_unix_nanoseconds` | `int` | Wall-clock end timestamp from `std.datetime.runtime.SystemTime` | +| `duration_nanoseconds` | `int` | Monotonic elapsed duration from `std.datetime.runtime.Instant` | +| `row_count` | `Option[int]` | Materialized row count when the operation supplies one | +| `byte_count` | `Option[int]` | Byte count when the operation supplies one | +| `trace_ids` | `list[str]` | Optional external trace or telemetry correlation IDs | +| `diagnostics` | `list[ExecutionDiagnostic]` | Structured diagnostics attached to the attempt | +| `coverage_records` | `list[AdapterCoverageRecord]` | Adapter coverage records linked to the attempt | +| `evidence_refs` | `list[str]` | Additional evidence artifact references | Observation records do not contain row payloads or backend logs by default. The first DataFusion-backed implementation reports unavailable adapter-version, semantic-profile, byte-count, and trace evidence as `None` or `[]` rather than fabricating values. ### Execution enums -| Enum | Values | -| ----------------------------- | ------------------------------------------------ | -| `ExecutionOperationKind` | `Execute`, `Collect`, `Write` | +| Enum | Values | +| ----------------------------- | ----------------------------------------------------------- | +| `ExecutionOperationKind` | `Execute`, `Collect`, `Write` | | `ExecutionObservationStatus` | `Success`, `Failure`, `Cancelled`, `Skipped`, `Unsupported` | -| `ExecutionDiagnosticSeverity` | `Info`, `Warning`, `Error` | +| `ExecutionDiagnosticSeverity` | `Info`, `Warning`, `Error` | ### `ExecutionDiagnostic` -| Field | Type | Meaning | -| ---------- | ----------------------------- | -------------------------------------------- | -| `severity` | `ExecutionDiagnosticSeverity` | Diagnostic severity | -| `code` | `str` | Stable diagnostic code | -| `message` | `str` | Human-readable diagnostic message | +| Field | Type | Meaning | +| ---------- | ----------------------------- | ---------------------------------------------- | +| `severity` | `ExecutionDiagnosticSeverity` | Diagnostic severity | +| `code` | `str` | Stable diagnostic code | +| `message` | `str` | Human-readable diagnostic message | | `target` | `Option[SemanticTarget]` | Semantic target associated with the diagnostic | ## Write surface -| API | Returns | Notes | -| ---------------------------------------- | ---------------------------- | ---------------------------------------------------- | -| `csv_sink(uri)` | `SinkTarget` | Build a typed CSV sink descriptor | -| `parquet_sink(uri)` | `SinkTarget` | Build a typed Parquet sink descriptor | -| `session.write(data, target)` | `Result[None, SessionError]` | Execute deferred input if needed, then write target | -| `session.write_csv(data, uri)` | `Result[None, SessionError]` | Convenience form for CSV sinks | -| `session.write_parquet(data, uri)` | `Result[None, SessionError]` | Convenience form for Parquet sinks | +| API | Returns | Notes | +| ---------------------------------- | ---------------------------- | --------------------------------------------------- | +| `csv_sink(uri)` | `SinkTarget` | Build a typed CSV sink descriptor | +| `parquet_sink(uri)` | `SinkTarget` | Build a typed Parquet sink descriptor | +| `session.write(data, target)` | `Result[None, SessionError]` | Execute deferred input if needed, then write target | +| `session.write_csv(data, uri)` | `Result[None, SessionError]` | Convenience form for CSV sinks | +| `session.write_parquet(data, uri)` | `Result[None, SessionError]` | Convenience form for Parquet sinks | These writes are Session-owned. They do not bypass the execution context even when the input is deferred. @@ -132,48 +132,48 @@ Plan inference is evidence-backed rather than policy-complete. The current imple ### `AdapterRequirement` -| Field | Type | Meaning | -| ---------------- | ------------------------------ | -------------------------------------------------- | -| `requirement_id` | `str` | Stable local requirement identifier | -| `target` | `SemanticTarget` | Semantic target that requires the capability | -| `capability` | `AdapterRequirementCapability` | Required adapter capability family | +| Field | Type | Meaning | +| ---------------- | ------------------------------ | --------------------------------------------------- | +| `requirement_id` | `str` | Stable local requirement identifier | +| `target` | `SemanticTarget` | Semantic target that requires the capability | +| `capability` | `AdapterRequirementCapability` | Required adapter capability family | | `guarantee` | `AdapterRequirementGuarantee` | Requirement strength: required, preferred, optional | -| `reason` | `str` | Human-readable reason for the requirement | -| `evidence_refs` | `list[str]` | Evidence artifacts that justify the requirement | +| `reason` | `str` | Human-readable reason for the requirement | +| `evidence_refs` | `list[str]` | Evidence artifacts that justify the requirement | ### `AdapterCoverageRecord` -| Field | Type | Meaning | -| --------------------- | ---------------------------- | ---------------------------------------------------- | -| `coverage_id` | `str` | Stable local coverage-record identifier | -| `requirement` | `AdapterRequirement` | Requirement that was evaluated | -| `adapter_name` | `str` | Adapter that was evaluated | -| `adapter_version` | `Option[str]` | Adapter version when reported | -| `semantic_profile_id` | `Option[str]` | Semantic profile identity when relevant | -| `state` | `AdapterCoverageState` | Coverage result | -| `diagnostics` | `list[ExecutionDiagnostic]` | Diagnostics explaining partial, uncovered, or unknown coverage | -| `evidence_refs` | `list[str]` | Evidence artifacts that support the coverage answer | +| Field | Type | Meaning | +| --------------------- | --------------------------- | -------------------------------------------------------------- | +| `coverage_id` | `str` | Stable local coverage-record identifier | +| `requirement` | `AdapterRequirement` | Requirement that was evaluated | +| `adapter_name` | `str` | Adapter that was evaluated | +| `adapter_version` | `Option[str]` | Adapter version when reported | +| `semantic_profile_id` | `Option[str]` | Semantic profile identity when relevant | +| `state` | `AdapterCoverageState` | Coverage result | +| `diagnostics` | `list[ExecutionDiagnostic]` | Diagnostics explaining partial, uncovered, or unknown coverage | +| `evidence_refs` | `list[str]` | Evidence artifacts that support the coverage answer | ### Adapter requirement enums -| Enum | Values | -| ----------------------------- | ------ | -| `AdapterRequirementGuarantee` | `Required`, `Preferred`, `Optional` | -| `AdapterCoverageState` | `Covered`, `PartiallyCovered`, `Uncovered`, `Unknown` | +| Enum | Values | +| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `AdapterRequirementGuarantee` | `Required`, `Preferred`, `Optional` | +| `AdapterCoverageState` | `Covered`, `PartiallyCovered`, `Uncovered`, `Unknown` | | `AdapterRequirementCapability` | `ExtensionFunction`, `VariantSemantics`, `DecimalSemantics`, `NullSemantics`, `LineagePreservation`, `AuditEmission`, `RowFilter`, `ColumnMask`, `AggregateThreshold`, `RegionBinding`, `OrderedExecution`, `SnapshotCapture`, `CanonicalDigest`, `CrossRelationReconciliation`, `IncrementalWatermark`, `VerificationEventStream`, `WaiverRecording`, `CryptographicQueryProof` | Coverage states are conservative. `Covered` means the selected adapter is known to cover that requirement family. `PartiallyCovered` means support depends on the concrete function, plan shape, or restriction. `Uncovered` means the selected adapter is known not to provide that guarantee. `Unknown` means IncQL has not classified coverage; consumers must not treat it as enforced behavior. ### Current DataFusion coverage classification -| Capability | State | -| --------------------------------------- | ------------------ | -| `RowFilter` | `Covered` | -| `OrderedExecution` | `Covered` | -| `NullSemantics` | `Covered` | -| `ExtensionFunction` | `PartiallyCovered` | -| `LineagePreservation` | `Uncovered` | -| `AuditEmission` | `Uncovered` | +| Capability | State | +| ---------------------------------------- | ------------------ | +| `RowFilter` | `Covered` | +| `OrderedExecution` | `Covered` | +| `NullSemantics` | `Covered` | +| `ExtensionFunction` | `PartiallyCovered` | +| `LineagePreservation` | `Uncovered` | +| `AuditEmission` | `Uncovered` | | Any other `AdapterRequirementCapability` | `Unknown` | For non-DataFusion backends, the current implementation returns `Unknown` for every capability until that adapter declares coverage metadata. @@ -182,9 +182,9 @@ For non-DataFusion backends, the current implementation returns `Unknown` for ev `Session` also evaluates quality assertions and returns structured quality observations. The quality reference owns the assertion and observation record details; this page lists the session entry points because they execute through the same session boundary as collection and adapter coverage. -| API | Input | Returns | -| --- | --- | --- | -| `session.observe_quality(data, assertions)` | `LazyFrame[T]`, `list[QualityAssertion]` | `list[QualityObservation]` | +| API | Input | Returns | +| ------------------------------------------------------- | -------------------------------------------------------- | -------------------------- | +| `session.observe_quality(data, assertions)` | `LazyFrame[T]`, `list[QualityAssertion]` | `list[QualityObservation]` | | `session.observe_quality_pair(left, right, assertions)` | `LazyFrame[T]`, `LazyFrame[U]`, `list[QualityAssertion]` | `list[QualityObservation]` | Use `observe_quality(...)` for relation, field, and group assertions. Use `observe_quality_pair(...)` for explicit cross-relation assertions. Failed checks return quality observations with `QualityObservationStatus.Failed`; they do not throw or filter the checked relation by default. @@ -226,6 +226,6 @@ DataFusion is the implemented execution backend. `Session` stores a backend kind - For quality assertion and observation design, see [RFC 034][rfc-034] [rfc-004]: ../../rfcs/004_incql_execution_context.md -[rfc-032]: ../../rfcs/032_execution_observations.md -[rfc-033]: ../../rfcs/033_adapter_requirements_coverage.md -[rfc-034]: ../../rfcs/034_quality_assertions_observations.md +[rfc-032]: ../../rfcs/closed/implemented/032_execution_observations.md +[rfc-033]: ../../rfcs/closed/implemented/033_adapter_requirements_coverage.md +[rfc-034]: ../../rfcs/closed/implemented/034_quality_assertions_observations.md diff --git a/docs/language/reference/functions/approximate.md b/docs/language/reference/functions/approximate.md index 69b363d2..b94961b3 100644 --- a/docs/language/reference/functions/approximate.md +++ b/docs/language/reference/functions/approximate.md @@ -4,10 +4,10 @@ Approximate helpers are explicit opt-in functions. IncQL does not silently repla The portable RFC 023 aggregate surface is: -| Function | Meaning | -| --- | --- | -| `approx_count_distinct(expr)` | Estimate the number of distinct non-null values produced by one expression. | -| `approx_percentile(expr, percentile, accuracy=10000)` | Estimate one percentile over numeric non-null values. | +| Function | Meaning | +| ----------------------------------------------------- | --------------------------------------------------------------------------- | +| `approx_count_distinct(expr)` | Estimate the number of distinct non-null values produced by one expression. | +| `approx_percentile(expr, percentile, accuracy=10000)` | Estimate one percentile over numeric non-null values. | `approx_count_distinct` is registered as an approximate aggregate with HyperLogLog-family metadata. The portable author contract is an approximate non-null distinct-count estimate. It does not expose a user-tunable relative-error parameter because the registered IncQL Substrait extension mapping for this function is unary. Backend adapters must keep this approximation visible in capability/error handling rather than redefining exact `count_distinct` semantics. diff --git a/docs/language/reference/functions/format.md b/docs/language/reference/functions/format.md index e6bd9fb8..f5e2f4c4 100644 --- a/docs/language/reference/functions/format.md +++ b/docs/language/reference/functions/format.md @@ -4,34 +4,34 @@ Format functions transform scalar values that are already present in a relation. The format catalog includes deterministic hashes, URL helpers, JSON helpers, and CSV helpers: -| Function | Meaning | -| --- | --- | -| `md5(expr)` | Return the lowercase hexadecimal MD5 digest for a string expression. | -| `sha1(expr)` | Return the lowercase hexadecimal SHA-1 digest for a string expression. | -| `sha224(expr)` | Return the lowercase hexadecimal SHA-224 digest for a string expression. | -| `sha256(expr)` | Return the lowercase hexadecimal SHA-256 digest for a string expression. | -| `sha384(expr)` | Return the lowercase hexadecimal SHA-384 digest for a string expression. | -| `sha512(expr)` | Return the lowercase hexadecimal SHA-512 digest for a string expression. | -| `sha2(expr, bit_length)` | Compatibility helper that rewrites to `sha224`, `sha256`, `sha384`, or `sha512` for supported literal bit lengths. | -| `crc32(expr)` | Return the lowercase eight-character hexadecimal CRC-32 digest for a string expression. | -| `xxhash64(expr)` | Return the lowercase sixteen-character hexadecimal xxHash64 digest for a string expression. | -| `parse_url(expr, key)` | Extract the first query parameter value for `key` from a URL string, returning null when the key is absent. | -| `url_encode(expr)` | Percent-encode a URL component string. | -| `url_decode(expr)` | Decode a percent-encoded URL component string and fail on malformed escapes. | -| `try_url_decode(expr)` | Decode a percent-encoded URL component string, returning null on malformed escapes. | -| `parse_json(expr)` | Validate and normalize a JSON payload string. | -| `check_json(expr)` | Return whether a string expression contains valid JSON. | -| `schema_of_json(expr)` | Infer a deterministic schema description from a JSON payload string. | -| `json_array_length(expr)` | Return the number of array elements for a JSON array payload, or null for non-array payloads. | -| `json_object_keys(expr)` | Return object keys from a JSON object payload as a JSON array string. | -| `get_json_object(expr, path)` | Extract a JSON value at a literal path and return it as JSON text. | -| `json_extract_path_text(expr, path)` | Extract a JSON value at a literal path and return scalar strings as plain text. | -| `from_json[Model](expr)` | Validate JSON with a schema derived from an Incan model type and return a normalized JSON payload string. | -| `try_from_json[Model](expr)` | Validate JSON with a schema derived from an Incan model type and return null when the payload is invalid. | -| `to_json(expr)` | Serialize a scalar expression as JSON text. | -| `schema_of_csv(expr)` | Infer a deterministic schema description from a CSV row string. | -| `from_csv[Model](expr)` | Parse a CSV row string into a logical map keyed by fields from an Incan model type. | -| `to_csv(expr)` | Serialize a scalar or JSON array/object payload as a CSV row string. | +| Function | Meaning | +| ------------------------------------ | ------------------------------------------------------------------------------------------------------------------ | +| `md5(expr)` | Return the lowercase hexadecimal MD5 digest for a string expression. | +| `sha1(expr)` | Return the lowercase hexadecimal SHA-1 digest for a string expression. | +| `sha224(expr)` | Return the lowercase hexadecimal SHA-224 digest for a string expression. | +| `sha256(expr)` | Return the lowercase hexadecimal SHA-256 digest for a string expression. | +| `sha384(expr)` | Return the lowercase hexadecimal SHA-384 digest for a string expression. | +| `sha512(expr)` | Return the lowercase hexadecimal SHA-512 digest for a string expression. | +| `sha2(expr, bit_length)` | Compatibility helper that rewrites to `sha224`, `sha256`, `sha384`, or `sha512` for supported literal bit lengths. | +| `crc32(expr)` | Return the lowercase eight-character hexadecimal CRC-32 digest for a string expression. | +| `xxhash64(expr)` | Return the lowercase sixteen-character hexadecimal xxHash64 digest for a string expression. | +| `parse_url(expr, key)` | Extract the first query parameter value for `key` from a URL string, returning null when the key is absent. | +| `url_encode(expr)` | Percent-encode a URL component string. | +| `url_decode(expr)` | Decode a percent-encoded URL component string and fail on malformed escapes. | +| `try_url_decode(expr)` | Decode a percent-encoded URL component string, returning null on malformed escapes. | +| `parse_json(expr)` | Validate and normalize a JSON payload string. | +| `check_json(expr)` | Return whether a string expression contains valid JSON. | +| `schema_of_json(expr)` | Infer a deterministic schema description from a JSON payload string. | +| `json_array_length(expr)` | Return the number of array elements for a JSON array payload, or null for non-array payloads. | +| `json_object_keys(expr)` | Return object keys from a JSON object payload as a JSON array string. | +| `get_json_object(expr, path)` | Extract a JSON value at a literal path and return it as JSON text. | +| `json_extract_path_text(expr, path)` | Extract a JSON value at a literal path and return scalar strings as plain text. | +| `from_json[Model](expr)` | Validate JSON with a schema derived from an Incan model type and return a normalized JSON payload string. | +| `try_from_json[Model](expr)` | Validate JSON with a schema derived from an Incan model type and return null when the payload is invalid. | +| `to_json(expr)` | Serialize a scalar expression as JSON text. | +| `schema_of_csv(expr)` | Infer a deterministic schema description from a CSV row string. | +| `from_csv[Model](expr)` | Parse a CSV row string into a logical map keyed by fields from an Incan model type. | +| `to_csv(expr)` | Serialize a scalar or JSON array/object payload as a CSV row string. | Hash helpers operate on UTF-8 string bytes and return lowercase hexadecimal strings. `sha2(...)` accepts `224`, `256`, `384`, and `512`; other digest lengths are rejected during expression construction. diff --git a/docs/language/reference/functions/generators.md b/docs/language/reference/functions/generators.md index 3bea8785..f62ce626 100644 --- a/docs/language/reference/functions/generators.md +++ b/docs/language/reference/functions/generators.md @@ -4,16 +4,16 @@ Generators are relation-shaping operations. They are registry-backed like scalar The explicit generator surface currently includes: -| Function | Output aliases | Relation effect | -| --- | --- | --- | -| `explode(expr, as_)` | one value column | Emits one row per array element; null or empty inputs emit zero rows. | -| `explode_outer(expr, as_)` | one value column | Preserves the input row for null or empty inputs and emits a null generated value. | -| `posexplode(expr, position_as, value_as)` | position and value columns | Emits one row per array element with a zero-based position column. | -| `posexplode_outer(expr, position_as, value_as)` | position and value columns | Outer positional explode with the same zero-based position rule. | -| `inline(expr, output_columns)` | one column per struct field | Expands array-of-struct values into generated rows and declared output columns. | -| `inline_outer(expr, output_columns)` | one column per struct field | Outer inline with the same null/empty row preservation rule. | -| `flatten(expr, as_)` | one value column | Portable table-valued flatten for one array expression. | -| `stack(row_count, values, output_columns)` | declared output columns | Emits `row_count` generated rows from row-major scalar values. | +| Function | Output aliases | Relation effect | +| ----------------------------------------------- | --------------------------- | ---------------------------------------------------------------------------------- | +| `explode(expr, as_)` | one value column | Emits one row per array element; null or empty inputs emit zero rows. | +| `explode_outer(expr, as_)` | one value column | Preserves the input row for null or empty inputs and emits a null generated value. | +| `posexplode(expr, position_as, value_as)` | position and value columns | Emits one row per array element with a zero-based position column. | +| `posexplode_outer(expr, position_as, value_as)` | position and value columns | Outer positional explode with the same zero-based position rule. | +| `inline(expr, output_columns)` | one column per struct field | Expands array-of-struct values into generated rows and declared output columns. | +| `inline_outer(expr, output_columns)` | one column per struct field | Outer inline with the same null/empty row preservation rule. | +| `flatten(expr, as_)` | one value column | Portable table-valued flatten for one array expression. | +| `stack(row_count, values, output_columns)` | declared output columns | Emits `row_count` generated rows from row-major scalar values. | Generator applications preserve input columns and append generated columns in declaration order. Generated aliases are required, must be non-empty, and must not collide with existing input columns. diff --git a/docs/language/reference/functions/index.md b/docs/language/reference/functions/index.md index 95adc523..eb90a7d4 100644 --- a/docs/language/reference/functions/index.md +++ b/docs/language/reference/functions/index.md @@ -25,33 +25,33 @@ Function policy category is separate from function class. Function class describ The registered helper surface currently includes: -| Function | Registry class | Mapping | -| --- | --- | --- | -| `col(...)` | scalar | deterministic field-reference rewrite | -| `lit(...)`, `int_expr(...)`, `float_expr(...)`, `str_expr(...)`, `bool_expr(...)`, `int_lit(...)`, `str_lit(...)`, `bool_lit(...)` | scalar | deterministic literal rewrites | -| `always_true()`, `always_false()` | scalar | deterministic boolean-literal rewrites | -| `cast(...)`, `try_cast(...)` | scalar | built-in Substrait `Cast` Rex shapes; primitive targets use type tokens such as `int`, `float`, `str`, and `bool`; explicit string target spellings remain available for compatibility aliases such as `int64` and `float64`; `try_cast` uses return-null failure behavior | -| `add(...)`, `sub(...)`, `mul(...)`, `div(...)`, `modulo(...)`, `neg(...)` | scalar | registered Substrait scalar mappings; `modulo(...)` registers canonical `mod` | -| `eq(...)`, `ne(...)`, `lt(...)`, `lte(...)`, `gt(...)`, `gte(...)`, `equal_null(...)` | scalar | registered Substrait scalar mappings; `equal_null(...)` lowers as null-safe equality | -| `and_(...)`, `or_(...)`, `not_(...)` | scalar | registered Substrait boolean mappings | -| `is_null(...)`, `is_not_null(...)`, `is_nan(...)`, `is_not_nan(...)` | scalar | registered predicate mappings; `is_not_nan(...)` lowers as `not(is_nan(...))` | -| `coalesce(...)`, `nullif(...)`, `case_when(...)` | scalar | registered Substrait mappings; `case_when(...)` lowers as built-in `IfThen` | -| `in_(...)`, `between(...)` | scalar | built-in membership/range lowering (`SingularOrList` and `between`) | -| `abs(...)`, `ceil(...)`, `floor(...)`, `round(...)`, `sqrt(...)`, `power(...)`, `exp(...)`, `ln(...)`, `log(...)`, `log10(...)`, `sign(...)`, `least(...)`, `greatest(...)`, `sin(...)`, `cos(...)`, `tan(...)`, `asin(...)`, `acos(...)`, `atan(...)`, `atan2(...)`, `degrees(...)`, `radians(...)` | scalar | registered RFC 018 math scalar mappings; `round(...)` accepts an optional precision expression | -| `char_length(...)`, `octet_length(...)`, `upper(...)`, `lower(...)`, `trim(...)`, `ltrim(...)`, `rtrim(...)`, `substring(...)`, `position(...)`, `overlay(...)`, `concat(...)`, `concat_ws(...)`, `replace(...)`, `translate(...)`, `repeat(...)`, `left(...)`, `right(...)`, `lpad(...)`, `rpad(...)`, `split_part(...)` | scalar | registered RFC 018 string scalar mappings with SQL-compatible one-based positions; string and integer literal parameters use `StrValueOrColumn` and `IntValueOrColumn` aliases | -| `substr(...)`, `ucase(...)`, `lcase(...)` | scalar | RFC 018 compatibility alias spellings for canonical `substring(...)`, `upper(...)`, and `lower(...)` | -| `encode(...)`, `decode(...)`, `base64(...)`, `unbase64(...)`, `hex(...)`, `unhex(...)` | scalar | registered RFC 018 text encoding helpers; convenience helpers rewrite to `encode(...)` or `decode(...)` with an explicit `StrValueOrColumn` format literal | -| `regexp_like(...)`, `regexp_replace(...)`, `regexp_extract(...)` | scalar | registered RFC 018 regex helpers using Rust-regex-compatible semantics; pattern, replacement, flags, and group-index parameters accept literal-or-column aliases | -| `current_date()`, `current_time()`, `current_timestamp()`, `date_part(...)`, `date_trunc(...)`, `time_trunc(...)`, `date_add(...)`, `date_sub(...)`, `date_diff(...)`, `timestamp_diff(...)`, `to_date(...)`, `to_time(...)`, `to_timestamp(...)`, `from_unixtime(...)`, `unix_seconds(...)`, `unix_millis(...)`, `unix_micros(...)`, `make_date(...)`, `make_time(...)`, `make_timestamp(...)`, `last_day(...)` | scalar | registered RFC 018 date/time helpers; current date/time helpers are nondeterministic registry entries, and part selectors, date amounts, epoch seconds, and constructors use `StrValueOrColumn`/`IntValueOrColumn` aliases | -| `extract(...)`, `dateadd(...)`, `datediff(...)`, `safe_cast(...)` | scalar | RFC 018 compatibility alias spellings for canonical `date_part(...)`, `date_add(...)`, `date_diff(...)`, and `try_cast(...)` | -| `array(...)`, `cardinality(...)`, `array_contains(...)`, `arrays_overlap(...)`, `array_position(...)`, `array_range(...)`, `element_at(...)`, `array_sort(...)`, `array_distinct(...)`, `array_except(...)`, `array_intersect(...)`, `array_union(...)`, `array_join(...)`, `array_slice(...)`, `array_reverse(...)`, `array_flatten(...)`, `map_from_arrays(...)`, `map_extract(...)`, `map_contains_key(...)`, `map_keys(...)`, `map_values(...)`, `map_entries(...)`, `named_struct(...)` | scalar | registered nested scalar helpers backed by Substrait extension mappings; `array_range(...)` registers canonical `range` for positional generator lowering and `map_contains_key(...)` lowers as a documented predicate rewrite | -| `explode(...)`, `explode_outer(...)`, `posexplode(...)`, `posexplode_outer(...)`, `inline(...)`, `inline_outer(...)`, `flatten(...)`, `stack(...)` | generator | relation-extension mappings consumed by `generate(...)`; positional forms use zero-based positions | -| `window()`, `unbounded_preceding()`, `preceding(...)`, `current_row()`, `following(...)`, `unbounded_following()`, `row_number()`, `rank()`, `dense_rank()`, `percent_rank()`, `cume_dist()`, `ntile(...)`, `lag(...)`, `lead(...)`, `first_value(...)`, `last_value(...)`, `nth_value(...)` | window | `window()` and bound helpers build structural window-spec metadata; placed ranking, distribution, offset, value, and aggregate-over-window helpers lower through `ConsistentPartitionWindowRel` and execute through the DataFusion session adapter | -| `md5(...)`, `sha1(...)`, `sha224(...)`, `sha256(...)`, `sha384(...)`, `sha512(...)`, `sha2(...)`, `crc32(...)`, `xxhash64(...)`, JSON helpers, CSV helpers, URL helpers | scalar | registered RFC 022 format helpers; concrete helpers lower through Substrait extension mappings, while `sha2(...)` rewrites to a supported concrete SHA-2 helper | -| `asc(...)`, `desc(...)`, `asc_nulls_first(...)`, `asc_nulls_last(...)`, `desc_nulls_first(...)`, `desc_nulls_last(...)` | ordering | structural sort-field helpers consumed by `order_by(...)` and lowered to Substrait `SortRel.sorts` | -| `sum(...)`, `count(...)`, `count_expr(...)`, `count_distinct(...)`, `count_if(...)`, `avg(...)`, `min(...)`, `max(...)` | aggregate | registered Substrait extension functions for core aggregates; `count_expr(...)` is an alias spelling for `count(expr)`, while `count_distinct(...)` and `count_if(...)` are compatibility rewrites; core aggregates allow `DISTINCT` and aggregate-local `FILTER` where the aggregate shape is valid | -| `approx_count_distinct(...)`, `approx_percentile(...)` | aggregate | registered approximate aggregate extension functions; both are explicit approximate choices and keep DataFusion implementation-name rewrites inside the backend adapter | -| `hll_sketch(...)`, `hll_merge(...)`, `hll_estimate(...)`, `hll_serialize(...)`, `hll_deserialize(...)` | aggregate/scalar | RFC 025 typed HyperLogLog sketch helpers; construction and merge are aggregate measures, estimate/serialization/deserialization are scalar helpers, and all carry explicit sketch metadata through registry and Substrait options | -| `parse_variant_json(...)`, `try_parse_variant_json(...)`, `variant_get(...)`, `typeof(...)`, `is_null_value(...)`, `is_boolean(...)`, `is_integer(...)`, `is_float(...)`, `is_string(...)`, `is_timestamp(...)`, `is_array(...)`, `is_object(...)` | scalar | RFC 026 typed variant helpers; parse helpers use `StrValueOrColumn`, access helpers return `VariantExpr` values, `typeof(...)` returns `StringColumnExpr`, predicates return `BoolColumnExpr`, and all carry explicit variant metadata through registry and Substrait options | +| Function | Registry class | Mapping | +| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `col(...)` | scalar | deterministic field-reference rewrite | +| `lit(...)`, `int_expr(...)`, `float_expr(...)`, `str_expr(...)`, `bool_expr(...)`, `int_lit(...)`, `str_lit(...)`, `bool_lit(...)` | scalar | deterministic literal rewrites | +| `always_true()`, `always_false()` | scalar | deterministic boolean-literal rewrites | +| `cast(...)`, `try_cast(...)` | scalar | built-in Substrait `Cast` Rex shapes; primitive targets use type tokens such as `int`, `float`, `str`, and `bool`; explicit string target spellings remain available for compatibility aliases such as `int64` and `float64`; `try_cast` uses return-null failure behavior | +| `add(...)`, `sub(...)`, `mul(...)`, `div(...)`, `modulo(...)`, `neg(...)` | scalar | registered Substrait scalar mappings; `modulo(...)` registers canonical `mod` | +| `eq(...)`, `ne(...)`, `lt(...)`, `lte(...)`, `gt(...)`, `gte(...)`, `equal_null(...)` | scalar | registered Substrait scalar mappings; `equal_null(...)` lowers as null-safe equality | +| `and_(...)`, `or_(...)`, `not_(...)` | scalar | registered Substrait boolean mappings | +| `is_null(...)`, `is_not_null(...)`, `is_nan(...)`, `is_not_nan(...)` | scalar | registered predicate mappings; `is_not_nan(...)` lowers as `not(is_nan(...))` | +| `coalesce(...)`, `nullif(...)`, `case_when(...)` | scalar | registered Substrait mappings; `case_when(...)` lowers as built-in `IfThen` | +| `in_(...)`, `between(...)` | scalar | built-in membership/range lowering (`SingularOrList` and `between`) | +| `abs(...)`, `ceil(...)`, `floor(...)`, `round(...)`, `sqrt(...)`, `power(...)`, `exp(...)`, `ln(...)`, `log(...)`, `log10(...)`, `sign(...)`, `least(...)`, `greatest(...)`, `sin(...)`, `cos(...)`, `tan(...)`, `asin(...)`, `acos(...)`, `atan(...)`, `atan2(...)`, `degrees(...)`, `radians(...)` | scalar | registered RFC 018 math scalar mappings; `round(...)` accepts an optional precision expression | +| `char_length(...)`, `octet_length(...)`, `upper(...)`, `lower(...)`, `trim(...)`, `ltrim(...)`, `rtrim(...)`, `substring(...)`, `position(...)`, `overlay(...)`, `concat(...)`, `concat_ws(...)`, `replace(...)`, `translate(...)`, `repeat(...)`, `left(...)`, `right(...)`, `lpad(...)`, `rpad(...)`, `split_part(...)` | scalar | registered RFC 018 string scalar mappings with SQL-compatible one-based positions; string and integer literal parameters use `StrValueOrColumn` and `IntValueOrColumn` aliases | +| `substr(...)`, `ucase(...)`, `lcase(...)` | scalar | RFC 018 compatibility alias spellings for canonical `substring(...)`, `upper(...)`, and `lower(...)` | +| `encode(...)`, `decode(...)`, `base64(...)`, `unbase64(...)`, `hex(...)`, `unhex(...)` | scalar | registered RFC 018 text encoding helpers; convenience helpers rewrite to `encode(...)` or `decode(...)` with an explicit `StrValueOrColumn` format literal | +| `regexp_like(...)`, `regexp_replace(...)`, `regexp_extract(...)` | scalar | registered RFC 018 regex helpers using Rust-regex-compatible semantics; pattern, replacement, flags, and group-index parameters accept literal-or-column aliases | +| `current_date()`, `current_time()`, `current_timestamp()`, `date_part(...)`, `date_trunc(...)`, `time_trunc(...)`, `date_add(...)`, `date_sub(...)`, `date_diff(...)`, `timestamp_diff(...)`, `to_date(...)`, `to_time(...)`, `to_timestamp(...)`, `from_unixtime(...)`, `unix_seconds(...)`, `unix_millis(...)`, `unix_micros(...)`, `make_date(...)`, `make_time(...)`, `make_timestamp(...)`, `last_day(...)` | scalar | registered RFC 018 date/time helpers; current date/time helpers are nondeterministic registry entries, and part selectors, date amounts, epoch seconds, and constructors use `StrValueOrColumn`/`IntValueOrColumn` aliases | +| `extract(...)`, `dateadd(...)`, `datediff(...)`, `safe_cast(...)` | scalar | RFC 018 compatibility alias spellings for canonical `date_part(...)`, `date_add(...)`, `date_diff(...)`, and `try_cast(...)` | +| `array(...)`, `cardinality(...)`, `array_contains(...)`, `arrays_overlap(...)`, `array_position(...)`, `array_range(...)`, `element_at(...)`, `array_sort(...)`, `array_distinct(...)`, `array_except(...)`, `array_intersect(...)`, `array_union(...)`, `array_join(...)`, `array_slice(...)`, `array_reverse(...)`, `array_flatten(...)`, `map_from_arrays(...)`, `map_extract(...)`, `map_contains_key(...)`, `map_keys(...)`, `map_values(...)`, `map_entries(...)`, `named_struct(...)` | scalar | registered nested scalar helpers backed by Substrait extension mappings; `array_range(...)` registers canonical `range` for positional generator lowering and `map_contains_key(...)` lowers as a documented predicate rewrite | +| `explode(...)`, `explode_outer(...)`, `posexplode(...)`, `posexplode_outer(...)`, `inline(...)`, `inline_outer(...)`, `flatten(...)`, `stack(...)` | generator | relation-extension mappings consumed by `generate(...)`; positional forms use zero-based positions | +| `window()`, `unbounded_preceding()`, `preceding(...)`, `current_row()`, `following(...)`, `unbounded_following()`, `row_number()`, `rank()`, `dense_rank()`, `percent_rank()`, `cume_dist()`, `ntile(...)`, `lag(...)`, `lead(...)`, `first_value(...)`, `last_value(...)`, `nth_value(...)` | window | `window()` and bound helpers build structural window-spec metadata; placed ranking, distribution, offset, value, and aggregate-over-window helpers lower through `ConsistentPartitionWindowRel` and execute through the DataFusion session adapter | +| `md5(...)`, `sha1(...)`, `sha224(...)`, `sha256(...)`, `sha384(...)`, `sha512(...)`, `sha2(...)`, `crc32(...)`, `xxhash64(...)`, JSON helpers, CSV helpers, URL helpers | scalar | registered RFC 022 format helpers; concrete helpers lower through Substrait extension mappings, while `sha2(...)` rewrites to a supported concrete SHA-2 helper | +| `asc(...)`, `desc(...)`, `asc_nulls_first(...)`, `asc_nulls_last(...)`, `desc_nulls_first(...)`, `desc_nulls_last(...)` | ordering | structural sort-field helpers consumed by `order_by(...)` and lowered to Substrait `SortRel.sorts` | +| `sum(...)`, `count(...)`, `count_expr(...)`, `count_distinct(...)`, `count_if(...)`, `avg(...)`, `min(...)`, `max(...)` | aggregate | registered Substrait extension functions for core aggregates; `count_expr(...)` is an alias spelling for `count(expr)`, while `count_distinct(...)` and `count_if(...)` are compatibility rewrites; core aggregates allow `DISTINCT` and aggregate-local `FILTER` where the aggregate shape is valid | +| `approx_count_distinct(...)`, `approx_percentile(...)` | aggregate | registered approximate aggregate extension functions; both are explicit approximate choices and keep DataFusion implementation-name rewrites inside the backend adapter | +| `hll_sketch(...)`, `hll_merge(...)`, `hll_estimate(...)`, `hll_serialize(...)`, `hll_deserialize(...)` | aggregate/scalar | RFC 025 typed HyperLogLog sketch helpers; construction and merge are aggregate measures, estimate/serialization/deserialization are scalar helpers, and all carry explicit sketch metadata through registry and Substrait options | +| `parse_variant_json(...)`, `try_parse_variant_json(...)`, `variant_get(...)`, `typeof(...)`, `is_null_value(...)`, `is_boolean(...)`, `is_integer(...)`, `is_float(...)`, `is_string(...)`, `is_timestamp(...)`, `is_array(...)`, `is_object(...)` | scalar | RFC 026 typed variant helpers; parse helpers use `StrValueOrColumn`, access helpers return `VariantExpr` values, `typeof(...)` returns `StringColumnExpr`, predicates return `BoolColumnExpr`, and all carry explicit variant metadata through registry and Substrait options | New function families should grow under this section instead of bloating dataset carrier or dataset method modules. diff --git a/docs/language/reference/functions/nested.md b/docs/language/reference/functions/nested.md index 00beef39..6caba394 100644 --- a/docs/language/reference/functions/nested.md +++ b/docs/language/reference/functions/nested.md @@ -6,36 +6,36 @@ Generator or table-valued operations such as row-expanding `explode(...)` are se ## Arrays -| Function | Meaning | -| --- | --- | -| `array(values)` | Build an array expression from one or more scalar expressions. | -| `cardinality(value)` | Return the size of an array or map. | -| `array_contains(array_expr, value)` | Return whether an array contains a value. | -| `arrays_overlap(left, right)` | Return whether two arrays have any elements in common. | -| `array_position(array_expr, value)` | Return the one-based position of a value. | -| `element_at(array_expr, index)` | Return an array element by one-based index. | -| `array_sort(array_expr)` | Sort one array value. | -| `array_distinct(array_expr)` | Remove duplicate elements from one array value. | -| `array_except(left, right)` | Return elements from `left` that are not in `right`. | -| `array_intersect(left, right)` | Return elements shared by both arrays. | -| `array_union(left, right)` | Return the union of both arrays. | -| `array_join(array_expr, delimiter)` | Join a string array into one string. | -| `array_range(start, stop)` | Build a row-level integer array from `start` inclusive to `stop` exclusive. | -| `array_slice(array_expr, start, stop)` | Return a one-based array slice using the backend adapter's slice contract. | -| `array_reverse(array_expr)` | Reverse one array value. | -| `array_flatten(array_expr)` | Flatten an array-of-arrays into one row-level array value. | +| Function | Meaning | +| -------------------------------------- | --------------------------------------------------------------------------- | +| `array(values)` | Build an array expression from one or more scalar expressions. | +| `cardinality(value)` | Return the size of an array or map. | +| `array_contains(array_expr, value)` | Return whether an array contains a value. | +| `arrays_overlap(left, right)` | Return whether two arrays have any elements in common. | +| `array_position(array_expr, value)` | Return the one-based position of a value. | +| `element_at(array_expr, index)` | Return an array element by one-based index. | +| `array_sort(array_expr)` | Sort one array value. | +| `array_distinct(array_expr)` | Remove duplicate elements from one array value. | +| `array_except(left, right)` | Return elements from `left` that are not in `right`. | +| `array_intersect(left, right)` | Return elements shared by both arrays. | +| `array_union(left, right)` | Return the union of both arrays. | +| `array_join(array_expr, delimiter)` | Join a string array into one string. | +| `array_range(start, stop)` | Build a row-level integer array from `start` inclusive to `stop` exclusive. | +| `array_slice(array_expr, start, stop)` | Return a one-based array slice using the backend adapter's slice contract. | +| `array_reverse(array_expr)` | Reverse one array value. | +| `array_flatten(array_expr)` | Flatten an array-of-arrays into one row-level array value. | ## Maps And Structs -| Function | Meaning | -| --- | --- | -| `map_from_arrays(keys, values)` | Build a map from key and value arrays. | -| `map_extract(map_expr, key)` | Return the values associated with a key. | -| `map_contains_key(map_expr, key)` | Return whether `map_extract(...)` finds at least one value for the key. | -| `map_keys(map_expr)` | Return the map's keys as an array. | -| `map_values(map_expr)` | Return the map's values as an array. | -| `map_entries(map_expr)` | Return map entries. | -| `named_struct(field_names, values)` | Build a struct expression with explicit field names. | +| Function | Meaning | +| ----------------------------------- | ----------------------------------------------------------------------- | +| `map_from_arrays(keys, values)` | Build a map from key and value arrays. | +| `map_extract(map_expr, key)` | Return the values associated with a key. | +| `map_contains_key(map_expr, key)` | Return whether `map_extract(...)` finds at least one value for the key. | +| `map_keys(map_expr)` | Return the map's keys as an array. | +| `map_values(map_expr)` | Return the map's values as an array. | +| `map_entries(map_expr)` | Return map entries. | +| `named_struct(field_names, values)` | Build a struct expression with explicit field names. | ## Semantics diff --git a/docs/language/reference/functions/sketches.md b/docs/language/reference/functions/sketches.md index e609fef9..92c6279e 100644 --- a/docs/language/reference/functions/sketches.md +++ b/docs/language/reference/functions/sketches.md @@ -2,16 +2,16 @@ Sketch helpers model approximate state as typed logical values, not as ordinary strings or binary payloads. The first portable family is HyperLogLog. -| Function | Meaning | -| --- | --- | -| `hll_type(value_domain=SketchValueDomain.StringIdentifier, precision=14)` | Build HyperLogLog logical type metadata. | -| `sketch_col(name, sketch_type)` | Reference a column as typed sketch state. | -| `sketch_value(expr, sketch_type)` | Attach sketch logical metadata to an existing scalar expression. | -| `hll_sketch(expr, value_domain=SketchValueDomain.StringIdentifier, precision=14)` | Aggregate source values into typed HyperLogLog state. `expr` accepts primitive values or scalar expressions. | -| `hll_merge(sketch)` | Aggregate compatible HyperLogLog sketches into one sketch. | -| `hll_estimate(sketch)` | Estimate approximate cardinality from typed HyperLogLog state. | -| `hll_serialize(sketch)` | Serialize typed HyperLogLog state explicitly. | -| `hll_deserialize(payload, value_domain=SketchValueDomain.StringIdentifier, precision=14)` | Decode an explicit string payload value or scalar expression into typed HyperLogLog state. | +| Function | Meaning | +| ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | +| `hll_type(value_domain=SketchValueDomain.StringIdentifier, precision=14)` | Build HyperLogLog logical type metadata. | +| `sketch_col(name, sketch_type)` | Reference a column as typed sketch state. | +| `sketch_value(expr, sketch_type)` | Attach sketch logical metadata to an existing scalar expression. | +| `hll_sketch(expr, value_domain=SketchValueDomain.StringIdentifier, precision=14)` | Aggregate source values into typed HyperLogLog state. `expr` accepts primitive values or scalar expressions. | +| `hll_merge(sketch)` | Aggregate compatible HyperLogLog sketches into one sketch. | +| `hll_estimate(sketch)` | Estimate approximate cardinality from typed HyperLogLog state. | +| `hll_serialize(sketch)` | Serialize typed HyperLogLog state explicitly. | +| `hll_deserialize(payload, value_domain=SketchValueDomain.StringIdentifier, precision=14)` | Decode an explicit string payload value or scalar expression into typed HyperLogLog state. | Sketch compatibility is structural. HyperLogLog sketches can merge only when family, value domain, precision, and serialization format match. `hll_deserialize(...)` requires those facts because they cannot be inferred from a payload alone. diff --git a/docs/language/reference/functions/variants.md b/docs/language/reference/functions/variants.md index 890bc17e..d3afb58b 100644 --- a/docs/language/reference/functions/variants.md +++ b/docs/language/reference/functions/variants.md @@ -2,23 +2,23 @@ Variant helpers model semi-structured payloads as typed logical values, not as ordinary JSON strings. Use RFC 022 JSON helpers when you want text validation or normalized payload strings. Use variant helpers when you need kind-aware inspection while preserving the distinction between SQL null and a present semi-structured null value. -| Function | Meaning | -| --- | --- | -| `variant_type(kind=VariantKind.Any, encoding=VariantEncoding.Json)` | Build variant logical type metadata. | -| `variant_col(name, variant_type=variant_type())` | Reference a column as typed variant state. | -| `variant_value(value, variant_type)` | Attach variant logical metadata to a primitive value or scalar expression. | -| `parse_variant_json(payload)` | Strictly parse JSON text from a string value or string-producing expression into typed variant state. | -| `try_parse_variant_json(payload)` | Parse JSON text from a string value or string-producing expression into typed variant state with recoverable malformed input. | -| `variant_get(variant, path)` | Access a path from a typed variant value. Literal paths are validated as `$`-rooted; string columns or expressions are accepted as dynamic paths. | -| `typeof(variant)` | Return the stable lowercase variant kind name. | -| `is_null_value(variant)` | Return whether the value is a present semi-structured null. | -| `is_boolean(variant)` | Return whether the value is a present boolean. | -| `is_integer(variant)` | Return whether the value is a present integer. | -| `is_float(variant)` | Return whether the value is a present floating-point value. | -| `is_string(variant)` | Return whether the value is a present string. | -| `is_timestamp(variant)` | Return whether the value is a present timestamp. | -| `is_array(variant)` | Return whether the value is a present array. | -| `is_object(variant)` | Return whether the value is a present object. | +| Function | Meaning | +| ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | +| `variant_type(kind=VariantKind.Any, encoding=VariantEncoding.Json)` | Build variant logical type metadata. | +| `variant_col(name, variant_type=variant_type())` | Reference a column as typed variant state. | +| `variant_value(value, variant_type)` | Attach variant logical metadata to a primitive value or scalar expression. | +| `parse_variant_json(payload)` | Strictly parse JSON text from a string value or string-producing expression into typed variant state. | +| `try_parse_variant_json(payload)` | Parse JSON text from a string value or string-producing expression into typed variant state with recoverable malformed input. | +| `variant_get(variant, path)` | Access a path from a typed variant value. Literal paths are validated as `$`-rooted; string columns or expressions are accepted as dynamic paths. | +| `typeof(variant)` | Return the stable lowercase variant kind name. | +| `is_null_value(variant)` | Return whether the value is a present semi-structured null. | +| `is_boolean(variant)` | Return whether the value is a present boolean. | +| `is_integer(variant)` | Return whether the value is a present integer. | +| `is_float(variant)` | Return whether the value is a present floating-point value. | +| `is_string(variant)` | Return whether the value is a present string. | +| `is_timestamp(variant)` | Return whether the value is a present timestamp. | +| `is_array(variant)` | Return whether the value is a present array. | +| `is_object(variant)` | Return whether the value is a present object. | `typeof(...)` accepts a `VariantExpr` value and returns a `StringColumnExpr`. Variant predicates accept `VariantExpr` values and return `BoolColumnExpr` values. They do not parse strings directly. Parse helpers accept `StrValueOrColumn` inputs; that keeps parsing, variant inspection, and RFC 022 JSON text helpers separate without forcing authors to wrap literal payloads in `lit(...)`. diff --git a/docs/language/reference/functions/windows.md b/docs/language/reference/functions/windows.md index e9f7d0e1..60ce9b98 100644 --- a/docs/language/reference/functions/windows.md +++ b/docs/language/reference/functions/windows.md @@ -4,20 +4,20 @@ Window helpers are relation-aware. A window function application produces one ou The window helper surface includes: -| Function | Meaning | Placement | -| --- | --- | --- | -| `window()` | Build an empty window specification with a whole-partition row frame. | Refine with `.partition_by(...)`, `.order_by(...)`, `.rows_between(...)`, or `.range_between(...)`, then pass to `.over(...)`. | -| `unbounded_preceding()`, `preceding(n)`, `current_row()`, `following(n)`, `unbounded_following()` | Build frame bounds. | Use with `.rows_between(...)` or `.range_between(...)`. | -| `row_number()` | Assign a sequential row number inside the ordered window. | Use `.over(window().order_by(...))`, then `with_window_column(...)`. | -| `rank()` | Rank rows with gaps after ties inside the ordered window. | Use `.over(window().order_by(...))`, then `with_window_column(...)`. | -| `dense_rank()` | Rank rows without gaps after ties inside the ordered window. | Use `.over(window().order_by(...))`, then `with_window_column(...)`. | -| `percent_rank()` | Return relative rank within the ordered window. | Use `.over(window().order_by(...))`, then `with_window_column(...)`. | -| `cume_dist()` | Return cumulative distribution within the ordered window. | Use `.over(window().order_by(...))`, then `with_window_column(...)`. | -| `ntile(n)` | Split ordered rows into `n` buckets. | Use `.over(window().order_by(...))`, then `with_window_column(...)`. | -| `lag(expr, offset=1, default_value=...)` | Read a prior row in the ordered window. | Use `.over(window().order_by(...))`, then `with_window_column(...)`. | -| `lead(expr, offset=1, default_value=...)` | Read a later row in the ordered window. | Use `.over(window().order_by(...))`, then `with_window_column(...)`. | -| `first_value(expr)`, `last_value(expr)`, `nth_value(expr, n)` | Read a value from the current frame. | Use `.over(window().order_by(...))`, then `with_window_column(...)`; value calls may use `.ignore_nulls()` or `.respect_nulls()` before `.over(...)`. | -| `sum(...)`, `count(...)`, `avg(...)`, `min(...)`, `max(...)` | Reuse aggregate helpers over a window frame. | Call `.over(window_spec)` on the aggregate measure, then `with_window_column(...)`. | +| Function | Meaning | Placement | +| ------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | +| `window()` | Build an empty window specification with a whole-partition row frame. | Refine with `.partition_by(...)`, `.order_by(...)`, `.rows_between(...)`, or `.range_between(...)`, then pass to `.over(...)`. | +| `unbounded_preceding()`, `preceding(n)`, `current_row()`, `following(n)`, `unbounded_following()` | Build frame bounds. | Use with `.rows_between(...)` or `.range_between(...)`. | +| `row_number()` | Assign a sequential row number inside the ordered window. | Use `.over(window().order_by(...))`, then `with_window_column(...)`. | +| `rank()` | Rank rows with gaps after ties inside the ordered window. | Use `.over(window().order_by(...))`, then `with_window_column(...)`. | +| `dense_rank()` | Rank rows without gaps after ties inside the ordered window. | Use `.over(window().order_by(...))`, then `with_window_column(...)`. | +| `percent_rank()` | Return relative rank within the ordered window. | Use `.over(window().order_by(...))`, then `with_window_column(...)`. | +| `cume_dist()` | Return cumulative distribution within the ordered window. | Use `.over(window().order_by(...))`, then `with_window_column(...)`. | +| `ntile(n)` | Split ordered rows into `n` buckets. | Use `.over(window().order_by(...))`, then `with_window_column(...)`. | +| `lag(expr, offset=1, default_value=...)` | Read a prior row in the ordered window. | Use `.over(window().order_by(...))`, then `with_window_column(...)`. | +| `lead(expr, offset=1, default_value=...)` | Read a later row in the ordered window. | Use `.over(window().order_by(...))`, then `with_window_column(...)`. | +| `first_value(expr)`, `last_value(expr)`, `nth_value(expr, n)` | Read a value from the current frame. | Use `.over(window().order_by(...))`, then `with_window_column(...)`; value calls may use `.ignore_nulls()` or `.respect_nulls()` before `.over(...)`. | +| `sum(...)`, `count(...)`, `avg(...)`, `min(...)`, `max(...)` | Reuse aggregate helpers over a window frame. | Call `.over(window_spec)` on the aggregate measure, then `with_window_column(...)`. | `WindowSpec.partition_by(...)` replaces the partition expressions. `WindowSpec.order_by(...)` replaces the ordering expressions. `WindowSpec.rows_between(...)` and `WindowSpec.range_between(...)` replace the frame. Ranking, distribution, offset, and value helpers require explicit ordering; missing ordering is rejected during logical lowering. diff --git a/docs/language/reference/governance.md b/docs/language/reference/governance.md index 9aee4558..2ac7140b 100644 --- a/docs/language/reference/governance.md +++ b/docs/language/reference/governance.md @@ -12,50 +12,50 @@ Policy checkpoints are decision records attached to semantic targets at authorin from pub::incql import governed_attribute, policy_checkpoint ``` -| Helper | Signature | Purpose | -| ------ | --------- | ------- | -| `governed_attribute` | `def governed_attribute(target, key, value, value_schema = "incql.governance.string.v0.1", scope = GovernedAttributeScope.Field, source = GovernedAttributeSource.Inferred, confidence = GovernedAttributeConfidence.Unknown, status = GovernedAttributeStatus.Inferred, authority = None, observed_at_unix_nanoseconds = None, expires_at_unix_nanoseconds = None, evidence_refs = []) -> GovernedAttribute` | Build one governed fact attached to a semantic target. | -| `policy_checkpoint` | `def policy_checkpoint(target, checkpoint, action, policy_ref, reason_code, evidence_refs = [], visibility = MetadataVisibility.Public, diagnostics = []) -> PolicyCheckpoint` | Build one policy decision or observation record attached to a semantic target. | +| Helper | Signature | Purpose | +| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | +| `governed_attribute` | `def governed_attribute(target, key, value, value_schema = "incql.governance.string.v0.1", scope = GovernedAttributeScope.Field, source = GovernedAttributeSource.Inferred, confidence = GovernedAttributeConfidence.Unknown, status = GovernedAttributeStatus.Inferred, authority = None, observed_at_unix_nanoseconds = None, expires_at_unix_nanoseconds = None, evidence_refs = []) -> GovernedAttribute` | Build one governed fact attached to a semantic target. | +| `policy_checkpoint` | `def policy_checkpoint(target, checkpoint, action, policy_ref, reason_code, evidence_refs = [], visibility = MetadataVisibility.Public, diagnostics = []) -> PolicyCheckpoint` | Build one policy decision or observation record attached to a semantic target. | ## Records -| Record | Purpose | -| ------ | ------- | -| `GovernedAttribute` | One governed fact attached to a semantic target with provenance, confidence, status, authority, lifetime, and evidence references. | -| `PolicyCheckpoint` | One policy decision or observation attached to a semantic target with checkpoint phase, action, policy reference, reason code, visibility, diagnostics, and evidence references. | +| Record | Purpose | +| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `GovernedAttribute` | One governed fact attached to a semantic target with provenance, confidence, status, authority, lifetime, and evidence references. | +| `PolicyCheckpoint` | One policy decision or observation attached to a semantic target with checkpoint phase, action, policy reference, reason code, visibility, diagnostics, and evidence references. | `GovernedAttribute.value` is a `MetadataPayload` with a schema and compact string value. The default helper shape is intentionally compact: inspection-derived primitive-kind attributes use `incql.governance.schema-primitive-kind.v0.1` to distinguish governed schema evidence from generic metadata strings. Catalog, contract, exchange, or policy-ingress steps can normalize richer external formats before they become governed attributes. `GovernedAttribute` exposes convenience methods: -| Method | Purpose | -| ------ | ------- | -| `with_status(status)` | Return the same attribute identity with a different status. | -| `with_confidence(confidence)` | Return the same attribute identity with a different confidence level. | -| `with_authority(authority)` | Return the same attribute identity with an authority reference. | +| Method | Purpose | +| ---------------------------------------- | ------------------------------------------------------------------------------ | +| `with_status(status)` | Return the same attribute identity with a different status. | +| `with_confidence(confidence)` | Return the same attribute identity with a different confidence level. | +| `with_authority(authority)` | Return the same attribute identity with an authority reference. | | `with_lifetime(observed_at, expires_at)` | Return the same attribute identity with observation and expiration timestamps. | `PolicyCheckpoint.with_visibility(visibility)` returns the same checkpoint with a different visibility policy. ## Enumerations -| Enum | Values | -| ---- | ------ | -| `GovernedAttributeScope` | `Field`, `Relation`, `Expression`, `Plan`, `Output`, `ExecutionPath` | -| `GovernedAttributeSource` | `Model`, `User`, `Lineage`, `Catalog`, `Policy`, `Adapter`, `ImportedArtifact`, `Inferred` | -| `GovernedAttributeConfidence` | `Exact`, `High`, `Medium`, `Low`, `Unknown` | -| `GovernedAttributeStatus` | `Asserted`, `Inferred`, `Accepted`, `Rejected`, `Overridden`, `Stale`, `PendingReview` | -| `PolicyCheckpointKind` | `Authoring`, `Planning`, `Binding`, `Execution` | -| `PolicyCheckpointAction` | `Allow`, `Deny`, `Redact`, `Mask`, `RowFilter`, `Warn`, `RequireQualityCheck`, `RequireApproval`, `Observe` | +| Enum | Values | +| ----------------------------- | ----------------------------------------------------------------------------------------------------------- | +| `GovernedAttributeScope` | `Field`, `Relation`, `Expression`, `Plan`, `Output`, `ExecutionPath` | +| `GovernedAttributeSource` | `Model`, `User`, `Lineage`, `Catalog`, `Policy`, `Adapter`, `ImportedArtifact`, `Inferred` | +| `GovernedAttributeConfidence` | `Exact`, `High`, `Medium`, `Low`, `Unknown` | +| `GovernedAttributeStatus` | `Asserted`, `Inferred`, `Accepted`, `Rejected`, `Overridden`, `Stale`, `PendingReview` | +| `PolicyCheckpointKind` | `Authoring`, `Planning`, `Binding`, `Execution` | +| `PolicyCheckpointAction` | `Allow`, `Deny`, `Redact`, `Mask`, `RowFilter`, `Warn`, `RequireQualityCheck`, `RequireApproval`, `Observe` | ## Inspection behavior `inspect_plan(...)` now includes two governed-evidence fields: -| Field | Type | Current behavior | -| ----- | ---- | ---------------- | +| Field | Type | Current behavior | +| --------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `governed_attributes` | `list[GovernedAttribute]` | Conservative field-level attributes inferred from local inspection evidence. When a field schema kind is known, inspection emits schema primitive-kind attributes. | -| `policy_checkpoints` | `list[PolicyCheckpoint]` | Local planning checkpoint records that make policy observation explicit without claiming enforcement. | +| `policy_checkpoints` | `list[PolicyCheckpoint]` | Local planning checkpoint records that make policy observation explicit without claiming enforcement. | The corresponding `InspectionArtifact` families are `governed_attributes` and `policy_checkpoints`. @@ -67,4 +67,4 @@ IncQL carries governed evidence. It does not decide legal obligations, own an or -[rfc-035]: ../../rfcs/035_governed_attributes_policy_checkpoints.md +[rfc-035]: ../../rfcs/closed/implemented/035_governed_attributes_policy_checkpoints.md diff --git a/docs/language/reference/inspection.md b/docs/language/reference/inspection.md index c391e8d2..d1bb47e4 100644 --- a/docs/language/reference/inspection.md +++ b/docs/language/reference/inspection.md @@ -17,19 +17,19 @@ lineage = inspect_lineage(summary) The inspection surface consumes shared evidence record families and adds the local `PlanInspection` wrapper around them: -| Record | Purpose | -| ------ | ------- | -| `SemanticTarget` | Stable local anchor for a plan, Prism node, relation output, field, read root, or future evidence family. | -| `PlanInspection` | Top-level inspection result with schema version, IncQL version, plan target, output schema, output fields, Prism nodes, lineage, artifacts, diagnostics, and unsupported-evidence markers. | -| `InspectionNodeKind` | Typed public node-kind vocabulary for authored and rewritten Prism node inspection records. | -| `LineageGraph` | Plan-local lineage graph with a rule version and typed lineage edges. | -| `LineageEdge` | Source-to-destination edge with relationship kind, transformation kind, confidence, expression reference, and evidence references. | -| `MetadataAttachment` | Typed attachment record for schema-versioned metadata payloads. The first inspection slice emits public version attachments and concrete output-field primitive-kind attachments when known. | -| `GovernedAttribute` | Governed fact attached to a semantic target with provenance, confidence, status, authority, lifetime, and evidence references. Inspection currently emits conservative schema primitive-kind attributes for known output fields. | -| `PolicyCheckpoint` | Policy decision or observation record attached to a semantic target. Inspection records local planning observation checkpoints without enforcing policy. | -| `AdapterRequirement` | Capability requirement inferred from plan evidence, such as row filtering, ordered execution, extension functions, variant semantics, baseline null semantics, or lineage-preservation evidence. | -| `InspectionArtifact` | Deterministic summary for artifact families such as plan graph, lineage graph, schema flow, metadata attachments, diagnostics, and unsupported evidence. | -| `UnsupportedEvidence` | Explicit marker for evidence families that are not computed by this inspection path. | +| Record | Purpose | +| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `SemanticTarget` | Stable local anchor for a plan, Prism node, relation output, field, read root, or future evidence family. | +| `PlanInspection` | Top-level inspection result with schema version, IncQL version, plan target, output schema, output fields, Prism nodes, lineage, artifacts, diagnostics, and unsupported-evidence markers. | +| `InspectionNodeKind` | Typed public node-kind vocabulary for authored and rewritten Prism node inspection records. | +| `LineageGraph` | Plan-local lineage graph with a rule version and typed lineage edges. | +| `LineageEdge` | Source-to-destination edge with relationship kind, transformation kind, confidence, expression reference, and evidence references. | +| `MetadataAttachment` | Typed attachment record for schema-versioned metadata payloads. The first inspection slice emits public version attachments and concrete output-field primitive-kind attachments when known. | +| `GovernedAttribute` | Governed fact attached to a semantic target with provenance, confidence, status, authority, lifetime, and evidence references. Inspection currently emits conservative schema primitive-kind attributes for known output fields. | +| `PolicyCheckpoint` | Policy decision or observation record attached to a semantic target. Inspection records local planning observation checkpoints without enforcing policy. | +| `AdapterRequirement` | Capability requirement inferred from plan evidence, such as row filtering, ordered execution, extension functions, variant semantics, baseline null semantics, or lineage-preservation evidence. | +| `InspectionArtifact` | Deterministic summary for artifact families such as plan graph, lineage graph, schema flow, metadata attachments, diagnostics, and unsupported evidence. | +| `UnsupportedEvidence` | Explicit marker for evidence families that are not computed by this inspection path. | ## Target identity diff --git a/docs/language/reference/quality.md b/docs/language/reference/quality.md index 112d6b67..8c42baf2 100644 --- a/docs/language/reference/quality.md +++ b/docs/language/reference/quality.md @@ -6,13 +6,13 @@ Quality assertions are declarations. They do not filter, quarantine, or mutate t ## Assertion helpers -| API | Scope | Purpose | -| --- | --- | --- | -| `row_count(min_count=None, max_count=None)` | relation | Assert that a relation row count is within optional inclusive thresholds | -| `null_rate(field, max_rate)` | field | Assert that the null rate for a field expression is at most `max_rate` | -| `unique(field)` | field | Assert that a field expression has no duplicate groups | -| `group_row_count(group_by, min_count=1, max_count=None)` | group | Assert that every group has a row count within inclusive thresholds | -| `cross_relation_row_count_equal()` | cross relation | Assert that two explicit relation inputs have the same row count | +| API | Scope | Purpose | +| -------------------------------------------------------- | -------------- | ------------------------------------------------------------------------ | +| `row_count(min_count=None, max_count=None)` | relation | Assert that a relation row count is within optional inclusive thresholds | +| `null_rate(field, max_rate)` | field | Assert that the null rate for a field expression is at most `max_rate` | +| `unique(field)` | field | Assert that a field expression has no duplicate groups | +| `group_row_count(group_by, min_count=1, max_count=None)` | group | Assert that every group has a row count within inclusive thresholds | +| `cross_relation_row_count_equal()` | cross relation | Assert that two explicit relation inputs have the same row count | The first implementation evaluates these helpers through ordinary IncQL plans and `Session.collect_observed(...)`. Row-count checks use structured materialization row counts. Field and group checks build filter and aggregate plans and evaluate the resulting row counts. They do not scrape rendered preview text. @@ -40,18 +40,18 @@ Each body item must be an assertion expression. Brace syntax uses newline-separa `QualityAssertion.mode` records handling intent for callers that choose to enforce checks outside the assertion semantics. The first implementation keeps session evaluation policy-neutral: `observe_quality(...)` reports `Passed`, `Failed`, `Errored`, or `Unsupported` for the built-in session checks; it does not throw on failed required checks. `Skipped` is part of the status vocabulary for caller-owned or future evaluators that intentionally bypass a check. -| API | Result | -| --- | --- | -| `assertion.with_mode(mode)` | Return the same assertion with a different `QualityAssertionMode` | +| API | Result | +| ----------------------------------- | --------------------------------------------------------------------- | +| `assertion.with_mode(mode)` | Return the same assertion with a different `QualityAssertionMode` | | `assertion.with_severity(severity)` | Return the same assertion with a different `QualityAssertionSeverity` | -| `assertion.require()` | Return the assertion with `QualityAssertionMode.Require` | -| `assertion.quarantine()` | Return the assertion with `QualityAssertionMode.Quarantine` | +| `assertion.require()` | Return the assertion with `QualityAssertionMode.Require` | +| `assertion.quarantine()` | Return the assertion with `QualityAssertionMode.Quarantine` | ## Session APIs -| API | Input | Returns | -| --- | --- | --- | -| `session.observe_quality(data, assertions)` | `LazyFrame[T]`, `list[QualityAssertion]` | `list[QualityObservation]` | +| API | Input | Returns | +| ------------------------------------------------------- | -------------------------------------------------------- | -------------------------- | +| `session.observe_quality(data, assertions)` | `LazyFrame[T]`, `list[QualityAssertion]` | `list[QualityObservation]` | | `session.observe_quality_pair(left, right, assertions)` | `LazyFrame[T]`, `LazyFrame[U]`, `list[QualityAssertion]` | `list[QualityObservation]` | Use `observe_quality(...)` for relation, field, and group assertions. Use `observe_quality_pair(...)` for explicit cross-relation assertions. Passing a cross-relation assertion to the single-relation API, or a single-relation assertion to the pair API, returns an `Unsupported` quality observation with a diagnostic; it is not silently accepted. @@ -60,53 +60,53 @@ Use `observe_quality(...)` for relation, field, and group assertions. Use `obser ### `QualityAssertion` -| Field | Type | Meaning | -| --- | --- | --- | -| `assertion_id` | `str` | Stable local assertion identity | -| `name` | `str` | Human-readable assertion name | -| `kind` | `QualityAssertionKind` | Assertion family | -| `target` | `SemanticTarget` | Assertion semantic target | -| `expression` | `ColumnExpr` | Field or predicate expression used by field-scoped checks | -| `group_by` | `list[ColumnExpr]` | Grouping expressions for group-scoped checks | -| `min_count` | `Option[int]` | Inclusive minimum threshold for count checks | -| `max_count` | `Option[int]` | Inclusive maximum threshold for count checks | -| `max_rate` | `Option[float]` | Maximum rate threshold for rate checks | -| `severity` | `QualityAssertionSeverity` | Diagnostic severity intent | -| `mode` | `QualityAssertionMode` | Policy-neutral handling intent | -| `scope` | `QualityAssertionScope` | Relation, field, group, or cross-relation scope | -| `evidence_refs` | `list[str]` | Additional evidence references attached to the assertion | +| Field | Type | Meaning | +| --------------- | -------------------------- | --------------------------------------------------------- | +| `assertion_id` | `str` | Stable local assertion identity | +| `name` | `str` | Human-readable assertion name | +| `kind` | `QualityAssertionKind` | Assertion family | +| `target` | `SemanticTarget` | Assertion semantic target | +| `expression` | `ColumnExpr` | Field or predicate expression used by field-scoped checks | +| `group_by` | `list[ColumnExpr]` | Grouping expressions for group-scoped checks | +| `min_count` | `Option[int]` | Inclusive minimum threshold for count checks | +| `max_count` | `Option[int]` | Inclusive maximum threshold for count checks | +| `max_rate` | `Option[float]` | Maximum rate threshold for rate checks | +| `severity` | `QualityAssertionSeverity` | Diagnostic severity intent | +| `mode` | `QualityAssertionMode` | Policy-neutral handling intent | +| `scope` | `QualityAssertionScope` | Relation, field, group, or cross-relation scope | +| `evidence_refs` | `list[str]` | Additional evidence references attached to the assertion | ### `QualityObservation` -| Field | Type | Meaning | -| --- | --- | --- | -| `observation_id` | `str` | Stable local quality observation identity | -| `target` | `SemanticTarget` | Observation semantic target | -| `assertion` | `QualityAssertion` | Assertion that was evaluated | -| `execution_observation_ids` | `list[str]` | Execution attempts used to produce the observation | -| `status` | `QualityObservationStatus` | Predicate outcome or execution support state | -| `metrics` | `list[QualityMetric]` | Compact metrics emitted by the check | -| `diagnostics` | `list[ExecutionDiagnostic]` | Structured diagnostics for errored or unsupported observations | -| `redacted_sample_refs` | `list[str]` | Optional references to separately managed samples | -| `evidence_refs` | `list[str]` | Assertion and execution evidence references | +| Field | Type | Meaning | +| --------------------------- | --------------------------- | -------------------------------------------------------------- | +| `observation_id` | `str` | Stable local quality observation identity | +| `target` | `SemanticTarget` | Observation semantic target | +| `assertion` | `QualityAssertion` | Assertion that was evaluated | +| `execution_observation_ids` | `list[str]` | Execution attempts used to produce the observation | +| `status` | `QualityObservationStatus` | Predicate outcome or execution support state | +| `metrics` | `list[QualityMetric]` | Compact metrics emitted by the check | +| `diagnostics` | `list[ExecutionDiagnostic]` | Structured diagnostics for errored or unsupported observations | +| `redacted_sample_refs` | `list[str]` | Optional references to separately managed samples | +| `evidence_refs` | `list[str]` | Assertion and execution evidence references | ### `QualityMetric` -| Field | Type | Meaning | -| --- | --- | --- | -| `name` | `str` | Metric name, such as `row_count`, `null_count`, `null_rate`, or `duplicate_group_count` | -| `value` | `str` | Compact value representation | -| `unit` | `str` | Metric unit, such as `count` or `ratio` | +| Field | Type | Meaning | +| ------- | ----- | --------------------------------------------------------------------------------------- | +| `name` | `str` | Metric name, such as `row_count`, `null_count`, `null_rate`, or `duplicate_group_count` | +| `value` | `str` | Compact value representation | +| `unit` | `str` | Metric unit, such as `count` or `ratio` | ## Enums -| Enum | Values | -| --- | --- | -| `QualityAssertionKind` | `RowCount`, `NullRate`, `Unique`, `GroupRowCount`, `CrossRelationRowCountEqual` | -| `QualityAssertionScope` | `Relation`, `Field`, `Group`, `CrossRelation` | -| `QualityAssertionMode` | `Observe`, `Require`, `Quarantine` | -| `QualityAssertionSeverity` | `Info`, `Warning`, `Error` | -| `QualityObservationStatus` | `Passed`, `Failed`, `Errored`, `Skipped`, `Unsupported` | +| Enum | Values | +| -------------------------- | ------------------------------------------------------------------------------- | +| `QualityAssertionKind` | `RowCount`, `NullRate`, `Unique`, `GroupRowCount`, `CrossRelationRowCountEqual` | +| `QualityAssertionScope` | `Relation`, `Field`, `Group`, `CrossRelation` | +| `QualityAssertionMode` | `Observe`, `Require`, `Quarantine` | +| `QualityAssertionSeverity` | `Info`, `Warning`, `Error` | +| `QualityObservationStatus` | `Passed`, `Failed`, `Errored`, `Skipped`, `Unsupported` | ## Boundaries @@ -114,4 +114,4 @@ Quality status is predicate outcome evidence. It does not claim verification ass -[rfc-034]: ../../rfcs/034_quality_assertions_observations.md +[rfc-034]: ../../rfcs/closed/implemented/034_quality_assertions_observations.md diff --git a/docs/language/reference/query_blocks.md b/docs/language/reference/query_blocks.md index 1f12677d..710dc57e 100644 --- a/docs/language/reference/query_blocks.md +++ b/docs/language/reference/query_blocks.md @@ -1,8 +1,12 @@ # Query blocks (Reference) -Query blocks are dependency-activated IncQL expressions. Import `pub::incql` to make the vocabulary and helper surface available in a downstream Incan package. +Query blocks are dependency-activated IncQL expressions. Use `import pub::incql` to activate the `query` vocabulary in a downstream Incan package, then import the carrier and helper names used by the block. + +If you are learning the surface rather than looking up its exact contract, start with [Part IV of the IncQL Book](../tutorials/book/08_first_query_block.md). ```incan +import pub::incql + from pub::incql import DataFrame, count, desc, sum from models import Order, OrderSummary @@ -53,14 +57,14 @@ The implemented v0.1 query-block surface supports: Query-block expressions use Incan expression operators and desugar to the same IncQL helper calls available in ordinary method-chain code: -| Query expression | Helper equivalent | -| ---------------- | ----------------- | +| Query expression | Helper equivalent | +| ------------------- | --------------------- | | `.status == "paid"` | `eq(.status, "paid")` | | `.status != "paid"` | `ne(.status, "paid")` | -| `.amount < 100` | `lt(.amount, 100)` | -| `.amount <= 100` | `lte(.amount, 100)` | -| `.amount > 100` | `gt(.amount, 100)` | -| `.amount >= 100` | `gte(.amount, 100)` | +| `.amount < 100` | `lt(.amount, 100)` | +| `.amount <= 100` | `lte(.amount, 100)` | +| `.amount > 100` | `gt(.amount, 100)` | +| `.amount >= 100` | `gte(.amount, 100)` | The comparison helper names use `lte` and `gte` for inclusive bounds; `le` and `ge` are not public helper names. Arithmetic operators lower the same way: `+` to `add`, `-` to `sub`, `*` to `mul`, `/` to `div`, and `%` to `modulo`. Boolean and unary operators lower to their helper forms as well, such as `and_`, `or_`, `not_`, and `neg`. Use `==` for equality; a single `=` remains assignment/binding syntax, not a query predicate. diff --git a/docs/language/reference/substrait/conformance.md b/docs/language/reference/substrait/conformance.md index 08c5745d..b95603f5 100644 --- a/docs/language/reference/substrait/conformance.md +++ b/docs/language/reference/substrait/conformance.md @@ -44,8 +44,8 @@ The numeric suffix is immutable after publication. If requirements change incomp Core scenarios currently implemented in `src/substrait/conformance_catalog.incn`: -| Scenario ID | Selector | Primary core `Rel` coverage | -| ------------------------------------------------------ | ---------------------------------------------------------- | --------------------------- | +| Scenario ID | Selector | Primary core `Rel` coverage | +| ------------------------------------------------------- | ---------------------------------------------------------- | --------------------------- | | `incql.substrait.core.read_named_table.001` | `core_scenario(CoreScenarioKey.ReadNamedTable)` | `ReadRel` (`NamedTable`) | | `incql.substrait.core.read_local_files.001` | `core_scenario(CoreScenarioKey.ReadLocalFiles)` | `ReadRel` (`LocalFiles`) | | `incql.substrait.core.read_virtual_table.001` | `core_scenario(CoreScenarioKey.ReadVirtualTable)` | `ReadRel` (`VirtualTable`) | @@ -79,5 +79,5 @@ Historical design context is captured in [IncQL RFC 002][rfc-002], but this page -[rfc-002]: ../../rfcs/002_apache_substrait_integration.md +[rfc-002]: ../../../rfcs/002_apache_substrait_integration.md [ref-operator-catalog]: ./operator_catalog.md diff --git a/docs/language/reference/substrait/operator_catalog.md b/docs/language/reference/substrait/operator_catalog.md index 8d3010c5..833c2778 100644 --- a/docs/language/reference/substrait/operator_catalog.md +++ b/docs/language/reference/substrait/operator_catalog.md @@ -19,7 +19,7 @@ The same status taxonomy is used in the [Substrait conformance corpus][ref-confo The following table maps IncQL plan capabilities to Substrait logical relations and expression patterns for the read/query analytical core — the minimum required for IncQL v0.1. -| IncQL capability (conceptual) | Substrait | Profile | +| IncQL capability (conceptual) | Substrait | Profile | | ------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ------- | | Logical table / registered name | `ReadRel` + `NamedTable` | core | | File or object scan as plan input | `ReadRel` + `LocalFiles` (format options in the pinned spec) | core | @@ -39,16 +39,16 @@ The following table maps IncQL plan capabilities to Substrait logical relations | Limit / offset | `FetchRel` | core | | Union, intersect, except | `SetRel` with the appropriate set operation enum | core | | Reuse of an identical subplan | `Plan` + `ReferenceRel` | core | -| Unnest / explode | Extension rel or documented expansion — **must** be pinned per implementation (see [Unnest / explode](#unnest--explode)) | gap | -| Pivot / unpivot | `ExtensionSingleRel` or documented rewrite to join + aggregate + project (see [Pivot / unpivot](#pivot--unpivot)) | gap | -| Asof / interval join | Gap or non-equi join expression only where consumer contract explicitly allows (see [Asof / interval joins](#asof--interval-joins)) | gap | +| Unnest / explode | Extension rel or documented expansion — **must** be pinned per implementation (see [Unnest / explode](#unnest-explode)) | gap | +| Pivot / unpivot | `ExtensionSingleRel` or documented rewrite to join + aggregate + project (see [Pivot / unpivot](#pivot-unpivot)) | gap | +| Asof / interval join | Gap or non-equi join expression only where consumer contract explicitly allows (see [Asof / interval joins](#asof-interval-joins)) | gap | | Streaming time semantics (watermarks, session windows, state) | Outside core Substrait unless via named extensions or a separate execution IR (see [Streaming time semantics](#streaming-time-semantics)) | gap | ## Optional mutation profile The following capabilities are part of the optional mutation profile. They are **not** required for IncQL read/query analytical core (v0.1). An implementation that exposes any mutation-profile capability **must** document which relations are supported for its target backend and what portability guarantees (if any) apply. -| IncQL capability | Substrait | Profile | +| IncQL capability | Substrait | Profile | | --------------------------------------------- | ----------- | ----------------- | | Write to a table / CTAS | `WriteRel` | optional-mutation | | Table update without a full child `Rel` input | `UpdateRel` | optional-mutation | @@ -111,5 +111,5 @@ Watermarks, session windows, and stateful streaming operations are outside the s -[rfc-002]: ../../rfcs/002_apache_substrait_integration.md +[rfc-002]: ../../../rfcs/002_apache_substrait_integration.md [ref-conformance-corpus]: ./conformance.md diff --git a/docs/language/reference/substrait/read_root_binding_contract.md b/docs/language/reference/substrait/read_root_binding_contract.md index 7b0c321a..3b948625 100644 --- a/docs/language/reference/substrait/read_root_binding_contract.md +++ b/docs/language/reference/substrait/read_root_binding_contract.md @@ -14,7 +14,7 @@ The execution context **must** resolve logical reads to physical resources throu ## `ReadRel` variant reference -| Variant | Substrait field | Typical IncQL use | Portability | +| Variant | Substrait field | Typical IncQL use | Portability | | -------------- | ------------------------------------------ | ---------------------------------------------------------------- | -------------------------------------------------------------------------------------- | | `NamedTable` | `named_table` (list of name parts) | Registered logical table name; resolved by session registry | Portable across conforming consumers that have registered the same logical name | | `LocalFiles` | `local_files` (file list + format options) | Parquet, CSV, Arrow IPC scan from a URI | Portable if consumers can resolve the URI; URI format is not standardized by Substrait | @@ -57,8 +57,8 @@ Adapter-specific "open connection" or "bind source" APIs **should not** be speci The following table summarizes how each `Session` read method maps to a `ReadRel` variant and the resulting IncQL carrier type. -| `Session` method | Returns | `ReadRel` variant | -| ------------------------------------ | -------------- | ------------------------------------------------- | +| `Session` method | Returns | `ReadRel` variant | +| --------------------------------- | -------------- | ------------------------------------------------- | | `session.table(name)` | `LazyFrame[T]` | `NamedTable` | | `session.read_csv(name, uri)` | `LazyFrame[T]` | `NamedTable` (via Session registration + binding) | | `session.read_parquet(name, uri)` | `LazyFrame[T]` | `NamedTable` (via Session registration + binding) | @@ -68,5 +68,5 @@ In all cases the `LazyFrame[T]` holds a deferred plan — no data is fetched unt -[rfc-002]: ../../rfcs/002_apache_substrait_integration.md +[rfc-002]: ../../../rfcs/002_apache_substrait_integration.md [rfc-004]: ../../../rfcs/004_incql_execution_context.md diff --git a/docs/language/reference/substrait/revision_and_extension_policy.md b/docs/language/reference/substrait/revision_and_extension_policy.md index 420d7210..0a0ed9d7 100644 --- a/docs/language/reference/substrait/revision_and_extension_policy.md +++ b/docs/language/reference/substrait/revision_and_extension_policy.md @@ -97,5 +97,5 @@ Reclassification from gap → core or extension → core is **not** breaking (it -[rfc-002]: ../../rfcs/002_apache_substrait_integration.md +[rfc-002]: ../../../rfcs/002_apache_substrait_integration.md [ref-operator-catalog]: ./operator_catalog.md diff --git a/docs/language/tutorials/book/01_typed_relation.md b/docs/language/tutorials/book/01_typed_relation.md new file mode 100644 index 00000000..df930e33 --- /dev/null +++ b/docs/language/tutorials/book/01_typed_relation.md @@ -0,0 +1,224 @@ +
+

THE INCQL BOOK

+ +# 1. Read a typed relation + +Create a Session-owned logical CSV source and carry its intended row shape in `LazyFrame[Order]`. +
+ +
+
+

Part I

+

Model the work

+

Chapter 1 of 2

+
+ +
+ +
+ +## Goal + +By the end of this chapter, the included consumer project can open `orders.csv` as a deferred `LazyFrame[Order]`. You will know which part is a checked Incan type, which part is a logical source registration, and which compatibility check is not yet performed. + +
+ +## Start from a real consumer project + +The book is not compiled as part of IncQL itself. It is a small downstream project whose manifest points at this checkout, which also verifies that the public `pub::incql` boundary works. + +
+ +**Project manifest** + +```toml +--8<-- "examples/tutorial_book/incan.toml" +``` + +
+ +The path dependency is the truthful setup for this checkout. It should not be read as evidence that IncQL has been published to a package registry. + +## Define the intended row shape + +The `Order` model gives the carrier an intended row type. Later functions can accept `LazyFrame[Order]` rather than an untyped table handle. + +
+ +**Shared domain model** + +```incan +--8<-- "examples/tutorial_book/src/domain.incn" +``` + +
+ +That type parameter is useful, but its boundary must be stated precisely: current CSV ingress does not validate every physical CSV field and type against the annotated Incan model. `LazyFrame[Order]` carries the author's intended model while IncQL's planned schema comes from the registered source. + +The included data is small enough to keep every later result understandable: + +
+ +**Tutorial CSV** + +```csv +--8<-- "examples/tutorial_book/orders.csv" +``` + +
+ +## Register the source + +`Session` owns source registration, backend selection, execution, materialization, and writes. `read_csv(...)` registers a logical relation name and returns deferred work; it does not hand you materialized rows. + +With this example, you are asking the Session to register the URI `orders.csv` under the logical name `tutorial_orders`. The assignment keeps the authored carrier contract as `LazyFrame[Order]`, while `?` propagates a registration failure instead of pretending that a carrier exists. The trace separates that Incan type, the Session registration, and the resulting named-table carrier so you can see which layer owns each fact. + +
+
+
+

Source registration · before backend execution

+

See what read_csv(...) actually establishes

+
+

The call joins an intended Incan carrier type to a Session-owned source registration. It reads the local CSV to establish a coarse planned schema, but it does not execute the relation through DataFusion.

+
+ +
    +
  1. +
    + + 01 +
    Intended row shapeState the carrier contract in checked Incan
    +
    +
    + +```incan +pub model Order: + pub order_id: int + pub customer_id: str + pub status: str + pub amount: float +``` + +
    +
    +
    Owner
    Incan authoring surface
    +
    Artifact
    Order and the intended LazyFrame[Order] type
    +
    Knowable now
    The row shape the consumer intends to carry
    +
    +
  2. + +
  3. +
    + + 02 +
    CSV registrationBind a logical name and establish a planned source schema
    +
    +
    + +```incan +orders: LazyFrame[Order] = session.read_csv( # (1)! + "tutorial_orders", # (2)! + "orders.csv", # (3)! +)? # (4)! +``` + +
      +
    1. Carrier contract. LazyFrame[Order] is the declared result type: it carries the intended Order row shape with deferred relational work. It is not a materialized DataFrame[Order].

    2. +
    3. Logical name. tutorial_orders becomes the Session-local name used by the ReadNamedTable plan root. The name must be non-empty and not already registered in this Session; changing it does not rename the CSV file.

    4. +
    5. Source URI. orders.csv identifies the physical CSV source. Here it is a non-empty path relative to the tutorial project; the current read_csv(...) surface takes the logical name and URI as its two positional arguments.

    6. +
    7. Failure propagation. read_csv(...) returns a Result. The trailing ? returns its SessionError from the caller when registration or source-schema discovery fails, instead of producing a carrier that cannot be used safely.

    8. +
    + +
    +
    +
    Owner
    Session and its CSV source policy
    +
    Artifact
    Logical source registration plus a coarse inferred CSV schema
    +
    Knowable now
    Header names, primitive kinds, nullability, and source identity
    +
    +
  4. + +
  5. +
    + + 03 +
    Deferred carrierReturn intent without materialising rows
    +
    +
    +
    +
    Logical root
    ReadNamedTable("tutorial_orders")
    +
    Returned carrier
    LazyFrame[Order]
    +
    Backend attempt
    None
    +
    +
    + Session registration + Deferred named-table plan +
    +
    +
  6. +
+ + +
+ +
+ Open the complete Chapter 1 checkpoint +
+ +```incan +--8<-- "examples/tutorial_book/src/chapter_01.incn" +``` + +
+
+ +Run the checkpoint from the tutorial project: + +```bash +incan run src/chapter_01.incn +``` + +
+ +**Expected output** + +```text +Chapter 1: created a LazyFrame[Order] without executing it +``` + +The checkpoint deliberately prints no schema or rows. It proves that the public consumer can register the logical source and create its lazy carrier. Later materialization exposes resolved columns, row counts, and preview text rather than typed `Order` iteration. + +
+ +
+ +## Try it + +Change only the logical table name passed to `read_csv(...)`, then run the checkpoint again. The physical CSV stays the same, but the name attached to the logical read changes. Restore the checkpoint before continuing. + +
+ +
+ +## Chapter complete + +You can explain why `LazyFrame[Order]` is deferred relational intent, why the Session owns the CSV binding, and why the `Order` annotation is not yet proof that the physical CSV conforms field-for-field. + +
+ + diff --git a/docs/language/tutorials/book/02_deferred_plan.md b/docs/language/tutorials/book/02_deferred_plan.md new file mode 100644 index 00000000..903786d3 --- /dev/null +++ b/docs/language/tutorials/book/02_deferred_plan.md @@ -0,0 +1,184 @@ +
+

THE INCQL BOOK

+ +# 2. Build deferred work + +Transform the logical source while keeping execution behind the Session boundary. +
+ +
+
+

Part I

+

Model the work

+

Chapter 2 of 2

+
+ +
+ +
+ +## Goal + +Build a review plan from the CSV-backed `LazyFrame` and run the checkpoint without collecting rows. The important result is a new logical plan, not a local table. + +
+ +## Add one honest relational step + +IncQL method calls append relational intent to the lazy carrier. This tutorial deliberately adds only `limit(3)`, producing a simple `ReadNamedTable → Limit` plan. A limit constrains the eventual result; it does not materialize that result by itself. + +IncQL also has filter, ordering, projection, aggregation, and query-block surfaces. They remain available in [Guides](../../how-to/README.md) and [Reference](../../reference/README.md). The book keeps this first plan deliberately small so the relationship between one authored operation, its Prism representation, and the later execution evidence stays easy to inspect. + +With this example, you are turning the source-rooted carrier into a new `LazyFrame[Order]` whose Prism tip is a `Limit` node with a count of three. The function returns that carrier directly and never calls `collect(...)` or a Session execution method, so the runnable checkpoint demonstrates that the plan changed while the data remained unmaterialized. The trace below follows that boundary from named source, through the appended node, to the still-deferred result. + +
+
+
+

Plan construction · no execution attempt

+

Follow one operation into the logical plan

+
+

The source registration supplies the plan root. limit(3) appends bounded relational intent: the eventual result may contain at most three rows, with no ordering promise.

+
+ +
    +
  1. +
    + + 01 +
    Registered sourceStart from the named-table carrier
    +
    +
    + +```incan +orders: LazyFrame[Order] = session.read_csv( + "tutorial_orders", # (1)! + "orders.csv", # (2)! +)? +``` + +
      +
    1. Logical plan name. tutorial_orders is the non-empty, Session-local identity that the Prism plan records in its ReadNamedTable root. It must be unique among this Session's registrations.

    2. +
    3. CSV location. orders.csv is the non-empty source URI registered under that logical name. In the tutorial it resolves relative to the project directory; changing this argument changes the physical source, not the plan's logical identity.

    4. +
    + +
    +
    +
    Owner
    Session
    +
    Artifact
    LazyFrame[Order] rooted at ReadNamedTable
    +
    Knowable now
    The source identity and planned CSV columns
    +
    +
  2. + +
  3. +
    + + 02 +
    Append bounded intentAdd one Prism plan node without collecting
    +
    +
    + +```incan +return Ok(orders.limit(3)) # (1)! +``` + +
      +
    1. Receiver and row cap. orders is the deferred carrier, and limit(3) returns a new carrier with a Limit node whose count must be non-negative. It caps the eventual result at three rows; it neither defines which three rows come first nor executes or materializes them. Use order_by(...) before limit(...) when row order matters.

    2. +
    + +
    +
    +
    Owner
    IncQL relational carrier
    +
    Artifact
    ReadNamedTable → Limit(3) logical plan
    +
    Knowable now
    The eventual result is capped at three rows
    +
    +
  4. + +
  5. +
    + + 03 +
    Deferred plan stateKeep backend work behind the Session boundary
    +
    +
    +
    +
    Plan shape
    ReadNamedTable → Limit
    +
    Output bound
    At most 3 rows
    +
    Backend attempt
    None
    +
    +
    + Prism logical plan + Execution still absent +
    +
    +
  6. +
+ + +
+ +
+ Open the complete Chapter 2 checkpoint +
+ +```incan +--8<-- "examples/tutorial_book/src/chapter_02.incn" +``` + +
+
+ +Run it from `examples/tutorial_book`: + +```bash +incan run src/chapter_02.incn +``` + +The source file deliberately stops before `collect(...)`. The returned value is still a `LazyFrame[Order]`, so further relational operations or inspection can be composed before runtime binding and execution. + +
+ +**Expected output** + +```text +Chapter 2: planned ReadNamedTable -> Limit; execution is still deferred +``` + +There is no materialized row count or table preview. That absence is meaningful: no `DataFrame` exists yet. + +
+ +For the complete method surface, use [Dataset methods](../../reference/dataset_methods.md). The book stays with the verified operation set needed by this one flow. + +
+ +## Try it + +Reduce the final limit by one and rerun the checkpoint. Explain why this changes the plan but still gives you no local rows. Restore the original limit before Chapter 3. + +
+ +
+ +## Chapter complete + +You can identify the logical source, the appended carrier operations, and the missing execution call. You can also explain why a `LazyFrame` is not an in-memory `DataFrame`. + +
+ + diff --git a/docs/language/tutorials/book/03_inspect_plan.md b/docs/language/tutorials/book/03_inspect_plan.md new file mode 100644 index 00000000..92d5c560 --- /dev/null +++ b/docs/language/tutorials/book/03_inspect_plan.md @@ -0,0 +1,179 @@ +
+

THE INCQL BOOK

+ +# 3. Inspect the plan + +Use Prism-backed inspection to explain the lazy work before a backend executes it. +
+ +
+
+

Part II

+

Explain and execute it

+

Chapter 1 of 3

+
+ +
+ +
+ +## Goal + +Inspect the review plan and its lineage without executing DataFusion. Locate the plan target, output schema, Prism nodes, and lineage relationships that explain the deferred work. + +
+ +## Prism is the planning layer + +IncQL is the library and public data-logic surface. Prism is its internal logical planning engine; the two names are related but not interchangeable. Today, the `LazyFrame` path owns Prism state, which is why `inspect_plan(...)` and `inspect_lineage(...)` can read this plan locally. + +Inspection is read-only. It does not bind a physical backend, inspect a DataFusion physical plan, or turn local evidence into a Substrait payload. + +With this example, you are reading the same `ReadNamedTable → Limit` plan at two levels of detail. `inspect_plan(plan.clone())` produces the full `PlanInspection`, while `inspect_lineage(plan)` returns only its lineage graph; cloning preserves a carrier for the second call. Both functions read the Prism store and current tip locally, so their node and edge counts explain authored intent rather than backend execution. + +
+
+
+

Query under glass · local plan evidence

+

See what Prism knows—and where inspection stops

+
+

The checkpoint builds one deferred carrier, then reads two views of its local Prism state. Neither inspection call crosses into backend execution.

+
+ +
    +
  1. +
    + + 01 +
    Deferred review planDescribe a named read followed by a limit
    +
    +
    + +```incan +orders: LazyFrame[Order] = session.read_csv( + "tutorial_orders", + "orders.csv", +)? +plan = orders.limit(3) # (1)! +``` + +
      +
    1. Inspection target. limit(3) returns the deferred LazyFrame whose current Prism tip is the new Limit node. Assigning it to plan gives both inspection functions the same ReadNamedTable → Limit target; no backend work occurs here.

    2. +
    + +
    +
    +
    Owner
    Authoring surface
    +
    Artifact
    LazyFrame[Order]
    +
    Knowable now
    The authored ReadNamedTable → Limit intent
    +
    +
  2. + +
  3. +
    + + 02 +
    Prism inspectionRead structure and lineage from local plan state
    +
    +
    + +```incan +inspection = inspect_plan(plan.clone()) # (1)! +lineage = inspect_lineage(plan) # (2)! +``` + +
      +
    1. Full inspection. inspect_plan(...) consumes a LazyFrame value and returns the complete PlanInspection: plan targets, schemas, authored and rewritten nodes, lineage, requirements, artifacts, diagnostics, and unsupported-evidence markers. Passing plan.clone() preserves another carrier value for the next call.

    2. +
    3. Lineage-only view. inspect_lineage(...) consumes the remaining carrier and returns its LineageGraph directly. Use this narrower function when callers need relationships rather than the complete inspection record. Like inspect_plan(...), it reads local Prism state without executing the plan.

    4. +
    + +
    +
    +
    Owner
    Prism inspection
    +
    Artifact
    PlanInspection and lineage graph
    +
    Knowable now
    Plan target, output schema, authored nodes, and lineage relationships
    +
    +
  4. + +
  5. +
    + + 03 +
    Local evidence returnReport plan facts without materialising rows
    +
    +
    +
    +
    Plan target
    One local Prism identifier
    +
    Authored nodes
    2
    +
    Lineage edges
    8
    +
    +
    + PlanInspection + Lineage graph +
    +
    +
  6. +
+ + +
+ + + +`inspect_plan(...)` returns a structured `PlanInspection`. `inspect_lineage(...)` exposes the lineage graph from the same Prism-backed state when that graph is all a caller needs. The plan identifier printed by the checkpoint is a local evidence anchor, not a global catalog ID. + +
+ Open the complete Chapter 3 checkpoint +
+ +```incan +--8<-- "examples/tutorial_book/src/chapter_03.incn" +``` + +
+
+ +Run it from the included tutorial project: + +```bash +incan run src/chapter_03.incn +``` + +A successful run prints the local plan ID, two authored nodes, eight lineage edges, and confirmation that inspection completed without executing the plan. + +The exact records and current limits are documented in [Local inspection](../../reference/inspection.md). + +
+ +## Try it + +Print each lineage edge's relationship value. For every edge you see, point back to the carrier operation that caused it. Do not interpret a missing edge as proof that no relationship exists; unsupported or conservative evidence is represented separately. + +
+ +
+ +## Chapter complete + +You can describe what Prism knows before execution and distinguish a local `PlanInspection` from the backend work that has not happened yet. + +
+ + diff --git a/docs/language/tutorials/book/04_collect_observed.md b/docs/language/tutorials/book/04_collect_observed.md new file mode 100644 index 00000000..1cce39ae --- /dev/null +++ b/docs/language/tutorials/book/04_collect_observed.md @@ -0,0 +1,179 @@ +
+

THE INCQL BOOK

+ +# 4. Collect an observed result + +Let the Session bind and execute the plan, then retain evidence about that concrete attempt. +
+ +
+
+

Part II

+

Explain and execute it

+

Chapter 2 of 3

+
+ +
+ +
+ +## Goal + +Materialize the review as a `DataFrame[Order]` and inspect the accompanying `ExecutionObservation`. Relate that runtime attempt back to the plan inspected in Chapter 3. + +
+ +This chapter keeps inspection and execution visibly separate. `inspect_plan(plan.clone())` and `inspect_lineage(plan.clone())` receive clones so the original deferred `plan` remains available; neither call materializes its rows. Passing that plan to `collect_observed(...)` crosses the execution boundary: the `Session` validates and runs the plan through the selected backend, then materializes the result locally. + +With this example, you are collecting at most three orders through DataFusion while retaining evidence about that specific attempt. The returned `ObservedDataFrame[Order]` always carries an `observation`; its `data` is `Some(DataFrame[Order])` only when materialization succeeds, and its optional `error` carries the typed failure otherwise. The trace keeps those artifacts separate so a runtime record never has to pretend that row data exists. + +
+
+
+

Query under glass · one concrete attempt

+

Follow the change across its owners

+
+

collect_observed(...) crosses three boundaries. Each boundary answers a different technical question and produces a different artifact.

+
+ +
    +
  1. +
    + + 01 +
    Chapter changeDescribe and inspect the deferred work
    +
    +
    + +```incan +plan = orders.limit(3) +inspection = inspect_plan(plan.clone()) # (1)! +lineage = inspect_lineage(plan.clone()) +``` + +
      +
    1. Inspect without consuming the plan. clone() gives the local inspection functions the same deferred work while leaving plan available for the later execution call. Both inspect_plan(...) and inspect_lineage(...) describe local plan evidence; neither materializes rows.

    2. +
    + +
    +
    +
    Owner
    Authoring surface
    +
    Artifact
    Prism-backed deferred plan
    +
    Knowable now
    Plan identity, authored nodes, schema, and lineage
    +
    +
  2. + +
  3. +
    + + 02 +
    Session dispatchLower, bind, and submit one attempt
    +
    +
    + +```incan +observed = session.collect_observed(plan) # (1)! +``` + +
      +
    1. Cross the execution boundary and keep its receipt. Session validates, lowers, binds, and submits this plan as a concrete collection attempt. The return value is an ObservedDataFrame[Order]: it always carries the attempt's observation, while data and error distinguish successful materialization from a typed failure.

    2. +
    + +
    +
    +
    Owner
    Session
    +
    Artifact
    ObservedDataFrame[Order]
    +
    Knowable now
    A distinct attempt exists and is correlated with the plan
    +
    +
  4. + +
  5. +
    + + 03 +
    DataFusion attemptExecute and return materialisation evidence
    +
    +
    +
    +
    Backend
    datafusion
    +
    Resolved shape
    4 columns
    +
    Materialised rows
    3
    +
    +
    + ExecutionObservation + DataFrame[Order] +
    +
    +
  6. +
+ + +
+ + + +The returned `data` and `observation` are deliberately separate. On success, `data` exposes the supported materialisation surface—resolved columns, row count, and preview text. IncQL does not currently expose typed `Order` iteration here, so the book does not imply that it does. On failure, the attempt can still return typed diagnostics without pretending that materialised data exists. + +The observation has its own attempt identity while retaining the plan target. This allows multiple concrete attempts to remain correlated with one logical plan without collapsing the plan and its executions into the same event. + +
+ Open the complete Chapter 4 checkpoint +
+ +```incan +--8<-- "examples/tutorial_book/src/chapter_04.incn" +``` + +
+
+ +Run it from the included tutorial project: + +```bash +incan run src/chapter_04.incn +``` + +
+ +**Expected evidence** + +The checkpoint reports the local plan ID and lineage count, names `datafusion` as the backend, and prints four resolved columns, three materialised rows, and a compact preview. The final line confirms that DataFusion collection completed. + +
+ +For all observation fields and failure behavior, see [Execution context](../../reference/execution_context.md). + +
+ +## Try it + +Print the observation's `attempt_target` next to its `plan_target`. Explain why the two targets must remain distinct even when this chapter performs only one attempt. + +
+ +
+ +## Chapter complete + +You can distinguish the Prism plan, the DataFusion execution attempt, and the local `DataFrame`, and you can show how the attempt remains correlated with the plan. + +
+ + diff --git a/docs/language/tutorials/book/05_adapter_coverage.md b/docs/language/tutorials/book/05_adapter_coverage.md new file mode 100644 index 00000000..24bd9ebe --- /dev/null +++ b/docs/language/tutorials/book/05_adapter_coverage.md @@ -0,0 +1,183 @@ +
+

THE INCQL BOOK

+ +# 5. Check adapter coverage + +Ask a separate question: what capabilities is the selected adapter known to cover for this plan? +
+ +
+
+

Part II

+

Explain and execute it

+

Chapter 3 of 3

+
+ +
+ +
+ +## Goal + +Evaluate the adapter requirements inferred from the review plan and interpret every coverage state conservatively. + +
+ +## Execution success is not capability evidence + +Chapter 4 showed that one concrete plan attempt ran. `check_plan_coverage(...)` answers an independent question: for requirements visible in the inspected plan, what does IncQL know about the selected adapter's capability or guarantee? In this checkpoint, DataFusion reports `null_semantics` as `covered` and `lineage_preservation` as `uncovered` even though the three-row collection succeeded. + +With this example, you are reading `record.requirement.capability.value()` and `record.state.value()` only to display the two enum values; calling `.value()` does not change or enforce either classification. `check_plan_coverage(plan)` is the convenient inspect-and-check form. Use `check_inspection_coverage(inspection)` when you already retain a `PlanInspection`, or `check_coverage(requirements)` when the caller supplies explicit requirements that plan inspection cannot infer. + +
+
+
+

Two questions · two independent answers

+

Keep execution and coverage separate

+
+

The checkpoint first records one concrete execution attempt, then explicitly checks the requirements visible in the plan. A successful attempt cannot upgrade a conservative coverage state.

+
+ +
    +
  1. +
    + + 01 +
    Concrete attemptExecute the review plan once
    +
    +
    + +```incan +observed = session.collect_observed(plan.clone()) # (1)! +``` + +
      +
    1. Preserve the plan for a second question. This clone is the input to one concrete collection attempt. The original plan remains available because the next stage asks about adapter coverage, which is evidence about known capability rather than proof inferred from execution success.

    2. +
    + +
    +
    +
    Owner
    Session and selected backend
    +
    Artifact
    ObservedDataFrame[Order]
    +
    Knowable now
    This attempt ran on datafusion and materialised three rows
    +
    +
  2. + +
  3. +
    + + 02 +
    Explicit coverage checkClassify requirements inferred from the plan
    +
    +
    + +```incan +coverage = session.check_plan_coverage(plan) # (1)! +for record in coverage: + capability = record.requirement.capability.value() + state = record.state.value() + println(f"- {capability}: {state}") # (2)! +``` + +
      +
    1. Inspect, then classify the inferred requirements. check_plan_coverage(plan) is the convenient form when you still have the LazyFrame. If you already retain a PlanInspection, use check_inspection_coverage(inspection); use check_coverage(requirements) when the caller supplies explicit requirements.

    2. +
    3. Read names, not an enforcement result. capability.value() names the requirement family and state.value() names the adapter's conservative classification. These accessors only render enum values; uncovered and unknown do not become acceptable merely because this example's collection succeeded.

    4. +
    + +
    +
    +
    Owner
    Session coverage evaluation
    +
    Artifact
    list[AdapterCoverageRecord]
    +
    Knowable now
    The selected adapter's classification for each plan-inferred requirement
    +
    +
  4. + +
  5. +
    + + 03 +
    Conservative resultRead every state literally
    +
    +
    +
    +
    Execution
    3 rows materialised
    +
    Null semantics
    covered
    +
    Lineage preservation
    uncovered
    +
    +
    + Observed result + Coverage records +
    +
    +
  6. +
+ + +
+ + + +Coverage states are deliberately conservative: + +- `Covered` means the adapter is known to cover that requirement family. +- `PartiallyCovered` means support depends on the specific expression or plan shape. +- `Uncovered` means the adapter is known not to provide the guarantee. +- `Unknown` means IncQL has no classification; it is not a soft success. + +The current DataFusion adapter provides the classifications printed by the checkpoint. They are not a hidden policy decision and do not replace the execution observation. + +
+ Open the complete Chapter 5 checkpoint +
+ +```incan +--8<-- "examples/tutorial_book/src/chapter_05.incn" +``` + +
+
+ +Run it from the included tutorial project: + +```bash +incan run src/chapter_05.incn +``` + +A successful run reports the concrete DataFusion result separately from two coverage records: `null_semantics: covered` and `lineage_preservation: uncovered`. + +Read the complete capability vocabulary and current DataFusion matrix in [Execution context](../../reference/execution_context.md#adapter-coverage). + +
+ +## Try it + +Add a `match` over the coverage states and print a separate message for `Unknown`. Make that message say “not classified,” not “supported,” then restore the checkpoint. + +
+ +
+ +## Chapter complete + +You can explain why a successful query may still have an uncovered or unknown requirement, and why only caller-owned policy can decide whether that evidence is acceptable. + +
+ + diff --git a/docs/language/tutorials/book/06_quality_observations.md b/docs/language/tutorials/book/06_quality_observations.md new file mode 100644 index 00000000..41b5dd1a --- /dev/null +++ b/docs/language/tutorials/book/06_quality_observations.md @@ -0,0 +1,177 @@ +
+

THE INCQL BOOK

+ +# 6. Observe data quality + +Evaluate explicit assertions and keep a failed observation separate from execution errors and policy enforcement. +
+ +
+
+

Part III

+

Decide what happens next

+

Chapter 1 of 2

+
+ +
+ +
+ +## Goal + +Evaluate one row-count assertion that passes and another that deliberately fails. Read their metrics and statuses without treating the failed check as an automatic exception or row filter. + +
+ +## Assertions describe; Session observes + +A quality assertion records the condition a caller wants evaluated. Here, `row_count(min_count=Some(1), max_count=Some(3))` describes an inclusive range that the three-row result satisfies, while `row_count(min_count=Some(4))` deliberately asks for more rows than the same plan produces. `session.observe_quality(...)` executes the work needed to evaluate each condition and returns one `QualityObservation` per assertion. + +With this example, you are performing three collections: the baseline `collect_observed(...)` call, one fresh collection for the passing quality check, and another for the failing check. `caller_accepts(...)` is local tutorial policy that interprets those records; it is not IncQL enforcement. The helper surface is broader than this row-count example: `null_rate(...)`, `unique(...)`, and `group_row_count(...)` use the single-relation API, while cross-relation checks use `observe_quality_pair(...)`. + +
+
+
+

Three collections · two quality answers

+

Make every observation attempt visible

+
+

The checkpoint has already called collect_observed(...) once. Each observe_quality(...) call executes and collects the plan again to evaluate its own assertion.

+
+ +
    +
  1. +
    + + 01 +
    Passing observationEvaluate the accepted range in a fresh collection
    +
    +
    + +```incan +passing = session.observe_quality( + plan.clone(), # (1)! + [row_count(min_count=Some(1), max_count=Some(3))], # (2)! +) +``` + +
      +
    1. Evaluate this deferred carrier. The first argument is the LazyFrame[Order] whose rows the assertion observes. The clone leaves the original plan available for the deliberately failing check below; it does not reuse previously materialized rows.

    2. +
    3. Supply an assertion list with explicit optional bounds. observe_quality(...) returns one QualityObservation for each item in this list. Some(1) and Some(3) set inclusive minimum and maximum row counts; use None for a bound you do not want to constrain.

    4. +
    + +
    +
    +
    Owner
    Session quality evaluation
    +
    Artifact
    One QualityObservation and its execution references
    +
    Knowable now
    The freshly collected row count satisfies the inclusive range
    +
    +
  2. + +
  3. +
    + + 02 +
    Deliberate failing observationEvaluate a stricter assertion in another collection
    +
    +
    + +```incan +failing = session.observe_quality( # (1)! + plan, + [row_count(min_count=Some(4))], +) +``` + +
      +
    1. Start another collection. This call does not inspect the baseline result or the earlier passing observation. Session collects the plan again, evaluates a minimum of four with no maximum bound, and records Failed because the fresh result has three rows.

    2. +
    + +
    +
    +
    Owner
    Session quality evaluation
    +
    Artifact
    A separate QualityObservation and execution attempt
    +
    Knowable now
    Three rows do not satisfy a minimum of four, so the status is Failed
    +
    +
  4. + +
  5. +
    + + 03 +
    Tutorial policyInterpret the evidence in caller-owned code
    +
    +
    +
    +
    Owner
    caller_accepts(...)
    +
    Passing list
    true
    +
    Failing list
    false
    +
    +
    + QualityObservation × 2 + Tutorial policy result +
    +
    +
  6. +
+ + +
+ + + +The assertion mode can express intended handling, but IncQL does not silently quarantine rows, stop a pipeline, or approve an output. A `Failed` observation means the predicate was evaluated and returned false; it is evidence, not an exception or automatic gate. `Errored` means the relational work needed by the check could not be executed. + +
+ Open the complete Chapter 6 checkpoint +
+ +```incan +--8<-- "examples/tutorial_book/src/chapter_06.incn" +``` + +
+
+ +Run it: + +```bash +incan run src/chapter_06.incn +``` + +The run reports `passed` for the inclusive range, `failed` for the deliberate minimum of four, and the corresponding `true` and `false` results from the tutorial's `caller_accepts(...)` function. + +For the assertion helpers, status vocabulary, and cross-relation path, see [Quality](../../reference/quality.md). + +
+ +## Try it + +Print the metrics and execution-observation references carried by both records. Then explain why the final chapter still needs caller-owned decision logic if a failed observation should prevent a write. + +
+ +
+ +## Chapter complete + +You can distinguish `Passed`, `Failed`, and `Errored`, read the reported metrics, and state clearly that a quality observation is evidence rather than enforcement. + +
+ + diff --git a/docs/language/tutorials/book/07_governed_write.md b/docs/language/tutorials/book/07_governed_write.md new file mode 100644 index 00000000..92331258 --- /dev/null +++ b/docs/language/tutorials/book/07_governed_write.md @@ -0,0 +1,220 @@ +
+

THE INCQL BOOK

+ +# 7. Make the write decision + +Turn evidence into an explicit application decision, then retain an observation about the write attempt. +
+ +
+
+

Part III

+

Decide what happens next

+

Chapter 2 of 2

+
+ +
+ +
+ +## Goal + +Read the evidence produced by the earlier stages, decide in ordinary Incan code whether the review may be written, and use `write_observed(...)` to retain evidence about the concrete sink attempt. + +
+ +## The decision belongs to the caller + +IncQL exposes distinct facts: + +- Prism inspection explains the deferred plan. +- Adapter coverage records what the selected adapter is known to cover. +- Quality observations report whether explicit data checks passed. +- Execution observations report what happened during a concrete attempt. + +None of those records silently defines your organization's acceptance rule. This checkpoint makes the rule visible in a normal function, so readers can see exactly which evidence permits or prevents the write. Its `required_quality` list contains the inclusive one-to-three-row check; the deliberately failing `deliberate_probe` is printed for comparison but is not passed into the gate. + +With this example, a rejected `required_quality` list returns `Ok(None)` deliberately: the application completed without asking for a write, rather than suffering an execution failure. On the accepted path, `csv_sink(output_uri)` constructs the typed sink descriptor and `write_observed(...)` starts a new execution of the plan for that sink attempt. The returned `ObservedWrite` retains an execution observation and an optional error, but it does not currently bundle the sink URI, row count, quality observations, or coverage records. + +
+
+
+

One narrow gate · one new write attempt

+

Show exactly what permits the write

+
+

The checkpoint prints coverage and a deliberate strict probe, but its gate reads only required_quality. If that gate passes, write_observed(...) executes the plan again.

+
+ +
    +
  1. +
    + + 01 +
    Quality inputsProduce the required check and an explanatory strict probe
    +
    +
    + +```incan +required_quality = session.observe_quality( # (1)! + plan.clone(), + [row_count(min_count=Some(1), max_count=Some(3))], +) +deliberate_probe = session.observe_quality( + plan.clone(), + [row_count(min_count=Some(4))], +) +``` + +
      +
    1. Separate required evidence from an explanatory probe. required_quality contains the check this tutorial will pass to its gate. deliberate_probe performs another observation with a stricter minimum so you can compare a failed record, but the checkpoint only prints it; that list does not decide whether writing is allowed.

    2. +
    + +
    +
    +
    Owner
    Session quality evaluation
    +
    Artifact
    Passing required evidence and a failing strict probe
    +
    Knowable now
    Each quality call collected the plan again; neither result enforces a write rule
    +
    +
  2. + +
  3. +
    + + 02 +
    Caller gateUse only the required quality list
    +
    +
    + +```incan +if not caller_accepts(required_quality): # (1)! + println("Caller decision: do not write") + return Ok(None) +``` + +
      +
    1. Make policy ordinary and explicit. caller_accepts(...) is tutorial application code, not an IncQL enforcement hook. A rejected list returns Ok(None) because the application deliberately chose not to request a write; that outcome is distinct from an execution error.

    2. +
    + +
    +
    +
    Owner
    Tutorial application code
    +
    Artifact
    An explicit control-flow decision
    +
    Knowable now
    Coverage and the strict probe are printed for inspection but do not participate in this gate
    +
    +
  4. + +
  5. +
    + + 03 +
    Observed writeExecute the accepted plan again and attempt the sink side effect
    +
    +
    + +```incan +output_uri = "target/tutorial-orders.csv" +written = session.write_observed( + plan, + csv_sink(output_uri), +) # (1)! +match written.error: # (2)! + Some(error) => return Err(error) + None => + println("Caller decision: write the plan accepted by the required policy") + println(f"wrote: {output_uri}") +``` + +
      +
    1. Describe the sink, then start a new attempt. csv_sink(output_uri) builds a typed CSV SinkTarget; parquet_sink(uri) is the corresponding Parquet alternative. write_observed(...) executes the deferred plan again for this sink attempt and returns an ObservedWrite alongside the file side effect.

    2. +
    3. Handle the optional typed failure. written.error is Some(SessionError) when the write attempt fails and None when it succeeds. The receipt also retains an execution observation, but it does not currently bundle the URI, row count, quality records, or coverage records.

    4. +
    + +
    +
    +
    +
    Owner
    Session and DataFusion sink path
    +
    Artifact
    ObservedWrite plus the CSV side effect
    +
    Knowable now
    This write attempt returned no typed error
    +
    +
    + tutorial-orders.csv + ObservedWrite +
    +
    +
  6. +
+ + +
+ + + +`write_observed(...)` owns the sink attempt through the Session and returns an observation plus an optional error. The CSV file is the side effect. The `ObservedWrite` receipt does not currently carry the sink URI, row count, quality observations, or coverage records, and its observation uses a fallback Substrait-root target rather than the earlier Prism plan target. It therefore must not be presented as an uninterrupted correlation edge or a complete governed-output bundle. + +
+ Open the complete Chapter 7 checkpoint +
+ +```incan +--8<-- "examples/tutorial_book/src/chapter_07.incn" +``` + +
+
+ +Run it: + +```bash +incan run src/chapter_07.incn +``` + +The run reports two coverage records—`null_semantics: covered` and `lineage_preservation: uncovered`—but does not gate on them. It reports `passed` for the required quality check and `failed` for the strict probe, then writes `target/tutorial-orders.csv` because the required list passed. Inspect that CSV as an output artifact, not as typed `Order` iteration. + +## Read the completed system path + +You have now followed one real `LazyFrame` through the current IncQL path: + +1. The author supplied a logical CSV source and intended row model. +2. IncQL stored deferred relational intent in a Prism-backed plan. +3. Local inspection exposed schema, plan, and lineage evidence before execution. +4. Session lowered and bound the plan, then dispatched DataFusion. +5. Execution, coverage, and quality records answered different questions about the result. +6. Caller-owned code made the acceptance decision and asked Session to write. + +Substrait remains the portable logical-plan boundary in that path. The earlier collection observation retains its plan correlation, and the quality observations reference their own execution attempts. The final write receipt currently starts from a fallback Substrait-root target, so the book does not draw a continuous evidence edge from the earlier Prism plan through that write. + +
+ +## Try it + +Pass the coverage records into a new caller-owned gate so an `Unknown` state also blocks writing. Print the reason for the decision, run both branches, and restore the checkpoint. Notice that the unmodified chapter does not gate on coverage and that you changed application policy without changing the evidence record types. + +
+ +
+ +## Core path complete + +You can build, inspect, execute, evaluate, and write one IncQL plan while explaining which layer owns each decision. Continue with [Part IV](index.md#part-query-blocks) to author checked SQL-familiar query blocks without method chains, [Guides](../../how-to/README.md) for task-specific transformations, [Reference](../../reference/README.md) for exact contracts, or [Architecture](../../../architecture.md) for the full system story. + +
+ + diff --git a/docs/language/tutorials/book/08_first_query_block.md b/docs/language/tutorials/book/08_first_query_block.md new file mode 100644 index 00000000..fc35c4ea --- /dev/null +++ b/docs/language/tutorials/book/08_first_query_block.md @@ -0,0 +1,211 @@ +
+

THE INCQL BOOK

+ +# 8. Write your first query block + +Shape a typed relation with SQL-familiar clauses, then cross the same explicit Session execution boundary. +
+ +
+
+

Part IV

+

Query blocks

+

Chapter 1 of 2

+
+ +
+ +
+ +## Goal + +Activate IncQL's query vocabulary, begin with a typed CSV-backed `LazyFrame[Order]`, express filtering, projection, ordering, and a limit in one `query { ... }` expression, then collect the resulting plan. + +
+ +## Change the authoring surface, not the system boundary + +A query block is an IncQL expression embedded in ordinary Incan code. It is not a SQL string and it does not open a file, choose an engine, or execute itself. `import pub::incql` activates the dependency-owned vocabulary, while the `Session` still owns source registration and execution. This means the source setup from the core Book remains valid: `read_csv(...)` registers a logical name and returns deferred work whose intended row shape is described by `Order`. + +The block then reads top to bottom. `FROM orders` establishes the current relation. `WHERE .status == "paid"` adds a predicate against that relation. `SELECT` publishes a new three-column query schema through explicit aliases. `ORDER BY desc(.amount)` reads the projected `amount` column and requests descending order; IncQL uses the `desc(...)` helper rather than postfix SQL such as `.amount DESC`. Finally, `LIMIT 10` caps the ordered result. + +With this example, you are asking for paid orders only, retaining `order_id`, `customer_id`, and `amount`, ordering the projected rows from highest to lowest amount, and returning no more than ten. Because the `FROM` value is a `LazyFrame`, the query expression also returns a `LazyFrame`. Its clauses become deferred carrier operations—`ReadNamedTable → Filter → SelectProject → OrderBy → Limit`—before `session.collect(...)` crosses the runtime boundary. + +
+
+
+

One typed source · one checked clause flow

+

Follow the query from activation to rows

+
+

The authoring syntax changes, but the ownership story does not: IncQL builds deferred intent and Session performs the concrete DataFusion collection.

+
+ +
    +
  1. +
    + + 01 +
    Activate and registerMake the vocabulary available, then establish a typed logical source
    +
    +
    + +```incan +import pub::incql # (1)! + +orders: LazyFrame[Order] = session.read_csv( + "tutorial_orders_query", + "orders.csv", +)? +``` + +
      +
    1. Activate the dependency-owned vocabulary. The plain import pub::incql makes query and its scoped helpers available to this compilation unit. The separate from pub::incql import ... statement in the complete source imports the ordinary types and functions that the surrounding Incan code names directly.

    2. +
    + +
    +
    +
    Owner
    Session source registration
    +
    Artifact
    LazyFrame[Order] rooted at ReadNamedTable
    +
    Knowable now
    The logical source identity and intended row model; no rows have been collected
    +
    +
  2. + +
  3. +
    + + 02 +
    Author the queryPublish a projected schema while keeping the result deferred
    +
    +
    + +```incan +paid_orders: LazyFrame[PaidOrderReview] = query { # (1)! + FROM orders + WHERE .status == "paid" + SELECT + .order_id as order_id, + .customer_id as customer_id, + .amount as amount, + ORDER BY desc(.amount) # (2)! + LIMIT 10 +} +``` + +
      +
    1. Describe the intended output without pretending validation is stronger than it is. A current query block requires FROM and SELECT. Because orders is lazy, the expression returns deferred work. LazyFrame[PaidOrderReview] documents the intended projected row model; the current implementation checks query-schema evolution and selected aliases, while complete field-for-field and type compatibility validation against that annotation remains follow-up work.

    2. +
    3. Resolve fields against the clause's current schema. Leading-dot references name fields visible at that point in the query. The SELECT aliases publish order_id, customer_id, and amount for later clauses, so .amount in ORDER BY refers to the projected column. Use asc(...) for the opposite direction; postfix ASC/DESC tokens are not query-block syntax.

    4. +
    + +
    +
    +
    Owner
    IncQL query vocabulary and carrier planning
    +
    Artifact
    LazyFrame[PaidOrderReview]
    +
    Knowable now
    The filter, projected schema, ordering, and upper row bound; execution is still absent
    +
    +
  4. + +
  5. +
    + + 03 +
    Collect the resultBind and execute the deferred query through Session
    +
    +
    + +```incan +result = session.collect(paid_orders)? # (1)! +println(f"columns: {result.resolved_columns():?}") +println(f"rows: {result.row_count()}") +println(result.preview_text()) +``` + +
      +
    1. Cross the execution boundary deliberately. collect(...) submits the deferred query through the selected backend and returns a materialized DataFrame[PaidOrderReview] on success. Use collect_observed(...) instead when the caller also needs the structured success-or-failure observation introduced earlier in the Book.

    2. +
    + +
    +
    +
    +
    Backend
    datafusion
    +
    Columns
    order_id, customer_id, amount
    +
    Rows
    2 paid orders, highest amount first
    +
    +
    + Query plan + Materialized result +
    +
    +
  6. +
+ + +
+ + + +The query block and an equivalent method chain converge on the same relational carrier semantics. Here, however, every relation-shaping step appears in one clause-oriented expression. That makes the current query schema especially visible: the source model supplies the fields before `SELECT`, and the aliases published by `SELECT` supply the fields seen by `ORDER BY`. + +The intended output type still deserves precise language. `PaidOrderReview` is valuable documentation and gives the surrounding Incan program a typed carrier contract, but this release does not yet prove complete compatibility between every selected field/type and that annotated model. The runnable checkpoint verifies the actual resolved column names and materialized rows instead of presenting the annotation alone as physical-schema evidence. + +
+ Open the complete Chapter 8 checkpoint +
+ +```incan +--8<-- "examples/tutorial_book/src/chapter_08.incn" +``` + +
+
+ +Run it from the included tutorial project: + +```bash +incan run src/chapter_08.incn +``` + +
+ +**Expected evidence** + +The checkpoint reports the resolved columns `order_id`, `customer_id`, and `amount`, then reports two rows. Its preview places order `1001` before order `1003`, demonstrating that the paid-row filter and descending order both reached execution. The final line confirms that Chapter 8 completed. + +
+ +For the complete clause inventory, expression operators, alias rules, and current limitations, use [Query blocks](../../reference/query_blocks.md). + +
+ +## Try it + +Change `desc(.amount)` to `asc(.amount)` and reduce the limit to `1`. Run the checkpoint and explain why order `1003` is now the only preview row. Restore the original ordering and limit before continuing. + +
+ +
+ +## Chapter complete + +You can activate IncQL's query vocabulary, explain how leading-dot fields and aliases follow the current query schema, distinguish deferred query authoring from Session collection, and state the present output-model validation limit honestly. + +
+ + diff --git a/docs/language/tutorials/book/09_summarize_query.md b/docs/language/tutorials/book/09_summarize_query.md new file mode 100644 index 00000000..c0be5757 --- /dev/null +++ b/docs/language/tutorials/book/09_summarize_query.md @@ -0,0 +1,194 @@ +
+

THE INCQL BOOK

+ +# 9. Summarize and order results + +Turn order rows into a grouped, typed summary with IncQL's SQL-familiar query clauses, then collect the deferred result. +
+ +
+
+

Part IV

+

Query blocks

+

Chapter 2 of 2

+
+ +
+ +
+ +## Goal + +Group the tutorial orders by status, calculate a row count and amount total for every group, order those summaries by the projected total, and materialize the result through the Session. + +
+ +## Turn rows into a summary relation + +The first query-block chapter filtered and projected individual rows. This chapter changes the result's grain: after `GROUP BY .status`, one output row represents one distinct status rather than one input order. Every non-aggregate field selected by the query must therefore be compatible with the available group keys, while `count()` and `sum(.amount)` produce one value for the whole group. + +The aggregate helpers are ordinary imported IncQL functions. The checkpoint imports `count`, `sum`, and `desc` from `pub::incql`; the query vocabulary decides where those expressions are valid and how they contribute to the relational plan. The aliases `order_count` and `total_amount` become columns in the projected schema, so the later `ORDER BY` can refer to `.total_amount` directly. + +This is intentionally SQL-familiar syntax, not an embedded SQL dialect. An IncQL query block requires an explicit `SELECT`; predicates use Incan equality `==`, not SQL assignment-like `=`; descending order is expressed as `desc(.total_amount)`, not postfix `DESC`. IncQL also has no `HAVING` keyword. To filter grouped output, place a second `WHERE` after `SELECT`, where the projected aliases are in scope. + +With this example, you are describing a deferred `LazyFrame[StatusSummary]` with three planned output columns—`status`, `order_count`, and `total_amount`—and then asking the Session to collect it. The `StatusSummary` annotation records the output model you intend to carry. IncQL checks the evolving query schema and the selected aliases, but full field-by-field and type-by-type compatibility validation against that annotated output model remains follow-up work; the annotation alone is not proof of complete model conformance. + +
+
+
+

Grouped intent · projected schema · one collection

+

Follow the summary from clauses to rows

+
+

The query block first produces deferred relational work. Only session.collect(...) crosses the execution boundary and materializes the three grouped rows.

+
+ +
    +
  1. +
    + + 01 +
    Grouped queryDescribe the grain, measures, and output order
    +
    +
    + +```incan +summaries: LazyFrame[StatusSummary] = query { + FROM orders + GROUP BY .status # (1)! + SELECT # (2)! + .status as status, + count() as order_count, # (3)! + sum(.amount) as total_amount, + ORDER BY desc(.total_amount) # (4)! +} +``` + +
      +
    1. Choose the result grain. GROUP BY .status partitions the current orders relation by its status field. After this clause, a selected non-aggregate expression must be compatible with the group keys; this is why the query selects .status alongside aggregate measures.

    2. +
    3. Publish an explicit output schema. SELECT is required rather than implied. Its aliases name the three columns available to following clauses and to the returned carrier: status, order_count, and total_amount.

    4. +
    5. Compute measures with imported functions. count() counts rows in each status group; sum(.amount) totals that group's amount values. Both helpers must be imported from pub::incql. Their aliases make the measures addressable after the projection boundary.

    6. +
    7. Order the projected relation. .total_amount resolves to the alias introduced by the preceding SELECT. desc(...) constructs descending ordering intent; postfix SQL syntax such as .total_amount DESC is not part of the query-block grammar.

    8. +
    + +
    +
    +
    Owner
    IncQL query vocabulary and relational carrier
    +
    Artifact
    A deferred LazyFrame[StatusSummary]
    +
    Knowable now
    Group key, projected aliases, aggregate intent, and descending order
    +
    +
  2. + +
  3. +
    + + 02 +
    Collection boundarySubmit the deferred summary through the Session
    +
    +
    + +```incan +result = session.collect(summaries)? +``` + +
    +
    +
    Owner
    Session and the selected DataFusion adapter
    +
    Artifact
    A materialized grouped result
    +
    Knowable now
    The query executed successfully and returned three status groups
    +
    +
  4. + +
  5. +
    + + 03 +
    Materialized summaryRead the projected columns and ordered group values
    +
    +
    +
    +
    Columns
    status, order_count, total_amount
    +
    Rows
    3 grouped statuses
    +
    First group
    paid with total 209.75
    +
    +
    + Grouped result + Collected through Session +
    +
    +
  6. +
+ + +
+ +
+ +**Result you should see** + +The preview formatting is backend-facing, but its ordered values should describe these three rows: + +```text +status order_count total_amount +paid 2 209.75 +pending 1 64.00 +cancelled 1 42.75 +``` + +
+ +The order is determined by the aggregate alias, not by the alphabetical spelling of `status`. Because `ORDER BY` appears after `SELECT`, it sees the projected schema and can resolve `.total_amount`; it does not reach backward into an unrelated outer variable. + +
+ Open the complete Chapter 9 checkpoint +
+ +```incan +--8<-- "examples/tutorial_book/src/chapter_09.incn" +``` + +
+
+ +Run it from `examples/tutorial_book`: + +```bash +incan run src/chapter_09.incn +``` + +The checkpoint prints the resolved output columns, a row count of three, and the preview. That collection is the only execution call in the chapter; building the query block itself only appends checked relational intent to the lazy carrier. + +For the complete clause inventory, expression rules, and resolution contract, use the [Query blocks reference](../../reference/query_blocks.md). The tutorial keeps this chapter focused on one grouped path rather than duplicating that catalog. + +
+ +## Try it + +After `SELECT`, add `WHERE .total_amount > 50` and run the checkpoint again. This is IncQL's post-projection filter: it uses the projected alias where SQL might use `HAVING`, leaving the `paid` and `pending` groups. Restore the checkpoint before continuing. + +
+ +
+ +## Part complete + +You can explain how a query block changes row grain, why grouped fields and aggregate measures obey different selection rules, how projected aliases flow into later clauses, and when deferred query intent becomes a materialized result. + +
+ + diff --git a/docs/language/tutorials/book/index.md b/docs/language/tutorials/book/index.md new file mode 100644 index 00000000..05dde997 --- /dev/null +++ b/docs/language/tutorials/book/index.md @@ -0,0 +1,208 @@ +
+

GUIDED TUTORIAL

+ +# The IncQL Book + +Follow one small order dataset from typed intent to an observed, deliberately governed result. The book is organised into focused Parts, so later topics can deepen or branch from the core path without turning it into one endless sequence. +
+ +
+
+
+

One project · expandable learning paths

+

Choose the part of the system you want to understand

+
+

Open one Part at a time. Every chapter names its owner, outcome, and primary artifact.

+
+ +
+
+ + Part I + Model the work + Typed source → deferred plan + 2 chapters + + +
+ +
+ + + Part II + Explain and execute it + Inspection → observed attempt → coverage + 3 chapters + + +
+ +
+ + Part III + Decide what happens next + Quality evidence → caller decision + 2 chapters + + +
+ +
+ + Part IV + Query blocks + SQL-familiar clauses → typed results + 2 chapters + + +
+
+ + +
+ +## What you will build + +The project reads an included CSV through a `Session`, carries an intended `Order` row model, and builds a deferred `LazyFrame`. You will inspect that plan through Prism before asking the current DataFusion adapter to execute it. The later core chapters separate adapter coverage from data-quality observations, then make the write decision explicitly in application code. + +Part IV is a direct-entry route for readers who prefer SQL-familiar clauses. It reuses the same project and typed source, but authors relational work with checked `query { ... }` blocks instead of method chains. You can begin there without completing Parts I–III first. + +The core path ends with a deliberately modest result: a bounded three-row order review written to CSV. The query path collects a filtered row set and a grouped summary instead. Across both routes, the important part is that you can explain what IncQL knew before execution, what the backend reported afterwards, and which decisions still belonged to your code. + +## Before you begin + +You need: + +- an Incan toolchain compatible with this checkout +- a working Rust and CMake build toolchain for the local IncQL dependency; its protobuf sources are vendored, so a system `protoc` is not required +- a local IncQL checkout; the tutorial project uses a path dependency and does not claim a registry release + +From the IncQL repository root, prepare the included project once: + +```bash +cd examples/tutorial_book +incan lock +``` + +Each chapter gives one `incan run` command from that directory. Chapters 1–7 are cumulative checkpoints for the core system path. Chapters 8–9 are independent query-block checkpoints that share the same fixture, so you can enter Part IV directly. + +## How this book fits the site + +This edition follows one bounded CSV-to-write path and one direct-entry query-language path. Later Parts can deepen the core language or introduce a separate execution path with its own prerequisites. Use [Guides](../../how-to/README.md) for independent tasks, [Reference](../../reference/README.md) for exact signatures and record fields, and [Architecture](../../../architecture.md) for the complete boundary and ownership story. + + diff --git a/docs/project.md b/docs/project.md new file mode 100644 index 00000000..3f580047 --- /dev/null +++ b/docs/project.md @@ -0,0 +1,29 @@ +# Project + +Project pages explain how the IncQL repository is organized, maintained, documented, and released. + +## Find your way around + +- [Docs map][docs-map] gives a compact inventory of the documentation set. +- [Contributor architecture][contributor-architecture] maps the conceptual system boundaries to source modules, build commands, and the Incan compiler seam. +- [Release notes][release-notes] summarize shipped user-visible changes. + +## Contribute + +- [Writing RFCs][writing-rfcs] explains the design-record workflow and formatting contract. +- [Prismplane docs theme][theme] documents the site's visual and authoring conventions. +- The repository-level [CONTRIBUTING guide][contributing] covers setup, testing, pull requests, and release hygiene. + +For normative design work, continue to [Design records][design-records]. For API usage, use [Learn][learn], [Guides][guides], or [Reference][reference]. + + +[contributing]: https://github.com/encero-systems/IncQL/blob/main/CONTRIBUTING.md +[contributor-architecture]: contributing/architecture.md +[design-records]: design_records.md +[docs-map]: docs_map.md +[guides]: language/how-to/README.md +[learn]: language/README.md +[reference]: language/reference/README.md +[release-notes]: release_notes/v0_1.md +[theme]: contributing/prismplane_docs_theme.md +[writing-rfcs]: contributing/writing_rfcs.md diff --git a/docs/release_notes/v0_1.md b/docs/release_notes/v0_1.md index 0bfdf921..da499572 100644 --- a/docs/release_notes/v0_1.md +++ b/docs/release_notes/v0_1.md @@ -42,4 +42,4 @@ Pipe-forward (`|>`) is specified in RFC 005 but **out of scope** for v0.1. ## Documentation -- Architecture and RFC series: [docs/README.md](../README.md), [IncQL architecture](../architecture.md), [RFCs](../rfcs/README.md). +- Architecture and RFC series: [docs/index.md](../index.md), [IncQL architecture](../architecture.md), [RFCs](../rfcs/README.md). diff --git a/docs/rfcs/README.md b/docs/rfcs/README.md index ce62ec55..c7daaefa 100644 --- a/docs/rfcs/README.md +++ b/docs/rfcs/README.md @@ -1,120 +1,3277 @@ +
+
# IncQL RFCs -IncQL uses its **own** RFC series (starting at 000), independent of the [Incan language RFCs][incan-rfcs]. - -**New RFC:** copy [TEMPLATE.md], name the file `NNN_short_slug.md`, pick the next number from the table (or from open issues), and open a PR. Section order and header fields follow that template. For workflow and conventions, see [Writing IncQL RFCs]. - -| RFC | Status | Title | | -| -------------- | ----------- | ------------------------------------------------------------------------------------------------- | --- | -| [000][rfc-000] | Planned | Language specification — core model, naming, schema shapes, layer boundaries | | -| [001][rfc-001] | In Progress | Dataset types and carriers (`DataSet[T]`, `BoundedDataSet[T]`, `UnboundedDataSet[T]`) | | -| [002][rfc-002] | In Progress | Apache Substrait — `Rel`-level contract, mapping catalog, binding boundaries | | -| [003][rfc-003] | Implemented | `query {}` blocks — grammar, typing, Substrait lowering | | -| [004][rfc-004] | In Progress | Execution context — session, DataFusion, read/transform/write | | -| [005][rfc-005] | Blocked | Pipe-forward relational syntax (`\|>`) — optional surface | | -| [006][rfc-006] | Blocked | Promote unnest/explode to core Substrait lowering — blocked on upstream Substrait standardization | | -| [007][rfc-007] | In Progress | Prism logical planning and optimization engine | | -| [008][rfc-008] | Planned | Optimizer boundary, statistics, cost-based optimization, and adaptive execution | | -| [009][rfc-009] | Draft | Session format handler registry (plugin-style source format registration) | | -| [010][rfc-010] | Draft | CSV dialect and interpretation contract | | -| [011][rfc-011] | Draft | Source discovery and parse-unit expansion | | -| [012][rfc-012] | Implemented | Unified scalar expression surface | | -| [013][rfc-013] | Implemented | Function catalog program | | -| [014][rfc-014] | Implemented | Function registry and catalog governance | | -| [015][rfc-015] | Implemented | Core scalar functions and operators | | -| [016][rfc-016] | Implemented | Core aggregate functions | | -| [017][rfc-017] | Implemented | Aggregate modifiers | | -| [018][rfc-018] | Implemented | Common scalar function catalog | | -| [019][rfc-019] | Implemented | Window functions | | -| [020][rfc-020] | Implemented | Nested data functions | | -| [021][rfc-021] | Implemented | Generator and table-valued functions | | -| [022][rfc-022] | Implemented | Semi-structured and format functions | | -| [023][rfc-023] | Implemented | Approximate and sketch functions | | -| [024][rfc-024] | Implemented | Function extension policy | | -| [025][rfc-025] | Implemented | Typed sketch logical values | | -| [026][rfc-026] | Implemented | Semi-structured variant logical values | | -| [027][rfc-027] | In Progress | Relational evidence program | | -| [028][rfc-028] | In Progress | Semantic identity and target model | | -| [029][rfc-029] | In Progress | Typed metadata attachments | | -| [030][rfc-030] | In Progress | Prism lineage graph | | -| [031][rfc-031] | In Progress | Local inspection APIs and artifacts | | -| [032][rfc-032] | Implemented | Execution observations | | -| [033][rfc-033] | Implemented | Adapter requirements and coverage | | -| [034][rfc-034] | Implemented | Quality assertions and observations | | -| [035][rfc-035] | Draft | Governed attributes and policy checkpoints | | -| [036][rfc-036] | Draft | Governed plan bundle | | -| [037][rfc-037] | Draft | Plan diff and blast-radius inputs | | -| [038][rfc-038] | Draft | Evidence exchange bridges | | -| [039][rfc-039] | Draft | Pandas-familiar exploration API | | -| [040][rfc-040] | Draft | Interoperability semantic profiles | | -| [041][rfc-041] | Draft | Prism plan ingress and external client frontends | | -| [042][rfc-042] | Draft | Async verification evidence | | -| [043][rfc-043] | Draft | Canonical equality and digest profiles | | -| [044][rfc-044] | Draft | Verifier statements and proof artifacts | | -| [045][rfc-045] | Draft | Constraint evidence and verification-aware planning | | -| [046][rfc-046] | Draft | Data contract ingress and product topology | | -| [047][rfc-047] | Draft | Semantic evidence graph and agent query surface | | -| [048][rfc-048] | Draft | Cluster execution backend mode | | -| [050][rfc-050] | Draft | Addon component registry and package contract | | - - - -**v0.1 tracking:** RFCs 000–004 plus RFC 007 remain the foundation that defines when IncQL v0.1 is complete: authors can read data, write typed queries, lower through Prism to Substrait, execute through DataFusion, and write results. The table also marks additional v0.1-shipped slices that landed before the whole foundation is closed, including the function catalog and evidence/observation work. - -New RFCs should follow [TEMPLATE.md] (aligned with Incan’s RFC structure, adapted for IncQL). +

Search and inspect IncQL's durable design records.

+
+ + +
+ + + + + + +
+ +| RFC | Status | Tags | Title | +| ---: | --- | --- | --- | +| [000](000_incql_syntax.md) | Planned | Authoring, Interoperability, Planning | Language Specification | +| [001](001_incql_dataset.md) | In Progress | Authoring, Planning, Types | Dataset types and carriers (DataSet[T]) | +| [002](002_apache_substrait_integration.md) | In Progress | Extensibility, Interoperability, Planning | Apache Substrait integration | +| [003](closed/implemented/003_incql_query_blocks.md) | Implemented | Authoring, Interoperability, Planning | query {} blocks — syntax, typing, Substrait | +| [004](004_incql_execution_context.md) | In Progress | Data access, Execution, Interoperability | Execution context and DataFusion | +| [005](005_incql_pipe_forward.md) | Blocked | Authoring, Planning | Pipe-forward relational syntax (\|>) | +| [006](006_unnest_core_substrait.md) | Blocked | Interoperability, Planning | Promote unnest/explode to core Substrait lowering | +| [007](007_prism_planning_engine.md) | In Progress | Evidence, Planning | Prism logical planning and optimization engine | +| [008](008_optimizer_boundary_stats_cbo_aqe.md) | Planned | Evidence, Execution, Planning | Optimizer boundary, statistics, cost-based optimization, and adaptive execution | +| [009](009_session_format_handler_registry.md) | Draft | Data access, Extensibility | Session Format Handler Registry | +| [010](010_csv_ingestion_contract.md) | Draft | Data access, Interoperability | CSV dialect and interpretation contract | +| [011](011_source_discovery_contract.md) | Draft | Data access, Interoperability | Source discovery and parse-unit expansion | +| [012](closed/implemented/012_unified_scalar_expression_surface.md) | Implemented | Authoring, Planning, Types | Unified scalar expression surface | +| [013](closed/implemented/013_function_catalog_program.md) | Implemented | Authoring, Extensibility, Functions | Function catalog program | +| [014](closed/implemented/014_function_registry.md) | Implemented | Extensibility, Functions, Interoperability | Function registry and catalog governance | +| [015](closed/implemented/015_core_scalar_functions.md) | Implemented | Authoring, Functions, Interoperability | Core scalar functions and operators | +| [016](closed/implemented/016_core_aggregate_functions.md) | Implemented | Authoring, Functions, Interoperability | Core aggregate functions | +| [017](closed/implemented/017_aggregate_modifiers.md) | Implemented | Authoring, Functions, Interoperability | Aggregate modifiers | +| [018](closed/implemented/018_common_scalar_function_catalog.md) | Implemented | Authoring, Functions, Interoperability | Common scalar function catalog | +| [019](closed/implemented/019_window_functions.md) | Implemented | Authoring, Functions, Planning | Window functions | +| [020](closed/implemented/020_nested_data_functions.md) | Implemented | Authoring, Functions, Planning | Nested data functions | +| [021](closed/implemented/021_generator_table_functions.md) | Implemented | Authoring, Functions, Planning | Generator and table-valued functions | +| [022](closed/implemented/022_semi_structured_format_functions.md) | Implemented | Authoring, Functions, Interoperability | Semi-structured and format functions | +| [023](closed/implemented/023_approximate_sketch_functions.md) | Implemented | Authoring, Functions, Interoperability | Approximate and sketch functions | +| [024](closed/implemented/024_function_extension_policy.md) | Implemented | Extensibility, Functions, Interoperability | Function extension policy | +| [025](closed/implemented/025_typed_sketch_logical_values.md) | Implemented | Interoperability, Types | Typed sketch logical values | +| [026](closed/implemented/026_semi_structured_variant_values.md) | Implemented | Interoperability, Types | Semi-structured variant logical values | +| [027](027_relational_evidence_program.md) | In Progress | Evidence, Governance, Interoperability, Verification | Relational evidence program | +| [028](028_semantic_identity_targets.md) | In Progress | Evidence | Semantic identity and target model | +| [029](029_metadata_attachments.md) | In Progress | Evidence, Extensibility | Typed metadata attachments | +| [030](030_prism_lineage_graph.md) | In Progress | Evidence, Planning | Prism lineage graph | +| [031](031_inspection_artifacts.md) | In Progress | Evidence, Interoperability | Local inspection APIs and artifacts | +| [032](closed/implemented/032_execution_observations.md) | Implemented | Evidence, Execution | Execution observations | +| [033](closed/implemented/033_adapter_requirements_coverage.md) | Implemented | Evidence, Execution, Interoperability | Adapter requirements and coverage | +| [034](closed/implemented/034_quality_assertions_observations.md) | Implemented | Authoring, Evidence, Execution | Quality assertions and observations | +| [035](closed/implemented/035_governed_attributes_policy_checkpoints.md) | Implemented | Evidence, Governance, Planning | Governed attributes and policy checkpoints | +| [036](036_governed_plan_bundle.md) | Draft | Evidence, Governance, Interoperability | Governed plan bundle | +| [037](037_plan_diff_blast_radius_inputs.md) | Draft | Evidence, Planning | Plan diff and blast-radius inputs | +| [038](038_evidence_exchange_bridges.md) | Draft | Evidence, Extensibility, Interoperability | Evidence exchange bridges | +| [039](039_pandas_familiar_exploration_api.md) | Draft | Authoring | Pandas-familiar exploration API | +| [040](040_interoperability_semantic_profiles.md) | Draft | Evidence, Interoperability | Interoperability semantic profiles | +| [041](041_prism_plan_ingress_frontends.md) | Draft | Authoring, Interoperability, Planning | Prism plan ingress and external client frontends | +| [042](042_async_verification_evidence.md) | Draft | Evidence, Verification | Async verification evidence | +| [043](043_canonical_equality_digest_profiles.md) | Draft | Evidence, Interoperability, Verification | Canonical equality and digest profiles | +| [044](044_verifier_statements_proof_artifacts.md) | Draft | Evidence, Verification | Verifier statements and proof artifacts | +| [045](045_constraint_evidence_verification_planning.md) | Draft | Evidence, Planning, Verification | Constraint evidence and verification-aware planning | +| [046](046_data_contract_ingress.md) | Draft | Evidence, Governance, Interoperability | Data contract ingress and product topology | +| [047](047_semantic_evidence_graph_agent_surface.md) | Draft | Evidence, Interoperability | Semantic evidence graph and agent query surface | +| [048](048_cluster_execution_backend_mode.md) | Draft | Evidence, Execution, Interoperability | Cluster execution backend mode | +| [050](050_addon_component_registry.md) | Draft | Data access, Execution, Extensibility, Interoperability | Addon component registry and package contract | + +
+ + +IncQL RFCs cover language, planning, execution, and governance decisions owned by IncQL. They form a separate series from the [Incan language RFCs](https://github.com/encero-systems/incan/tree/main/workspaces/docs-site/docs/RFCs), which govern the host language itself. + +## How the RFC lifecycle works + +Active RFCs remain in `docs/rfcs/` while the design is Draft, Planned, In Progress, Blocked, or Deferred. Closed records move without changing their RFC number or filename: + +- `docs/rfcs/closed/implemented/` contains implemented RFCs. +- `docs/rfcs/closed/superseded/` contains RFCs replaced by a newer design record. +- `docs/rfcs/closed/rejected/` contains rejected or withdrawn RFCs. + +The generated catalog treats each RFC's heading, metadata, summary, and lifecycle path as source data. The docs build fails when the index, controlled tag assignments, or lifecycle placement drifts from those records. + +**v0.1 tracking:** RFCs 000–004 plus RFC 007 remain the foundation that defines when IncQL v0.1 is complete: authors can read data, write typed queries, lower through Prism to Substrait, execute through DataFusion, and write results. The catalog also includes v0.1-shipped slices that landed before the whole foundation is closed, including the function catalog and evidence/observation work. + +## Create or update an RFC + +Copy [TEMPLATE.md], name the file `NNN_short_slug.md`, choose the next available number, and open a pull request. Follow [Writing IncQL RFCs] for the metadata contract, lifecycle transitions, and review conventions. [TEMPLATE.md]: TEMPLATE.md [Writing IncQL RFCs]: ../contributing/writing_rfcs.md -[rfc-000]: 000_incql_syntax.md -[rfc-001]: 001_incql_dataset.md -[rfc-002]: 002_apache_substrait_integration.md -[rfc-003]: 003_incql_query_blocks.md -[rfc-004]: 004_incql_execution_context.md -[rfc-005]: 005_incql_pipe_forward.md -[rfc-006]: 006_unnest_core_substrait.md -[rfc-007]: 007_prism_planning_engine.md -[rfc-008]: 008_optimizer_boundary_stats_cbo_aqe.md -[rfc-009]: 009_session_format_handler_registry.md -[rfc-010]: 010_csv_ingestion_contract.md -[rfc-011]: 011_source_discovery_contract.md -[rfc-012]: 012_unified_scalar_expression_surface.md -[rfc-013]: 013_function_catalog_program.md -[rfc-014]: 014_function_registry.md -[rfc-015]: 015_core_scalar_functions.md -[rfc-016]: 016_core_aggregate_functions.md -[rfc-017]: 017_aggregate_modifiers.md -[rfc-018]: 018_common_scalar_function_catalog.md -[rfc-019]: 019_window_functions.md -[rfc-020]: 020_nested_data_functions.md -[rfc-021]: 021_generator_table_functions.md -[rfc-022]: 022_semi_structured_format_functions.md -[rfc-023]: 023_approximate_sketch_functions.md -[rfc-024]: 024_function_extension_policy.md -[rfc-025]: 025_typed_sketch_logical_values.md -[rfc-026]: 026_semi_structured_variant_values.md -[rfc-027]: 027_relational_evidence_program.md -[rfc-028]: 028_semantic_identity_targets.md -[rfc-029]: 029_metadata_attachments.md -[rfc-030]: 030_prism_lineage_graph.md -[rfc-031]: 031_inspection_artifacts.md -[rfc-032]: 032_execution_observations.md -[rfc-033]: 033_adapter_requirements_coverage.md -[rfc-034]: 034_quality_assertions_observations.md -[rfc-035]: 035_governed_attributes_policy_checkpoints.md -[rfc-036]: 036_governed_plan_bundle.md -[rfc-037]: 037_plan_diff_blast_radius_inputs.md -[rfc-038]: 038_evidence_exchange_bridges.md -[rfc-039]: 039_pandas_familiar_exploration_api.md -[rfc-040]: 040_interoperability_semantic_profiles.md -[rfc-041]: 041_prism_plan_ingress_frontends.md -[rfc-042]: 042_async_verification_evidence.md -[rfc-043]: 043_canonical_equality_digest_profiles.md -[rfc-044]: 044_verifier_statements_proof_artifacts.md -[rfc-045]: 045_constraint_evidence_verification_planning.md -[rfc-046]: 046_data_contract_ingress.md -[rfc-047]: 047_semantic_evidence_graph_agent_surface.md -[rfc-048]: 048_cluster_execution_backend_mode.md -[rfc-050]: 050_addon_component_registry.md -[incan-rfcs]: https://github.com/encero-systems/incan/tree/main/workspaces/docs-site/docs/RFCs diff --git a/docs/rfcs/catalog.json b/docs/rfcs/catalog.json new file mode 100644 index 00000000..0ebd84f6 --- /dev/null +++ b/docs/rfcs/catalog.json @@ -0,0 +1,248 @@ +{ + "definitions": { + "authoring": "Authoring", + "types": "Types", + "functions": "Functions", + "planning": "Planning", + "execution": "Execution", + "data-access": "Data access", + "interoperability": "Interoperability", + "evidence": "Evidence", + "governance": "Governance", + "verification": "Verification", + "extensibility": "Extensibility" + }, + "records": { + "000": [ + "authoring", + "planning", + "interoperability" + ], + "001": [ + "authoring", + "types", + "planning" + ], + "002": [ + "planning", + "interoperability", + "extensibility" + ], + "003": [ + "authoring", + "planning", + "interoperability" + ], + "004": [ + "execution", + "data-access", + "interoperability" + ], + "005": [ + "authoring", + "planning" + ], + "006": [ + "planning", + "interoperability" + ], + "007": [ + "planning", + "evidence" + ], + "008": [ + "planning", + "execution", + "evidence" + ], + "009": [ + "data-access", + "extensibility" + ], + "010": [ + "data-access", + "interoperability" + ], + "011": [ + "data-access", + "interoperability" + ], + "012": [ + "authoring", + "types", + "planning" + ], + "013": [ + "authoring", + "functions", + "extensibility" + ], + "014": [ + "functions", + "extensibility", + "interoperability" + ], + "015": [ + "authoring", + "functions", + "interoperability" + ], + "016": [ + "authoring", + "functions", + "interoperability" + ], + "017": [ + "authoring", + "functions", + "interoperability" + ], + "018": [ + "authoring", + "functions", + "interoperability" + ], + "019": [ + "authoring", + "functions", + "planning" + ], + "020": [ + "authoring", + "functions", + "planning" + ], + "021": [ + "authoring", + "functions", + "planning" + ], + "022": [ + "authoring", + "functions", + "interoperability" + ], + "023": [ + "authoring", + "functions", + "interoperability" + ], + "024": [ + "functions", + "extensibility", + "interoperability" + ], + "025": [ + "types", + "interoperability" + ], + "026": [ + "types", + "interoperability" + ], + "027": [ + "interoperability", + "evidence", + "governance", + "verification" + ], + "028": [ + "evidence" + ], + "029": [ + "evidence", + "extensibility" + ], + "030": [ + "planning", + "evidence" + ], + "031": [ + "interoperability", + "evidence" + ], + "032": [ + "execution", + "evidence" + ], + "033": [ + "execution", + "interoperability", + "evidence" + ], + "034": [ + "authoring", + "execution", + "evidence" + ], + "035": [ + "planning", + "evidence", + "governance" + ], + "036": [ + "interoperability", + "evidence", + "governance" + ], + "037": [ + "planning", + "evidence" + ], + "038": [ + "interoperability", + "evidence", + "extensibility" + ], + "039": [ + "authoring" + ], + "040": [ + "interoperability", + "evidence" + ], + "041": [ + "authoring", + "planning", + "interoperability" + ], + "042": [ + "evidence", + "verification" + ], + "043": [ + "interoperability", + "evidence", + "verification" + ], + "044": [ + "evidence", + "verification" + ], + "045": [ + "planning", + "evidence", + "verification" + ], + "046": [ + "interoperability", + "evidence", + "governance" + ], + "047": [ + "interoperability", + "evidence" + ], + "048": [ + "execution", + "interoperability", + "evidence" + ], + "050": [ + "execution", + "data-access", + "interoperability", + "extensibility" + ] + } +} diff --git a/docs/rfcs/003_incql_query_blocks.md b/docs/rfcs/closed/implemented/003_incql_query_blocks.md similarity index 100% rename from docs/rfcs/003_incql_query_blocks.md rename to docs/rfcs/closed/implemented/003_incql_query_blocks.md diff --git a/docs/rfcs/012_unified_scalar_expression_surface.md b/docs/rfcs/closed/implemented/012_unified_scalar_expression_surface.md similarity index 100% rename from docs/rfcs/012_unified_scalar_expression_surface.md rename to docs/rfcs/closed/implemented/012_unified_scalar_expression_surface.md diff --git a/docs/rfcs/013_function_catalog_program.md b/docs/rfcs/closed/implemented/013_function_catalog_program.md similarity index 100% rename from docs/rfcs/013_function_catalog_program.md rename to docs/rfcs/closed/implemented/013_function_catalog_program.md diff --git a/docs/rfcs/014_function_registry.md b/docs/rfcs/closed/implemented/014_function_registry.md similarity index 100% rename from docs/rfcs/014_function_registry.md rename to docs/rfcs/closed/implemented/014_function_registry.md diff --git a/docs/rfcs/015_core_scalar_functions.md b/docs/rfcs/closed/implemented/015_core_scalar_functions.md similarity index 100% rename from docs/rfcs/015_core_scalar_functions.md rename to docs/rfcs/closed/implemented/015_core_scalar_functions.md diff --git a/docs/rfcs/016_core_aggregate_functions.md b/docs/rfcs/closed/implemented/016_core_aggregate_functions.md similarity index 100% rename from docs/rfcs/016_core_aggregate_functions.md rename to docs/rfcs/closed/implemented/016_core_aggregate_functions.md diff --git a/docs/rfcs/017_aggregate_modifiers.md b/docs/rfcs/closed/implemented/017_aggregate_modifiers.md similarity index 100% rename from docs/rfcs/017_aggregate_modifiers.md rename to docs/rfcs/closed/implemented/017_aggregate_modifiers.md diff --git a/docs/rfcs/018_common_scalar_function_catalog.md b/docs/rfcs/closed/implemented/018_common_scalar_function_catalog.md similarity index 100% rename from docs/rfcs/018_common_scalar_function_catalog.md rename to docs/rfcs/closed/implemented/018_common_scalar_function_catalog.md diff --git a/docs/rfcs/019_window_functions.md b/docs/rfcs/closed/implemented/019_window_functions.md similarity index 100% rename from docs/rfcs/019_window_functions.md rename to docs/rfcs/closed/implemented/019_window_functions.md diff --git a/docs/rfcs/020_nested_data_functions.md b/docs/rfcs/closed/implemented/020_nested_data_functions.md similarity index 100% rename from docs/rfcs/020_nested_data_functions.md rename to docs/rfcs/closed/implemented/020_nested_data_functions.md diff --git a/docs/rfcs/021_generator_table_functions.md b/docs/rfcs/closed/implemented/021_generator_table_functions.md similarity index 100% rename from docs/rfcs/021_generator_table_functions.md rename to docs/rfcs/closed/implemented/021_generator_table_functions.md diff --git a/docs/rfcs/022_semi_structured_format_functions.md b/docs/rfcs/closed/implemented/022_semi_structured_format_functions.md similarity index 100% rename from docs/rfcs/022_semi_structured_format_functions.md rename to docs/rfcs/closed/implemented/022_semi_structured_format_functions.md diff --git a/docs/rfcs/023_approximate_sketch_functions.md b/docs/rfcs/closed/implemented/023_approximate_sketch_functions.md similarity index 100% rename from docs/rfcs/023_approximate_sketch_functions.md rename to docs/rfcs/closed/implemented/023_approximate_sketch_functions.md diff --git a/docs/rfcs/024_function_extension_policy.md b/docs/rfcs/closed/implemented/024_function_extension_policy.md similarity index 100% rename from docs/rfcs/024_function_extension_policy.md rename to docs/rfcs/closed/implemented/024_function_extension_policy.md diff --git a/docs/rfcs/025_typed_sketch_logical_values.md b/docs/rfcs/closed/implemented/025_typed_sketch_logical_values.md similarity index 100% rename from docs/rfcs/025_typed_sketch_logical_values.md rename to docs/rfcs/closed/implemented/025_typed_sketch_logical_values.md diff --git a/docs/rfcs/026_semi_structured_variant_values.md b/docs/rfcs/closed/implemented/026_semi_structured_variant_values.md similarity index 100% rename from docs/rfcs/026_semi_structured_variant_values.md rename to docs/rfcs/closed/implemented/026_semi_structured_variant_values.md diff --git a/docs/rfcs/032_execution_observations.md b/docs/rfcs/closed/implemented/032_execution_observations.md similarity index 100% rename from docs/rfcs/032_execution_observations.md rename to docs/rfcs/closed/implemented/032_execution_observations.md diff --git a/docs/rfcs/033_adapter_requirements_coverage.md b/docs/rfcs/closed/implemented/033_adapter_requirements_coverage.md similarity index 100% rename from docs/rfcs/033_adapter_requirements_coverage.md rename to docs/rfcs/closed/implemented/033_adapter_requirements_coverage.md diff --git a/docs/rfcs/034_quality_assertions_observations.md b/docs/rfcs/closed/implemented/034_quality_assertions_observations.md similarity index 100% rename from docs/rfcs/034_quality_assertions_observations.md rename to docs/rfcs/closed/implemented/034_quality_assertions_observations.md diff --git a/docs/rfcs/035_governed_attributes_policy_checkpoints.md b/docs/rfcs/closed/implemented/035_governed_attributes_policy_checkpoints.md similarity index 100% rename from docs/rfcs/035_governed_attributes_policy_checkpoints.md rename to docs/rfcs/closed/implemented/035_governed_attributes_policy_checkpoints.md diff --git a/docs/shared/brand/incql-mark.png b/docs/shared/brand/incql-mark.png new file mode 100644 index 00000000..2c227bd2 Binary files /dev/null and b/docs/shared/brand/incql-mark.png differ diff --git a/docs/shared/icons/chart-timeline-variant.svg b/docs/shared/icons/chart-timeline-variant.svg new file mode 100644 index 00000000..cf04a9e9 --- /dev/null +++ b/docs/shared/icons/chart-timeline-variant.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/shared/icons/code-braces-box.svg b/docs/shared/icons/code-braces-box.svg new file mode 100644 index 00000000..8c6c8234 --- /dev/null +++ b/docs/shared/icons/code-braces-box.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/shared/icons/database-cog-outline.svg b/docs/shared/icons/database-cog-outline.svg new file mode 100644 index 00000000..dedbc3c7 --- /dev/null +++ b/docs/shared/icons/database-cog-outline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/shared/icons/duck.svg b/docs/shared/icons/duck.svg new file mode 100644 index 00000000..7c87a33b --- /dev/null +++ b/docs/shared/icons/duck.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/shared/icons/file-tree-outline.svg b/docs/shared/icons/file-tree-outline.svg new file mode 100644 index 00000000..3168379a --- /dev/null +++ b/docs/shared/icons/file-tree-outline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/shared/icons/flash-outline.svg b/docs/shared/icons/flash-outline.svg new file mode 100644 index 00000000..a9b6b0a1 --- /dev/null +++ b/docs/shared/icons/flash-outline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/shared/icons/link-variant.svg b/docs/shared/icons/link-variant.svg new file mode 100644 index 00000000..705cc5e7 --- /dev/null +++ b/docs/shared/icons/link-variant.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/shared/icons/shield-check-outline.svg b/docs/shared/icons/shield-check-outline.svg new file mode 100644 index 00000000..d93666c9 --- /dev/null +++ b/docs/shared/icons/shield-check-outline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/shared/icons/source-branch.svg b/docs/shared/icons/source-branch.svg new file mode 100644 index 00000000..e11705a4 --- /dev/null +++ b/docs/shared/icons/source-branch.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/shared/icons/table-check.svg b/docs/shared/icons/table-check.svg new file mode 100644 index 00000000..d7211baf --- /dev/null +++ b/docs/shared/icons/table-check.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/shared/icons/vector-polyline.svg b/docs/shared/icons/vector-polyline.svg new file mode 100644 index 00000000..64659dd7 --- /dev/null +++ b/docs/shared/icons/vector-polyline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/shared/prismplane/architecture-system-path-v1.png b/docs/shared/prismplane/architecture-system-path-v1.png new file mode 100644 index 00000000..fb9cedd2 Binary files /dev/null and b/docs/shared/prismplane/architecture-system-path-v1.png differ diff --git a/docs/shared/prismplane/prismplane-hero-light.png b/docs/shared/prismplane/prismplane-hero-light.png new file mode 100644 index 00000000..20170f83 Binary files /dev/null and b/docs/shared/prismplane/prismplane-hero-light.png differ diff --git a/docs/shared/prismplane/prismplane-hero.jpg b/docs/shared/prismplane/prismplane-hero.jpg new file mode 100644 index 00000000..4bc93c28 Binary files /dev/null and b/docs/shared/prismplane/prismplane-hero.jpg differ diff --git a/docs/shared/prismplane/process-flow-stage-v3.png b/docs/shared/prismplane/process-flow-stage-v3.png new file mode 100644 index 00000000..951cc2fb Binary files /dev/null and b/docs/shared/prismplane/process-flow-stage-v3.png differ diff --git a/docs/shared/prismplane/semantic-convergence-stage.png b/docs/shared/prismplane/semantic-convergence-stage.png new file mode 100644 index 00000000..ab7c9219 Binary files /dev/null and b/docs/shared/prismplane/semantic-convergence-stage.png differ diff --git a/docs/shared/prismplane/semantic-convergence.jpg b/docs/shared/prismplane/semantic-convergence.jpg new file mode 100644 index 00000000..400671d4 Binary files /dev/null and b/docs/shared/prismplane/semantic-convergence.jpg differ diff --git a/docs/stylesheets/prismplane.css b/docs/stylesheets/prismplane.css new file mode 100644 index 00000000..a5068c06 --- /dev/null +++ b/docs/stylesheets/prismplane.css @@ -0,0 +1,7511 @@ +/* + * Prismplane docs theme. + * + * MkDocs Material remains the documentation shell. The homepage follows the + * Prismplane launch reference and uses bitmap imagery for the visual identity. + * Do not add CSS-drawn diagrams, fake illustrations, or inline SVG stand-ins. + */ + +:root { + color-scheme: light; + + --incql-font-body: Inter, "SF Pro Text", -apple-system, BlinkMacSystemFont, "Segoe UI Variable", "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + --incql-font-display: Inter, "SF Pro Display", -apple-system, BlinkMacSystemFont, "Segoe UI Variable Display", "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + --incql-font-mono: "SFMono-Regular", "Cascadia Code", "JetBrains Mono", "Roboto Mono", Menlo, Consolas, monospace; + --incql-prose-size: 0.7rem; + --incql-radius-sm: 8px; + --incql-radius-md: 12px; + --incql-radius-lg: 18px; + --incql-radius-xl: 26px; + + --incql-page: #f5f8ff; + --incql-page-strong: #ffffff; + --incql-page-ink: #071534; + --incql-page-copy: #344461; + --incql-page-muted: #667491; + --incql-page-line: rgba(96, 126, 186, 0.2); + --incql-page-line-strong: rgba(91, 115, 255, 0.42); + --incql-page-panel: rgba(255, 255, 255, 0.72); + --incql-page-panel-strong: rgba(255, 255, 255, 0.9); + --incql-page-cyan: #0aaee8; + --incql-page-blue: #476dff; + --incql-page-violet: #7b5cff; + --incql-page-magenta: #bf60ff; + --incql-page-shadow: 0 28px 80px rgba(55, 74, 123, 0.12); +} + +body[data-md-color-scheme="default"] { + --md-default-bg-color: var(--incql-page); + --md-default-fg-color: var(--incql-page-ink); + --md-default-fg-color--light: var(--incql-page-copy); + --md-default-fg-color--lighter: #5c6c87; + --md-primary-fg-color: rgba(248, 251, 255, 0.92); + --md-primary-bg-color: var(--incql-page-ink); + --md-accent-fg-color: #315df0; + --md-typeset-color: var(--incql-page-ink); + --md-typeset-a-color: #315df0; + --md-code-bg-color: #f3f6fc; + --md-code-fg-color: #172443; +} + +html { + background: var(--incql-page); +} + +body { + overflow-x: hidden; + background: + radial-gradient(circle at 76% 8rem, rgba(80, 136, 255, 0.1), transparent 34rem), + linear-gradient(180deg, #f8fbff 0%, var(--incql-page) 62rem, #f7f9ff 100%); + color: var(--incql-page-ink); + font-family: var(--incql-font-body); +} + +.md-grid { + max-width: 1440px; +} + +body[data-md-color-scheme="default"] .md-header { + border-bottom: 1px solid rgba(96, 126, 186, 0.18); + background: rgba(248, 251, 255, 0.88); + box-shadow: 0 18px 60px rgba(55, 74, 123, 0.08); + backdrop-filter: blur(10px); +} + +.md-header__title { + color: var(--incql-page-ink); + font-family: var(--incql-font-display); + font-weight: 730; + letter-spacing: 0; +} + +.md-header__button.md-logo img, +.md-header__button.md-logo svg { + width: 1.48rem; + height: 1.48rem; + filter: drop-shadow(0 0 10px rgba(49, 200, 255, 0.18)); +} + +.md-header__button { + color: var(--incql-page-copy); +} + +.md-header__button:hover, +.md-header__button:focus { + color: #315df0; +} + +body[data-md-color-scheme="default"] .md-tabs { + border-bottom: 1px solid rgba(96, 126, 186, 0.12); + background: rgba(248, 251, 255, 0.78); +} + +.md-tabs__link { + margin-top: 0.45rem; + color: var(--incql-page-copy); + font-weight: 620; + letter-spacing: 0; + opacity: 1; +} + +.md-tabs__link--active, +.md-tabs__link:hover, +.md-tabs__link:focus { + color: var(--incql-page-ink); +} + +.md-tabs__item--active { + border-bottom: 2px solid #315df0; +} + +.md-search__form { + border: 1px solid rgba(77, 103, 158, 0.18); + border-radius: var(--incql-radius-md); + background: rgba(255, 255, 255, 0.88); + box-shadow: 0 10px 34px rgba(55, 74, 123, 0.08); +} + +.md-search__input { + color: var(--incql-page-ink); +} + +.md-search__input::placeholder { + color: #5c6c87; + opacity: 1; +} + +.md-typeset { + color: var(--incql-page-ink); + font-size: 0.84rem; +} + +.md-typeset h1, +.md-typeset h2, +.md-typeset h3, +.md-typeset h4 { + color: var(--incql-page-ink); + font-family: var(--incql-font-display); + font-weight: 760; + letter-spacing: 0; +} + +.md-typeset p, +.md-typeset li { + color: var(--incql-page-copy); + line-height: 1.72; +} + +.md-content__inner.md-typeset > :where(p, ul, ol, dl, blockquote) { + font-size: var(--incql-prose-size); +} + +@media screen and (min-width: 100em) { + :root { + --incql-prose-size: 0.68rem; + } +} + +@media screen and (min-width: 125em) { + :root { + --incql-prose-size: 0.63rem; + } +} + +.md-typeset a { + color: #315df0; + text-decoration-color: rgba(49, 93, 240, 0.34); + text-decoration-thickness: 0.08em; + text-underline-offset: 0.18em; +} + +.md-typeset a:hover, +.md-typeset a:focus { + color: #153fc7; + text-decoration-color: #315df0; +} + +.md-typeset code, +.md-typeset pre, +.md-typeset kbd { + font-family: var(--incql-font-mono); +} + +.md-typeset :not(pre) > code { + border: 1px solid rgba(83, 111, 183, 0.16); + border-radius: var(--incql-radius-sm); + background: rgba(237, 243, 255, 0.82); + color: #20345f; +} + +.md-typeset .highlight { + --md-code-fg-color: #172443; + --md-code-bg-color: #f3f6fc; + --md-code-hl-color: rgba(71, 109, 255, 0.1); + --md-code-hl-number-color: #9c304c; + --md-code-hl-special-color: #97275f; + --md-code-hl-function-color: #6f2f9f; + --md-code-hl-constant-color: #4f47a7; + --md-code-hl-keyword-color: #2d55ad; + --md-code-hl-string-color: #087a42; + --md-code-hl-name-color: #172443; + --md-code-hl-operator-color: #344461; + --md-code-hl-punctuation-color: #40506c; + --md-code-hl-comment-color: #5c6c87; + --md-code-hl-generic-color: #344461; + --md-code-hl-variable-color: #172443; + border: 1px solid rgba(83, 111, 183, 0.17); + border-radius: var(--incql-radius-lg); + background: #f3f6fc; + box-shadow: 0 14px 36px rgba(55, 74, 123, 0.08); +} + +.md-typeset pre > code { + padding: 0.88rem 0.95rem; + border: 0; + border-radius: var(--incql-radius-lg); + background: transparent; + line-height: 1.62; +} + +body:not(:has(.incql-launch)):not(:has(.incql-architecture)):not(:has(.pp-rfc-reader-host)) .md-main__inner { + margin-top: 1.1rem; +} + +body:not(:has(.incql-launch)):not(:has(.incql-architecture)):not(:has(.pp-rfc-reader-host)) .md-content__inner { + padding-top: 0.4rem; +} + +body:not(:has(.incql-launch)):not(:has(.incql-architecture)):not(:has(.pp-rfc-reader-host)) .md-typeset > h1 { + margin-bottom: 0.82em; + font-size: 1.84em; +} + +body:not(:has(.incql-launch)):not(:has(.incql-architecture)):not(:has(.pp-rfc-reader-host)) .md-typeset > h2 { + margin-top: 1.3em; +} + +.md-nav__title { + background: transparent !important; + box-shadow: none !important; + color: var(--incql-page-ink); + font-weight: 710; + letter-spacing: 0; +} + +.md-nav__link { + color: #5c6c87; +} + +.md-nav__link[for]:focus, +.md-nav__link[for]:hover, +.md-nav__link[href]:focus, +.md-nav__link[href]:hover, +.md-nav__link--active { + color: #315df0; +} + +.md-sidebar, +.md-sidebar__scrollwrap { + scrollbar-color: rgba(49, 93, 240, 0.32) transparent; +} + +.pp-primary-nav-toggle { + display: none; +} + +@media screen and (min-width: 76.25em) { + .md-sidebar--primary:has(> .pp-primary-nav-toggle) { + transition: width 180ms ease, flex-basis 180ms ease; + } + + .pp-primary-nav-toggle { + position: absolute; + z-index: 5; + top: 0.48rem; + right: 0.28rem; + display: inline-flex; + width: 1.8rem; + height: 1.8rem; + align-items: center; + justify-content: center; + padding: 0.4rem; + border: 1px solid rgba(96, 126, 186, 0.2); + border-radius: 8px; + background: rgba(248, 251, 255, 0.9); + color: #5c6c87; + cursor: pointer; + box-shadow: 0 8px 20px rgba(55, 74, 123, 0.07); + } + + .pp-primary-nav-toggle:hover, + .pp-primary-nav-toggle:focus-visible { + border-color: rgba(49, 93, 240, 0.36); + color: #315df0; + } + + .pp-primary-nav-toggle:focus-visible { + outline: 2px solid #315df0; + outline-offset: 2px; + } + + .pp-primary-nav-toggle svg { + width: 1rem; + height: 1rem; + transition: transform 180ms ease; + fill: currentcolor; + } + + body.pp-primary-nav-collapsed .md-sidebar--primary { + width: 2.45rem; + flex: 0 0 2.45rem; + padding-right: 0; + padding-left: 0; + border-right: 1px solid rgba(96, 126, 186, 0.14); + background: rgba(248, 251, 255, 0.34); + } + + body.pp-primary-nav-collapsed .md-sidebar--primary .md-sidebar__scrollwrap { + visibility: hidden; + opacity: 0; + pointer-events: none; + } + + body.pp-primary-nav-collapsed .pp-primary-nav-toggle { + right: 0.3rem; + } + + body.pp-primary-nav-collapsed .pp-primary-nav-toggle svg { + transform: rotate(180deg); + } +} + +@media (prefers-reduced-motion: reduce) { + .md-sidebar--primary:has(> .pp-primary-nav-toggle), + .pp-primary-nav-toggle svg { + transition: none; + } +} + +.md-sidebar--secondary .md-sidebar__inner { + margin-top: 1rem; + border-left: 1px solid rgba(96, 126, 186, 0.18); + background: transparent; +} + +.md-sidebar--secondary .md-nav__title { + padding: 0 0.75rem 0.45rem; + color: var(--incql-page-copy); + font-size: 0.62rem; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.md-sidebar--secondary .md-nav__link { + padding-left: 0.75rem; + border-left: 2px solid transparent; + color: #5c6c87; + font-size: 0.72rem; + line-height: 1.45; +} + +.md-sidebar--secondary .md-nav__link--active { + border-left-color: #315df0; + color: var(--incql-page-ink); +} + +.md-sidebar--secondary .md-nav .md-nav { + display: none; +} + +/* Portable documentation components ------------------------------------- */ + +.pp-sr-only { + position: absolute !important; + width: 1px !important; + height: 1px !important; + padding: 0 !important; + overflow: hidden !important; + clip: rect(0, 0, 0, 0) !important; + white-space: nowrap !important; + border: 0 !important; +} + +body:has(.pp-rfc-reader-host) .md-sidebar--secondary { + display: none; +} + +body:has(.pp-rfc-reader-host) .md-main__inner { + max-width: none; +} + +body:has(.pp-rfc-reader-host) .md-content { + min-width: 0; +} + +body:has(.pp-rfc-reader-host) .md-content__inner { + max-width: none; + margin: 0; + padding: 1.1rem 0 4rem; +} + +body:has(.pp-rfc-reader-host) .md-content__inner::before { + display: none; +} + +.pp-rfc-index-header { + display: flex; + gap: 1rem 2rem; + align-items: end; + justify-content: space-between; + margin: 0 clamp(1rem, 3vw, 2.6rem); +} + +.pp-rfc-index-intro { + min-width: 0; +} + +.md-typeset .pp-rfc-index-header h1 { + margin: 0; + color: var(--incql-page-ink); + font-size: clamp(1.55rem, 2.2vw, 2.15rem); + line-height: 1.05; +} + +.md-typeset .pp-rfc-index-lede { + margin: 0.35rem 0 0; + color: var(--incql-page-copy); + font-size: 0.82rem; + line-height: 1.5; +} + +.md-typeset .pp-rfc-index-links { + display: flex; + flex-wrap: wrap; + gap: 0.35rem 1rem; + margin: 0 0 0.1rem; + font-size: 0.66rem; + font-weight: 680; + white-space: nowrap; +} + +.pp-rfc-reader-host { + --rfc-reader-accent: var(--pp-docs-accent, #315df0); + --rfc-reader-accent-secondary: var(--pp-docs-accent-secondary, #7b5cff); + --rfc-reader-accent-cyan: var(--pp-docs-accent-cyan, #0aaee8); + --rfc-reader-ink: var(--pp-docs-ink, var(--incql-page-ink, #071534)); + --rfc-reader-copy: var(--pp-docs-copy, var(--incql-page-copy, #344461)); + --rfc-reader-muted: var(--pp-docs-muted, #5c6c87); + --rfc-reader-line: var(--pp-docs-line, rgba(96, 126, 186, 0.2)); + --rfc-reader-line-strong: var(--pp-docs-line-strong, rgba(49, 93, 240, 0.34)); + --rfc-reader-surface: var(--pp-docs-surface, rgba(255, 255, 255, 0.74)); + --rfc-reader-surface-strong: var(--pp-docs-surface-strong, rgba(255, 255, 255, 0.94)); + margin: 0.8rem 0 0; +} + +.pp-rfc-reader-host[hidden], +.pp-rfc-fallback[hidden] { + display: none !important; +} + +.pp-rfc-reader { + display: grid; + grid-template-columns: minmax(19rem, 23rem) minmax(0, 1fr); + min-height: 0; + overflow: hidden; + overflow-anchor: none; + border-top: 1px solid var(--rfc-reader-line); + border-bottom: 1px solid var(--rfc-reader-line); + background: rgba(255, 255, 255, 0.24); +} + +.pp-rfc-reader__index { + display: grid; + grid-template-rows: auto auto minmax(0, 1fr); + min-width: 0; + min-height: 0; + overflow: hidden; + border-right: 1px solid var(--rfc-reader-line); + background: var(--rfc-reader-surface); +} + +.pp-rfc-reader__toolbar { + display: grid; + grid-template-columns: minmax(0, 1fr); + gap: 0.65rem; + align-items: center; + padding: 0.62rem 0.8rem; + background: transparent; +} + +.pp-rfc-reader__search { + position: relative; + z-index: 5; + min-width: 0; + margin: 0; +} + +.pp-rfc-reader__search-shell { + display: flex; + width: 100%; + min-height: 2.35rem; + align-items: center; + gap: 0.28rem; + padding: 0.28rem 0.38rem 0.28rem 0.5rem; + border: 1px solid var(--rfc-reader-line); + border-radius: 11px; + background: rgba(255, 255, 255, 0.82); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.72); +} + +.pp-rfc-reader__search-shell:focus-within, +.pp-rfc-reader__search[data-tags-open="true"] .pp-rfc-reader__search-shell { + border-color: var(--rfc-reader-accent); + box-shadow: 0 0 0 3px rgba(49, 93, 240, 0.14); +} + +.pp-rfc-reader__search input { + min-width: 4rem; + flex: 1 1 auto; + padding: 0.24rem 0.15rem; + border: 0; + outline: 0; + background: transparent; + color: var(--rfc-reader-ink); + font: inherit; + font-size: 0.68rem; +} + +.pp-rfc-reader__search input::placeholder { + color: var(--rfc-reader-muted); + opacity: 1; +} + +.pp-rfc-reader__tag-menu-button { + display: inline-flex; + min-height: 1.65rem; + flex: 0 0 auto; + align-items: center; + gap: 0.28rem; + padding: 0.26rem 0.42rem; + border: 1px solid var(--rfc-reader-line); + border-radius: 7px; + background: rgba(247, 250, 255, 0.96); + color: var(--rfc-reader-copy); + cursor: pointer; + font: inherit; + font-size: 0.58rem; + font-weight: 700; +} + +.pp-rfc-reader__tag-menu-count { + min-width: 1rem; + padding: 0.05rem 0.2rem; + border-radius: 999px; + background: rgba(49, 93, 240, 0.1); + color: var(--rfc-reader-accent); + font-variant-numeric: tabular-nums; + text-align: center; +} + +.pp-rfc-reader__tag-menu-count[hidden] { + display: none; +} + +.pp-rfc-reader__search kbd { + flex: 0 0 auto; + min-width: 1.55rem; + padding: 0.18rem 0.34rem; + border: 1px solid var(--rfc-reader-line); + border-radius: 6px; + background: rgba(247, 250, 255, 0.96); + color: var(--rfc-reader-muted); + font-size: 0.58rem; + text-align: center; +} + +.pp-rfc-reader__facets { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 0.35rem 0.5rem; + align-items: center; + min-width: 0; +} + +.pp-rfc-segments { + display: flex; + grid-column: 1 / -1; + min-width: 0; + margin: 0; + padding: 0; + border: 0; +} + +.pp-rfc-segments__item { + position: relative; +} + +.pp-rfc-segments input { + position: absolute; + inset: 0; + opacity: 0; + pointer-events: none; +} + +.pp-rfc-segments label { + display: inline-flex; + min-height: 1.9rem; + align-items: center; + gap: 0.3rem; + padding: 0.34rem 0.46rem; + border-bottom: 2px solid transparent; + color: var(--rfc-reader-muted); + cursor: pointer; + font-size: 0.63rem; + font-weight: 670; + white-space: nowrap; +} + +.pp-rfc-segments label span { + color: var(--rfc-reader-copy); + font-variant-numeric: tabular-nums; +} + +.pp-rfc-segments input:checked + label { + border-bottom-color: var(--rfc-reader-accent); + color: var(--rfc-reader-accent); +} + +.pp-rfc-segments input:focus-visible + label { + border-radius: 7px; + outline: 2px solid var(--rfc-reader-accent); + outline-offset: 2px; +} + +.pp-rfc-reader__tag-menu-button:focus-visible, +.pp-rfc-reader__tag-option input:focus-visible + label, +.pp-rfc-reader__clear-tags:focus-visible, +.pp-rfc-reader__active-tag:focus-visible, +.pp-rfc-reader__tag-chip:focus-visible, +.pp-rfc-reader__status-filter summary:focus-visible, +.pp-rfc-reader__status-option input:focus-visible + span, +.pp-rfc-reader__reset:focus-visible, +.pp-rfc-reader__back:focus-visible, +.pp-rfc-reader__open:focus-visible, +.pp-rfc-reader__related a:focus-visible { + outline: 2px solid var(--rfc-reader-accent); + outline-offset: 2px; +} + +.pp-rfc-reader__tag-panel { + position: absolute; + z-index: 4; + top: calc(100% + 0.3rem); + right: 0; + width: min(17rem, calc(100vw - 2rem)); + max-height: min(22rem, 45vh); + padding: 0.65rem; + overflow-y: auto; + border: 1px solid var(--rfc-reader-line); + border-radius: 11px; + background: var(--rfc-reader-surface-strong); + box-shadow: 0 18px 45px rgba(55, 74, 123, 0.16); + overscroll-behavior: contain; + scrollbar-color: rgba(49, 93, 240, 0.28) transparent; +} + +.pp-rfc-reader__tag-panel[hidden] { + display: none; +} + +.pp-rfc-reader__tag-panel fieldset { + display: grid; + gap: 0.18rem; + margin: 0; + padding: 0; + border: 0; +} + +.pp-rfc-reader__tag-panel legend { + margin-bottom: 0.35rem; + color: var(--rfc-reader-ink); + font-size: 0.62rem; + font-weight: 740; +} + +.pp-rfc-reader__tag-option { + position: relative; +} + +.pp-rfc-reader__tag-option input { + position: absolute; + width: 1px; + height: 1px; + opacity: 0; +} + +.pp-rfc-reader__tag-option label { + display: flex; + min-height: 1.8rem; + align-items: center; + justify-content: space-between; + gap: 0.75rem; + padding: 0.3rem 0.4rem 0.3rem 1.65rem; + border-radius: 7px; + color: var(--rfc-reader-copy); + cursor: pointer; + font-size: 0.61rem; + font-weight: 620; +} + +.pp-rfc-reader__tag-option label::before { + position: absolute; + left: 0.42rem; + width: 0.78rem; + height: 0.78rem; + border: 1px solid var(--rfc-reader-line-strong); + border-radius: 3px; + background: rgba(255, 255, 255, 0.82); + content: ""; +} + +.pp-rfc-reader__tag-option label::after { + position: absolute; + top: 50%; + left: 0.68rem; + width: 0.2rem; + height: 0.4rem; + border: solid #fff; + border-width: 0 1.5px 1.5px 0; + content: ""; + opacity: 0; + transform: translateY(-58%) rotate(45deg); +} + +.pp-rfc-reader__tag-option input:checked + label { + background: rgba(49, 93, 240, 0.06); + color: var(--rfc-reader-ink); +} + +.pp-rfc-reader__tag-option input:checked + label::before { + border-color: var(--rfc-reader-accent); + background: var(--rfc-reader-accent); + box-shadow: inset 0 0 0 2px #fff; +} + +.pp-rfc-reader__tag-option input:checked + label::after { + opacity: 1; +} + +.pp-rfc-reader__tag-option label span:last-child { + color: var(--rfc-reader-muted); + font-variant-numeric: tabular-nums; +} + +.pp-rfc-reader__clear-tags { + margin-top: 0.45rem; + padding: 0.32rem 0.4rem; + border: 0; + border-radius: 7px; + background: transparent; + color: var(--rfc-reader-accent); + cursor: pointer; + font: inherit; + font-size: 0.59rem; + font-weight: 710; +} + +.pp-rfc-reader__active-tags { + display: flex; + min-width: 0; + flex: 0 1 auto; + gap: 0.22rem; +} + +.pp-rfc-reader__active-tags[hidden] { + display: none; +} + +.pp-rfc-reader__active-tag, +.pp-rfc-reader__tag-chip { + min-height: 1.55rem; + padding: 0.24rem 0.48rem; + border: 1px solid rgba(49, 93, 240, 0.28); + border-radius: 999px; + background: rgba(49, 93, 240, 0.055); + color: var(--rfc-reader-accent); + cursor: pointer; + font: inherit; + font-size: 0.57rem; + font-weight: 680; +} + +.pp-rfc-reader__active-tag { + overflow: hidden; + max-width: 7.5rem; + flex: 0 1 auto; + padding: 0.22rem 0.42rem; + text-overflow: ellipsis; + white-space: nowrap; +} + +.pp-rfc-reader__active-tag:hover, +.pp-rfc-reader__tag-chip:hover { + background: rgba(49, 93, 240, 0.11); +} + +.pp-rfc-reader__tag-chip[aria-pressed="true"] { + border-color: var(--rfc-reader-accent); + background: var(--rfc-reader-accent); + color: #fff; +} + +@media (forced-colors: active) { + .pp-rfc-reader__tag-option input { + top: 50%; + left: 0.4rem; + width: 0.9rem; + height: 0.9rem; + opacity: 1; + transform: translateY(-50%); + } + + .pp-rfc-reader__tag-option label::before, + .pp-rfc-reader__tag-option label::after { + display: none; + } +} + +.pp-rfc-reader__reset, +.pp-rfc-reader__back { + min-height: 2.2rem; + padding: 0.45rem 0.62rem; + border: 0; + border-radius: 9px; + background: transparent; + color: var(--rfc-reader-accent); + cursor: pointer; + font: inherit; + font-size: 0.66rem; + font-weight: 720; +} + +.pp-rfc-reader__statusbar { + display: flex; + gap: 0.55rem 0.8rem; + align-items: center; + justify-content: space-between; + min-height: 2rem; + padding: 0 0.8rem 0.45rem; +} + +.pp-rfc-reader__count, +.pp-rfc-reader__hint { + margin: 0; + color: var(--rfc-reader-muted); + font-size: 0.61rem; + line-height: 1.4; +} + +.pp-rfc-reader__count { + padding: 0; +} + +.pp-rfc-reader__hint { + margin-left: auto; + opacity: 0; + transition: opacity 120ms ease; + white-space: nowrap; +} + +.pp-rfc-reader[data-list-focus="true"] .pp-rfc-reader__hint { + opacity: 1; +} + +.pp-rfc-reader__master { + display: grid; + grid-template-rows: auto minmax(0, 1fr); + min-width: 0; + min-height: 0; + overflow: hidden; + border-top: 1px solid var(--rfc-reader-line); + background: rgba(248, 251, 255, 0.38); +} + +.pp-rfc-reader__list-header, +.pp-rfc-row { + display: grid; + grid-template-columns: 2.8rem 5rem minmax(6.8rem, 1fr); + gap: 0.5rem; + align-items: center; +} + +.pp-rfc-reader__list-header { + position: relative; + z-index: 3; + min-height: 2.45rem; + padding: 0 0.78rem; + border-bottom: 1px solid var(--rfc-reader-line); + color: var(--rfc-reader-muted); + font-size: 0.53rem; + font-weight: 760; + letter-spacing: 0.04em; + text-transform: uppercase; +} + +.pp-rfc-reader__status-slot { + min-width: 0; +} + +.md-typeset details.pp-rfc-reader__status-filter { + position: relative; + min-width: 0; + margin: 0; + padding: 0; + border: 0; + background: transparent; + box-shadow: none; + color: var(--rfc-reader-muted); + font: inherit; +} + +.md-typeset details.pp-rfc-reader__status-filter > summary { + display: flex; + min-height: 1.8rem; + align-items: center; + justify-content: space-between; + gap: 0.2rem; + margin: 0; + padding: 0.24rem 1rem 0.24rem 0.3rem; + border: 1px solid transparent; + border-radius: 7px; + cursor: pointer; + list-style: none; +} + +.md-typeset details.pp-rfc-reader__status-filter > summary::-webkit-details-marker, +.md-typeset details.pp-rfc-reader__status-filter > summary::before { + display: none; +} + +.md-typeset details.pp-rfc-reader__status-filter > summary::after { + top: 50%; + right: 0.22rem; + width: 0.7rem; + height: 0.7rem; + transform: translateY(-50%); +} + +.md-typeset details.pp-rfc-reader__status-filter[open] > summary, +.md-typeset details.pp-rfc-reader__status-filter > summary:hover, +.md-typeset details.pp-rfc-reader__status-filter[data-filtered="true"] > summary { + border-color: var(--rfc-reader-line); + background: rgba(49, 93, 240, 0.045); + color: var(--rfc-reader-accent); +} + +.pp-rfc-reader__status-summary-state { + min-width: 0; + overflow: hidden; + color: var(--rfc-reader-accent); + font-size: 0.48rem; + font-weight: 800; + letter-spacing: 0; + text-overflow: ellipsis; + text-transform: none; + white-space: nowrap; +} + +.pp-rfc-reader__status-summary-state[hidden] { + display: none; +} + +.pp-rfc-reader__status-panel { + position: absolute; + z-index: 6; + top: calc(100% + 0.25rem); + left: -0.2rem; + display: grid; + width: 14rem; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 0.65rem; + padding: 0.65rem; + border: 1px solid var(--rfc-reader-line); + border-radius: 11px; + background: var(--rfc-reader-surface-strong); + box-shadow: 0 18px 45px rgba(55, 74, 123, 0.16); + color: var(--rfc-reader-copy); + font-size: 0.6rem; + font-weight: 620; + letter-spacing: 0; + text-transform: none; +} + +.pp-rfc-reader__status-panel fieldset { + display: grid; + align-content: start; + gap: 0.12rem; + margin: 0; + padding: 0; + border: 0; +} + +.pp-rfc-reader__status-panel legend { + margin-bottom: 0.28rem; + color: var(--rfc-reader-ink); + font-size: 0.58rem; + font-weight: 760; +} + +.pp-rfc-reader__status-option { + display: flex; + min-height: 1.65rem; + align-items: center; + gap: 0.35rem; + padding: 0.22rem 0.25rem; + border-radius: 6px; + cursor: pointer; +} + +.pp-rfc-reader__status-option:hover { + background: rgba(49, 93, 240, 0.055); +} + +.pp-rfc-reader__status-option input { + width: 0.75rem; + height: 0.75rem; + accent-color: var(--rfc-reader-accent); +} + +.pp-rfc-reader__status-option span { + min-width: 0; + line-height: 1.25; +} + +.pp-rfc-reader__records { + min-height: 0; + max-height: none; + margin: 0; + padding: 0; + overflow-y: auto; + border: 0; + outline: 0; + scrollbar-color: rgba(49, 93, 240, 0.28) transparent; + scrollbar-gutter: stable; +} + +.pp-rfc-row { + position: relative; + min-height: 2.75rem; + padding: 0.45rem 0.78rem; + border-bottom: 1px solid var(--rfc-reader-line); + color: var(--rfc-reader-copy); + cursor: pointer; + font-size: 0.6rem; + line-height: 1.32; + transition: background-color 150ms ease, box-shadow 150ms ease; +} + +.pp-rfc-row[hidden] { + display: none; +} + +.pp-rfc-row:hover { + background: rgba(49, 93, 240, 0.045); +} + +.pp-rfc-row__number { + color: var(--rfc-reader-accent); + font-size: 0.64rem; + font-variant-numeric: tabular-nums; + font-weight: 740; +} + +.pp-rfc-row__status { + color: var(--rfc-reader-muted); +} + +.pp-rfc-row__title { + display: -webkit-box; + overflow: hidden; + color: var(--rfc-reader-ink); + font-weight: 680; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; +} + +.pp-rfc-row[aria-selected="true"] { + background: linear-gradient(90deg, rgba(10, 174, 232, 0.08), rgba(123, 92, 255, 0.07)); + box-shadow: inset 3px 0 0 var(--rfc-reader-accent-cyan); +} + +.pp-rfc-reader__records:focus-visible .pp-rfc-row[aria-selected="true"] { + outline: 2px solid var(--rfc-reader-accent); + outline-offset: -2px; +} + +.pp-rfc-reader__empty { + padding: 2.4rem 1rem; + color: var(--rfc-reader-copy); + font-size: 0.72rem; + text-align: center; +} + +.pp-rfc-reader__detail { + min-width: 0; + min-height: 0; + overflow-y: auto; + background: + radial-gradient(circle at 15% 0, rgba(123, 92, 255, 0.045), transparent 27rem), + rgba(255, 255, 255, 0.58); + scrollbar-color: rgba(49, 93, 240, 0.28) transparent; + scrollbar-gutter: stable; +} + +.pp-rfc-reader__detail:focus-visible { + outline: 2px solid var(--rfc-reader-accent); + outline-offset: -2px; +} + +.pp-rfc-reader__detail-header { + position: sticky; + z-index: 2; + top: 0; + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + gap: 0.75rem 1.2rem; + align-items: end; + padding: clamp(1rem, 1.9vw, 1.55rem) clamp(1rem, 1.9vw, 1.7rem) 0.9rem; + border-bottom: 1px solid var(--rfc-reader-line); + background: rgba(251, 252, 255, 0.91); + backdrop-filter: blur(14px); +} + +.pp-rfc-reader__detail-heading { + min-width: 0; +} + +.pp-rfc-reader__detail-body { + padding: 0 clamp(1rem, 1.9vw, 1.7rem) clamp(1rem, 1.9vw, 1.7rem); +} + +.pp-rfc-reader__back { + display: none; + margin: -0.55rem 0 0.8rem -0.35rem; +} + +.pp-rfc-reader__eyebrow { + margin: 0; + color: var(--rfc-reader-accent); + font-size: 0.66rem; + font-weight: 800; + letter-spacing: 0.04em; + text-transform: uppercase; +} + +.md-typeset .pp-rfc-reader__detail h2 { + margin: 0.35rem 0 0; + color: var(--rfc-reader-ink); + font-size: clamp(1.3rem, 1.9vw, 1.7rem); + line-height: 1.12; +} + +.pp-rfc-reader__metadata { + display: flex; + flex-wrap: wrap; + gap: 0; + margin: 1rem 0 0; +} + +.pp-rfc-reader__metadata div { + min-width: 6rem; + padding: 0.2rem 0.9rem 0.2rem 0; +} + +.pp-rfc-reader__metadata div + div { + padding-left: 0.9rem; + border-left: 1px solid var(--rfc-reader-line); +} + +.pp-rfc-reader__metadata dt, +.pp-rfc-reader__metadata dd { + margin: 0; +} + +.pp-rfc-reader__metadata dt { + color: var(--rfc-reader-muted); + font-size: 0.56rem; + line-height: 1.3; +} + +.pp-rfc-reader__metadata dd { + margin-top: 0.25rem; + color: var(--rfc-reader-copy); + font-size: 0.66rem; + line-height: 1.38; +} + +.pp-rfc-reader__metadata a { + color: var(--rfc-reader-accent); + text-decoration: none; +} + +.pp-rfc-reader__source-links { + display: inline-flex; + flex-wrap: wrap; + gap: 0.2rem 0.35rem; +} + +.pp-rfc-reader__detail-tags { + display: flex; + flex-wrap: wrap; + gap: 0.28rem; +} + +.pp-rfc-reader__status-pill { + display: inline-flex; + min-height: 1.75rem; + align-items: center; + padding: 0.3rem 0.55rem; + border: 1px solid rgba(49, 93, 240, 0.46); + border-radius: 7px; + background: rgba(49, 93, 240, 0.04); + color: var(--rfc-reader-accent); + font-size: 0.6rem; + font-weight: 710; +} + +.md-typeset .pp-rfc-reader__open { + display: inline-flex; + min-height: 2.75rem; + align-items: center; + margin: 0; + color: var(--rfc-reader-accent); + font-size: 0.7rem; + font-weight: 740; + text-decoration: none; +} + +.pp-rfc-reader__section { + margin-top: 1rem; + padding-top: 1rem; + border-top: 1px solid var(--rfc-reader-line); +} + +.md-typeset .pp-rfc-reader__section h3 { + margin: 0; + color: var(--rfc-reader-ink); + font-size: 0.86rem; +} + +.md-typeset .pp-rfc-reader__section p { + max-width: 66ch; + margin: 0.6rem 0 0; + color: var(--rfc-reader-copy); + font-size: 0.72rem; + line-height: 1.72; +} + +.pp-rfc-reader__related { + display: flex; + flex-wrap: wrap; + gap: 0.4rem 0.75rem; + margin-top: 0.65rem; +} + +.md-typeset .pp-rfc-reader__related a { + color: var(--rfc-reader-accent); + font-size: 0.65rem; + font-weight: 680; + text-decoration: none; +} + +.pp-rfc-fallback { + margin-top: 1.4rem; +} + +.pp-rfc-fallback table { + background: rgba(255, 255, 255, 0.72); +} + +@media screen and (max-width: 75em) { + .pp-rfc-reader { + grid-template-columns: minmax(19rem, 21rem) minmax(0, 1fr); + } + + .pp-rfc-reader__list-header, + .pp-rfc-row { + grid-template-columns: 2.8rem 5rem minmax(7.5rem, 1fr); + } + +} + +@media screen and (min-width: 56.001em) { + .pp-rfc-reader { + height: clamp(32rem, 72dvh, 43rem); + } + + .pp-rfc-reader__records, + .pp-rfc-reader__detail { + overscroll-behavior-y: contain; + } +} + +@media screen and (max-width: 56em) { + body:has(.pp-rfc-reader-host) .md-content__inner { + padding: 1rem 0.75rem 4rem; + } + + .pp-rfc-index-header { + margin-right: 0; + margin-left: 0; + } + + .pp-rfc-index-header { + display: block; + } + + .md-typeset .pp-rfc-index-links { + margin-top: 0.55rem; + } + + .pp-rfc-reader { + display: block; + overflow: hidden; + border: 1px solid var(--rfc-reader-line); + border-radius: 14px; + background: var(--rfc-reader-surface); + box-shadow: 0 24px 60px rgba(55, 74, 123, 0.09); + } + + .pp-rfc-reader:has(.pp-rfc-reader__search[data-tags-open="true"]) { + overflow: visible; + } + + .pp-rfc-reader:has(.pp-rfc-reader__search[data-tags-open="true"]) .pp-rfc-reader__index { + overflow: visible; + } + + .pp-rfc-reader__toolbar { + background: transparent; + } + + .pp-rfc-reader__statusbar { + padding: 0.1rem 1rem 0.7rem; + } + + .pp-rfc-reader__index { + display: block; + border-right: 0; + } + + .pp-rfc-reader__master { + border-top: 1px solid var(--rfc-reader-line); + border-right: 0; + } + + .pp-rfc-reader__records { + max-height: none; + overflow-y: visible; + scrollbar-gutter: auto; + } + + .pp-rfc-reader[data-mobile-view="results"] .pp-rfc-reader__detail, + .pp-rfc-reader[data-mobile-view="detail"] .pp-rfc-reader__index { + display: none; + } + + .pp-rfc-reader__back { + display: inline-flex; + } + + .pp-rfc-reader__hint { + display: none; + } + + .pp-rfc-reader__detail { + overflow: visible; + scrollbar-gutter: auto; + } + + .pp-rfc-reader__detail-header { + position: static; + display: block; + padding: 1.15rem 1.15rem 0.9rem; + } + + .pp-rfc-reader__detail-body { + padding: 0 1.15rem 1.15rem; + } + + .md-typeset .pp-rfc-reader__open { + margin-top: 0.8rem; + } +} + +@media screen and (max-width: 38em) { + .pp-rfc-reader__toolbar { + padding: 0.8rem; + } + + .pp-rfc-reader__facets { + gap: 0.35rem 0.45rem; + } + + .pp-rfc-reader__search kbd { + display: none; + } + + .pp-rfc-reader__active-tag { + max-width: 5.5rem; + } + + .pp-rfc-segments { + grid-column: 1 / -1; + overflow-x: auto; + } + + .md-typeset details.pp-rfc-reader__status-filter { + grid-column: 1 / -1; + width: 100%; + color: var(--rfc-reader-copy); + font-size: 0.63rem; + font-weight: 650; + letter-spacing: 0; + text-transform: none; + } + + .md-typeset details.pp-rfc-reader__status-filter > summary { + min-height: 2.05rem; + padding: 0.42rem 1.45rem 0.42rem 0.55rem; + border-color: var(--rfc-reader-line); + background: rgba(255, 255, 255, 0.82); + } + + .pp-rfc-reader__status-panel { + left: 0; + width: min(18rem, calc(100vw - 3rem)); + } + + .pp-rfc-reader__reset { + grid-column: 1 / -1; + justify-self: start; + } + + .pp-rfc-reader__list-header, + .pp-rfc-row { + grid-template-columns: 2.8rem minmax(0, 1fr); + gap: 0.55rem; + } + + .pp-rfc-reader__list-header > :nth-child(2), + .pp-rfc-row__status { + display: none; + } + + .pp-rfc-row { + min-height: 3.8rem; + } + + .pp-rfc-reader__detail { + min-height: 0; + } + + .pp-rfc-reader__metadata { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 0.75rem; + } + + .pp-rfc-reader__metadata div, + .pp-rfc-reader__metadata div + div { + min-width: 0; + padding: 0; + border-left: 0; + } +} + +body:has(.incql-launch) { + --md-default-bg-color: var(--incql-page); + --md-default-fg-color: var(--incql-page-ink); + --md-default-fg-color--light: var(--incql-page-copy); + --md-default-fg-color--lighter: var(--incql-page-muted); + --md-primary-fg-color: rgba(255, 255, 255, 0.84); + --md-primary-bg-color: var(--incql-page-ink); + --md-typeset-color: var(--incql-page-ink); + --md-typeset-a-color: var(--incql-page-blue); + background: var(--incql-page); +} + +body:has(.incql-launch) .md-header { + border-bottom: 1px solid rgba(96, 126, 186, 0.18); + background: rgba(248, 251, 255, 0.82); + box-shadow: 0 18px 60px rgba(55, 74, 123, 0.08); +} + +body:has(.incql-launch) .md-header__title, +body:has(.incql-launch) .md-header__button, +body:has(.incql-launch) .md-tabs__link, +body:has(.incql-launch) .md-source, +body:has(.incql-launch) .md-source__repository { + color: var(--incql-page-ink); +} + +body:has(.incql-launch) .md-tabs { + background: rgba(248, 251, 255, 0.72); +} + +body:has(.incql-launch) .md-tabs__item--active { + border-bottom-color: var(--incql-page-blue); +} + +body:has(.incql-launch) .md-search__form { + border-color: rgba(77, 103, 158, 0.18); + background: rgba(255, 255, 255, 0.82); + box-shadow: 0 10px 34px rgba(55, 74, 123, 0.08); +} + +body:has(.incql-launch) .md-search__input, +body:has(.incql-launch) .md-search__input::placeholder { + color: var(--incql-page-muted); +} + +body:has(.incql-launch) .md-main__inner { + max-width: none; + margin: 0; +} + +body:has(.incql-launch) .md-content { + min-width: 0; +} + +body:has(.incql-launch) .md-content__inner { + margin: 0; + padding: 0; +} + +body:has(.incql-launch) .md-content__inner::before { + display: none; +} + +body:has(.incql-launch) .md-sidebar--secondary { + display: none; +} + +body:has(.incql-launch) .md-typeset { + color: var(--incql-page-ink); +} + +body:has(.incql-launch) .md-typeset h1, +body:has(.incql-launch) .md-typeset h2, +body:has(.incql-launch) .md-typeset h3, +body:has(.incql-launch) .md-typeset h4 { + color: var(--incql-page-ink); +} + +body:has(.incql-launch) .md-typeset p, +body:has(.incql-launch) .md-typeset li { + color: var(--incql-page-copy); +} + +.incql-launch { + position: relative; + margin: 0 calc(50% - 50vw); + overflow: hidden; + background: + radial-gradient(circle at 72% 11rem, rgba(80, 136, 255, 0.2), transparent 38rem), + radial-gradient(circle at 92% 26rem, rgba(198, 109, 255, 0.14), transparent 35rem), + linear-gradient(180deg, #f8fbff 0%, #f3f7ff 46rem, #ffffff 100%); +} + +.incql-launch > section { + width: min(100% - 2rem, 1760px); + margin: 0 auto; +} + +.incql-gradient-text { + background: linear-gradient(100deg, var(--incql-page-blue) 0%, #6e6cff 46%, var(--incql-page-magenta) 100%); + background-clip: text; + color: transparent; +} + +.incql-eyebrow { + display: inline-flex; + margin: 0 0 0.88rem; + padding: 0.38rem 0.62rem; + border: 1px solid rgba(75, 105, 255, 0.18); + border-radius: 999px; + background: rgba(79, 116, 255, 0.07); + color: #315df0; + font-size: 0.62rem; + font-weight: 830; + letter-spacing: 0.08em; + line-height: 1; + text-transform: uppercase; +} + +.incql-hero { + position: relative; + display: flex; + align-items: flex-start; + min-height: clamp(23rem, 40svh, 26rem); + padding: clamp(0.8rem, 1.4vw, 1.3rem) 0 clamp(0.6rem, 1vw, 0.8rem); +} + +.incql-hero::before { + content: ""; + position: absolute; + z-index: 1; + inset: 0 41% 0 0; + background: + linear-gradient(90deg, rgba(248, 251, 255, 0.98) 0%, rgba(248, 251, 255, 0.92) 58%, rgba(248, 251, 255, 0) 100%); + pointer-events: none; +} + +.incql-hero__copy { + position: relative; + z-index: 2; + max-width: 34rem; +} + +.incql-hero h1 { + max-width: 19ch; + margin: 0 0 0.86rem; + color: var(--incql-page-ink); + font-size: clamp(2.1rem, 2.45vw, 2.7rem); + font-weight: 850; + line-height: 1; + letter-spacing: 0; +} + +.incql-hero__intro { + max-width: 32rem; + margin: 0; + color: var(--incql-page-copy) !important; + font-size: clamp(0.92rem, 1vw, 1.05rem); + line-height: 1.58; +} + +.incql-actions { + display: flex; + flex-wrap: wrap; + gap: 0.8rem; + margin-top: 1.35rem; +} + +.md-typeset .incql-button { + display: inline-flex; + align-items: center; + justify-content: center; + min-height: 2.3rem; + padding: 0.52rem 0.9rem; + border: 1px solid rgba(72, 100, 160, 0.2); + border-radius: var(--incql-radius-md); + background: rgba(255, 255, 255, 0.76); + color: var(--incql-page-ink); + font-size: 0.86rem; + font-weight: 730; + text-decoration: none; + box-shadow: 0 14px 36px rgba(55, 74, 123, 0.08); +} + +.md-typeset .incql-button:hover, +.md-typeset .incql-button:focus { + border-color: rgba(71, 109, 255, 0.42); + color: #315df0; +} + +.md-typeset .incql-button--primary { + border-color: #071534; + background: #071534; + color: #ffffff; + box-shadow: 0 18px 40px rgba(7, 21, 52, 0.22); +} + +.md-typeset .incql-button--primary:hover, +.md-typeset .incql-button--primary:focus { + color: #ffffff; +} + +.incql-proof-row { + display: grid; + grid-template-columns: repeat(4, minmax(6rem, 1fr)); + gap: 0.6rem; + max-width: 43rem; + margin-top: clamp(1.2rem, 2vw, 1.8rem); + padding-top: 0; +} + +.incql-proof-row span { + min-width: 0; + padding: 0.66rem 0.7rem; + border: 1px solid rgba(91, 115, 255, 0.16); + border-radius: 12px; + background: rgba(255, 255, 255, 0.56); + color: var(--incql-page-muted); + font-size: 0.72rem; + line-height: 1.35; + box-shadow: 0 14px 34px rgba(55, 74, 123, 0.07), inset 0 1px 0 rgba(255, 255, 255, 0.72); +} + +.incql-proof-row strong { + display: block; + margin-bottom: 0.25rem; + color: var(--incql-page-ink); + font-size: 0.76rem; +} + +.incql-hero__visual { + position: absolute; + z-index: 0; + inset: 0 calc(50% - 50vw) 0 33%; + min-height: 0; + margin-right: 0; + pointer-events: none; +} + +.incql-hero__visual img { + display: block; + width: 100%; + height: 100%; + min-height: 0; + object-fit: cover; + object-position: center right; + filter: saturate(1.04) contrast(1.02); +} + +.incql-launch > .incql-convergence-section, +.incql-launch > .incql-process, +.incql-launch > .incql-prism-visible, +.incql-launch > .incql-surfaces, +.incql-launch > .incql-trust, +.incql-launch > .incql-final-cta { + margin-top: clamp(1.6rem, 3vw, 2.7rem); +} + +.incql-launch > .incql-convergence-section { + margin-top: 0; +} + +.incql-convergence-section, +.incql-process, +.incql-prism-visible, +.incql-surfaces, +.incql-final-cta { + border: 1px solid var(--incql-page-line); + border-radius: var(--incql-radius-xl); + background: var(--incql-page-panel); + box-shadow: var(--incql-page-shadow); + overflow: hidden; +} + +.incql-convergence-section { + display: grid; + grid-template-columns: minmax(18rem, 0.34fr) minmax(0, 1fr); + gap: clamp(1.25rem, 2.2vw, 2.6rem); + align-items: center; + min-height: clamp(23rem, 27vw, 28rem); + padding: clamp(1.3rem, 2.5vw, 2.6rem); + background: + radial-gradient(circle at 72% 48%, rgba(92, 148, 255, 0.13), transparent 34rem), + radial-gradient(circle at 88% 54%, rgba(190, 98, 255, 0.09), transparent 28rem), + linear-gradient(110deg, rgba(255, 255, 255, 0.95), rgba(248, 251, 255, 0.8)), + rgba(255, 255, 255, 0.9); +} + +.incql-convergence-section h2, +.incql-process h2, +.incql-prism-visible h2, +.incql-surfaces h2, +.incql-trust h2, +.incql-final-cta h2 { + margin: 0 0 0.7rem; + color: var(--incql-page-ink); + font-size: clamp(1.35rem, 1.52vw, 1.78rem); + line-height: 1.08; +} + +.incql-section-kicker { + margin: 0 0 0.62rem !important; + color: #315df0 !important; + font-size: 0.58rem !important; + font-weight: 830; + letter-spacing: 0.1em; + line-height: 1; + text-transform: uppercase; +} + +.incql-friction-list { + display: grid; + gap: 0; + margin-top: clamp(1rem, 1.7vw, 1.4rem); + border-top: 1px solid var(--incql-page-line); +} + +.incql-friction-list article { + display: grid; + grid-template-columns: 3.1rem minmax(0, 1fr); + gap: 0.9rem; + align-items: start; + padding: clamp(0.7rem, 1vw, 0.9rem) 0; + border-bottom: 1px solid var(--incql-page-line); +} + +.incql-friction-list article > span { + display: inline-flex; + align-items: center; + justify-content: center; + width: 2.45rem; + height: 2.45rem; + border: 1px solid rgba(71, 109, 255, 0.2); + border-radius: 11px; + background: rgba(255, 255, 255, 0.68); + color: var(--incql-page-blue); + font-size: 0.62rem; + font-weight: 830; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.82), 0 10px 28px rgba(55, 74, 123, 0.07); +} + +.incql-friction-list h3 { + margin: 0 0 0.22rem; + color: var(--incql-page-ink); + font-size: clamp(0.78rem, 0.86vw, 0.94rem); + line-height: 1.22; +} + +.incql-friction-list p { + margin: 0; + color: var(--incql-page-copy) !important; + font-size: clamp(0.75rem, 0.78vw, 0.82rem); + line-height: 1.42; +} + +.incql-step-card, +.incql-code-card, +.incql-plan-card, +.incql-engine-card, +.incql-surface-demo, +.incql-same-plan, +.incql-trust article { + border: 1px solid var(--incql-page-line); + border-radius: var(--incql-radius-lg); + background: rgba(255, 255, 255, 0.66); + box-shadow: 0 16px 44px rgba(55, 74, 123, 0.07); + min-width: 0; + overflow: hidden; +} + +.incql-step-card h3 { + margin: 0 0 0.45rem; + color: var(--incql-page-ink); + font-size: 0.9rem; + line-height: 1.28; + overflow-wrap: anywhere; +} + +.incql-step-card p { + margin: 0; + color: var(--incql-page-copy) !important; + font-size: 0.76rem; + line-height: 1.52; + overflow-wrap: anywhere; +} + +.incql-diagnosis { + align-self: center; + max-width: 29rem; + min-width: 0; + padding: 0; + border: 0; + background: transparent; + box-shadow: none; +} + +.incql-diagnosis h2 { + max-width: 13ch; + margin-bottom: 0.95rem; + font-size: clamp(1.6rem, 1.8vw, 2.15rem); + line-height: 1.1; +} + +.incql-diagnosis > p { + max-width: 27rem; + margin: 0; + color: var(--incql-page-copy) !important; + font-size: clamp(0.82rem, 0.9vw, 0.96rem); + line-height: 1.55; +} + +.incql-semantic-map { + position: relative; + min-width: 0; + padding-left: clamp(0.85rem, 1.75vw, 2.05rem); + border-left: 1px solid var(--incql-page-line); +} + +.incql-map-heading { + position: relative; + z-index: 2; +} + +.incql-map-heading h2 { + margin-bottom: 0.4rem; + font-size: clamp(1.45rem, 1.62vw, 1.9rem); + line-height: 1.08; +} + +.incql-map-heading p { + margin: 0; + color: var(--incql-page-copy) !important; + font-size: clamp(0.78rem, 0.84vw, 0.9rem); + line-height: 1.45; +} + +.incql-map-body { + position: relative; + display: block; + min-height: clamp(21rem, 26vw, 28rem); + margin-top: clamp(0.95rem, 1.6vw, 1.45rem); + border-radius: clamp(1rem, 1.55vw, 1.4rem); + background: linear-gradient(90deg, rgba(255, 255, 255, 0.38), rgba(255, 255, 255, 0.04) 42%, rgba(255, 255, 255, 0.2) 100%); + isolation: isolate; + overflow: visible; +} + +.incql-map-body::before { + content: ""; + position: absolute; + z-index: 0; + inset: -6% -4%; + background: url("../shared/prismplane/semantic-convergence-stage.png") center / 80% auto no-repeat; + filter: saturate(1.04) contrast(1.01); + mix-blend-mode: multiply; + opacity: 0.88; + -webkit-mask-image: radial-gradient(ellipse 84% 78% at 50% 50%, #000 50%, rgba(0, 0, 0, 0.92) 66%, transparent 100%); + mask-image: radial-gradient(ellipse 84% 78% at 50% 50%, #000 50%, rgba(0, 0, 0, 0.92) 66%, transparent 100%); + pointer-events: none; +} + +.incql-map-body::after { + content: ""; + position: absolute; + z-index: 0; + inset: 18% 18% 10% 28%; + border-radius: 999px; + background: radial-gradient(circle, rgba(86, 139, 255, 0.12), rgba(190, 98, 255, 0.06) 46%, transparent 72%); + filter: blur(14px); + pointer-events: none; +} + +.incql-map-sources, +.incql-map-targets { + position: absolute; + z-index: 2; + display: grid; + gap: clamp(1.1rem, 1.8vw, 1.95rem); + min-width: 0; +} + +.incql-map-sources { + top: 19%; + left: 6.2%; + width: 24%; +} + +.incql-map-targets { + top: 19%; + right: 3.4%; + width: 23.4%; +} + +.incql-map-sources article, +.incql-map-targets article { + min-width: 0; + min-height: clamp(2.55rem, 3.15vw, 3.35rem); + border: 1px solid rgba(91, 115, 255, 0.12); + border-radius: 13px; + background: rgba(255, 255, 255, 0.76); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.78), 0 16px 36px rgba(55, 74, 123, 0.08); +} + +.incql-map-sources article { + display: grid; + grid-template-columns: clamp(1.85rem, 2.25vw, 2.4rem) minmax(0, 1fr); + gap: clamp(0.46rem, 0.6vw, 0.62rem); + align-items: center; + padding: clamp(0.38rem, 0.52vw, 0.48rem) clamp(0.54rem, 0.74vw, 0.7rem); +} + +.incql-map-sources article > span { + display: inline-flex; + align-items: center; + justify-content: center; + width: clamp(1.7rem, 2vw, 2.1rem); + height: clamp(1.7rem, 2vw, 2.1rem); + border: 1px solid rgba(71, 109, 255, 0.14); + border-radius: 9px; + background: rgba(249, 252, 255, 0.78); + color: var(--incql-page-blue); + font-size: clamp(0.5rem, 0.55vw, 0.6rem); + font-weight: 820; + letter-spacing: 0.02em; +} + +.incql-map-sources strong, +.incql-map-targets strong { + display: block; + color: var(--incql-page-ink); + font-size: clamp(0.75rem, 0.78vw, 0.84rem); + line-height: 1.16; + overflow-wrap: normal; +} + +.incql-map-sources p, +.incql-map-targets p, +.incql-map-targets small { + display: block; + margin: 0.12rem 0 0; + color: var(--incql-page-copy) !important; + font-size: clamp(0.72rem, 0.74vw, 0.78rem); + line-height: 1.28; +} + +.incql-target-copy { + display: block; + min-width: 0; +} + +.incql-map-core { + position: absolute; + z-index: 3; + top: 16%; + left: 39.6%; + display: flex; + flex-direction: column; + justify-content: center; + width: 20.8%; + height: 66%; + min-width: 0; + min-height: 0; + padding: clamp(0.72rem, 1.05vw, 1rem); + border: 0; + border-radius: 0; + background: transparent; + text-align: center; + box-shadow: none; +} + +.incql-map-core img { + display: block; + width: clamp(4.5rem, 5.25vw, 5.75rem); + height: auto; + margin: 0 auto 0.5rem; + filter: drop-shadow(0 12px 28px rgba(80, 108, 255, 0.24)); +} + +.incql-map-core > strong { + display: block; + color: var(--incql-page-ink); + font-size: clamp(2rem, 2.35vw, 2.75rem); + line-height: 1; +} + +.incql-map-core > p { + margin: 0.36rem 0 0; + color: var(--incql-page-ink) !important; + font-size: clamp(0.72rem, 0.78vw, 0.8rem); + font-weight: 740; + line-height: 1.35; + white-space: nowrap; +} + +.incql-map-core ul { + display: grid; + gap: clamp(0.25rem, 0.38vw, 0.4rem); + margin: clamp(0.55rem, 0.8vw, 0.82rem) 0 0 !important; + padding: 0 !important; + list-style: none !important; + text-align: left; +} + +.incql-map-core li { + margin: 0 !important; + padding: clamp(0.28rem, 0.44vw, 0.42rem) clamp(0.42rem, 0.6vw, 0.58rem); + border: 1px solid rgba(71, 109, 255, 0.12); + border-radius: 9px; + background: rgba(255, 255, 255, 0.56); + color: var(--incql-page-copy) !important; + font-size: clamp(0.72rem, 0.74vw, 0.78rem); + font-weight: 670; + line-height: 1.25; + list-style: none !important; +} + +.incql-map-core li::marker, +.incql-map-core li::before { + content: none !important; + display: none !important; +} + +.incql-map-bridge { + position: absolute; + z-index: 3; + top: 50%; + left: 66%; + transform: translate(-50%, -50%); + width: 5rem; + padding: 0.25rem 0.36rem; + border: 1px solid rgba(71, 109, 255, 0.12); + border-radius: 999px; + background: rgba(255, 255, 255, 0.55); + color: #315df0; + font-size: 0.54rem; + font-weight: 800; + line-height: 1.25; + text-align: center; + white-space: normal; + box-shadow: 0 18px 48px rgba(55, 74, 123, 0.1); +} + +.incql-map-bridge span { + display: block; +} + +.incql-map-targets { + padding: 0; + border: 0; + border-radius: 0; + background: transparent; + box-shadow: none; +} + +.incql-map-targets article { + display: grid; + grid-template-columns: clamp(1.75rem, 2vw, 2.1rem) minmax(0, 1fr); + gap: clamp(0.46rem, 0.6vw, 0.62rem); + align-items: center; + min-height: clamp(2.55rem, 3.15vw, 3.35rem); + padding: clamp(0.38rem, 0.52vw, 0.48rem) clamp(0.54rem, 0.72vw, 0.7rem); +} + +.incql-target-icon { + display: inline-flex; + align-items: center; + justify-content: center; + width: clamp(1.7rem, 2vw, 2.1rem); + height: clamp(1.7rem, 2vw, 2.1rem); + border: 1px solid rgba(71, 109, 255, 0.14); + border-radius: 9px; + background: linear-gradient(145deg, rgba(232, 246, 255, 0.9), rgba(241, 232, 255, 0.72)); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.82); +} + +.incql-target-icon img { + display: block; + width: 1rem; + height: 1rem; + opacity: 0.78; + filter: brightness(0) saturate(100%) invert(39%) sepia(82%) saturate(2174%) hue-rotate(213deg) brightness(97%) contrast(93%); +} + +.incql-process, +.incql-prism-visible, +.incql-surfaces { + padding: clamp(1.1rem, 2.4vw, 1.8rem); +} + +.incql-process { + background: rgba(255, 255, 255, 0.88); +} + +.incql-section-heading { + max-width: 55rem; + margin-bottom: 1.05rem; +} + +.incql-section-heading p { + max-width: 46rem; + margin: 0; + color: var(--incql-page-copy) !important; +} + +.incql-process-heading { + max-width: 46rem; + margin-bottom: clamp(0.9rem, 1.6vw, 1.35rem); +} + +.incql-process-stage { + position: relative; + height: clamp(23rem, 30vw, 28rem); + padding: clamp(0.6rem, 1vw, 0.9rem); + border: 0; + border-radius: clamp(1rem, 1.5vw, 1.35rem); + background: radial-gradient(ellipse 56% 72% at 50% 50%, rgba(110, 142, 255, 0.08), transparent 74%); + overflow: hidden; + isolation: isolate; +} + +.incql-process-stage::before { + content: ""; + position: absolute; + z-index: 0; + inset: 0; + background: + linear-gradient(90deg, #ffffff 0%, rgba(255, 255, 255, 0) 15%, rgba(255, 255, 255, 0) 85%, #ffffff 100%) center / 100% 100% no-repeat, + url("../shared/prismplane/process-flow-stage-v3.png") 50% 0.75rem / 88% auto no-repeat; + opacity: 0.9; + -webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 12%, #000 82%, transparent 100%); + mask-image: linear-gradient(180deg, transparent 0%, #000 12%, #000 82%, transparent 100%); + pointer-events: none; +} + +.incql-process-rail { + position: relative; + z-index: 2; + display: grid; + grid-template-columns: 0.88fr 0.96fr 1.3fr 0.96fr 0.88fr; + gap: clamp(0.65rem, 1vw, 1rem); + align-items: center; + height: 100%; +} + +.incql-step-card { + display: flex; + min-width: 0; + min-height: clamp(14rem, 17.5vw, 16.5rem); + padding: clamp(0.62rem, 0.78vw, 0.78rem); + flex-direction: column; + border: 1px solid rgba(88, 116, 214, 0.16); + border-radius: clamp(0.72rem, 1vw, 0.95rem); + background: rgba(255, 255, 255, 0.8); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.94), 0 18px 42px rgba(55, 74, 123, 0.1); +} + +.incql-step-card__title { + display: flex; + gap: 0.42rem; + align-items: baseline; + margin-bottom: 0.52rem; +} + +.incql-step-card__title > span { + color: var(--incql-page-blue); + font-size: 0.58rem; + font-weight: 830; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.incql-step-card__title h3 { + margin: 0; + color: var(--incql-page-ink); + font-size: clamp(0.82rem, 0.92vw, 1rem); + line-height: 1; +} + +.incql-step-card > p { + margin: 0; + color: var(--incql-page-copy) !important; + font-size: clamp(0.75rem, 0.78vw, 0.82rem); + line-height: 1.42; +} + +.incql-step-card--prism { + position: relative; + min-height: clamp(20rem, 25vw, 23.5rem); + padding: clamp(0.85rem, 1.2vw, 1.15rem) clamp(0.9rem, 1.3vw, 1.3rem); + border-color: rgba(123, 92, 255, 0.2); + background: linear-gradient(180deg, rgba(255, 255, 255, 0.82) 0%, rgba(255, 255, 255, 0.34) 28%, rgba(255, 255, 255, 0.28) 70%, rgba(255, 255, 255, 0.72) 100%); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.96), 0 22px 48px rgba(55, 74, 123, 0.1); + transform: translateY(0.25rem); +} + +.incql-step-card--author { + border-right-color: transparent; + background: linear-gradient(90deg, rgba(255, 255, 255, 0.95) 0%, rgba(255, 255, 255, 0.8) 68%, rgba(255, 255, 255, 0.34) 100%); + box-shadow: inset 1px 1px 0 rgba(255, 255, 255, 0.94), -10px 18px 42px rgba(55, 74, 123, 0.08); +} + +.incql-step-card--execute { + border-left-color: transparent; + background: linear-gradient(270deg, rgba(255, 255, 255, 0.95) 0%, rgba(255, 255, 255, 0.8) 68%, rgba(255, 255, 255, 0.34) 100%); + box-shadow: inset -1px 1px 0 rgba(255, 255, 255, 0.94), 10px 18px 42px rgba(55, 74, 123, 0.08); +} + +.incql-step-card--prism > .incql-step-card__title, +.incql-step-card--prism > p, +.incql-step-card--prism > .incql-process-list { + position: relative; + z-index: 2; +} + +.incql-step-card--compile, +.incql-step-card--optimize { + min-height: clamp(15.5rem, 19vw, 18rem); + transform: translateY(0.3rem); +} + +.incql-step-card--author, +.incql-step-card--execute { + transform: translateY(0.65rem); +} + +.incql-tag-row { + margin-top: auto !important; + padding-top: 0.72rem; + color: var(--incql-page-blue) !important; + font-size: 0.62rem !important; + font-weight: 780; + letter-spacing: 0.06em; + text-transform: uppercase; +} + +.incql-process-list { + display: grid; + gap: 0.28rem; + margin: auto 0 0 !important; + padding: 0; + list-style: none; +} + +.incql-process-list li { + min-height: 1.42rem; + margin: 0 !important; + padding: 0.28rem 0.44rem; + border: 1px solid rgba(71, 109, 255, 0.1); + border-radius: 8px; + background: rgba(255, 255, 255, 0.72); + color: var(--incql-page-copy) !important; + font-size: 0.74rem; + font-weight: 700; + line-height: 1.18; + list-style: none !important; + box-shadow: 0 9px 20px rgba(55, 74, 123, 0.05); +} + +.incql-process-list li::marker, +.incql-process-list li::before { + content: none !important; + display: none !important; +} + +.incql-mini-code { + margin: auto 0 0.58rem; + padding: 0; + border: 1px solid rgba(71, 109, 255, 0.12); + border-radius: var(--incql-radius-md); + background: var(--md-code-bg-color); + color: var(--md-code-fg-color); + font-size: 0.72rem; + line-height: 1.42; + overflow-x: auto; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.8), 0 10px 22px rgba(55, 74, 123, 0.06); +} + +.incql-mini-code .highlight { + margin: 0; + border-radius: inherit; + background: transparent; + box-shadow: none; + overflow-x: auto; +} + +.incql-mini-code pre { + margin: 0; + background: transparent; +} + +.incql-mini-code pre > code { + padding: 0.62rem 0.7rem; + border: 0; + background: transparent; + color: var(--md-code-fg-color); + font-family: var(--incql-font-mono); + font-size: 0.78rem; + line-height: 1.42; + white-space: pre; + box-shadow: none; +} + +.incql-prism-board { + position: relative; + display: grid; + grid-template-columns: minmax(14rem, 0.84fr) minmax(22rem, 1.34fr) minmax(13rem, 0.72fr); + gap: clamp(0.85rem, 1.35vw, 1.3rem); + align-items: center; + padding: clamp(0.6rem, 1.1vw, 1rem) 0; +} + +.incql-prism-board::before { + content: ""; + position: absolute; + z-index: 0; + top: 50%; + right: 3%; + left: 3%; + height: 2px; + background: linear-gradient(90deg, rgba(10, 174, 232, 0.1), rgba(71, 109, 255, 0.72) 42%, rgba(191, 96, 255, 0.5) 72%, rgba(191, 96, 255, 0.08)); + box-shadow: 0 0 24px rgba(71, 109, 255, 0.28); +} + +.incql-code-card, +.incql-plan-card, +.incql-engine-card { + position: relative; + z-index: 1; + min-height: clamp(14rem, 17vw, 16.5rem); + padding: clamp(0.78rem, 1.1vw, 1.05rem); + min-width: 0; +} + +.incql-code-card, +.incql-engine-card { + border-color: rgba(91, 115, 255, 0.16); + background: rgba(255, 255, 255, 0.82); + backdrop-filter: blur(12px); +} + +.incql-plan-card { + isolation: isolate; + min-height: clamp(17rem, 20vw, 20rem); + border-color: rgba(91, 115, 255, 0.34); + background: + radial-gradient(circle at 86% 12%, rgba(191, 96, 255, 0.17), transparent 15rem), + radial-gradient(circle at 12% 88%, rgba(10, 174, 232, 0.13), transparent 16rem), + rgba(255, 255, 255, 0.94); + box-shadow: 0 30px 72px rgba(55, 74, 123, 0.16), inset 0 1px 0 rgba(255, 255, 255, 0.96); +} + +.incql-plan-card::before { + content: ""; + position: absolute; + z-index: 0; + inset: 3.15rem -8% 0; + background: url("../shared/prismplane/process-flow-stage-v3.png") center 46% / 138% auto no-repeat; + filter: saturate(1.08) contrast(1.04); + mix-blend-mode: multiply; + opacity: 0.42; + -webkit-mask-image: radial-gradient(ellipse 66% 76% at 50% 51%, #000 44%, rgba(0, 0, 0, 0.78) 66%, transparent 100%); + mask-image: radial-gradient(ellipse 66% 76% at 50% 51%, #000 44%, rgba(0, 0, 0, 0.78) 66%, transparent 100%); + pointer-events: none; +} + +.incql-plan-card > * { + position: relative; + z-index: 1; +} + +.incql-feature-card__header { + display: flex; + align-items: center; + gap: 0.68rem; + min-height: 2.7rem; + margin-bottom: 0.85rem; +} + +.incql-feature-card__step { + display: inline-flex; + align-items: center; + justify-content: center; + width: 2.1rem; + height: 2.1rem; + flex: 0 0 auto; + border: 1px solid rgba(71, 109, 255, 0.2); + border-radius: 10px; + background: rgba(71, 109, 255, 0.08); + color: #315df0; + font-size: 0.6rem; + font-weight: 830; + letter-spacing: 0.08em; +} + +.incql-feature-card__header > div { + display: grid; + gap: 0.12rem; + min-width: 0; +} + +.incql-feature-card__header strong { + color: var(--incql-page-ink); + font-size: 0.78rem; + line-height: 1.2; +} + +.incql-feature-card__header small { + color: var(--incql-page-muted); + font-size: 0.72rem; + line-height: 1.25; +} + +.incql-feature-card__step--prism { + border-color: rgba(107, 47, 191, 0.24); + background: rgba(107, 47, 191, 0.08); + color: #6b2fbf; +} + +.incql-feature-card__status { + margin-left: auto; + padding: 0.34rem 0.56rem; + border: 1px solid rgba(71, 109, 255, 0.2); + border-radius: 999px; + background: rgba(71, 109, 255, 0.07); + color: #315df0; + font-size: 0.54rem; + font-weight: 820; + letter-spacing: 0.06em; + text-transform: uppercase; +} + +.incql-code-card .highlight, +.incql-plan-card .highlight, +.incql-surface-demo .highlight { + margin: 0; + border-radius: var(--incql-radius-lg); + background: transparent; + box-shadow: none; + overflow-x: auto; +} + +.incql-code-card pre > code, +.incql-plan-card pre > code, +.incql-surface-demo pre > code { + padding: 1rem 1.08rem; + border-color: rgba(62, 91, 164, 0.18); + background: var(--md-code-bg-color); + color: var(--md-code-fg-color); + font-size: 0.78rem; + line-height: 1.68; + white-space: pre; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.8), 0 12px 30px rgba(55, 74, 123, 0.08); +} + +.incql-code-card pre, +.incql-plan-card pre, +.incql-surface-demo pre { + margin: 0; + background: transparent; +} + +.incql-engine-card ul { + display: grid; + gap: 0.58rem; + margin: 0.2rem 0 0 !important; + padding: 0 !important; + list-style: none !important; +} + +.incql-engine-card li { + display: flex; + align-items: center; + gap: 0.62rem; + min-width: 0; + margin: 0 !important; + padding: 0.7rem 0.76rem; + border: 1px solid rgba(71, 109, 255, 0.12); + border-radius: 11px; + background: rgba(247, 250, 255, 0.86); + color: var(--incql-page-copy) !important; + font-size: 0.78rem; + font-weight: 700; + list-style: none !important; +} + +.incql-engine-card li img { + display: block; + width: 1rem; + height: 1rem; + flex: 0 0 auto; + opacity: 0.76; + filter: brightness(0) saturate(100%) invert(39%) sepia(82%) saturate(2174%) hue-rotate(213deg) brightness(97%) contrast(93%); +} + +.incql-engine-card li span { + min-width: 0; + overflow-wrap: anywhere; +} + +.incql-engine-card li::marker, +.incql-engine-card li::before { + content: none !important; + display: none !important; +} + +.incql-evidence-strip { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 0.5rem; + margin-top: 0.8rem; +} + +.incql-evidence-strip span { + padding: 0.55rem 0.62rem; + border: 1px solid rgba(71, 109, 255, 0.12); + border-radius: 10px; + background: rgba(255, 255, 255, 0.72); + color: var(--incql-page-muted); + font-size: 0.72rem; + line-height: 1.25; +} + +.incql-evidence-strip strong { + display: block; + margin-bottom: 0.18rem; + color: var(--incql-page-ink); + font-size: 0.76rem; +} + +.incql-surfaces { + position: relative; + display: grid; + grid-template-columns: minmax(24rem, 1.18fr) minmax(6rem, 0.2fr) minmax(19rem, 0.82fr); + gap: clamp(0.85rem, 1.35vw, 1.3rem); + align-items: center; + background: + radial-gradient(circle at 50% 64%, rgba(96, 125, 255, 0.09), transparent 26rem), + rgba(255, 255, 255, 0.76); +} + +.incql-surfaces__copy { + grid-column: 1 / -1; + align-self: start; + padding-bottom: 1.15rem; + border-bottom: 1px solid var(--incql-page-line); +} + +.incql-surfaces__copy > p:last-child { + max-width: 36rem; + margin: 0; +} + +.incql-surface-demo, +.incql-same-plan { + position: relative; + padding: clamp(0.82rem, 1.2vw, 1.1rem); + min-width: 0; +} + +.incql-surface-demo { + border-color: rgba(10, 174, 232, 0.2); + background: rgba(255, 255, 255, 0.9); + box-shadow: 0 24px 58px rgba(42, 83, 143, 0.11), inset 0 3px 0 rgba(10, 174, 232, 0.52); +} + +.incql-same-plan { + border-color: rgba(191, 96, 255, 0.2); + background: rgba(255, 255, 255, 0.9); + box-shadow: 0 24px 58px rgba(74, 52, 142, 0.1), inset 0 3px 0 rgba(191, 96, 255, 0.46); +} + +.incql-surface-labels { + position: relative; + display: flex; + flex-wrap: wrap; + gap: 0.4rem; + margin-bottom: 0.9rem; +} + +.incql-surface-labels button { + display: inline-flex; + align-items: center; + justify-content: center; + min-height: 2.2rem; + padding: 0.45rem 0.68rem; + border: 1px solid rgba(71, 109, 255, 0.1); + border-radius: 999px; + background: rgba(247, 250, 255, 0.68); + color: var(--incql-page-copy); + font: inherit; + font-size: 0.75rem; + font-weight: 730; + line-height: 1; + cursor: pointer; + transition: border-color 160ms ease, background 160ms ease, color 160ms ease, box-shadow 160ms ease, transform 160ms ease; +} + +.incql-surface-labels button:hover { + border-color: rgba(71, 109, 255, 0.26); + background: rgba(241, 246, 255, 0.96); + color: var(--incql-page-ink); +} + +.incql-surface-labels button[aria-selected="true"] { + border-color: rgba(71, 109, 255, 0.38); + background: linear-gradient(135deg, rgba(223, 239, 255, 0.98), rgba(238, 226, 255, 0.9)); + color: #2448cf; + box-shadow: 0 8px 22px rgba(71, 109, 255, 0.14), inset 0 1px 0 rgba(255, 255, 255, 0.9); +} + +.incql-surface-labels button:focus-visible { + outline: 3px solid rgba(10, 174, 232, 0.28); + outline-offset: 2px; +} + +.incql-surface-panel { + min-height: 11.5rem; +} + +.incql-surface-panel[hidden] { + display: none !important; +} + +.incql-surface-panel .highlight, +.incql-surface-panel pre, +.incql-surface-panel pre > code { + min-height: inherit; +} + +.incql-surface-bridge { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + gap: 0.5rem; + min-width: 0; +} + +.incql-surface-bridge img { + display: block; + width: clamp(3.5rem, 4.4vw, 4.8rem); + height: auto; + filter: drop-shadow(0 14px 28px rgba(71, 109, 255, 0.22)); +} + +.incql-surface-bridge span { + color: #315df0; + font-size: 0.7rem; + font-weight: 820; + letter-spacing: 0.06em; + line-height: 1.2; + text-align: center; + text-transform: uppercase; +} + +.incql-same-plan__heading { + display: flex; + align-items: baseline; + gap: 0.55rem; + padding-bottom: 0.7rem; + border-bottom: 1px solid var(--incql-page-line); +} + +.incql-same-plan__heading > span { + color: #6b2fbf; + font-size: 0.7rem; + font-weight: 830; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.incql-same-plan__heading h3 { + margin: 0; + font-size: 1rem; +} + +.incql-plan-list { + display: grid; + gap: 0.5rem; + margin: 0.85rem 0 0 !important; + padding: 0 !important; + list-style: none !important; +} + +.incql-plan-list li { + display: grid; + grid-template-columns: minmax(5.8rem, 0.62fr) minmax(0, 1fr); + gap: 0.8rem; + align-items: baseline; + padding: 0.66rem 0.7rem; + border: 1px solid rgba(71, 109, 255, 0.1); + border-radius: 10px; + background: rgba(247, 250, 255, 0.82); +} + +.incql-plan-list li:first-child { + border-top: 1px solid rgba(71, 109, 255, 0.1); +} + +.incql-plan-list strong { + color: var(--incql-page-blue); + font-size: 0.74rem; + letter-spacing: 0.06em; + text-transform: uppercase; +} + +.incql-plan-list span { + color: var(--incql-page-copy); + font-size: 0.8rem; +} + +.incql-trust { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr)); + gap: clamp(0.85rem, 1.35vw, 1.3rem); +} + +.incql-trust article { + position: relative; + min-height: clamp(14rem, 18vw, 16.5rem); + padding: clamp(1.1rem, 2.1vw, 1.8rem); + overflow: hidden; +} + +.incql-trust article::after { + content: ""; + position: absolute; + right: -4.5rem; + bottom: -5.5rem; + width: 16rem; + aspect-ratio: 1; + background: url("../shared/brand/incql-mark.png") center / contain no-repeat; + opacity: 0.07; + pointer-events: none; +} + +.incql-trust__card--confidence { + border-color: rgba(10, 174, 232, 0.2) !important; + background: + radial-gradient(circle at 92% 10%, rgba(10, 174, 232, 0.12), transparent 15rem), + rgba(255, 255, 255, 0.86) !important; +} + +.incql-trust__card--developer { + border-color: rgba(191, 96, 255, 0.2) !important; + background: + radial-gradient(circle at 92% 10%, rgba(191, 96, 255, 0.12), transparent 15rem), + rgba(255, 255, 255, 0.86) !important; +} + +.incql-trust__number { + position: absolute; + top: 1.3rem; + right: 1.45rem; + color: rgba(71, 109, 255, 0.18); + font-size: clamp(2.4rem, 4vw, 4.2rem); + font-weight: 860; + line-height: 1; +} + +.incql-trust article .incql-section-kicker { + max-width: calc(100% - 5rem); + margin-bottom: 0.72rem !important; +} + +.incql-trust ul { + display: grid; + gap: 0.15rem; + margin: 1.15rem 0 0; + padding-left: 1.1rem; +} + +.incql-trust li { + padding: 0.32rem 0; + color: var(--incql-page-copy) !important; +} + +.incql-final-cta { + display: flex; + align-items: center; + justify-content: space-between; + gap: 1.4rem; + margin-bottom: clamp(2.4rem, 4.8vw, 4rem) !important; + padding: clamp(1.05rem, 2.1vw, 1.8rem); + border-color: rgba(109, 180, 255, 0.24); + background: + radial-gradient(circle at 16% 50%, rgba(10, 174, 232, 0.24), transparent 17rem), + radial-gradient(circle at 72% 20%, rgba(191, 96, 255, 0.2), transparent 22rem), + #071534; + box-shadow: 0 34px 84px rgba(7, 21, 52, 0.24); +} + +.incql-final-cta__brand { + display: block; + flex: 0 0 auto; + margin: 0; +} + +.incql-final-cta__mark { + display: block; + width: clamp(4.4rem, 6vw, 6.4rem); + height: auto; + filter: drop-shadow(0 18px 36px rgba(49, 200, 255, 0.18)); +} + +.incql-final-cta__copy { + min-width: 0; +} + +.incql-final-cta h2, +.incql-final-cta p { + margin-right: auto; +} + +.incql-final-cta h2 { + color: #ffffff !important; +} + +.incql-final-cta p { + color: rgba(226, 237, 255, 0.8) !important; +} + +.incql-final-cta .incql-section-kicker { + color: #8beeff !important; +} + +.md-typeset .incql-final-cta .incql-button { + border-color: rgba(255, 255, 255, 0.3); + background: rgba(255, 255, 255, 0.08); + color: #ffffff; + box-shadow: none; +} + +.md-typeset .incql-final-cta .incql-button--primary { + border-color: #ffffff; + background: #ffffff; + color: #071534; + box-shadow: 0 18px 42px rgba(0, 0, 0, 0.2); +} + +.md-typeset .incql-final-cta .incql-button:hover, +.md-typeset .incql-final-cta .incql-button:focus { + border-color: #8beeff; + color: #8beeff; +} + +.md-typeset .incql-final-cta .incql-button--primary:hover, +.md-typeset .incql-final-cta .incql-button--primary:focus { + color: #315df0; +} + +@media screen and (max-width: 76.25em) { + body:has(.incql-launch) .md-main__inner { + margin-top: 0; + } + + .incql-hero, + .incql-convergence-section, + .incql-prism-board, + .incql-surfaces, + .incql-trust { + grid-template-columns: 1fr; + } + + .incql-hero { + min-height: clamp(26rem, 46vw, 29rem); + align-items: flex-start; + padding-top: 1.8rem; + } + + .incql-hero::before { + inset: 0 20% 0 0; + background: linear-gradient(90deg, rgba(248, 251, 255, 0.98) 0%, rgba(248, 251, 255, 0.92) 66%, rgba(248, 251, 255, 0) 100%); + } + + .incql-hero__copy { + max-width: 38rem; + } + + .incql-hero__visual { + position: absolute; + inset: 0 calc(50% - 50vw) 0 38%; + width: auto; + min-height: 0; + margin-right: 0; + } + + .incql-convergence-section { + min-height: 0; + } + + .incql-prism-board::before { + display: none; + } + + .incql-code-card, + .incql-plan-card, + .incql-engine-card { + min-height: 0; + } + + .incql-surface-bridge { + flex-direction: row; + min-height: 4.5rem; + } + + .incql-surface-bridge img { + width: 3.8rem; + } + + .incql-diagnosis { + max-width: 100%; + } + + .incql-semantic-map { + padding-left: 0; + border-left: 0; + } + + .incql-map-body { + grid-template-columns: minmax(10rem, 0.85fr) minmax(13rem, 1fr) minmax(9rem, 0.75fr); + } + + .incql-map-targets { + padding: 0; + border: 0; + background: transparent; + box-shadow: none; + } +} + +@media screen and (max-width: 68em) { + .incql-process-rail { + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 0.85rem; + height: auto; + min-height: auto; + } + + .incql-process-stage { + height: auto; + min-height: auto; + background: radial-gradient(ellipse 62% 42% at 50% 34%, rgba(110, 142, 255, 0.09), transparent 76%); + } + + .incql-process-stage::before { + display: none; + } + + .incql-step-card, + .incql-step-card--prism { + min-height: 14rem; + transform: none; + } + + .incql-step-card--prism { + grid-column: 1 / -1; + min-height: 22rem; + border: 1px solid rgba(123, 92, 255, 0.24); + background: rgba(255, 255, 255, 0.58); + isolation: isolate; + overflow: hidden; + } + + .incql-step-card--prism::before { + content: ""; + position: absolute; + z-index: 0; + inset: 0; + background: + linear-gradient(90deg, #ffffff 0%, rgba(255, 255, 255, 0) 13%, rgba(255, 255, 255, 0) 87%, #ffffff 100%) center / 100% 100% no-repeat, + url("../shared/prismplane/process-flow-stage-v3.png") center 0.7rem / 92% auto no-repeat; + opacity: 0.92; + -webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 14%, #000 84%, transparent 100%); + mask-image: linear-gradient(180deg, transparent 0%, #000 14%, #000 84%, transparent 100%); + pointer-events: none; + } + + .incql-step-card--prism::after { + content: ""; + position: absolute; + z-index: 1; + inset: 0 0 auto; + height: 8.5rem; + background: linear-gradient(180deg, rgba(255, 255, 255, 0.98) 0%, rgba(255, 255, 255, 0.9) 54%, rgba(255, 255, 255, 0) 100%); + pointer-events: none; + } + + .incql-step-card--author, + .incql-step-card--execute { + border-color: rgba(88, 116, 214, 0.16); + background: rgba(255, 255, 255, 0.78); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.94), 0 18px 42px rgba(55, 74, 123, 0.1); + } +} + +@media screen and (max-width: 62em) { + .incql-map-body { + display: grid; + grid-template-columns: 1fr; + gap: 1rem; + min-height: 0; + background: linear-gradient(180deg, rgba(255, 255, 255, 0.88), rgba(255, 255, 255, 0.56)); + } + + .incql-map-body::before { + inset: -4% -7%; + background-size: cover; + opacity: 0.42; + -webkit-mask-image: radial-gradient(ellipse 92% 72% at 50% 50%, #000 42%, transparent 100%); + mask-image: radial-gradient(ellipse 92% 72% at 50% 50%, #000 42%, transparent 100%); + } + + .incql-map-heading h2 { + max-width: 16ch; + } + + .incql-map-sources, + .incql-map-targets { + position: relative; + inset: auto; + width: auto; + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .incql-map-core { + position: relative; + inset: auto; + width: auto; + height: auto; + min-height: 0; + border: 1px solid rgba(123, 92, 255, 0.22); + border-radius: 18px; + background: rgba(255, 255, 255, 0.62); + } + +} + +@media screen and (max-width: 44em) { + .md-typeset { + font-size: 0.82rem; + } + + body:has(.incql-launch) .md-header__topic + .md-header__topic { + display: none; + } + + .incql-launch > section { + width: min(100% - 1.25rem, 1240px); + } + + .incql-hero { + gap: 0.8rem; + min-height: calc(100svh - 3rem); + padding: 1.1rem 0 0.75rem; + } + + .incql-hero::before { + inset: 0; + background: linear-gradient(180deg, rgba(248, 251, 255, 0.96) 0%, rgba(248, 251, 255, 0.82) 54%, rgba(248, 251, 255, 0.2) 100%); + } + + .incql-hero h1 { + max-width: 18ch; + margin-bottom: 0.9rem; + font-size: clamp(2.2rem, 9vw, 2.75rem); + line-height: 1; + } + + .incql-hero__intro { + font-size: 0.86rem; + line-height: 1.48; + } + + .incql-actions { + display: grid; + gap: 0.65rem; + margin-top: 1rem; + } + + .md-typeset .incql-button { + min-height: 2.45rem; + padding: 0.58rem 0.8rem; + } + + .incql-process-rail { + grid-template-columns: 1fr; + } + + .incql-process-stage { + padding: 0.85rem; + background: radial-gradient(ellipse 82% 19rem at 50% 9rem, rgba(110, 142, 255, 0.09), transparent 76%); + } + + .incql-step-card { + min-height: 0; + padding: 0.9rem; + background: rgba(255, 255, 255, 0.74); + } + + .incql-process-list { + margin-top: 0.8rem; + } + + .incql-step-card--prism { + min-height: 24rem; + } + + .incql-step-card--prism::before { + inset: 0; + background: + linear-gradient(90deg, #ffffff 0%, rgba(255, 255, 255, 0) 13%, rgba(255, 255, 255, 0) 87%, #ffffff 100%) center / 100% 100% no-repeat, + url("../shared/prismplane/process-flow-stage-v3.png") center 2rem / auto 18rem no-repeat; + } + + .incql-step-card--prism > .incql-process-list { + margin-top: 7.4rem !important; + } + + .incql-step-card--prism::after { + height: 7.5rem; + } + + .incql-mini-code { + margin-top: 0.8rem; + } + + .incql-friction-list article { + grid-template-columns: 2rem minmax(0, 1fr); + gap: 0.82rem; + padding: 0.85rem 0; + } + + .incql-friction-list article > span { + width: 1.85rem; + height: 1.85rem; + border-radius: 10px; + font-size: 0.62rem; + } + + .incql-convergence-section { + padding: 1.1rem; + gap: 1.6rem; + } + + .incql-diagnosis h2, + .incql-map-heading h2 { + max-width: none; + font-size: clamp(1.75rem, 8vw, 2.35rem); + } + + .incql-map-body { + margin-top: 1.2rem; + } + + .incql-map-body::before, + .incql-map-body::after { + display: none; + } + + .incql-map-sources, + .incql-map-targets { + grid-template-columns: 1fr; + } + + .incql-map-core { + padding: 1rem; + } + + .incql-map-core ul { + gap: 0.45rem; + } + + .incql-map-core li { + padding: 0.5rem 0.62rem; + font-size: 0.78rem; + } + + .incql-proof-row { + display: none; + } + + .incql-hero__visual { + position: absolute; + inset: 8.2rem -6.5rem 2rem 0; + width: auto; + min-height: 15rem; + opacity: 0.56; + } + + .incql-hero__visual img { + width: 100%; + height: 100%; + min-height: 0; + object-fit: cover; + object-position: center right; + } + + .incql-convergence-section, + .incql-process, + .incql-prism-visible, + .incql-surfaces, + .incql-trust article, + .incql-final-cta { + border-radius: var(--incql-radius-lg); + } + + .incql-convergence-section, + .incql-process, + .incql-prism-visible, + .incql-surfaces, + .incql-final-cta { + padding: 1rem; + } + + .incql-convergence-section h2, + .incql-process h2, + .incql-prism-visible h2, + .incql-surfaces h2, + .incql-trust h2, + .incql-final-cta h2 { + font-size: clamp(1.55rem, 7vw, 2.05rem); + } + + .incql-step-card:not(.incql-step-card--prism) { + min-height: 0; + } + + .incql-code-card pre > code, + .incql-plan-card pre > code, + .incql-surface-demo pre > code { + font-size: 0.74rem; + } + + .incql-evidence-strip { + grid-template-columns: 1fr; + } + + .incql-feature-card__status { + display: none; + } + + .incql-plan-card::before { + inset: 3.6rem -20% 0; + background-position: center 46%; + background-size: auto 72%; + opacity: 0.28; + } + + .incql-plan-card::after { + content: ""; + position: absolute; + z-index: 0; + top: -0.2rem; + right: -1.2rem; + width: 6.5rem; + height: 5.8rem; + background: url("../shared/prismplane/process-flow-stage-v3.png") center 46% / 320% auto no-repeat; + filter: saturate(1.08) contrast(1.05); + mix-blend-mode: multiply; + opacity: 0.48; + -webkit-mask-image: radial-gradient(ellipse 62% 72% at 50% 48%, #000 44%, transparent 100%); + mask-image: radial-gradient(ellipse 62% 72% at 50% 48%, #000 44%, transparent 100%); + pointer-events: none; + } + + .incql-surface-labels { + flex-wrap: nowrap; + margin-right: -0.65rem; + margin-left: -0.65rem; + padding: 0.2rem 5.65rem 0.45rem 0.65rem; + overflow-x: auto; + overscroll-behavior-inline: contain; + scroll-padding-inline: 0.65rem; + scrollbar-width: none; + -webkit-mask-image: linear-gradient(90deg, transparent, #000 0.45rem, #000 calc(100% - 0.45rem), transparent); + mask-image: linear-gradient(90deg, transparent, #000 0.45rem, #000 calc(100% - 0.45rem), transparent); + } + + .incql-surface-labels::-webkit-scrollbar { + display: none; + } + + .incql-surface-labels button { + min-height: 2.75rem; + flex: 0 0 auto; + } + + .incql-surface-panel, + .incql-surface-panel .highlight, + .incql-surface-panel pre, + .incql-surface-panel pre > code { + min-height: 12.5rem; + } + + .incql-trust article { + min-height: 0; + } + + .incql-plan-list li { + grid-template-columns: 1fr; + gap: 0.22rem; + } + + .incql-final-cta { + display: block; + } + + .incql-final-cta__brand { + margin-bottom: 1rem; + } + + .incql-final-cta__mark { + width: 4.7rem; + } +} + +@media screen and (max-width: 86em) { + .incql-map-bridge { + display: none; + } +} + +/* Portable learning routes ---------------------------------------------- */ + +.pp-learn-kicker { + margin: -0.3rem 0 0.55rem !important; + color: var(--incql-page-blue) !important; + font-size: 0.64rem !important; + font-weight: 820; + letter-spacing: 0.11em; + text-transform: uppercase; +} + +.pp-learn-hero { + display: grid; + grid-template-columns: minmax(0, 1.08fr) minmax(15rem, 0.92fr); + gap: 0; + margin: 1.15rem 0 2rem; + overflow: hidden; + border: 1px solid var(--incql-page-line); + border-radius: var(--incql-radius-xl); + background: + radial-gradient(circle at 84% 12%, rgba(123, 92, 255, 0.12), transparent 18rem), + rgba(255, 255, 255, 0.8); + box-shadow: var(--incql-page-shadow); +} + +.pp-learn-hero__copy { + min-width: 0; + padding: clamp(1.25rem, 2.8vw, 2.2rem); +} + +.md-typeset .pp-learn-hero h2 { + max-width: 18ch; + margin: 0 0 0.75rem; + font-size: clamp(1.45rem, 2.4vw, 2.2rem); + line-height: 1.08; +} + +.md-typeset .pp-learn-hero__copy p { + max-width: 58ch; + margin: 0.55rem 0; + color: var(--incql-page-copy); + font-size: var(--incql-prose-size); + line-height: 1.65; +} + +.pp-learn-hero__copy .incql-button { + margin-top: 0.8rem; +} + +.md-typeset .pp-learn-hero__copy .pp-learn-query-entry { + display: flex; + flex-wrap: wrap; + gap: 0.25rem 0.45rem; + align-items: baseline; + margin: 0.72rem 0 0; + color: var(--incql-page-muted); + font-size: 0.61rem; + line-height: 1.4; +} + +.pp-learn-query-entry a { + font-weight: 740; +} + +.pp-learn-receipt { + align-self: stretch; + min-width: 0; + padding: clamp(1rem, 2.2vw, 1.65rem); + border-left: 1px solid var(--incql-page-line); + background: rgba(245, 248, 255, 0.72); +} + +.pp-learn-receipt__label { + margin: 0 0 0.45rem !important; + color: var(--incql-page-muted) !important; + font-size: 0.61rem !important; + font-weight: 820; + letter-spacing: 0.1em; + text-transform: uppercase; +} + +.pp-learn-receipt ol { + display: grid; + gap: 0; + margin: 0 !important; + padding: 0 !important; + list-style: none !important; +} + +.pp-learn-receipt li { + display: grid; + grid-template-columns: 4.2rem minmax(0, 1fr); + gap: 0.75rem; + align-items: baseline; + margin: 0 !important; + padding: 0.62rem 0; + border-bottom: 1px solid var(--incql-page-line); + font-size: 0.72rem; + line-height: 1.35; +} + +.pp-learn-receipt li:last-child { + border-bottom: 0; +} + +.pp-learn-receipt li span { + color: var(--incql-page-blue); + font-size: 0.61rem; + font-weight: 790; + letter-spacing: 0.07em; + text-transform: uppercase; +} + +.pp-learn-receipt li strong { + color: var(--incql-page-ink); + font-weight: 690; +} + +.pp-learn-route-grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 0.8rem; + margin: 0.9rem 0 2rem; +} + +.md-typeset .pp-learn-route-card { + position: relative; + display: grid; + min-width: 0; + min-height: 9.5rem; + align-content: start; + gap: 0.28rem; + padding: 1rem; + overflow: hidden; + border: 1px solid var(--incql-page-line); + border-top: 2px solid var(--incql-page-blue); + border-radius: var(--incql-radius-lg); + background: var(--incql-page-panel-strong); + box-shadow: 0 16px 40px rgba(55, 74, 123, 0.08); + color: var(--incql-page-ink); + text-decoration: none; + transition: border-color 150ms ease, box-shadow 150ms ease, transform 150ms ease; +} + +.md-typeset .pp-learn-route-card--guides { + border-top-color: var(--incql-page-cyan); +} + +.md-typeset .pp-learn-route-card--reference { + border-top-color: var(--incql-page-blue); +} + +.md-typeset .pp-learn-route-card--architecture { + border-top-color: var(--incql-page-violet); +} + +.md-typeset .pp-learn-route-card:hover, +.md-typeset .pp-learn-route-card:focus { + border-color: var(--incql-page-line-strong); + box-shadow: 0 20px 48px rgba(55, 74, 123, 0.13); + color: var(--incql-page-ink); + transform: translateY(-2px); +} + +.pp-learn-route-card > span { + color: var(--incql-page-muted); + font-size: 0.59rem; + font-weight: 790; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.pp-learn-route-card > strong { + color: var(--incql-page-ink); + font-size: 0.94rem; + font-weight: 760; +} + +.md-typeset .pp-learn-route-card > p { + margin: 0.2rem 0 0; + color: var(--incql-page-copy); + font-size: 0.68rem; + line-height: 1.55; +} + +.pp-learn-concepts { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 0.8rem; + margin: 0.9rem 0 1.4rem; +} + +.pp-learn-concepts article { + min-width: 0; + padding: 0.95rem 1rem; + border: 1px solid var(--incql-page-line); + border-radius: var(--incql-radius-lg); + background: rgba(255, 255, 255, 0.68); +} + +.md-typeset .pp-learn-concepts h3 { + margin: 0 0 0.35rem; + font-size: 0.88rem; +} + +.md-typeset .pp-learn-concepts p { + margin: 0; + color: var(--incql-page-copy); + font-size: 0.68rem; + line-height: 1.55; +} + +/* Portable tutorial-book chapters --------------------------------------- */ + +body:has(.pp-book-header) { + --pp-book-canvas: var(--incql-page); + --pp-book-surface: #ffffff; + --pp-book-surface-soft: #f3f6fc; + --pp-book-surface-inset: #e8eef9; + --pp-book-line: #c5d2e8; + --pp-book-cyan-ink: #08769c; + --pp-book-blue-ink: #3158d8; + --pp-book-violet-ink: #6345d8; + --pp-book-magenta-ink: #9c2ec6; + background: + radial-gradient(circle at 76% 8rem, rgba(80, 136, 255, 0.12), transparent 34rem), + linear-gradient(180deg, #f8fbff 0%, var(--pp-book-canvas) 62rem, #f7f9ff 100%); +} + +body:has(.pp-book-header) .md-main { + background: transparent; +} + +body:has(.pp-book-header) .md-sidebar--secondary { + display: none; +} + +body:has(.pp-book-header) .md-content, +body:has(.pp-book-header) .md-content__inner { + min-width: 0; + max-width: none; +} + +body:has(.pp-book-header) .md-content__inner { + box-sizing: border-box; + width: min(calc(100% - 1rem), 76rem); + margin: 0 auto 3rem; + padding: clamp(1.25rem, 2.6vw, 2.55rem); + border: 1px solid var(--incql-page-line); + border-radius: var(--incql-radius-xl); + background: + radial-gradient(circle at 82% 14%, rgba(127, 109, 255, 0.045), transparent 28rem), + var(--incql-page-panel-strong); + box-shadow: var(--incql-page-shadow); +} + +body:has(.pp-book-header) .md-content__inner > +:where(h2, h3, p, ul, ol, dl, blockquote, .highlight) { + width: min(100%, 46rem); + margin-right: auto; + margin-left: auto; +} + +.pp-book-header { + position: relative; + margin: 0 0 0.72rem; + padding: 0.48rem 0.18rem 0.78rem; + border: 0; + border-bottom: 1px solid rgba(76, 103, 166, 0.28); + background: transparent; +} + +.md-typeset .pp-book-header > p:first-child { + margin: 0 0 0.35rem; + color: var(--incql-page-blue); + font-size: 0.59rem; + font-weight: 830; + letter-spacing: 0.11em; + text-transform: uppercase; +} + +.md-typeset .pp-book-header h1 { + max-width: 22ch; + margin: 0; + font-size: clamp(1.38rem, 2.15vw, 1.72rem); + line-height: 1.08; +} + +.md-typeset .pp-book-header h1 + p { + max-width: 68ch; + margin: 0.55rem 0 0; + color: var(--incql-page-copy); + font-size: 0.72rem; + line-height: 1.58; +} + +.pp-book-step, +.pp-book-exercise, +.pp-book-complete { + margin: 1rem 0 1.25rem; + padding: 0.68rem 0 0.68rem 0.82rem; + border: 0; + border-left: 3px solid var(--incql-page-blue); + border-radius: 0; + background: transparent; + box-shadow: none; +} + +.pp-book-step { + border-left-color: var(--incql-page-cyan); +} + +.pp-book-part-context + .pp-book-step { + display: grid; + grid-template-columns: auto minmax(0, 1fr); + gap: 0.65rem; + align-items: baseline; + margin: 0 0 0.78rem; + padding: 0.58rem 0.72rem; +} + +.md-typeset .pp-book-part-context + .pp-book-step h2 { + margin: 0; + color: var(--pp-book-cyan-ink); + font-size: 0.7rem; + white-space: nowrap; +} + +.pp-book-exercise { + border-left-color: var(--incql-page-violet); + background: transparent; +} + +.pp-book-complete { + border-left-color: var(--incql-page-blue); + background: transparent; +} + +.md-typeset .pp-book-step h2, +.md-typeset .pp-book-exercise h2, +.md-typeset .pp-book-complete h2 { + margin: 0 0 0.34rem; + font-size: 0.86rem; +} + +.md-typeset .pp-book-step p, +.md-typeset .pp-book-step li, +.md-typeset .pp-book-exercise p, +.md-typeset .pp-book-exercise li, +.md-typeset .pp-book-complete p, +.md-typeset .pp-book-complete li { + margin-top: 0; + margin-bottom: 0; + color: var(--incql-page-copy); + font-size: var(--incql-prose-size); + line-height: 1.6; +} + +.pp-book-workbench { + min-width: 0; + margin: 0.85rem 0 1.15rem; + padding: 0.72rem; + overflow: hidden; + border: 1px solid rgba(10, 174, 232, 0.2); + border-top: 2px solid var(--incql-page-cyan); + border-radius: var(--incql-radius-sm); + background: + linear-gradient(180deg, rgba(239, 249, 255, 0.8), rgba(255, 255, 255, 0.72)); + box-shadow: none; +} + +.md-typeset .pp-book-workbench > p:first-child { + margin: 0 0 0.48rem; + color: var(--incql-page-muted); + font-size: 0.59rem; + font-weight: 790; + letter-spacing: 0.07em; + text-transform: uppercase; +} + +.pp-book-workbench .highlight, +.pp-book-workbench pre, +.pp-book-workbench pre > code { + min-width: 0; + margin: 0; +} + +.pp-book-workbench .highlight { + box-shadow: none; +} + +.pp-book-output { + min-width: 0; + margin: 0.9rem 0 1.2rem; + padding: 0.82rem 0.9rem; + border: 0; + border-left: 3px solid var(--incql-page-violet); + border-radius: 0; + background: rgba(250, 248, 255, 0.56); +} + +.md-typeset .pp-book-output > p:first-child { + margin: 0 0 0.42rem; + color: #6546c4; + font-size: 0.59rem; + font-weight: 790; + letter-spacing: 0.07em; + text-transform: uppercase; +} + +.md-typeset .pp-book-output p, +.md-typeset .pp-book-output li { + color: var(--incql-page-copy); + font-size: var(--incql-prose-size); + line-height: 1.6; +} + +.pp-book-output .highlight, +.pp-book-output pre, +.pp-book-output pre > code { + margin: 0; + box-shadow: none; +} + +.pp-book-pagination { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 0.75rem; + width: min(100%, 46rem); + margin: 1.65rem auto 0; +} + +.md-typeset .pp-book-pagination > a, +.pp-book-pagination > span { + display: grid; + min-width: 0; + min-height: 4.2rem; + align-content: center; + gap: 0.18rem; + padding: 0.72rem 0.82rem; + border: 1px solid var(--incql-page-line); + border-radius: var(--incql-radius-md); + background: rgba(255, 255, 255, 0.7); + color: var(--incql-page-ink); + text-decoration: none; +} + +.md-typeset .pp-book-pagination > a:last-child { + border-color: rgba(71, 109, 255, 0.28); + background: + radial-gradient(circle at 94% 12%, rgba(123, 92, 255, 0.1), transparent 11rem), + rgba(255, 255, 255, 0.76); +} + +.md-typeset .pp-book-pagination > a:hover, +.md-typeset .pp-book-pagination > a:focus { + border-color: var(--incql-page-line-strong); + color: var(--incql-page-blue); +} + +.pp-book-pagination small { + color: var(--incql-page-muted); + font-size: 0.57rem; + font-weight: 720; + letter-spacing: 0.05em; + text-transform: uppercase; +} + +.pp-book-pagination strong { + color: inherit; + font-size: 0.71rem; + line-height: 1.3; +} + +.pp-book-eyebrow, +.md-typeset .pp-book-eyebrow { + margin: 0 0 0.28rem; + color: var(--incql-page-blue); + font-size: 0.57rem; + font-weight: 820; + letter-spacing: 0.1em; + line-height: 1.3; + text-transform: uppercase; +} + +.pp-book-map { + margin: 0.8rem 0 1.5rem; + padding: 0; + border: 0; + border-radius: 0; + background: transparent; + box-shadow: none; +} + +.pp-book-map__header { + display: grid; + grid-template-columns: minmax(0, 1.3fr) minmax(14rem, 0.7fr); + gap: 1.25rem; + align-items: end; + margin: 0; + padding: 0.75rem 0 0.9rem; + border-bottom: 1px solid rgba(76, 103, 166, 0.28); +} + +.md-typeset .pp-book-map__header h2 { + margin: 0; + font-size: clamp(1rem, 1.7vw, 1.28rem); + line-height: 1.18; +} + +.md-typeset .pp-book-map__header > p { + margin: 0; + color: var(--incql-page-muted); + font-size: 0.64rem; + line-height: 1.55; +} + +.pp-book-parts { + display: grid; + gap: 0; + min-width: 0; + border-top: 1px solid rgba(76, 103, 166, 0.28); +} + +.pp-book-part { + --pp-book-part-accent: var(--incql-page-blue); + --pp-book-part-ink: var(--pp-book-blue-ink); + width: 100%; + min-width: 0; + margin: 0; + overflow: visible; + border: 0; + border-bottom: 1px solid rgba(76, 103, 166, 0.24); + border-radius: 0; + background: transparent; + box-shadow: none; +} + +.md-typeset details.pp-book-part { + margin: 0; +} + +.md-typeset .pp-book-part > summary::before { + display: none; +} + +.pp-book-part--model { + --pp-book-part-accent: var(--incql-page-cyan); + --pp-book-part-ink: var(--pp-book-cyan-ink); +} + +.pp-book-part--evidence { + --pp-book-part-accent: var(--incql-page-violet); + --pp-book-part-ink: var(--pp-book-violet-ink); +} + +.pp-book-part--govern { + --pp-book-part-accent: var(--incql-page-magenta); + --pp-book-part-ink: var(--pp-book-magenta-ink); +} + +.pp-book-part--query { + --pp-book-part-accent: var(--incql-page-blue); + --pp-book-part-ink: var(--pp-book-blue-ink); +} + +.pp-book-part[open] { + border-bottom-color: rgba(76, 103, 166, 0.3); + background: transparent; + box-shadow: none; +} + +.pp-book-part__summary { + position: relative; + display: grid; + grid-template-columns: 5.2rem minmax(9rem, 1fr) minmax(12rem, 1.25fr) auto; + gap: 0.65rem; + align-items: center; + min-height: 2.85rem; + padding: 0.58rem 0.2rem; + cursor: pointer; + list-style-position: outside; +} + +.pp-book-part__summary::marker { + color: var(--pp-book-part-accent); + font-size: 0.72rem; +} + +.pp-book-part__summary:focus-visible { + outline: 2px solid var(--incql-page-blue); + outline-offset: -3px; +} + +.pp-book-part[open] .pp-book-part__summary { + border-bottom: 1px solid color-mix(in srgb, var(--pp-book-part-accent) 28%, transparent); + background: color-mix(in srgb, var(--pp-book-part-accent) 4%, transparent); +} + +.pp-book-part:not([open]) .pp-book-part__summary { + background: transparent; +} + +.pp-book-part__marker { + position: relative; + z-index: 1; + width: max-content; + padding: 0; + border: 0; + border-radius: 0; + background: transparent; + color: var(--pp-book-part-ink); + font-size: 0.56rem; + font-weight: 840; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.pp-book-part__name { + position: relative; + z-index: 1; + color: var(--incql-page-ink); + font-size: 0.75rem; + font-weight: 760; +} + +.pp-book-part__outcome, +.pp-book-part__count { + position: relative; + z-index: 1; + color: #52617d; + font-size: 0.6rem; + line-height: 1.35; +} + +.pp-book-part__count { + color: var(--incql-page-copy); + font-weight: 690; + white-space: nowrap; +} + +.md-typeset img.pp-book-part__prism { + position: absolute; + z-index: 0; + top: 50%; + right: auto; + left: 0.45rem; + width: 2.7rem; + height: 3.4rem; + max-width: none; + object-fit: cover; + object-position: center; + opacity: 0.28; + pointer-events: none; + filter: saturate(1.08) contrast(1.04); + transform: translateY(-50%); +} + +.pp-book-part__body { + padding: 0; + background: transparent; +} + +.pp-book-part__columns, +.pp-book-part__chapters a { + display: grid; + grid-template-columns: minmax(0, 1.2fr) minmax(0, 1.3fr) minmax(0, 0.7fr) minmax(0, 1.25fr); + gap: 0.75rem; + align-items: center; +} + +.pp-book-part__columns { + padding: 0.45rem 0.2rem 0.34rem; + color: var(--incql-page-muted); + font-size: 0.51rem; + font-weight: 760; + letter-spacing: 0.06em; + text-transform: uppercase; +} + +.md-typeset ol.pp-book-part__chapters { + margin: 0; + padding: 0; + list-style: none; +} + +[dir="ltr"] .md-typeset ol.pp-book-part__chapters > li, +[dir="ltr"] .md-typeset ol.pp-book-part-journey__list > li, +[dir="ltr"] .md-typeset ol.pp-book-trace__stages > li { + margin-left: 0; +} + +[dir="rtl"] .md-typeset ol.pp-book-part__chapters > li, +[dir="rtl"] .md-typeset ol.pp-book-part-journey__list > li, +[dir="rtl"] .md-typeset ol.pp-book-trace__stages > li { + margin-right: 0; +} + +.md-typeset ol.pp-book-part__chapters > li, +.md-typeset ol.pp-book-part-journey__list > li, +.md-typeset ol.pp-book-trace__stages > li { + margin-bottom: 0; +} + +.pp-book-part__chapters li { + margin: 0; + border-top: 1px solid rgba(76, 103, 166, 0.24); +} + +.md-typeset .pp-book-part__chapters a { + min-height: 3rem; + padding: 0.55rem 0.2rem; + background: transparent; + color: var(--incql-page-copy); + font-size: 0.58rem; + line-height: 1.42; + text-decoration: none; +} + +.md-typeset .pp-book-part__chapters a:hover, +.md-typeset .pp-book-part__chapters a:focus-visible { + background: color-mix(in srgb, var(--pp-book-part-accent) 6%, transparent); + color: var(--incql-page-ink); +} + +.pp-book-part__chapter { + display: grid; + grid-template-columns: 1.65rem minmax(0, 1fr); + gap: 0.48rem; + align-items: baseline; +} + +.pp-book-part__chapter b { + color: var(--pp-book-part-ink); + font-family: var(--incql-font-mono); + font-size: 0.67rem; +} + +.pp-book-part__chapter strong { + color: var(--incql-page-ink); + font-size: 0.64rem; + line-height: 1.35; +} + +.pp-book-part__artifact { + display: flex; + gap: 0.38rem; + align-items: center; + color: var(--incql-page-ink); + font-family: var(--incql-font-mono); + font-size: 0.53rem; +} + +.pp-book-part__artifact img, +.pp-book-dossier img, +.pp-book-trace__artifacts img { + width: 0.85rem; + height: 0.85rem; + object-fit: contain; +} + +.pp-book-dossier img { + width: 0.95rem; + height: 0.95rem; + padding: 0; + border: 0; + border-radius: 0; + background: transparent; + box-sizing: border-box; +} + +.pp-book-dossier { + margin: 0.95rem 0 0; + padding: 0.72rem 0; + border: 0; + border-top: 1px solid rgba(76, 103, 166, 0.28); + border-bottom: 1px solid rgba(76, 103, 166, 0.28); + border-radius: 0; + background: transparent; + box-shadow: none; +} + +.pp-book-dossier > header { + display: flex; + gap: 0.55rem; + align-items: baseline; + margin: 0 0 0.55rem; + color: var(--incql-page-muted); + font-size: 0.54rem; + font-weight: 790; + letter-spacing: 0.07em; + text-transform: uppercase; +} + +.pp-book-dossier > header code { + color: var(--incql-page-blue); + font-size: 0.58rem; + letter-spacing: 0; + text-transform: none; +} + +.pp-book-dossier__grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + border: 0; + border-radius: 0; + background: transparent; +} + +.pp-book-dossier__grid section { + display: grid; + grid-template-columns: auto minmax(0, 1fr); + gap: 0.55rem; + align-items: start; + min-width: 0; + padding: 0.65rem; +} + +.pp-book-dossier__grid section + section { + border-left: 1px solid rgba(76, 103, 166, 0.26); +} + +.pp-book-dossier__grid strong, +.pp-book-dossier__grid span { + display: block; +} + +.pp-book-dossier__grid strong { + margin: 0 0 0.14rem; + color: var(--incql-page-ink); + font-size: 0.59rem; +} + +.pp-book-dossier__grid span { + color: var(--incql-page-muted); + font-size: 0.54rem; + line-height: 1.45; +} + +.pp-book-part-context { + display: grid; + grid-template-columns: minmax(11rem, 0.75fr) minmax(0, 1.25fr); + gap: 1rem; + align-items: center; + margin: 0 0 0.72rem; + padding: 0 0 0.62rem; + border: 0; + border-bottom: 1px solid rgba(76, 103, 166, 0.28); + border-radius: 0; + background: transparent; + box-shadow: none; +} + +.pp-book-part-context__summary { + display: grid; + grid-template-columns: auto minmax(7rem, 1fr) auto; + gap: 0.5rem; + align-items: baseline; +} + +.md-typeset .pp-book-part-context__summary p { + margin: 0; +} + +.pp-book-part-context__label { + color: var(--pp-book-violet-ink); + font-size: 0.55rem; + font-weight: 820; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.pp-book-part-context__title { + color: var(--incql-page-ink); + font-size: 0.66rem; +} + +.pp-book-part-context__position { + color: var(--incql-page-muted); + font-size: 0.54rem; + white-space: nowrap; +} + +.md-typeset ol.pp-book-part-journey__list { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 0.35rem; + margin: 0; + padding: 0; + list-style: none; +} + +.md-typeset .pp-book-part-context--query ol.pp-book-part-journey__list { + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + +.pp-book-part-journey__item { + min-width: 0; + margin: 0; +} + +.md-typeset .pp-book-part-journey__link { + display: grid; + grid-template-columns: auto minmax(0, 1fr); + gap: 0.36rem; + align-items: center; + min-height: 1.85rem; + padding: 0.28rem 0.18rem; + border: 0; + border-bottom: 2px solid transparent; + border-radius: 0; + background: transparent; + color: var(--incql-page-copy); + font-size: 0.54rem; + line-height: 1.3; + text-decoration: none; +} + +.md-typeset .pp-book-part-journey__link[aria-current="page"] { + border-bottom-color: var(--incql-page-blue); + background: rgba(71, 109, 255, 0.06); + color: var(--incql-page-blue); + font-weight: 720; +} + +.pp-book-part-journey__number { + color: var(--pp-book-violet-ink); + font-family: var(--incql-font-mono); + font-size: 0.58rem; +} + +.pp-book-part-context--rail { + position: static; + top: auto; + display: grid; + float: none; + width: auto; + margin: 0 0 0.72rem; + padding: 0 0 0.62rem; +} + +.pp-book-part-context--rail .pp-book-part-context__summary { + display: grid; + grid-template-columns: auto minmax(7rem, 1fr) auto; + gap: 0.5rem; + padding: 0; + border-bottom: 0; +} + +.pp-book-part-context--rail .pp-book-part-context__position { + margin-top: 0.18rem; +} + +.pp-book-part-context--rail .pp-book-part-journey__list { + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 0.35rem; + margin-top: 0; +} + +.pp-book-part-context--rail + .pp-book-step, +.pp-book-part-context--rail + .pp-book-step + .pp-book-trace { + margin-left: 0; +} + +.pp-book-chapter-clear { + clear: both; +} + +.pp-book-trace { + min-width: 0; + margin: 1rem 0 1.45rem; + padding: 0; + border: 0; + border-radius: 0; + background: transparent; + box-shadow: none; +} + +.pp-book-trace__header { + display: grid; + grid-template-columns: minmax(0, 1.1fr) minmax(14rem, 0.9fr); + gap: 1rem; + align-items: end; + margin: 0; + padding: 0 0 0.76rem; + border-bottom: 1px solid rgba(76, 103, 166, 0.26); +} + +.md-typeset .pp-book-trace__header h2 { + margin: 0; + font-size: 0.94rem; +} + +.md-typeset .pp-book-trace__header > p { + margin: 0; + color: var(--incql-page-muted); + font-size: 0.58rem; + line-height: 1.5; +} + +.md-typeset ol.pp-book-trace__stages { + display: grid; + gap: 0; + margin: 0; + padding: 0; + list-style: none; +} + +.pp-book-trace__stage { + --pp-book-stage-accent: var(--incql-page-blue); + --pp-book-stage-ink: var(--pp-book-blue-ink); + width: 100%; + min-width: 0; + margin: 0; + padding: 0.9rem 0; + overflow: visible; + border: 0; + border-bottom: 1px solid rgba(76, 103, 166, 0.24); + border-radius: 0; + background: transparent; + box-shadow: none; +} + +.pp-book-trace__stage--source { + --pp-book-stage-accent: var(--incql-page-cyan); + --pp-book-stage-ink: var(--pp-book-cyan-ink); +} + +.pp-book-trace__stage--session { + --pp-book-stage-accent: var(--incql-page-violet); + --pp-book-stage-ink: var(--pp-book-violet-ink); +} + +.pp-book-trace__stage--runtime { + --pp-book-stage-accent: #405777; + --pp-book-stage-ink: #405777; +} + +.pp-book-trace__stage > header { + display: grid; + grid-template-columns: 2rem 1.2rem minmax(0, 1fr); + gap: 0.42rem; + align-items: start; + margin: 0 0 0.42rem; +} + +.pp-book-trace__stage > header > div, +.pp-book-trace__body { + min-width: 0; +} + +.pp-book-trace__stage > header > div { + grid-column: 3; + grid-row: 1; +} + +.pp-book-trace__stage-icon { + grid-column: 2; + grid-row: 1; + width: 1.18rem; + height: 1.18rem; + flex: 0 0 auto; + padding: 0.2rem; + border: 1px solid color-mix(in srgb, var(--pp-book-stage-accent) 48%, var(--pp-book-line)); + border-radius: 0.38rem; + background: color-mix(in srgb, var(--pp-book-stage-accent) 12%, white); + object-fit: contain; + box-sizing: border-box; +} + +.pp-book-trace__number { + grid-column: 1; + grid-row: 1; + color: var(--pp-book-stage-ink); + font-family: var(--incql-font-mono); + font-size: 0.64rem; + font-weight: 760; +} + +.pp-book-trace__stage > header strong, +.pp-book-trace__stage > header small { + display: block; +} + +.pp-book-trace__stage > header strong { + color: var(--incql-page-ink); + font-size: 0.67rem; + text-transform: uppercase; +} + +.pp-book-trace__stage > header small { + margin: 0.12rem 0 0; + color: var(--incql-page-muted); + font-size: 0.52rem; +} + +.pp-book-trace__body .highlight, +.pp-book-trace__body pre, +.pp-book-trace__body pre > code { + min-width: 0; + max-width: 100%; + margin: 0; + box-shadow: none; +} + +.pp-book-trace__body .highlight, +.pp-book-trace__body pre { + overflow: hidden; + border: 0; + border-radius: 0; + background: var(--pp-book-surface-soft); + box-shadow: none; +} + +.pp-book-trace__body pre > code { + overflow-x: visible; + white-space: pre-wrap; + overflow-wrap: anywhere; +} + +.pp-book-code-explainer { + --md-tooltip-width: 18rem; +} + +.pp-book-code-explainer .md-annotation__index::after { + background-color: var(--pp-book-stage-accent); + color: #ffffff; +} + +.pp-book-code-explainer .md-tooltip__inner { + max-height: min(50vh, 18rem); + overflow: auto; + border: 1px solid color-mix(in srgb, var(--pp-book-stage-accent) 34%, var(--pp-book-line)); + background: #ffffff; + color: var(--incql-page-copy); + font-size: 0.58rem; + line-height: 1.5; + box-shadow: 0 16px 40px rgba(31, 54, 96, 0.16); +} + +.pp-book-code-explainer .md-tooltip__inner strong { + color: var(--incql-page-ink); +} + +.pp-book-code-explainer .md-tooltip__inner code { + font-size: 0.92em; +} + +.pp-book-trace__facts, +.pp-book-trace__result > dl, +.pp-book-receipt dl { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 0.45rem; + margin: 0.52rem 0 0; +} + +.pp-book-trace__facts div, +.pp-book-trace__result > dl div, +.pp-book-receipt dl div { + min-width: 0; + padding: 0; + border: 0; + border-radius: 0; + background: transparent; +} + +.pp-book-trace dt, +.pp-book-receipt dt { + color: #52617d; + font-size: 0.49rem; + font-weight: 760; + letter-spacing: 0.05em; + text-transform: uppercase; +} + +.pp-book-trace dd, +.pp-book-receipt dd { + margin: 0.14rem 0 0; + color: var(--incql-page-copy); + font-size: 0.54rem; + line-height: 1.42; +} + +.pp-book-trace__result { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + gap: 0.65rem; + align-items: center; +} + +.pp-book-trace__result > dl { + margin: 0; +} + +.pp-book-trace__artifacts { + display: grid; + gap: 0.35rem; +} + +.pp-book-trace__artifacts span { + display: flex; + gap: 0.35rem; + align-items: center; + color: var(--incql-page-copy); + font-family: var(--incql-font-mono); + font-size: 0.51rem; +} + +.pp-book-receipt { + margin: 0; + padding: 0.75rem 0 0; + border: 0; + border-top: 1px solid rgba(123, 92, 255, 0.38); + border-radius: 0; + background: transparent; + box-shadow: none; +} + +.pp-book-receipt > header { + display: flex; + justify-content: space-between; + gap: 0.6rem; + align-items: baseline; +} + +.pp-book-receipt > header span { + color: var(--pp-book-violet-ink); + font-size: 0.53rem; + font-weight: 820; + letter-spacing: 0.07em; + text-transform: uppercase; +} + +.pp-book-receipt > header strong { + color: var(--incql-page-ink); + font-size: 0.58rem; +} + +.pp-book-receipt dl { + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 0; + margin-top: 0.55rem; +} + +.pp-book-receipt dl div { + padding: 0.12rem 0.58rem; + border-left: 1px solid rgba(76, 103, 166, 0.24); +} + +.pp-book-receipt dl div:first-child { + padding-left: 0; + border-left: 0; +} + +.pp-book-receipt dl:has(div:nth-child(5)) { + grid-template-columns: repeat(3, minmax(0, 1fr)); +} + +.pp-book-receipt dl:has(div:nth-child(5)) div:nth-child(4) { + padding-left: 0; + border-left: 0; +} + +.pp-book-receipt dl:has(div:nth-child(5)) div:nth-child(n + 4) { + margin-top: 0.5rem; + padding-top: 0.5rem; + border-top: 1px solid rgba(76, 103, 166, 0.18); +} + +.pp-book-source { + margin: 0.9rem 0 1rem; + overflow: hidden; + border: 1px solid var(--incql-page-line); + border-radius: var(--incql-radius-sm); + background: rgba(255, 255, 255, 0.66); +} + +.pp-book-source > summary { + padding: 0.62rem 0.75rem; + color: var(--incql-page-ink); + cursor: pointer; + font-size: 0.62rem; + font-weight: 720; +} + +.pp-book-source > div { + padding: 0 0.7rem 0.7rem; +} + +.pp-book-source .highlight, +.pp-book-source pre { + margin: 0; +} + +@media screen and (max-width: 82em) { + .pp-book-part-context--rail { + position: static; + float: none; + width: auto; + display: grid; + grid-template-columns: minmax(11rem, 0.75fr) minmax(0, 1.25fr); + gap: 1rem; + margin: 0 0 0.72rem; + } + + .pp-book-part-context--rail .pp-book-part-context__summary { + grid-template-columns: auto minmax(7rem, 1fr) auto; + min-width: 0; + padding: 0; + border: 0; + } + + .pp-book-part-context--rail .pp-book-part-journey__list { + grid-template-columns: repeat(3, minmax(0, 1fr)); + margin: 0; + } + + .pp-book-part-context--rail + .pp-book-step, + .pp-book-part-context--rail + .pp-book-step + .pp-book-trace { + margin-left: 0; + } +} + +@media screen and (min-width: 62.01em) { + .pp-book-trace__stage { + display: grid; + grid-template-columns: minmax(0, 1fr) clamp(10.5rem, 18vw, 13.5rem); + gap: 0.42rem 1.1rem; + } + + .pp-book-trace__stage > header { + grid-column: 1; + grid-row: 1; + margin-bottom: 0; + } + + .pp-book-trace__stage > .pp-book-trace__body { + grid-column: 1; + grid-row: 2; + padding-left: 3.62rem; + } + + .pp-book-trace__stage > .pp-book-trace__facts { + grid-column: 2; + grid-row: 1 / span 2; + grid-template-columns: 1fr; + gap: 0.55rem; + align-self: stretch; + margin: 0; + padding: 0.08rem 0 0.08rem 0.85rem; + border-left: 2px solid color-mix(in srgb, var(--pp-book-stage-accent) 70%, var(--pp-book-line)); + } + + .pp-book-trace__stage--runtime:not(:has(.pp-book-trace__body)) > .pp-book-trace__result { + grid-column: 1 / -1; + grid-row: 2; + grid-template-columns: minmax(0, 1fr) clamp(10.5rem, 18vw, 13.5rem); + gap: 1.1rem; + } + + .pp-book-trace__stage--runtime:not(:has(.pp-book-trace__body)) > .pp-book-trace__result > dl { + grid-column: 1; + grid-row: 1; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 0; + margin: 0; + padding-left: 3.62rem; + } + + .pp-book-trace__stage--runtime:not(:has(.pp-book-trace__body)) > .pp-book-trace__result > dl div + div { + margin-left: 0.55rem; + padding-left: 0.55rem; + border-left: 1px solid rgba(76, 103, 166, 0.2); + } + + .pp-book-trace__stage--runtime:not(:has(.pp-book-trace__body)) .pp-book-trace__artifacts { + grid-column: 2; + grid-row: 1; + align-content: start; + padding: 0.08rem 0 0.08rem 0.85rem; + border-left: 2px solid color-mix(in srgb, var(--pp-book-stage-accent) 70%, var(--pp-book-line)); + } + + .pp-book-trace__stage--runtime:has(.pp-book-trace__body) > .pp-book-trace__result { + grid-column: 2; + grid-row: 1 / span 2; + display: block; + align-self: stretch; + margin: 0; + padding: 0.08rem 0 0.08rem 0.85rem; + border-left: 2px solid color-mix(in srgb, var(--pp-book-stage-accent) 70%, var(--pp-book-line)); + } + + .pp-book-trace__stage--runtime:has(.pp-book-trace__body) > .pp-book-trace__result > dl { + grid-template-columns: 1fr; + gap: 0.55rem; + margin: 0; + } + + .pp-book-trace__stage--runtime:has(.pp-book-trace__body) .pp-book-trace__artifacts { + margin-top: 0.65rem; + } +} + +@media screen and (max-width: 62em) { + .pp-learn-hero { + grid-template-columns: 1fr; + } + + .pp-learn-receipt { + border-top: 1px solid var(--incql-page-line); + border-left: 0; + } + + .pp-learn-route-grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .pp-book-map__header { + grid-template-columns: 1fr; + gap: 0.45rem; + } + + .pp-book-part__summary { + grid-template-columns: 4.5rem minmax(8rem, 1fr) auto; + } + + .pp-book-part__outcome { + display: none; + } + + .pp-book-part__columns { + display: none; + } + + .pp-book-part__chapters a { + grid-template-columns: minmax(10rem, 1.2fr) minmax(12rem, 1.4fr) minmax(8rem, 0.8fr); + } + + .pp-book-part__chapters a > :nth-child(3) { + display: none; + } + + .pp-book-dossier__grid { + grid-template-columns: 1fr; + } + + .pp-book-dossier__grid section + section { + border-top: 1px solid rgba(76, 103, 166, 0.26); + border-left: 0; + } + + .pp-book-trace__header { + grid-template-columns: 1fr; + gap: 0.35rem; + } + + .pp-book-trace__facts, + .pp-book-trace__stage--runtime > .pp-book-trace__result { + grid-template-columns: 1fr; + gap: 0.55rem; + margin: 0.65rem 0 0; + padding: 0.65rem 0 0; + border-top: 2px solid color-mix(in srgb, var(--pp-book-stage-accent) 58%, var(--pp-book-line)); + } + + .pp-book-trace__stage--runtime > .pp-book-trace__result > dl { + grid-template-columns: repeat(3, minmax(0, 1fr)); + margin: 0; + } + + .pp-book-trace__stage--runtime > .pp-book-trace__result .pp-book-trace__artifacts { + margin-top: 0.55rem; + } + + .pp-book-receipt dl { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .pp-book-receipt dl:has(div:nth-child(5)) { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} + +@media screen and (max-width: 44em) { + .pp-learn-hero { + border-radius: var(--incql-radius-lg); + } + + .pp-learn-hero__copy, + .pp-learn-receipt { + padding: 1rem; + } + + .pp-learn-route-grid, + .pp-learn-concepts { + grid-template-columns: 1fr; + } + + .md-typeset .pp-learn-route-card { + min-height: 0; + } + + .pp-book-header { + padding: 0.42rem 0 0.68rem; + } + + body:has(.pp-book-header) .md-content__inner { + width: calc(100% - 0.4rem); + margin-bottom: 2rem; + padding: 0.85rem; + border-radius: var(--incql-radius-lg); + } + + .pp-book-part-context + .pp-book-step { + grid-template-columns: 1fr; + gap: 0.22rem; + } + + .md-typeset .pp-book-part-context + .pp-book-step h2 { + white-space: normal; + } + + .pp-book-map { + padding: 0; + } + + .pp-book-part__summary { + grid-template-columns: 3.7rem minmax(0, 1fr) auto; + gap: 0.42rem; + padding: 0.58rem 0.1rem; + } + + .md-typeset img.pp-book-part__prism { + left: 0.3rem; + display: block; + width: 2.3rem; + height: 3rem; + } + + .pp-book-part__body { + padding: 0; + } + + .pp-book-part__marker { + grid-column: 1; + grid-row: 1; + } + + .pp-book-part__name { + grid-column: 2; + grid-row: 1; + } + + .pp-book-part__count { + grid-column: 3; + grid-row: 1; + } + + .pp-book-part__outcome { + display: block; + grid-column: 2 / -1; + grid-row: 2; + } + + .pp-book-part__chapters a { + grid-template-columns: 1fr; + gap: 0.22rem; + padding: 0.56rem 0.45rem; + } + + .pp-book-part__chapters a > :nth-child(3) { + display: block; + } + + .pp-book-part__artifact { + margin-left: 2.14rem; + } + + .pp-book-dossier__grid section { + padding: 0.58rem 0; + } + + .pp-book-part-context, + .pp-book-part-context--rail { + display: block; + } + + .pp-book-part-context__summary, + .pp-book-part-context--rail .pp-book-part-context__summary { + grid-template-columns: auto minmax(0, 1fr) auto; + gap: 0.55rem; + padding: 0 0 0.5rem; + border-bottom: 1px solid rgba(96, 126, 186, 0.15); + } + + .pp-book-part-journey__list, + .md-typeset .pp-book-part-context--query ol.pp-book-part-journey__list, + .pp-book-part-context--rail .pp-book-part-journey__list { + grid-template-columns: 1fr; + margin-top: 0.5rem; + } + + .pp-book-trace__facts, + .pp-book-trace__result > dl, + .pp-book-receipt dl { + grid-template-columns: 1fr; + } + + .pp-book-receipt dl:has(div:nth-child(5)) { + grid-template-columns: 1fr; + } + + .pp-book-code-explainer { + --md-tooltip-width: min(18rem, calc(100vw - 1.6rem)); + } + + .pp-book-receipt dl div, + .pp-book-receipt dl div:first-child, + .pp-book-receipt dl:has(div:nth-child(5)) div:nth-child(4) { + margin: 0; + padding: 0.5rem 0; + border: 0; + border-top: 1px solid rgba(76, 103, 166, 0.18); + } + + .pp-book-receipt dl div:first-child { + padding-top: 0; + border-top: 0; + } + + .pp-book-trace__result { + grid-template-columns: 1fr; + } + + .pp-book-trace__stage > header { + align-items: flex-start; + } + + .pp-book-trace__stage > header small { + display: block; + margin: 0.12rem 0 0; + line-height: 1.35; + } + + .pp-book-pagination { + grid-template-columns: 1fr; + } + + .md-typeset .pp-book-pagination > a, + .pp-book-pagination > span { + min-height: 3.8rem; + } +} + +/* Architecture chapters -------------------------------------------------- */ + +body:has(.incql-architecture) { + --incql-page-blue: #315df0; + --md-default-bg-color: var(--incql-page); + --md-default-fg-color: var(--incql-page-ink); + --md-default-fg-color--light: var(--incql-page-copy); + --md-default-fg-color--lighter: var(--incql-page-muted); + --md-primary-fg-color: rgba(255, 255, 255, 0.86); + --md-primary-bg-color: var(--incql-page-ink); + --md-typeset-color: var(--incql-page-ink); + --md-typeset-a-color: var(--incql-page-blue); + background: + radial-gradient(circle at 76% 8rem, rgba(80, 136, 255, 0.12), transparent 34rem), + linear-gradient(180deg, #f8fbff 0%, var(--incql-page) 62rem, #f7f9ff 100%); + color: var(--incql-page-ink); + overflow-x: clip; +} + +body:has(.incql-architecture) .md-header { + border-bottom: 1px solid rgba(96, 126, 186, 0.18); + background: rgba(248, 251, 255, 0.86); + box-shadow: 0 18px 60px rgba(55, 74, 123, 0.08); +} + +body:has(.incql-architecture) .md-header__title, +body:has(.incql-architecture) .md-header__button, +body:has(.incql-architecture) .md-tabs__link, +body:has(.incql-architecture) .md-source, +body:has(.incql-architecture) .md-source__repository { + color: var(--incql-page-ink); +} + +body:has(.incql-architecture) .md-header__topic + .md-header__topic { + display: none; +} + +body:has(.incql-architecture) .md-header__topic:first-child { + position: relative; + opacity: 1; + transform: none; +} + +body:has(.incql-architecture) .md-tabs { + background: rgba(248, 251, 255, 0.76); +} + +body:has(.incql-architecture) .md-tabs__item--active { + border-bottom-color: var(--incql-page-blue); +} + +body:has(.incql-architecture) .md-search__form { + border-color: rgba(77, 103, 158, 0.18); + background: rgba(255, 255, 255, 0.86); + box-shadow: 0 10px 34px rgba(55, 74, 123, 0.08); +} + +body:has(.incql-architecture) .md-search__input, +body:has(.incql-architecture) .md-search__input::placeholder { + color: var(--incql-page-muted); +} + +body:has(.incql-architecture) .md-main__inner { + max-width: none; + margin: 0; +} + +body:has(.incql-architecture) .md-content, +body:has(.incql-architecture) .md-content__inner { + min-width: 0; +} + +body:has(.incql-architecture) .md-content__inner { + margin: 0; + padding: 0; +} + +body:has(.incql-architecture) .md-content__inner::before { + display: none; +} + +body:has(.incql-architecture) .md-sidebar--secondary { + display: none; +} + +body:has(.incql-architecture) .md-typeset, +body:has(.incql-architecture) .md-typeset h1, +body:has(.incql-architecture) .md-typeset h2, +body:has(.incql-architecture) .md-typeset h3, +body:has(.incql-architecture) .md-typeset h4 { + color: var(--incql-page-ink); +} + +body:has(.incql-architecture) .md-typeset p, +body:has(.incql-architecture) .md-typeset li { + color: var(--incql-page-copy); +} + +body:has(.incql-architecture) .md-typeset a { + color: var(--incql-page-blue); + text-decoration-color: rgba(71, 109, 255, 0.34); +} + +body:has(.incql-architecture) .md-typeset :not(pre) > code { + border-color: rgba(83, 111, 183, 0.16); + background: rgba(237, 243, 255, 0.82); + color: #20345f; +} + +body:has(.incql-launch) .md-sidebar--primary, +body:has(.incql-architecture) .md-sidebar--primary { + color: var(--incql-page-copy); + scrollbar-color: rgba(49, 93, 240, 0.34) transparent; +} + +body:has(.incql-launch) .md-sidebar--primary .md-nav__title, +body:has(.incql-architecture) .md-sidebar--primary .md-nav__title { + color: var(--incql-page-ink) !important; +} + +body:has(.incql-launch) .md-sidebar--primary .md-nav__source, +body:has(.incql-architecture) .md-sidebar--primary .md-nav__source { + border-bottom: 1px solid rgba(96, 126, 186, 0.18); + background: rgba(237, 243, 255, 0.98); + color: var(--incql-page-ink); +} + +body:has(.incql-launch) .md-sidebar--primary .md-source, +body:has(.incql-launch) .md-sidebar--primary .md-source__repository, +body:has(.incql-architecture) .md-sidebar--primary .md-source, +body:has(.incql-architecture) .md-sidebar--primary .md-source__repository, +body:has(.incql-launch) .md-sidebar--primary .md-nav__link, +body:has(.incql-architecture) .md-sidebar--primary .md-nav__link { + color: var(--incql-page-copy); +} + +body:has(.incql-launch) .md-sidebar--primary .md-nav__link[for]:focus, +body:has(.incql-launch) .md-sidebar--primary .md-nav__link[for]:hover, +body:has(.incql-launch) .md-sidebar--primary .md-nav__link[href]:focus, +body:has(.incql-launch) .md-sidebar--primary .md-nav__link[href]:hover, +body:has(.incql-launch) .md-sidebar--primary .md-nav__link--active, +body:has(.incql-architecture) .md-sidebar--primary .md-nav__link[for]:focus, +body:has(.incql-architecture) .md-sidebar--primary .md-nav__link[for]:hover, +body:has(.incql-architecture) .md-sidebar--primary .md-nav__link[href]:focus, +body:has(.incql-architecture) .md-sidebar--primary .md-nav__link[href]:hover, +body:has(.incql-architecture) .md-sidebar--primary .md-nav__link--active { + color: #315df0; +} + +.incql-architecture-shell { + display: grid; + grid-template-columns: minmax(9rem, 10.5rem) minmax(0, 1fr); + gap: clamp(1rem, 1.8vw, 1.65rem); + width: min(calc(100% - 2rem), 1680px); + margin: 0 auto; + padding: 1rem 0 4rem; +} + +.incql-architecture-rail { + position: sticky; + top: 6.7rem; + align-self: start; + display: grid; + gap: 0.15rem; + padding: 1.5rem 0.85rem 1.5rem 0.3rem; + border-right: 1px solid rgba(96, 126, 186, 0.18); +} + +.incql-architecture-rail__title { + margin: 0 0 0.75rem 0.55rem; + color: var(--incql-page-muted); + font-size: 0.62rem; + font-weight: 820; + letter-spacing: 0.1em; + text-transform: uppercase; +} + +.md-typeset .incql-architecture-rail a { + display: flex; + min-height: 2.25rem; + align-items: center; + padding: 0.42rem 0.55rem; + border-left: 2px solid transparent; + color: var(--incql-page-muted); + font-size: 0.74rem; + font-weight: 610; + line-height: 1.25; + text-decoration: none; + transition: border-color 140ms ease, color 140ms ease, background 140ms ease; +} + +.md-typeset .incql-architecture-rail a:hover, +.md-typeset .incql-architecture-rail a:focus-visible, +.md-typeset .incql-architecture-rail a.is-active, +.md-typeset .incql-architecture-rail a[aria-current="location"] { + border-left-color: var(--incql-page-blue); + background: linear-gradient(90deg, rgba(71, 109, 255, 0.08), transparent); + color: var(--incql-page-blue); +} + +.incql-architecture { + min-width: 0; +} + +.incql-architecture-chapter { + position: relative; + min-height: clamp(35rem, calc(100svh - 6rem), 44rem); + margin: 0 0 1.25rem; + padding: clamp(1.8rem, 3.5vw, 3.75rem); + overflow: hidden; + scroll-margin-top: 6.5rem; + border: 1px solid rgba(96, 126, 186, 0.18); + border-radius: 28px; + background: + radial-gradient(circle at 82% 14%, rgba(127, 109, 255, 0.06), transparent 28rem), + rgba(255, 255, 255, 0.82); + box-shadow: var(--incql-page-shadow); +} + +.incql-architecture-chapter::after { + content: ""; + position: absolute; + inset: 0; + z-index: 0; + border-radius: inherit; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.96); + pointer-events: none; +} + +.incql-architecture-chapter > * { + position: relative; + z-index: 1; +} + +.incql-architecture-heading { + max-width: 56rem; +} + +.incql-architecture-kicker { + margin: 0 0 0.8rem !important; + color: #315df0 !important; + font-size: 0.63rem; + font-weight: 840; + letter-spacing: 0.12em; + line-height: 1.2; + text-transform: uppercase; +} + +.md-typeset .incql-architecture-heading h1, +.md-typeset .incql-architecture-heading h2 { + max-width: 17ch; + margin: 0; + font-size: clamp(2.2rem, 3.5vw, 3.35rem); + font-weight: 790; + letter-spacing: -0.045em; + line-height: 1.04; +} + +.md-typeset #system-path .incql-architecture-heading h1 { + max-width: none; + font-size: clamp(2.25rem, 3.1vw, 3.1rem); +} + +.md-typeset .incql-architecture-lede { + max-width: 50rem; + margin: 0.95rem 0 0; + color: var(--incql-page-copy); + font-size: clamp(0.96rem, 1.2vw, 1.08rem); + line-height: 1.56; +} + +.md-typeset .incql-system-path { + position: relative; + display: grid; + gap: 0; + width: 100%; + margin: 1.65rem 0 0; + text-align: left; +} + +.md-typeset ol.incql-system-path__stages { + position: relative; + display: grid; + z-index: 2; + grid-template-columns: repeat(5, minmax(0, 1fr)); + gap: clamp(0.8rem, 1.5vw, 1.45rem); + margin: 0; + padding: 0 5.8%; + list-style: none; +} + +.md-typeset .incql-system-path__stages li { + min-width: 0; + margin: 0; + padding: 0; +} + +.incql-system-path__stages span, +.incql-system-path__stages strong, +.incql-system-path__stages small { + display: block; +} + +.incql-system-path__stages span { + margin-bottom: 0.32rem; + color: var(--incql-page-blue); + font-size: 0.59rem; + font-weight: 820; + letter-spacing: 0.1em; + text-transform: uppercase; +} + +.incql-system-path__stages [data-system-boundary="author"] > span { + color: var(--incql-page-cyan); +} + +.incql-system-path__stages [data-system-boundary="substrait"] > span, +.incql-system-path__stages [data-system-boundary="session"] > span { + color: var(--incql-page-violet); +} + +.incql-system-path__stages [data-system-boundary="adapter"] > span { + color: var(--incql-page-muted); +} + +.incql-system-path__stages strong { + color: var(--incql-page-ink); + font-size: clamp(0.88rem, 1.15vw, 1.02rem); + line-height: 1.25; +} + +.incql-system-path__stages small { + margin-top: 0.42rem; + color: var(--incql-page-copy); + font-size: 0.7rem; + line-height: 1.45; +} + +.incql-system-path__stages small code { + padding: 0 0.12rem; + font-size: 0.65rem; +} + +.incql-system-path__visual { + position: relative; + z-index: 1; + width: 100%; + margin-top: -0.2rem; + aspect-ratio: 1942 / 710; +} + +.md-typeset .incql-system-path__art { + display: block; + width: 100%; + height: auto; + max-width: none !important; + margin: 0; + object-fit: contain; + opacity: 0.98; +} + +.incql-system-path__glyphs { + position: absolute; + inset: 0; + display: grid; + grid-template-columns: repeat(5, minmax(0, 1fr)); + align-items: center; + padding: 0 5.8%; + transform: translateY(-5.8%); + pointer-events: none; +} + +.incql-system-path__glyphs > span:not(.incql-system-path__glyph--prism) { + display: grid; + width: clamp(2.8rem, 3.8vw, 3.65rem); + aspect-ratio: 1; + place-self: center; + place-items: center; + transform: translateY(-4%); + border: 1px solid rgba(87, 116, 198, 0.2); + border-radius: 14px; + background: rgba(255, 255, 255, 0.72); + box-shadow: 0 14px 32px rgba(54, 77, 133, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.96); + backdrop-filter: blur(12px); +} + +.incql-system-path__glyphs img { + width: clamp(1.25rem, 1.75vw, 1.6rem); + margin: 0; + opacity: 0.68; +} + +.incql-system-path__glyphs > span:first-child img { + filter: invert(58%) sepia(88%) saturate(1387%) hue-rotate(151deg) brightness(91%); +} + +.incql-system-path__glyphs > span:nth-child(3) img, +.incql-system-path__glyphs > span:nth-child(4) img { + filter: invert(42%) sepia(95%) saturate(2230%) hue-rotate(232deg) brightness(101%); +} + +.incql-system-path__glyphs > span:last-child img { + filter: invert(22%) sepia(20%) saturate(1051%) hue-rotate(183deg) brightness(90%); +} + +.incql-system-path__flow-label { + position: absolute; + z-index: 2; + padding: 0.25rem 0.42rem; + border-radius: 999px; + background: rgba(255, 255, 255, 0.78); + color: var(--incql-page-muted); + font-size: 0.58rem; + font-weight: 760; + letter-spacing: 0.04em; + line-height: 1; + text-transform: uppercase; + box-shadow: 0 8px 20px rgba(52, 68, 97, 0.06); + backdrop-filter: blur(8px); +} + +.incql-system-path__flow-label--forward { + top: 44%; + left: 0.4%; + color: #078ab8; +} + +.incql-system-path__flow-label--return { + bottom: 22%; + left: 0.4%; + color: var(--incql-page-copy); +} + +.incql-system-evidence { + position: relative; + z-index: 3; + display: grid; + grid-template-columns: minmax(7.5rem, 0.7fr) minmax(0, 4.3fr); + gap: 0.55rem 0.75rem; + align-items: center; + margin: clamp(-4.7rem, -5vw, -3rem) 4.2% 0; + padding: 0.72rem; + border: 1px solid rgba(96, 126, 186, 0.18); + border-radius: 18px; + background: rgba(251, 253, 255, 0.78); + box-shadow: 0 18px 45px rgba(55, 74, 123, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.96); + backdrop-filter: blur(16px); +} + +.incql-system-evidence__heading { + display: grid; + gap: 0.22rem; + padding: 0 0.35rem; +} + +.incql-system-evidence__heading > span { + color: var(--incql-page-blue); + font-size: 0.56rem; + font-weight: 840; + letter-spacing: 0.11em; + text-transform: uppercase; +} + +.incql-system-evidence__heading strong { + color: var(--incql-page-ink); + font-size: 0.73rem; + line-height: 1.3; +} + +.incql-system-evidence__heading code { + padding: 0; + border: 0 !important; + background: transparent !important; + font-size: 0.67rem; +} + +.incql-system-evidence__rail { + display: grid; + grid-template-columns: repeat(5, minmax(0, 1fr)); + gap: 0.45rem; +} + +.incql-system-evidence__rail article { + display: grid; + min-width: 0; + grid-template-columns: auto minmax(0, 1fr); + gap: 0.45rem; + align-items: center; + margin: 0; + padding: 0.58rem 0.52rem; + border: 1px solid rgba(96, 126, 186, 0.16); + border-radius: 12px; + background: rgba(255, 255, 255, 0.76); +} + +.incql-system-evidence__rail article::before { + content: none; +} + +.incql-system-evidence__rail article img { + width: 1.05rem; + margin: 0; + opacity: 0.66; + filter: invert(39%) sepia(86%) saturate(1929%) hue-rotate(213deg) brightness(101%); +} + +.incql-system-evidence__rail article > span, +.incql-system-evidence__rail strong, +.incql-system-evidence__rail small { + display: block; + min-width: 0; +} + +.incql-system-evidence__rail strong { + color: var(--incql-page-ink); + font-size: 0.64rem; + line-height: 1.25; +} + +.incql-system-evidence__rail small { + margin-top: 0.13rem; + color: var(--incql-page-muted); + font-size: 0.56rem; + line-height: 1.28; +} + +.incql-system-evidence__rail .incql-system-evidence__observation { + border-color: rgba(102, 116, 145, 0.25); + background: rgba(244, 247, 252, 0.84); +} + +.incql-system-evidence__rail .incql-system-evidence__observation img { + filter: invert(31%) sepia(16%) saturate(936%) hue-rotate(181deg) brightness(91%); +} + +.incql-system-evidence__note { + grid-column: 2; + display: block; + padding: 0 0.25rem; + color: var(--incql-page-copy); + font-size: 0.61rem; + line-height: 1.45; +} + +.md-typeset .incql-system-path figcaption { + margin: 1.4rem 4.2% 0; + padding-top: 1.15rem; + border-top: 1px solid var(--incql-page-line); + color: var(--incql-page-copy); + font-size: 0.75rem; + font-style: normal; + line-height: 1.65; +} + +.incql-architecture-explanation { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: clamp(1.4rem, 3vw, 3.4rem); + margin-top: 2rem; + padding-top: 1.6rem; + border-top: 1px solid var(--incql-page-line); +} + +.md-typeset .incql-architecture-explanation p, +.md-typeset .incql-architecture-note, +.md-typeset .incql-architecture-links { + font-size: 0.8rem; + line-height: 1.7; +} + +.md-typeset .incql-architecture-explanation p { + margin: 0; +} + +.md-typeset .incql-architecture-links { + margin: 1.35rem 0 0; + color: var(--incql-page-muted); +} + +.incql-evidence-story { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(7.6rem, 0.34fr) minmax(0, 1fr); + gap: 1rem; + margin-top: 2.05rem; + color: var(--incql-page-copy); +} + +.incql-evidence-story__key { + grid-row: 1; + grid-column: 2; + display: grid; + align-content: center; + gap: 0.38rem; + min-width: 0; + padding: 1.1rem; + border: 1px solid rgba(91, 115, 255, 0.28); + border-radius: 18px; + background: linear-gradient(155deg, rgba(239, 247, 255, 0.9), rgba(249, 244, 255, 0.88)); + box-shadow: 0 18px 45px rgba(55, 74, 123, 0.07), inset 0 1px 0 rgba(255, 255, 255, 0.94); +} + +.incql-evidence-story__key span, +.incql-evidence-lane > header > span, +.incql-evidence-lane__anchor span, +.incql-evidence-lane__facts dt { + color: var(--incql-page-blue); + font-size: 0.54rem; + font-weight: 820; + letter-spacing: 0.085em; + text-transform: uppercase; +} + +.incql-evidence-story__key > img { + width: 1.7rem; + margin: 0 0 0.15rem; + opacity: 0.56; + filter: invert(39%) sepia(86%) saturate(1929%) hue-rotate(213deg) brightness(101%); +} + +.incql-evidence-story__key strong, +.incql-evidence-story__key small { + display: block; +} + +.incql-evidence-story__key strong { + color: var(--incql-page-ink); + font-size: 0.8rem; +} + +.incql-evidence-story__key small { + color: var(--incql-page-copy); + font-size: 0.63rem; + line-height: 1.5; +} + +.incql-evidence-lane { + display: grid; + grid-template-columns: minmax(0, 1fr); + gap: 0.85rem 1rem; + min-width: 0; + padding: 1.05rem; + border: 1px solid rgba(96, 126, 186, 0.2); + border-radius: 18px; + background: rgba(255, 255, 255, 0.76); + box-shadow: 0 18px 45px rgba(55, 74, 123, 0.07), inset 0 1px 0 rgba(255, 255, 255, 0.96); + backdrop-filter: blur(12px); +} + +.incql-evidence-lane--plan { + grid-column: 1; + border-left: 3px solid var(--incql-page-blue); + background: linear-gradient(120deg, rgba(244, 250, 255, 0.88), rgba(255, 255, 255, 0.75)); +} + +.incql-evidence-lane--runtime { + grid-column: 3; + border-left: 3px solid var(--incql-page-muted); + background: linear-gradient(120deg, rgba(249, 250, 253, 0.9), rgba(255, 255, 255, 0.76)); +} + +.incql-evidence-lane > header { + min-width: 0; +} + +.incql-evidence-lane--runtime > header > span, +.incql-evidence-lane--runtime .incql-evidence-lane__anchor span, +.incql-evidence-lane--runtime .incql-evidence-lane__facts dt { + color: var(--incql-page-muted); +} + +.md-typeset .incql-evidence-lane h3 { + margin: 0.32rem 0 0; + color: var(--incql-page-ink); + font-size: 1.05rem; + line-height: 1.28; +} + +.md-typeset .incql-evidence-lane > header p { + margin: 0.56rem 0 0; + color: var(--incql-page-copy); + font-size: 0.69rem; + line-height: 1.58; +} + +.md-typeset ul.incql-evidence-lane__artifacts, +.incql-evidence-lane__facts { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 0.48rem; + min-width: 0; + margin: 0; + padding: 0; +} + +.incql-evidence-lane__artifacts { + list-style: none; +} + +.incql-evidence-lane__artifacts li, +.incql-evidence-lane__facts div { + min-width: 0; + margin: 0; + padding: 0.66rem; + border: 1px solid rgba(96, 126, 186, 0.16); + border-radius: 12px; + background: rgba(255, 255, 255, 0.76); +} + +.incql-evidence-lane__artifacts li { + display: grid; + grid-template-columns: auto minmax(0, 1fr); + gap: 0.5rem; + align-items: start; +} + +.incql-evidence-lane__artifacts img { + width: 1.05rem; + margin: 0.05rem 0 0; + opacity: 0.66; + filter: invert(39%) sepia(86%) saturate(1929%) hue-rotate(213deg) brightness(101%); +} + +.incql-evidence-lane__artifacts span, +.incql-evidence-lane__artifacts strong, +.incql-evidence-lane__artifacts small, +.incql-evidence-lane__facts dt, +.incql-evidence-lane__facts dd { + display: block; + min-width: 0; +} + +.incql-evidence-lane__artifacts strong { + color: var(--incql-page-ink); + font-size: 0.65rem; + line-height: 1.3; +} + +.incql-evidence-lane__artifacts small, +.incql-evidence-lane__facts dd { + margin-top: 0.2rem; + color: var(--incql-page-muted); + font-size: 0.57rem; + line-height: 1.4; +} + +.incql-evidence-lane__facts dt, +.incql-evidence-lane__facts dd { + margin-left: 0; +} + +.incql-evidence-lane__anchor { + grid-column: 1 / -1; + display: flex; + gap: 0.55rem; + align-items: baseline; + margin: 0; + padding-top: 0.1rem; +} + +.incql-evidence-lane__anchor code { + color: var(--incql-page-ink); + font-size: 0.64rem; + font-weight: 700; +} + +.md-typeset .incql-evidence-story__boundary { + grid-column: 1 / -1; + margin: 0; + padding: 0.95rem 1.05rem; + border: 1px solid rgba(123, 92, 255, 0.18); + border-radius: 14px; + background: rgba(248, 245, 255, 0.68); + color: var(--incql-page-copy); + font-size: 0.7rem; + line-height: 1.62; +} + +.md-typeset .incql-architecture-note { + margin: 1.6rem 0 0; + padding: 1rem 1.15rem; + border: 1px solid rgba(71, 109, 255, 0.14); + border-radius: 14px; + background: rgba(239, 244, 255, 0.72); +} + +.md-typeset ol.incql-query-boundary-map { + display: grid; + grid-template-columns: repeat(5, minmax(0, 1fr)); + margin: 2rem 0 0; + padding: 0; + border-bottom: 1px solid var(--incql-page-line); + list-style: none; +} + +.md-typeset .incql-query-boundary-map li { + min-width: 0; + margin: 0; + padding: 0.85rem 0.8rem 0.9rem; + border-top: 2px solid var(--incql-page-blue); + border-right: 1px solid var(--incql-page-line); + background: rgba(255, 255, 255, 0.38); +} + +.md-typeset .incql-query-boundary-map li:first-child { + border-top-color: var(--incql-page-cyan); + background: rgba(236, 251, 255, 0.42); +} + +.md-typeset .incql-query-boundary-map li:nth-child(3), +.md-typeset .incql-query-boundary-map li:nth-child(4) { + border-top-color: var(--incql-page-violet); + background: rgba(248, 244, 255, 0.42); +} + +.md-typeset .incql-query-boundary-map li:last-child { + border-top-color: var(--incql-page-muted); + border-right: 0; + background: rgba(246, 248, 252, 0.5); +} + +.incql-query-boundary-map span, +.incql-query-boundary-map code { + display: block; + min-width: 0; +} + +.incql-query-boundary-map span { + color: var(--incql-page-blue); + font-size: 0.55rem; + font-weight: 830; + letter-spacing: 0.085em; + text-transform: uppercase; +} + +.incql-query-boundary-map [data-system-boundary="author"] span { + color: var(--incql-page-cyan); +} + +.incql-query-boundary-map [data-system-boundary="substrait"] span, +.incql-query-boundary-map [data-system-boundary="session"] span { + color: var(--incql-page-violet); +} + +.incql-query-boundary-map [data-system-boundary="adapter"] span { + color: var(--incql-page-muted); +} + +.incql-query-boundary-map code { + margin-top: 0.3rem; + padding: 0; + border: 0 !important; + background: transparent !important; + color: var(--incql-page-ink); + font-size: 0.57rem; + font-weight: 660; + line-height: 1.42; + overflow-wrap: anywhere; + white-space: normal; +} + +.incql-query-entry { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + margin-top: 1.2rem; + overflow: hidden; + border: 1px solid rgba(71, 109, 255, 0.17); + border-radius: 20px; + background: rgba(248, 250, 255, 0.8); + box-shadow: 0 20px 54px rgba(55, 74, 123, 0.08); +} + +.incql-query-specimen, +.incql-query-desugar { + min-width: 0; + padding: clamp(1.1rem, 2.2vw, 1.7rem); +} + +.incql-query-specimen { + border-right: 1px solid var(--incql-page-line); + background: rgba(242, 247, 255, 0.76); + box-shadow: inset 0 3px 0 rgba(10, 174, 232, 0.58); +} + +.incql-query-desugar { + background: linear-gradient(135deg, rgba(255, 255, 255, 0.86), rgba(241, 250, 255, 0.74)); + box-shadow: inset 0 3px 0 rgba(10, 174, 232, 0.34); +} + +.incql-query-subheading > span, +.incql-query-phase__heading span, +.incql-evidence-correlation > header span, +.incql-query-side-check > span { + display: block; + color: var(--incql-page-blue); + font-size: 0.56rem; + font-weight: 840; + letter-spacing: 0.1em; + line-height: 1.25; + text-transform: uppercase; +} + +.md-typeset .incql-query-subheading h3, +.md-typeset .incql-query-phase__heading h3, +.md-typeset .incql-evidence-correlation > header h3 { + margin: 0.35rem 0 0; + color: var(--incql-page-ink); + font-size: clamp(1rem, 1.6vw, 1.3rem); + line-height: 1.25; +} + +.incql-query-entry .highlight { + margin: 1.1rem 0 0; + overflow: hidden; + border: 1px solid rgba(71, 109, 255, 0.14); + border-radius: 14px; + background: var(--md-code-bg-color); +} + +.incql-query-entry .highlight pre, +.incql-query-entry .highlight pre > code { + background: transparent; +} + +.incql-query-entry .highlight pre { + margin: 0; + overflow-x: auto; +} + +.incql-query-entry .highlight pre > code { + min-height: 0; + padding: 1rem 1.05rem; + border: 0; + color: var(--md-code-fg-color); + font-size: 0.72rem; + line-height: 1.62; +} + +.incql-query-entry .md-clipboard { + color: #40506c; + opacity: 0.82; +} + +.incql-query-facts { + display: grid; + grid-template-columns: minmax(0, 1fr); + margin: 1rem 0 0; + border-top: 1px solid var(--incql-page-line); + border-left: 1px solid var(--incql-page-line); +} + +.incql-query-facts div { + display: grid; + grid-template-columns: 6.5rem minmax(0, 1fr); + gap: 0.7rem; + align-items: baseline; + min-width: 0; + padding: 0.55rem 0.7rem; + border-right: 1px solid var(--incql-page-line); + border-bottom: 1px solid var(--incql-page-line); +} + +.incql-query-facts dt, +.incql-query-facts dd { + margin: 0; +} + +.incql-query-facts dt { + color: var(--incql-page-copy); + font-size: 0.56rem; + font-weight: 760; + letter-spacing: 0.05em; + text-transform: uppercase; +} + +.incql-query-facts dd { + margin-top: 0; + color: var(--incql-page-ink); + font-size: 0.68rem; + font-weight: 670; + line-height: 1.4; +} + +.md-typeset .incql-query-desugar > p:last-child { + margin: 0.95rem 0 0; + color: var(--incql-page-copy); + font-size: 0.7rem; + line-height: 1.62; +} + +.incql-query-phase, +.incql-evidence-correlation { + margin-top: 2.4rem; + padding-top: 1.8rem; + border-top: 1px solid var(--incql-page-line); +} + +.incql-query-phase__heading { + display: grid; + grid-template-columns: minmax(16rem, 0.78fr) minmax(0, 1.22fr); + gap: clamp(1.4rem, 3.4vw, 3.8rem); + align-items: end; +} + +.md-typeset .incql-query-phase__heading > p { + margin: 0; + color: var(--incql-page-copy); + font-size: 0.72rem; + line-height: 1.62; +} + +.incql-rewrite-ledger { + display: grid; + grid-template-columns: 0.8fr 1.45fr 0.8fr 1fr; + margin: 1.45rem 0 0; + border: 1px solid rgba(71, 109, 255, 0.15); + border-radius: 14px; + background: linear-gradient(90deg, rgba(236, 245, 255, 0.68), rgba(250, 242, 255, 0.68)); +} + +.incql-rewrite-ledger div { + min-width: 0; + padding: 0.76rem 0.85rem; + border-right: 1px solid var(--incql-page-line); +} + +.incql-rewrite-ledger div:last-child { + border-right: 0; +} + +.incql-rewrite-ledger dt, +.incql-rewrite-ledger dd { + margin: 0; +} + +.incql-rewrite-ledger dt { + color: var(--incql-page-copy); + font-size: 0.56rem; + font-weight: 760; + letter-spacing: 0.05em; + text-transform: uppercase; +} + +.incql-rewrite-ledger dd { + margin-top: 0.27rem; + color: var(--incql-page-ink); + font-size: 0.67rem; + font-weight: 690; + line-height: 1.4; +} + +.incql-query-table-wrap { + width: 100%; + margin-top: 1rem; + overflow-x: auto; + border: 1px solid rgba(71, 109, 255, 0.16); + border-radius: 16px; + background: rgba(255, 255, 255, 0.8); + box-shadow: 0 16px 42px rgba(55, 74, 123, 0.06); + outline: 2px solid transparent; + outline-offset: 2px; +} + +.incql-query-table-wrap:focus-visible { + outline: 3px solid #315df0; + outline-offset: 3px; + box-shadow: 0 16px 42px rgba(55, 74, 123, 0.08); +} + +.md-typeset table.incql-query-matrix, +.md-typeset table.incql-runtime-ledger { + display: table; + width: 100%; + min-width: 44rem; + margin: 0; + border: 0; + border-collapse: collapse; + table-layout: fixed; + background: transparent; + font-size: 0.66rem; +} + +.md-typeset table.incql-query-matrix { + min-width: 44rem; +} + +.incql-query-matrix caption, +.incql-runtime-ledger caption { + padding: 0.85rem 1rem 0.75rem; + border-bottom: 1px solid var(--incql-page-line); + color: var(--incql-page-copy); + font-size: 0.61rem; + font-weight: 640; + line-height: 1.45; + text-align: left; +} + +.incql-query-matrix th, +.incql-query-matrix td, +.incql-runtime-ledger th, +.incql-runtime-ledger td { + min-width: 0; + padding: 0.82rem 0.78rem; + border-right: 1px solid var(--incql-page-line); + border-bottom: 1px solid var(--incql-page-line); + color: var(--incql-page-copy); + line-height: 1.48; + text-align: left; + vertical-align: top; +} + +.incql-query-matrix th:last-child, +.incql-query-matrix td:last-child, +.incql-runtime-ledger th:last-child, +.incql-runtime-ledger td:last-child { + border-right: 0; +} + +.incql-query-matrix tbody tr:last-child > *, +.incql-runtime-ledger tbody tr:last-child > * { + border-bottom: 0; +} + +.incql-query-matrix thead th, +.incql-runtime-ledger thead th { + padding-top: 0.66rem; + padding-bottom: 0.66rem; + background: rgba(238, 244, 255, 0.78); + color: var(--incql-page-ink); + font-size: 0.54rem; + font-weight: 790; + letter-spacing: 0.055em; + line-height: 1.35; + text-transform: uppercase; +} + +.incql-query-matrix thead th:first-child, +.incql-query-matrix tbody th { + background: rgba(236, 251, 255, 0.32); +} + +.incql-query-matrix thead th:first-child { + box-shadow: inset 0 3px 0 rgba(10, 174, 232, 0.66); +} + +.incql-query-matrix thead th:nth-child(2), +.incql-query-matrix tbody td:nth-child(2) { + background: rgba(238, 246, 255, 0.46); +} + +.incql-query-matrix thead th:nth-child(2) { + box-shadow: inset 0 3px 0 rgba(49, 93, 240, 0.62); +} + +.incql-query-matrix thead th:nth-child(3), +.incql-query-matrix tbody td:nth-child(3) { + background: rgba(245, 241, 255, 0.46); +} + +.incql-query-matrix thead th:nth-child(3) { + box-shadow: inset 0 3px 0 rgba(123, 92, 255, 0.6); +} + +.incql-query-matrix thead th:nth-child(4), +.incql-query-matrix tbody td:nth-child(4) { + background: rgba(246, 248, 252, 0.52); +} + +.incql-query-matrix thead th:nth-child(4) { + box-shadow: inset 0 3px 0 rgba(102, 116, 145, 0.5); +} + +.incql-query-matrix__rewrite > th { + box-shadow: inset 3px 0 0 rgba(71, 109, 255, 0.7); +} + +.incql-query-matrix strong, +.incql-query-matrix code, +.incql-query-matrix small, +.incql-runtime-ledger strong, +.incql-runtime-ledger code, +.incql-runtime-ledger small { + display: block; +} + +.incql-query-matrix th > code, +.incql-query-matrix td > code, +.incql-runtime-ledger th > code, +.incql-runtime-ledger td > code, +.incql-runtime-ledger td > strong, +.incql-runtime-ledger th > strong, +.incql-query-matrix td > strong { + color: var(--incql-page-ink); + font-size: 0.67rem; + font-weight: 710; + line-height: 1.4; + white-space: normal; + overflow-wrap: normal; + word-break: normal; +} + +.incql-query-matrix small, +.incql-runtime-ledger small { + margin-top: 0.3rem; + color: var(--incql-page-muted); + font-size: 0.59rem; + line-height: 1.48; +} + +.incql-runtime-ledger th > code, +.incql-runtime-ledger td > code { + font-size: 0.59rem; +} + +.incql-runtime-ledger tbody tr[data-system-boundary="session"] > th { + box-shadow: inset 3px 0 0 rgba(123, 92, 255, 0.68); +} + +.incql-runtime-ledger tbody tr[data-system-boundary="adapter"] > th { + box-shadow: inset 3px 0 0 rgba(102, 116, 145, 0.68); +} + +.incql-runtime-ledger tbody tr[data-system-return="observation"] > th { + box-shadow: inset 3px 0 0 rgba(49, 93, 240, 0.48); +} + +.incql-query-matrix small code, +.incql-runtime-ledger small code { + display: inline; + font-size: inherit; + overflow-wrap: anywhere; + white-space: normal; +} + +.incql-query-cell-label { + display: none; +} + +.md-typeset .incql-query-caption { + margin: 0.82rem 0 0; + color: var(--incql-page-muted); + font-size: 0.61rem; + line-height: 1.55; +} + +.incql-query-side-check { + display: grid; + grid-template-columns: minmax(10rem, 0.75fr) minmax(16rem, 1.05fr) minmax(0, 1.7fr); + gap: 0.65rem 1.2rem; + align-items: center; + margin-top: 1rem; + padding: 0.9rem 1rem; + border-left: 3px solid rgba(71, 109, 255, 0.62); + background: rgba(239, 244, 255, 0.6); +} + +.incql-query-side-check > code { + color: var(--incql-page-ink); + font-size: 0.6rem; + font-weight: 680; + overflow-wrap: anywhere; + white-space: nowrap; +} + +.md-typeset .incql-query-side-check > p { + margin: 0; + color: var(--incql-page-copy); + font-size: 0.63rem; + line-height: 1.52; +} + +.incql-evidence-correlation > header { + max-width: 44rem; +} + +.incql-evidence-correlation__grid { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(10rem, 0.58fr) minmax(0, 1fr); + margin-top: 1.4rem; + overflow: hidden; + border-top: 1px solid var(--incql-page-line); + border-bottom: 1px solid var(--incql-page-line); +} + +.incql-evidence-correlation__grid > article, +.incql-evidence-correlation__key { + min-width: 0; + padding: 1.1rem 1rem; +} + +.incql-evidence-correlation__grid > article:first-child { + background: rgba(238, 246, 255, 0.46); +} + +.incql-evidence-correlation__grid > article:last-child { + background: rgba(248, 242, 255, 0.46); +} + +.incql-evidence-correlation__key { + display: flex; + flex-direction: column; + justify-content: center; + border-right: 1px solid var(--incql-page-line); + border-left: 1px solid var(--incql-page-line); + background: rgba(237, 252, 252, 0.58); + text-align: center; +} + +.incql-evidence-correlation__grid span { + color: var(--incql-page-blue); + font-size: 0.53rem; + font-weight: 800; + letter-spacing: 0.075em; + text-transform: uppercase; +} + +.incql-evidence-correlation__grid strong { + display: block; + margin-top: 0.35rem; + color: var(--incql-page-ink); + font-size: 0.72rem; + line-height: 1.4; +} + +.md-typeset .incql-evidence-correlation__grid p, +.incql-evidence-correlation__key small { + display: block; + margin: 0.5rem 0 0; + color: var(--incql-page-copy); + font-size: 0.62rem; + line-height: 1.55; +} + +.incql-ownership-bands { + display: grid; + gap: 1rem; + margin-top: 3rem; +} + +.incql-ownership-band { + display: grid; + grid-template-columns: minmax(13rem, 0.82fr) minmax(0, 2.18fr); + overflow: hidden; + border: 1px solid rgba(96, 126, 186, 0.2); + border-left: 3px solid var(--incql-page-blue); + border-radius: 20px; + background: rgba(255, 255, 255, 0.72); + box-shadow: 0 18px 46px rgba(55, 74, 123, 0.07), inset 0 1px 0 rgba(255, 255, 255, 0.96); +} + +.incql-ownership-band[data-ownership-phase="authoring"] { + border-left-color: var(--incql-page-cyan); +} + +.incql-ownership-band[data-ownership-phase="planning"] { + border-left-color: var(--incql-page-blue); +} + +.incql-ownership-band[data-ownership-phase="runtime"] { + border-left-color: var(--incql-page-muted); +} + +.incql-ownership-band > header { + min-width: 0; + padding: 1.15rem; + border-right: 1px solid var(--incql-page-line); + background: linear-gradient(145deg, rgba(242, 248, 255, 0.78), rgba(255, 255, 255, 0.6)); +} + +.incql-ownership-band[data-ownership-phase="authoring"] > header { + background: linear-gradient(145deg, rgba(236, 251, 255, 0.8), rgba(255, 255, 255, 0.62)); +} + +.incql-ownership-band[data-ownership-phase="planning"] > header { + background: linear-gradient(145deg, rgba(241, 247, 255, 0.82), rgba(249, 244, 255, 0.62)); +} + +.incql-ownership-band[data-ownership-phase="runtime"] > header { + background: linear-gradient(145deg, rgba(246, 248, 252, 0.9), rgba(255, 255, 255, 0.64)); +} + +.incql-ownership-band > header > span, +.incql-ownership-pair article > span, +.incql-ownership-handoff > span, +.incql-module-topology > header > span, +.incql-module-evidence > header > span { + color: var(--incql-page-blue); + font-size: 0.55rem; + font-weight: 830; + letter-spacing: 0.09em; + text-transform: uppercase; +} + +.incql-ownership-band[data-ownership-phase="authoring"] > header > span { + color: var(--incql-page-cyan); +} + +.incql-ownership-band[data-ownership-phase="runtime"] > header > span { + color: var(--incql-page-muted); +} + +.md-typeset .incql-ownership-band > header h3 { + margin: 0.35rem 0 0; + color: var(--incql-page-ink); + font-size: 1.02rem; + line-height: 1.28; +} + +.md-typeset .incql-ownership-band > header p { + margin: 0.55rem 0 0; + color: var(--incql-page-copy); + font-size: 0.71rem; + line-height: 1.58; +} + +.incql-ownership-pair { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + min-width: 0; +} + +.incql-ownership-pair article { + min-width: 0; + padding: 1.05rem 1.1rem; + border-right: 1px solid var(--incql-page-line); +} + +.incql-ownership-pair article:last-child { + border-right: 0; +} + +.md-typeset .incql-ownership-pair h4 { + margin: 0.34rem 0 0.6rem; + color: var(--incql-page-ink); + font-size: 1.02rem; + line-height: 1.25; +} + +.md-typeset .incql-ownership-pair p { + margin: 0.4rem 0 0; + color: var(--incql-page-copy); + font-size: 0.74rem; + line-height: 1.6; +} + +.md-typeset .incql-ownership-handoff { + grid-column: 1 / -1; + display: grid; + grid-template-columns: auto minmax(0, 1fr) auto minmax(0, 1fr); + gap: 0.55rem; + align-items: center; + margin: 0; + padding: 0.72rem 1rem; + border-top: 1px solid var(--incql-page-line); + background: rgba(247, 250, 255, 0.76); +} + +.incql-ownership-handoff code, +.incql-ownership-handoff b { + min-width: 0; + overflow-wrap: anywhere; +} + +.incql-ownership-handoff code { + padding: 0; + border: 0 !important; + background: transparent !important; + color: var(--incql-page-ink); + font-size: 0.64rem; + font-weight: 680; +} + +.incql-ownership-handoff b { + color: var(--incql-page-muted); + font-size: 0.57rem; + font-weight: 720; + text-align: center; +} + +.incql-module-topology { + margin-top: 3rem; + overflow: hidden; + border: 1px solid rgba(96, 126, 186, 0.2); + border-radius: 22px; + background: rgba(255, 255, 255, 0.72); + box-shadow: 0 22px 58px rgba(55, 74, 123, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.96); +} + +.incql-module-topology > header { + display: grid; + grid-template-columns: minmax(10rem, 0.62fr) minmax(15rem, 1.1fr) minmax(0, 1.6fr); + gap: 1rem; + align-items: end; + padding: 1.2rem 1.3rem; + background: linear-gradient(120deg, rgba(238, 249, 255, 0.76), rgba(248, 244, 255, 0.68)); +} + +.md-typeset .incql-module-topology > header h3, +.md-typeset .incql-module-topology > header p { + margin: 0; +} + +.md-typeset .incql-module-topology > header h3 { + color: var(--incql-page-ink); + font-size: 1.1rem; + line-height: 1.28; +} + +.md-typeset .incql-module-topology > header p { + color: var(--incql-page-copy); + font-size: 0.72rem; + line-height: 1.58; +} + +.incql-module-table-wrap { + width: 100%; + overflow-x: auto; + border-top: 1px solid var(--incql-page-line); + outline: 2px solid transparent; + outline-offset: -2px; +} + +.incql-module-table-wrap:focus-visible { + outline-color: var(--incql-page-blue); +} + +.md-typeset table.incql-module-table { + display: table; + width: 100%; + min-width: 40rem; + margin: 0; + border: 0; + border-collapse: collapse; + table-layout: fixed; + background: transparent; +} + +.incql-module-table th, +.incql-module-table td { + padding: 0.9rem 1rem; + border-right: 1px solid var(--incql-page-line); + border-bottom: 1px solid var(--incql-page-line); + color: var(--incql-page-copy); + text-align: left; + vertical-align: top; +} + +.incql-module-table th:last-child, +.incql-module-table td:last-child { + border-right: 0; +} + +.incql-module-table tbody tr:last-child > * { + border-bottom: 0; +} + +.incql-module-table thead th { + background: rgba(241, 246, 255, 0.82); + color: var(--incql-page-ink); + font-size: 0.58rem; + font-weight: 790; + letter-spacing: 0.055em; + line-height: 1.38; + text-transform: uppercase; +} + +.incql-module-table thead th:first-child { + width: 19%; +} + +.incql-module-table tbody th { + background: rgba(248, 250, 255, 0.8); + box-shadow: inset 3px 0 0 var(--incql-page-blue); +} + +.incql-module-table tbody tr[data-system-boundary="author"] > th { + box-shadow: inset 3px 0 0 var(--incql-page-cyan); +} + +.incql-module-table tbody tr[data-system-boundary="substrait"] > th, +.incql-module-table tbody tr[data-system-boundary="session"] > th { + box-shadow: inset 3px 0 0 var(--incql-page-violet); +} + +.incql-module-table tbody tr[data-system-boundary="adapter"] > th { + box-shadow: inset 3px 0 0 var(--incql-page-muted); +} + +.incql-module-table tbody th span, +.incql-module-table tbody th strong, +.incql-module-table td code, +.incql-module-table td strong, +.incql-module-table td small { + display: block; +} + +.incql-module-table tbody th span { + color: var(--incql-page-blue); + font-size: 0.53rem; + font-weight: 820; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.incql-module-table tbody tr[data-system-boundary="author"] th span { + color: var(--incql-page-cyan); +} + +.incql-module-table tbody tr[data-system-boundary="substrait"] th span, +.incql-module-table tbody tr[data-system-boundary="session"] th span { + color: var(--incql-page-violet); +} + +.incql-module-table tbody tr[data-system-boundary="adapter"] th span { + color: var(--incql-page-muted); +} + +.incql-module-table tbody th strong { + margin-top: 0.28rem; + color: var(--incql-page-ink); + font-size: 0.69rem; + line-height: 1.4; +} + +.incql-module-table td code, +.incql-module-table td strong { + color: var(--incql-page-ink); + font-size: 0.66rem; + font-weight: 690; + line-height: 1.42; + overflow-wrap: anywhere; + white-space: normal; +} + +.incql-module-table td small { + margin-top: 0.26rem; + color: var(--incql-page-muted); + font-size: 0.61rem; + line-height: 1.48; +} + +.incql-module-table td small + code { + margin-top: 0.7rem; +} + +.incql-module-evidence { + display: grid; + grid-template-columns: minmax(12rem, 1.15fr) repeat(3, minmax(0, 1fr)); + gap: 0.55rem; + padding: 0.8rem; + border-top: 1px solid var(--incql-page-line); + background: linear-gradient(90deg, rgba(241, 248, 255, 0.7), rgba(249, 246, 255, 0.72)); +} + +.incql-module-evidence > header, +.incql-module-evidence article { + min-width: 0; + padding: 0.75rem; +} + +.incql-module-evidence > header { + align-content: center; +} + +.incql-module-evidence > header strong { + display: block; + margin-top: 0.35rem; + color: var(--incql-page-ink); + font-size: 0.68rem; + line-height: 1.48; +} + +.incql-module-evidence article { + display: grid; + grid-template-columns: auto minmax(0, 1fr); + gap: 0.55rem; + align-items: start; + border: 1px solid rgba(96, 126, 186, 0.16); + border-radius: 12px; + background: rgba(255, 255, 255, 0.75); +} + +.incql-module-evidence article img { + width: 1.05rem; + margin: 0.08rem 0 0; + opacity: 0.66; + filter: invert(39%) sepia(86%) saturate(1929%) hue-rotate(213deg) brightness(101%); +} + +.incql-module-evidence article span, +.incql-module-evidence article code, +.incql-module-evidence article small { + display: block; + min-width: 0; +} + +.incql-module-evidence article code { + color: var(--incql-page-ink); + font-size: 0.62rem; + font-weight: 690; + overflow-wrap: anywhere; + white-space: normal; +} + +.incql-module-evidence article small { + margin-top: 0.24rem; + color: var(--incql-page-muted); + font-size: 0.58rem; + line-height: 1.45; +} + +.incql-repo-boundary { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 1px; + margin-top: 2.1rem; + overflow: hidden; + border: 1px solid rgba(71, 109, 255, 0.16); + border-radius: 18px; + background: rgba(71, 109, 255, 0.16); +} + +.incql-repo-boundary div { + padding: 1.25rem; + background: rgba(255, 255, 255, 0.9); +} + +.incql-repo-boundary span, +.incql-repo-boundary strong { + display: block; +} + +.incql-repo-boundary span { + color: var(--incql-page-blue); + font-size: 0.59rem; + font-weight: 820; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.incql-repo-boundary strong { + margin-top: 0.4rem; + color: var(--incql-page-ink); + font-size: 0.73rem; + line-height: 1.5; +} + +.incql-architecture-next { + display: flex; + gap: 2rem; + align-items: center; + justify-content: space-between; + margin-top: 2.1rem; + padding: 1.2rem 1.3rem; + border-radius: 18px; + background: linear-gradient(120deg, #071534, #152960 68%, #432a80); + box-shadow: 0 24px 58px rgba(7, 21, 52, 0.18); +} + +body:has(.incql-architecture) .md-typeset .incql-architecture-next h3 { + margin: 0; + color: #ffffff; + font-size: 1.15rem; +} + +.md-typeset .incql-architecture-next .incql-architecture-kicker { + color: #8beeff !important; +} + +.md-typeset .incql-architecture-next > div p:last-child { + max-width: 48rem; + margin: 0.45rem 0 0; + color: rgba(226, 237, 255, 0.76); + font-size: 0.72rem; + line-height: 1.55; +} + +.md-typeset .incql-architecture-next__action { + flex: 0 0 auto; + margin: 0; +} + +body:has(.incql-architecture) .md-typeset .incql-architecture-next a { + display: inline-flex; + min-height: 2.2rem; + align-items: center; + justify-content: center; + padding: 0.55rem 0.9rem; + border: 1px solid rgba(255, 255, 255, 0.34); + border-radius: 10px; + background: rgba(255, 255, 255, 0.08); + color: #ffffff; + font-size: 0.72rem; + font-weight: 740; + text-decoration: none; +} + +body:has(.incql-architecture) .md-typeset .incql-architecture-next a:hover, +body:has(.incql-architecture) .md-typeset .incql-architecture-next a:focus-visible { + border-color: #8beeff; + color: #8beeff; +} + +@media screen and (min-width: 76.25em) { + body:has(.incql-launch) .md-sidebar--primary, + body:has(.incql-architecture) .md-sidebar--primary { + display: none; + } +} + +@media screen and (max-width: 76.24em) { + body:has(.incql-architecture) .md-main__inner { + margin-top: 0; + } + + .incql-architecture-shell { + grid-template-columns: minmax(0, 1fr); + width: min(calc(100% - 1.5rem), 1120px); + padding-top: 0.75rem; + } + + .incql-architecture-rail { + top: 3rem; + z-index: 3; + display: flex; + gap: 0.15rem; + margin: 0 -0.15rem; + padding: 0.35rem 0.15rem; + overflow-x: auto; + border-right: 0; + border-bottom: 1px solid rgba(96, 126, 186, 0.18); + background: rgba(248, 251, 255, 0.9); + box-shadow: 0 12px 30px rgba(55, 74, 123, 0.06); + backdrop-filter: blur(12px); + scrollbar-width: none; + } + + .incql-architecture-rail::-webkit-scrollbar { + display: none; + } + + .incql-architecture-rail__title { + display: none; + } + + .md-typeset .incql-architecture-rail a { + min-height: 2.45rem; + flex: 0 0 auto; + padding: 0.55rem 0.7rem; + border-bottom: 2px solid transparent; + border-left: 0; + white-space: nowrap; + } + + .md-typeset .incql-architecture-rail a:hover, + .md-typeset .incql-architecture-rail a:focus-visible, + .md-typeset .incql-architecture-rail a.is-active, + .md-typeset .incql-architecture-rail a[aria-current="location"] { + border-bottom-color: var(--incql-page-blue); + background: rgba(71, 109, 255, 0.06); + } + + .incql-architecture-chapter { + scroll-margin-top: 6.2rem; + } +} + +@media screen and (max-width: 62em) { + .md-typeset .incql-system-path { + margin-top: 1.9rem; + } + + .incql-system-path__visual { + order: 1; + margin-top: 0; + } + + .md-typeset ol.incql-system-path__stages { + order: 2; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 0.7rem; + padding: 0.6rem 2% 0; + } + + .md-typeset .incql-system-path__stages li { + min-height: 7.5rem; + padding: 0.85rem; + border: 1px solid rgba(96, 126, 186, 0.17); + border-radius: 14px; + background: rgba(255, 255, 255, 0.7); + box-shadow: 0 12px 28px rgba(55, 74, 123, 0.06); + } + + .incql-system-path__stages li:last-child { + grid-column: 1 / -1; + } + + .incql-system-evidence { + order: 3; + grid-template-columns: minmax(0, 1fr); + margin: 1rem 2% 0; + } + + .incql-system-evidence__heading { + grid-template-columns: auto minmax(0, 1fr); + column-gap: 0.7rem; + align-items: baseline; + } + + .incql-system-evidence__rail { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .incql-system-evidence__rail .incql-system-evidence__observation { + grid-column: 1 / -1; + } + + .incql-system-evidence__note { + grid-column: 1; + } + + .md-typeset .incql-system-path figcaption { + order: 4; + margin-inline: 2%; + } + + .incql-evidence-story, + .incql-evidence-lane { + grid-template-columns: minmax(0, 1fr); + } + + .incql-evidence-lane--plan, + .incql-evidence-lane--runtime { + grid-row: auto; + grid-column: auto; + } + + .incql-evidence-story__key { + grid-row: auto; + grid-column: auto; + grid-template-columns: auto minmax(8rem, 0.55fr) minmax(0, 1fr); + column-gap: 0.8rem; + align-items: center; + } + + .incql-evidence-story__key > img { + grid-row: 1 / span 2; + grid-column: 1; + } + + .incql-evidence-story__key > span { + grid-row: 1; + grid-column: 2; + } + + .incql-evidence-story__key > strong { + grid-row: 2; + grid-column: 2; + } + + .incql-evidence-story__key small { + grid-row: 1 / span 2; + grid-column: 3; + } + + .incql-evidence-lane__artifacts, + .incql-evidence-lane__facts { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .md-typeset ol.incql-query-boundary-map { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .md-typeset .incql-query-boundary-map li:nth-child(2n) { + border-right: 0; + } + + .md-typeset .incql-query-boundary-map li:last-child { + grid-column: 1 / -1; + } + + .incql-query-entry, + .incql-query-phase__heading { + grid-template-columns: minmax(0, 1fr); + } + + .incql-query-specimen { + border-right: 0; + border-bottom: 1px solid var(--incql-page-line); + } + + .incql-query-phase__heading { + gap: 0.75rem; + } + + .incql-query-side-check { + grid-template-columns: minmax(12rem, 0.8fr) minmax(0, 1.2fr); + } + + .incql-query-side-check > p { + grid-column: 1 / -1; + } + + .incql-evidence-correlation__grid { + grid-template-columns: minmax(0, 1fr) minmax(8.5rem, 0.48fr) minmax(0, 1fr); + } + + .incql-ownership-band { + grid-template-columns: minmax(0, 1fr); + } + + .incql-ownership-band > header { + border-right: 0; + border-bottom: 1px solid var(--incql-page-line); + } + + .incql-module-topology > header { + grid-template-columns: minmax(0, 1fr) minmax(0, 1.5fr); + } + + .incql-module-topology > header > span { + grid-column: 1 / -1; + } + + .incql-module-evidence { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .incql-module-evidence > header { + grid-column: 1 / -1; + } +} + +@media screen and (max-width: 44em) { + .incql-architecture-shell { + width: min(calc(100% - 1rem), 760px); + padding-bottom: 2.5rem; + } + + .incql-architecture-chapter { + min-height: 0; + margin-bottom: 0.9rem; + padding: 3.1rem 1.1rem 1.35rem; + border-radius: 20px; + scroll-margin-top: 6.2rem; + } + + .md-typeset .incql-architecture-heading h1, + .md-typeset .incql-architecture-heading h2 { + max-width: none; + font-size: clamp(2.1rem, 10vw, 2.65rem); + line-height: 1.02; + } + + .md-typeset .incql-architecture-lede { + font-size: 0.9rem; + line-height: 1.55; + } + + .md-typeset .incql-system-path { + margin-top: 1.4rem; + } + + .incql-system-path__glyphs, + .incql-system-path__flow-label { + display: none; + } + + .md-typeset ol.incql-system-path__stages, + .incql-architecture-explanation, + .incql-repo-boundary { + grid-template-columns: minmax(0, 1fr); + } + + .md-typeset ol.incql-system-path__stages { + gap: 0.55rem; + padding: 0.4rem 0 0; + } + + .incql-system-path__stages li, + .incql-system-path__stages li:last-child { + grid-column: auto; + min-height: 0; + padding: 0.82rem; + } + + .incql-system-path__stages small { + font-size: 0.7rem; + } + + .incql-system-evidence { + margin: 0.8rem 0 0; + padding: 0.7rem; + } + + .incql-system-evidence__heading, + .incql-system-evidence__rail { + grid-template-columns: minmax(0, 1fr); + } + + .incql-system-evidence__rail .incql-system-evidence__observation { + grid-column: auto; + } + + .incql-system-evidence__rail strong, + .incql-system-evidence__rail small { + overflow: visible; + text-overflow: clip; + white-space: normal; + } + + .md-typeset .incql-system-path figcaption { + margin: 1rem 0 0; + } + + .incql-architecture-explanation { + gap: 1rem; + margin-top: 1.4rem; + padding-top: 1.25rem; + } + + .incql-evidence-story { + margin-top: 1.4rem; + } + + .incql-evidence-story__key { + grid-template-columns: minmax(0, 1fr); + } + + .incql-evidence-story__key > img, + .incql-evidence-story__key > span, + .incql-evidence-story__key > strong, + .incql-evidence-story__key small { + grid-row: auto; + grid-column: auto; + } + + .incql-evidence-lane { + padding: 0.88rem; + } + + .md-typeset ul.incql-evidence-lane__artifacts, + .incql-evidence-lane__facts { + grid-template-columns: minmax(0, 1fr); + } + + .md-typeset ol.incql-query-boundary-map { + grid-template-columns: minmax(0, 1fr); + margin-top: 1.55rem; + } + + .md-typeset .incql-query-boundary-map li, + .md-typeset .incql-query-boundary-map li:nth-child(2n), + .md-typeset .incql-query-boundary-map li:last-child { + grid-column: auto; + border-right: 0; + border-bottom: 1px solid var(--incql-page-line); + } + + .md-typeset .incql-query-boundary-map li:last-child { + border-bottom: 0; + } + + .incql-query-entry { + margin-top: 0.8rem; + border-radius: 16px; + } + + .incql-query-entry .highlight, + .incql-query-entry .highlight pre { + overflow-x: auto; + } + + .incql-query-entry .highlight pre > code { + padding: 0.9rem; + font-size: 0.7rem; + } + + .incql-query-facts, + .incql-rewrite-ledger, + .incql-query-side-check, + .incql-evidence-correlation__grid { + grid-template-columns: minmax(0, 1fr); + } + + .incql-query-facts div, + .incql-rewrite-ledger div { + border-right: 0; + } + + .incql-query-facts div { + grid-template-columns: minmax(0, 1fr); + gap: 0.2rem; + } + + .incql-rewrite-ledger { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .incql-rewrite-ledger div, + .incql-rewrite-ledger div:last-child { + border-right: 1px solid var(--incql-page-line); + border-bottom: 1px solid var(--incql-page-line); + } + + .incql-rewrite-ledger div:nth-child(2n) { + border-right: 0; + } + + .incql-rewrite-ledger div:nth-child(n + 3) { + border-bottom: 0; + } + + .incql-query-facts div:last-child, + .incql-rewrite-ledger div:last-child { + border-bottom: 0; + } + + .incql-query-phase, + .incql-evidence-correlation { + margin-top: 2rem; + padding-top: 1.5rem; + } + + .md-typeset table.incql-query-matrix, + .md-typeset table.incql-runtime-ledger { + display: block; + min-width: 0; + } + + .incql-query-matrix caption, + .incql-runtime-ledger caption { + display: block; + } + + .incql-query-matrix thead, + .incql-runtime-ledger thead { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0 0 0 0); + white-space: nowrap; + border: 0; + } + + .incql-query-matrix tbody, + .incql-runtime-ledger tbody, + .incql-query-matrix tr, + .incql-runtime-ledger tr, + .incql-query-matrix th, + .incql-query-matrix td, + .incql-runtime-ledger th, + .incql-runtime-ledger td { + display: block; + width: 100%; + } + + .incql-query-matrix tbody tr, + .incql-runtime-ledger tbody tr { + border-bottom: 1px solid rgba(71, 109, 255, 0.2); + } + + .incql-query-matrix tbody tr:last-child, + .incql-runtime-ledger tbody tr:last-child { + border-bottom: 0; + } + + .incql-query-matrix th, + .incql-query-matrix td, + .incql-runtime-ledger th, + .incql-runtime-ledger td, + .incql-query-matrix tbody tr:last-child > *, + .incql-runtime-ledger tbody tr:last-child > * { + padding: 0.78rem 0.85rem; + border-right: 0; + border-bottom: 1px solid var(--incql-page-line); + } + + .incql-query-matrix tbody tr > *:last-child, + .incql-runtime-ledger tbody tr > *:last-child { + border-bottom: 0; + } + + .incql-query-matrix__rewrite > th { + box-shadow: inset 3px 0 0 rgba(71, 109, 255, 0.7); + } + + .incql-query-cell-label { + display: block; + margin-bottom: 0.28rem; + color: var(--incql-page-blue); + font-size: 0.49rem; + font-weight: 800; + letter-spacing: 0.075em; + text-transform: uppercase; + } + + .incql-query-side-check > p { + grid-column: auto; + } + + .incql-query-side-check > code { + white-space: normal; + } + + .incql-evidence-correlation__grid > article, + .incql-evidence-correlation__key { + padding: 0.95rem 0.8rem; + border-right: 0; + border-left: 0; + border-bottom: 1px solid var(--incql-page-line); + text-align: left; + } + + .incql-evidence-correlation__grid > article:last-child { + border-bottom: 0; + } + + .incql-ownership-bands, + .incql-module-topology { + margin-top: 1.8rem; + } + + .incql-ownership-pair { + grid-template-columns: minmax(0, 1fr); + } + + .incql-ownership-pair article { + border-right: 0; + border-bottom: 1px solid var(--incql-page-line); + } + + .incql-ownership-pair article:last-child { + border-bottom: 0; + } + + .md-typeset .incql-ownership-handoff { + grid-template-columns: minmax(0, 1fr); + gap: 0.3rem; + } + + .incql-ownership-handoff b { + text-align: left; + } + + .incql-module-topology > header, + .incql-module-evidence { + grid-template-columns: minmax(0, 1fr); + } + + .incql-module-topology > header { + gap: 0.55rem; + padding: 1rem; + } + + .incql-module-topology > header > span, + .incql-module-evidence > header { + grid-column: auto; + } + + .incql-module-table-wrap { + overflow: visible; + } + + .md-typeset table.incql-module-table { + display: block; + min-width: 0; + } + + .incql-module-table thead { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0 0 0 0); + white-space: nowrap; + border: 0; + } + + .incql-module-table tbody, + .incql-module-table tr, + .incql-module-table th, + .incql-module-table td { + display: block; + width: 100%; + } + + .incql-module-table tbody tr { + border-bottom: 1px solid rgba(96, 126, 186, 0.24); + } + + .incql-module-table tbody tr:last-child { + border-bottom: 0; + } + + .incql-module-table th, + .incql-module-table td, + .incql-module-table tbody tr:last-child > * { + padding: 0.8rem; + border-right: 0; + border-bottom: 1px solid var(--incql-page-line); + } + + .incql-module-table tbody tr > *:last-child { + border-bottom: 0; + } + + .incql-module-table td:first-of-type { + background: rgba(240, 248, 255, 0.5); + } + + .incql-module-table td:last-of-type { + background: rgba(248, 245, 255, 0.45); + } + + .incql-module-table td::before { + content: attr(data-path-label); + display: block; + margin-bottom: 0.38rem; + color: var(--incql-page-blue); + font-size: 0.5rem; + font-weight: 820; + letter-spacing: 0.075em; + text-transform: uppercase; + } + + .incql-architecture-next { + display: grid; + gap: 1rem; + padding: 1.2rem; + } + + .md-typeset .incql-architecture-next__action, + body:has(.incql-architecture) .md-typeset .incql-architecture-next a { + width: 100%; + } + + body:has(.incql-architecture) .md-typeset .incql-architecture-next a { + text-align: center; + } +} diff --git a/docs/whitepapers/incql_db.md b/docs/whitepapers/incql_db.md index 30165273..5bf6e048 100644 --- a/docs/whitepapers/incql_db.md +++ b/docs/whitepapers/incql_db.md @@ -19,7 +19,7 @@ The product shape is closer to SQLite-style local ownership plus DuckDB-style an IncQL-DB should be opened directly by an application: ```incan -let db = IncQLDB.open("./agent_memory.incqldb") +db = IncQLDB.open("./agent_memory.incqldb") ``` and inspected with the IncQL CLI: diff --git a/examples/README.md b/examples/README.md index 06a7e0f5..523e5bb7 100644 --- a/examples/README.md +++ b/examples/README.md @@ -18,6 +18,8 @@ The examples are split between compile-safe API shape examples and executable Se - `advanced_retail_analytics.incn` — Larger 100-row retail method-chain spike covering scalar functions, JSON, URL parsing, hashing, aggregates, windows, and generators - `advanced_retail_query_blocks/` — Dependency-consumer query-block version of the retail spike, covering the query-block vocabulary over the same 100-row fixture +- `tutorial_book/` — Runnable downstream project behind the seven-chapter IncQL Book, following one CSV-backed + `LazyFrame` through Prism inspection, DataFusion execution, evidence, a caller-owned decision, and an observed write - `models.incn` — Shared `@derive(Clone)` row models for examples ## Running examples @@ -32,6 +34,7 @@ incan run examples/session_grouped_aggregate_csv.incn incan run examples/session_with_column_csv.incn incan run examples/advanced_retail_analytics.incn (cd examples/advanced_retail_query_blocks && incan lock && incan run src/main.incn) +(cd examples/tutorial_book && incan lock && incan run src/main.incn --locked) ``` > Note: Session examples expect repo fixtures in `tests/fixtures/` and write output files to `tests/target/`. diff --git a/examples/tutorial_book/incan.lock b/examples/tutorial_book/incan.lock new file mode 100644 index 00000000..c34d0d25 --- /dev/null +++ b/examples/tutorial_book/incan.lock @@ -0,0 +1,3563 @@ +# Auto-generated by Incan - do not edit manually +# Regenerate with: incan lock + +[incan] +format = 1 +incan-version = "0.3.0" +deps-fingerprint = "sha256:03a4888868cd34aa3de480b00ad16782df968854048cb5fdbc307e4de32970ba" +cargo-features = [] +cargo-no-default-features = false +cargo-all-features = false + +[cargo] +lock = """ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "const-random", + "getrandom 0.3.4", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e76a019e91224d279006ff972f1e984179a6e9feb050adba6ce8274aef23195" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" + +[[package]] +name = "ar_archive_writer" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4087686b4b0a3427190bae57a1d9a478dbb2d40c5dc1bd6e2b6d797913bdd348" +dependencies = [ + "object", +] + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" + +[[package]] +name = "arrow" +version = "58.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "378530e55cd479eda3c14eb345310799717e6f76d0c332041e8487022166b471" +dependencies = [ + "arrow-arith", + "arrow-array", + "arrow-buffer", + "arrow-cast", + "arrow-csv", + "arrow-data", + "arrow-ipc", + "arrow-json", + "arrow-ord", + "arrow-row", + "arrow-schema", + "arrow-select", + "arrow-string", +] + +[[package]] +name = "arrow-arith" +version = "58.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0ab212d2c1886e802f51c5212d78ebbcbb0bec980fff9dadc1eb8d45cd0b738" +dependencies = [ + "arrow-array", + "arrow-buffer", + "arrow-data", + "arrow-schema", + "chrono", + "num-traits", +] + +[[package]] +name = "arrow-array" +version = "58.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfd33d3e92f207444098c75b42de99d329562be0cf686b307b097cc52b4e999e" +dependencies = [ + "ahash", + "arrow-buffer", + "arrow-data", + "arrow-schema", + "chrono", + "chrono-tz", + "half", + "hashbrown 0.17.1", + "num-complex", + "num-integer", + "num-traits", +] + +[[package]] +name = "arrow-buffer" +version = "58.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6cd424c2693bcdbc150d843dc9d4d137dd2de4782ce6df491ad11a3a0416c0" +dependencies = [ + "bytes", + "half", + "num-bigint", + "num-traits", +] + +[[package]] +name = "arrow-cast" +version = "58.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c5aefb56a2c02e9e2b30746241058b85f8983f0fcff2ba0c6d09006e1cded7f" +dependencies = [ + "arrow-array", + "arrow-buffer", + "arrow-data", + "arrow-ord", + "arrow-schema", + "arrow-select", + "atoi", + "base64", + "chrono", + "comfy-table", + "half", + "lexical-core", + "num-traits", + "ryu", +] + +[[package]] +name = "arrow-csv" +version = "58.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94e8cf7e517657a52b91ea1263acf38c4ca62a84655d72458a3359b12ab97de" +dependencies = [ + "arrow-array", + "arrow-cast", + "arrow-schema", + "chrono", + "csv", + "csv-core", + "regex", +] + +[[package]] +name = "arrow-data" +version = "58.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c88210023a2bfee1896af366309a3028fc3bcbd6515fa29a7990ee1baa08ee0" +dependencies = [ + "arrow-buffer", + "arrow-schema", + "half", + "num-integer", + "num-traits", +] + +[[package]] +name = "arrow-ipc" +version = "58.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "238438f0834483703d88896db6fe5a7138b2230debc31b34c0336c2996e3c64f" +dependencies = [ + "arrow-array", + "arrow-buffer", + "arrow-data", + "arrow-schema", + "arrow-select", + "flatbuffers", + "lz4_flex", + "zstd", +] + +[[package]] +name = "arrow-json" +version = "58.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "205ca2119e6d679d5c133c6f30e68f027738d95ed948cf77677ea69c7800036b" +dependencies = [ + "arrow-array", + "arrow-buffer", + "arrow-cast", + "arrow-ord", + "arrow-schema", + "arrow-select", + "chrono", + "half", + "indexmap", + "itoa", + "lexical-core", + "memchr", + "num-traits", + "ryu", + "serde_core", + "serde_json", + "simdutf8", +] + +[[package]] +name = "arrow-ord" +version = "58.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bffd8fd2579286a5d63bac898159873e5094a79009940bcb42bbfce4f19f1d0" +dependencies = [ + "arrow-array", + "arrow-buffer", + "arrow-data", + "arrow-schema", + "arrow-select", +] + +[[package]] +name = "arrow-row" +version = "58.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab5994731204603c73ba69267616c50f80780774c6bb0476f1f830625115e0c" +dependencies = [ + "arrow-array", + "arrow-buffer", + "arrow-data", + "arrow-schema", + "half", +] + +[[package]] +name = "arrow-schema" +version = "58.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f633dbfdf39c039ada1bf9e34c694816eb71fbb7dc78f613993b7245e078a1ed" +dependencies = [ + "serde_core", + "serde_json", +] + +[[package]] +name = "arrow-select" +version = "58.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cd065c54172ac787cf3f2f8d4107e0d3fdc26edba76fdf4f4cc170258942222" +dependencies = [ + "ahash", + "arrow-array", + "arrow-buffer", + "arrow-data", + "arrow-schema", + "num-traits", +] + +[[package]] +name = "arrow-string" +version = "58.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29dd7cda3ab9692f43a2e4acc444d760cc17b12bb6d8232ddf64e9bab7c06b42" +dependencies = [ + "arrow-array", + "arrow-buffer", + "arrow-data", + "arrow-schema", + "arrow-select", + "memchr", + "num-traits", + "regex", + "regex-syntax", +] + +[[package]] +name = "async-compression" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e79b3f8a79cccc2898f31920fc69f304859b3bd567490f75ebf51ae1c792a9ac" +dependencies = [ + "compression-codecs", + "compression-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "async-trait" +version = "0.1.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae36dc4177970ef04fde5178d3e2429882def40e57a451f919c098f72baa6cec" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.0", +] + +[[package]] +name = "atoi" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits", +] + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bigdecimal" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d6867f1565b3aad85681f1015055b087fcfd840d6aeee6eee7f2da317603695" +dependencies = [ + "autocfg", + "libm", + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "bitflags" +version = "2.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + +[[package]] +name = "blake3" +version = "1.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aa83c34e62843d924f905e0f5c866eb1dd6545fc4d719e803d9ba6030371fce" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", + "cpufeatures 0.3.0", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "brotli" +version = "8.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc91aac060a7a1e25823bdccbfb6af1875b88f17c6daac97894eed8207166b3" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "5.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a32acac15fe1967bc3986b2a6347dffc965602354ea6f450ad07e8bfd253583" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" + +[[package]] +name = "bzip2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a53fac24f34a81bc9954b5d6cfce0c21e18ec6959f44f56e8e90e4bb7c346c" +dependencies = [ + "libbz2-rs-sys", +] + +[[package]] +name = "cc" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c89588d05638b5b4594a3348a2d6c20277e43a7f5c5202b05cc56888475a47b8" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "iana-time-zone", + "num-traits", + "windows-link", +] + +[[package]] +name = "chrono-tz" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6139a8597ed92cf816dfb33f5dd6cf0bb93a6adc938f11039f371bc5bcd26c3" +dependencies = [ + "chrono", + "phf", +] + +[[package]] +name = "cmake" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" +dependencies = [ + "cc", +] + +[[package]] +name = "comfy-table" +version = "7.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "958c5d6ecf1f214b4c2bbbbf6ab9523a864bd136dcf71a7e8904799acfe1ad47" +dependencies = [ + "unicode-segmentation", + "unicode-width", +] + +[[package]] +name = "compression-codecs" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce2548391e9c1929c21bf6aa2680af86fe4c1b33e6cea9ac1cfeec0bd11218cf" +dependencies = [ + "bzip2", + "compression-core", + "flate2", + "liblzma", + "memchr", + "zstd", + "zstd-safe", +] + +[[package]] +name = "compression-core" +version = "0.4.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc14f565cf027a105f7a44ccf9e5b424348421a1d8952a8fc9d499d313107789" + +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom 0.2.17", + "once_cell", + "tiny-keccak", +] + +[[package]] +name = "constant_time_eq" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "csv" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde_core", +] + +[[package]] +name = "csv-core" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782" +dependencies = [ + "memchr", +] + +[[package]] +name = "dashmap" +version = "6.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6361d5c062261c78a176addb82d4c821ae42bed6089de0e12603cd25de2059c" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "datafusion" +version = "53.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93db0e623840612f7f2cd757f7e8a8922064192363732c88692e0870016e141b" +dependencies = [ + "arrow", + "arrow-schema", + "async-trait", + "bytes", + "bzip2", + "chrono", + "datafusion-catalog", + "datafusion-catalog-listing", + "datafusion-common", + "datafusion-common-runtime", + "datafusion-datasource", + "datafusion-datasource-arrow", + "datafusion-datasource-csv", + "datafusion-datasource-json", + "datafusion-datasource-parquet", + "datafusion-execution", + "datafusion-expr", + "datafusion-expr-common", + "datafusion-functions", + "datafusion-functions-aggregate", + "datafusion-functions-nested", + "datafusion-functions-table", + "datafusion-functions-window", + "datafusion-optimizer", + "datafusion-physical-expr", + "datafusion-physical-expr-adapter", + "datafusion-physical-expr-common", + "datafusion-physical-optimizer", + "datafusion-physical-plan", + "datafusion-session", + "datafusion-sql", + "flate2", + "futures", + "itertools", + "liblzma", + "log", + "object_store", + "parking_lot", + "parquet", + "rand", + "regex", + "sqlparser", + "tempfile", + "tokio", + "url", + "uuid", + "zstd", +] + +[[package]] +name = "datafusion-catalog" +version = "53.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37cefde60b26a7f4ff61e9d2ff2833322f91df2b568d7238afe67bde5bdffb66" +dependencies = [ + "arrow", + "async-trait", + "dashmap", + "datafusion-common", + "datafusion-common-runtime", + "datafusion-datasource", + "datafusion-execution", + "datafusion-expr", + "datafusion-physical-expr", + "datafusion-physical-plan", + "datafusion-session", + "futures", + "itertools", + "log", + "object_store", + "parking_lot", + "tokio", +] + +[[package]] +name = "datafusion-catalog-listing" +version = "53.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17e112307715d6a7a331111a4c2330ff54bc237183511c319e3708a4cff431fb" +dependencies = [ + "arrow", + "async-trait", + "datafusion-catalog", + "datafusion-common", + "datafusion-datasource", + "datafusion-execution", + "datafusion-expr", + "datafusion-physical-expr", + "datafusion-physical-expr-adapter", + "datafusion-physical-expr-common", + "datafusion-physical-plan", + "futures", + "itertools", + "log", + "object_store", +] + +[[package]] +name = "datafusion-common" +version = "53.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d72a11ca44a95e1081870d3abb80c717496e8a7acb467a1d3e932bb636af5cc2" +dependencies = [ + "ahash", + "arrow", + "arrow-ipc", + "chrono", + "half", + "hashbrown 0.16.1", + "indexmap", + "itertools", + "libc", + "log", + "object_store", + "parquet", + "paste", + "recursive", + "sqlparser", + "tokio", + "web-time", +] + +[[package]] +name = "datafusion-common-runtime" +version = "53.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89f4afaed29670ec4fd6053643adc749fe3f4bc9d1ce1b8c5679b22c67d12def" +dependencies = [ + "futures", + "log", + "tokio", +] + +[[package]] +name = "datafusion-datasource" +version = "53.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9fb386e1691355355a96419978a0022b7947b44d4a24a6ea99f00b6b485cbb6" +dependencies = [ + "arrow", + "async-compression", + "async-trait", + "bytes", + "bzip2", + "chrono", + "datafusion-common", + "datafusion-common-runtime", + "datafusion-execution", + "datafusion-expr", + "datafusion-physical-expr", + "datafusion-physical-expr-adapter", + "datafusion-physical-expr-common", + "datafusion-physical-plan", + "datafusion-session", + "flate2", + "futures", + "glob", + "itertools", + "liblzma", + "log", + "object_store", + "rand", + "tokio", + "tokio-util", + "url", + "zstd", +] + +[[package]] +name = "datafusion-datasource-arrow" +version = "53.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffa6c52cfed0734c5f93754d1c0175f558175248bf686c944fb05c373e5fc096" +dependencies = [ + "arrow", + "arrow-ipc", + "async-trait", + "bytes", + "datafusion-common", + "datafusion-common-runtime", + "datafusion-datasource", + "datafusion-execution", + "datafusion-expr", + "datafusion-physical-expr-common", + "datafusion-physical-plan", + "datafusion-session", + "futures", + "itertools", + "object_store", + "tokio", +] + +[[package]] +name = "datafusion-datasource-csv" +version = "53.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "503f29e0582c1fc189578d665ff57d9300da1f80c282777d7eb67bb79fb8cdca" +dependencies = [ + "arrow", + "async-trait", + "bytes", + "datafusion-common", + "datafusion-common-runtime", + "datafusion-datasource", + "datafusion-execution", + "datafusion-expr", + "datafusion-physical-expr-common", + "datafusion-physical-plan", + "datafusion-session", + "futures", + "object_store", + "regex", + "tokio", +] + +[[package]] +name = "datafusion-datasource-json" +version = "53.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33804749abc8d0c8cb7473228483cb8070e524c6f6086ee1b85a64debe2b3d2" +dependencies = [ + "arrow", + "async-trait", + "bytes", + "datafusion-common", + "datafusion-common-runtime", + "datafusion-datasource", + "datafusion-execution", + "datafusion-expr", + "datafusion-physical-expr-common", + "datafusion-physical-plan", + "datafusion-session", + "futures", + "object_store", + "serde_json", + "tokio", + "tokio-stream", +] + +[[package]] +name = "datafusion-datasource-parquet" +version = "53.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a8e0365e0e08e8ff94d912f0ababcf9065a1a304018ba90b1fc83c855b4997" +dependencies = [ + "arrow", + "async-trait", + "bytes", + "datafusion-common", + "datafusion-common-runtime", + "datafusion-datasource", + "datafusion-execution", + "datafusion-expr", + "datafusion-functions-aggregate-common", + "datafusion-physical-expr", + "datafusion-physical-expr-adapter", + "datafusion-physical-expr-common", + "datafusion-physical-plan", + "datafusion-pruning", + "datafusion-session", + "futures", + "itertools", + "log", + "object_store", + "parking_lot", + "parquet", + "tokio", +] + +[[package]] +name = "datafusion-doc" +version = "53.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de6ac0df1662b9148ad3c987978b32cbec7c772f199b1d53520c8fa764a87ee" + +[[package]] +name = "datafusion-execution" +version = "53.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c03c7fbdaefcca4ef6ffe425a5fc2325763bfb426599bb0bf4536466efabe709" +dependencies = [ + "arrow", + "arrow-buffer", + "async-trait", + "chrono", + "dashmap", + "datafusion-common", + "datafusion-expr", + "datafusion-physical-expr-common", + "futures", + "log", + "object_store", + "parking_lot", + "rand", + "tempfile", + "url", +] + +[[package]] +name = "datafusion-expr" +version = "53.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "574b9b6977fedbd2a611cbff12e5caf90f31640ad9dc5870f152836d94bad0dd" +dependencies = [ + "arrow", + "async-trait", + "chrono", + "datafusion-common", + "datafusion-doc", + "datafusion-expr-common", + "datafusion-functions-aggregate-common", + "datafusion-functions-window-common", + "datafusion-physical-expr-common", + "indexmap", + "itertools", + "paste", + "recursive", + "serde_json", + "sqlparser", +] + +[[package]] +name = "datafusion-expr-common" +version = "53.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d7c3adf3db8bf61e92eb90cb659c8e8b734593a8f7c8e12a843c7ddba24b87e" +dependencies = [ + "arrow", + "datafusion-common", + "indexmap", + "itertools", + "paste", +] + +[[package]] +name = "datafusion-functions" +version = "53.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28aa4e10384e782774b10e72aca4d93ef7b31aa653095d9d4536b0a3dbc51b6" +dependencies = [ + "arrow", + "arrow-buffer", + "base64", + "blake2", + "blake3", + "chrono", + "chrono-tz", + "datafusion-common", + "datafusion-doc", + "datafusion-execution", + "datafusion-expr", + "datafusion-expr-common", + "datafusion-macros", + "hex", + "itertools", + "log", + "md-5", + "memchr", + "num-traits", + "rand", + "regex", + "sha2", + "unicode-segmentation", + "uuid", +] + +[[package]] +name = "datafusion-functions-aggregate" +version = "53.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00aa6217e56098ba84e0a338176fe52f0a84cca398021512c6c8c5eff806d0ad" +dependencies = [ + "ahash", + "arrow", + "datafusion-common", + "datafusion-doc", + "datafusion-execution", + "datafusion-expr", + "datafusion-functions-aggregate-common", + "datafusion-macros", + "datafusion-physical-expr", + "datafusion-physical-expr-common", + "half", + "log", + "num-traits", + "paste", +] + +[[package]] +name = "datafusion-functions-aggregate-common" +version = "53.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b511250349407db7c43832ab2de63f5557b19a20dfd236b39ca2c04468b50d47" +dependencies = [ + "ahash", + "arrow", + "datafusion-common", + "datafusion-expr-common", + "datafusion-physical-expr-common", +] + +[[package]] +name = "datafusion-functions-nested" +version = "53.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef13a858e20d50f0a9bb5e96e7ac82b4e7597f247515bccca4fdd2992df0212a" +dependencies = [ + "arrow", + "arrow-ord", + "datafusion-common", + "datafusion-doc", + "datafusion-execution", + "datafusion-expr", + "datafusion-expr-common", + "datafusion-functions", + "datafusion-functions-aggregate", + "datafusion-functions-aggregate-common", + "datafusion-macros", + "datafusion-physical-expr-common", + "hashbrown 0.16.1", + "itertools", + "itoa", + "log", + "paste", +] + +[[package]] +name = "datafusion-functions-table" +version = "53.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b40d3f5bbb3905f9ccb1ce9485a9595c77b69758a7c24d3ba79e334ff51e7e" +dependencies = [ + "arrow", + "async-trait", + "datafusion-catalog", + "datafusion-common", + "datafusion-expr", + "datafusion-physical-plan", + "parking_lot", + "paste", +] + +[[package]] +name = "datafusion-functions-window" +version = "53.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4e88ec9d57c9b685d02f58bfee7be62d72610430ddcedb82a08e5d9925dbfb6" +dependencies = [ + "arrow", + "datafusion-common", + "datafusion-doc", + "datafusion-expr", + "datafusion-functions-window-common", + "datafusion-macros", + "datafusion-physical-expr", + "datafusion-physical-expr-common", + "log", + "paste", +] + +[[package]] +name = "datafusion-functions-window-common" +version = "53.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8307bb93519b1a91913723a1130cfafeee3f72200d870d88e91a6fc5470ede5c" +dependencies = [ + "datafusion-common", + "datafusion-physical-expr-common", +] + +[[package]] +name = "datafusion-macros" +version = "53.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e367e6a71051d0ebdd29b2f85d12059b38b1d1f172c6906e80016da662226bd" +dependencies = [ + "datafusion-doc", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "datafusion-optimizer" +version = "53.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e929015451a67f77d9d8b727b2bf3a40c4445fdef6cdc53281d7d97c76888ace" +dependencies = [ + "arrow", + "chrono", + "datafusion-common", + "datafusion-expr", + "datafusion-expr-common", + "datafusion-physical-expr", + "indexmap", + "itertools", + "log", + "recursive", + "regex", + "regex-syntax", +] + +[[package]] +name = "datafusion-physical-expr" +version = "53.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b1e68aba7a4b350401cfdf25a3d6f989ad898a7410164afe9ca52080244cb59" +dependencies = [ + "ahash", + "arrow", + "datafusion-common", + "datafusion-expr", + "datafusion-expr-common", + "datafusion-functions-aggregate-common", + "datafusion-physical-expr-common", + "half", + "hashbrown 0.16.1", + "indexmap", + "itertools", + "parking_lot", + "paste", + "petgraph", + "recursive", + "tokio", +] + +[[package]] +name = "datafusion-physical-expr-adapter" +version = "53.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea22315f33cf2e0adc104e8ec42e285f6ed93998d565c65e82fec6a9ee9f9db4" +dependencies = [ + "arrow", + "datafusion-common", + "datafusion-expr", + "datafusion-functions", + "datafusion-physical-expr", + "datafusion-physical-expr-common", + "itertools", +] + +[[package]] +name = "datafusion-physical-expr-common" +version = "53.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b04b45ea8ad3ac2d78f2ea2a76053e06591c9629c7a603eda16c10649ecf4362" +dependencies = [ + "ahash", + "arrow", + "chrono", + "datafusion-common", + "datafusion-expr-common", + "hashbrown 0.16.1", + "indexmap", + "itertools", + "parking_lot", +] + +[[package]] +name = "datafusion-physical-optimizer" +version = "53.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cb13397809a425918f608dfe8653f332015a3e330004ab191b4404187238b95" +dependencies = [ + "arrow", + "datafusion-common", + "datafusion-execution", + "datafusion-expr", + "datafusion-expr-common", + "datafusion-physical-expr", + "datafusion-physical-expr-common", + "datafusion-physical-plan", + "datafusion-pruning", + "itertools", + "recursive", +] + +[[package]] +name = "datafusion-physical-plan" +version = "53.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5edc023675791af9d5fb4cc4c24abf5f7bd3bd4dcf9e5bd90ea1eff6976dcc79" +dependencies = [ + "ahash", + "arrow", + "arrow-ord", + "arrow-schema", + "async-trait", + "datafusion-common", + "datafusion-common-runtime", + "datafusion-execution", + "datafusion-expr", + "datafusion-functions", + "datafusion-functions-aggregate-common", + "datafusion-functions-window-common", + "datafusion-physical-expr", + "datafusion-physical-expr-common", + "futures", + "half", + "hashbrown 0.16.1", + "indexmap", + "itertools", + "log", + "num-traits", + "parking_lot", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "datafusion-pruning" +version = "53.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac8c76860e355616555081cab5968cec1af7a80701ff374510860bcd567e365a" +dependencies = [ + "arrow", + "datafusion-common", + "datafusion-datasource", + "datafusion-expr-common", + "datafusion-physical-expr", + "datafusion-physical-expr-common", + "datafusion-physical-plan", + "itertools", + "log", +] + +[[package]] +name = "datafusion-session" +version = "53.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5412111aa48e2424ba926112e192f7a6b7e4ccb450145d25ce5ede9f19dc491e" +dependencies = [ + "async-trait", + "datafusion-common", + "datafusion-execution", + "datafusion-expr", + "datafusion-physical-plan", + "parking_lot", +] + +[[package]] +name = "datafusion-sql" +version = "53.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa0d133ddf8b9b3b872acac900157f783e7b879fe9a6bccf389abebbfac45ec1" +dependencies = [ + "arrow", + "bigdecimal", + "chrono", + "datafusion-common", + "datafusion-expr", + "datafusion-functions-nested", + "indexmap", + "log", + "recursive", + "regex", + "sqlparser", +] + +[[package]] +name = "datafusion-substrait" +version = "53.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98494539a5468979cc42d86c7bc5f0f8cb71ee5c742694c26fc34efdd29dd2e5" +dependencies = [ + "async-recursion", + "async-trait", + "chrono", + "datafusion", + "half", + "itertools", + "object_store", + "pbjson-types", + "prost", + "substrait 0.62.2", + "tokio", + "url", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "displaydoc" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + +[[package]] +name = "flatbuffers" +version = "25.12.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35f6839d7b3b98adde531effaf34f0c2badc6f4735d26fe74709d8e513a96ef3" +dependencies = [ + "bitflags", + "rustc_version", +] + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", + "zlib-rs", +] + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a88cf1f829d945f548cf8fec32c61b1f202b6d93b45848602fc02af4b12ad218" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "262590f4fe6afeb0bc83be1daa64e52657fe185690a958af7f3ad0e92085c5ae" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7" + +[[package]] +name = "futures-executor" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6754879cc9f2c66f88c6e5c35344bb0bdb0708b0352b1201815667c7eabc7458" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4577ecaa3c4f96589d473f679a71b596316f6641bc350038b962a5daf0085d7a" + +[[package]] +name = "futures-macro" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d6d3cde68c518367be28956066ddfef33813991b77a55005a69dae04bf3b10b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "futures-sink" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e34418ac499d6305c2fb5ad0ed2f6ac998c5f8ca209b4510f7f94242c647e307" + +[[package]] +name = "futures-task" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109" + +[[package]] +name = "futures-util" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi 5.3.0", + "wasip2", +] + +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "num-traits", + "zerocopy", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "http" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "humantime" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15cdd26707701c53297e2fa6afb323d55fbc1d0810c3aec078ae3ef0424c3c15" + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" + +[[package]] +name = "icu_properties" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" + +[[package]] +name = "icu_provider" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "incan_core" +version = "0.3.0" +dependencies = [ + "serde", +] + +[[package]] +name = "incan_derive" +version = "0.3.0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "incan_stdlib" +version = "0.3.0" +dependencies = [ + "incan_core", + "incan_derive", + "serde", + "serde_json", + "tokio", + "xxhash-rust", +] + +[[package]] +name = "incql" +version = "0.3.0" +dependencies = [ + "blake2", + "blake3", + "byteorder", + "crc32fast", + "datafusion", + "datafusion-common", + "datafusion-expr", + "datafusion-substrait", + "encoding_rs", + "incan_derive", + "incan_stdlib", + "md-5", + "prost", + "prost-types", + "regex", + "rustix", + "serde", + "sha1", + "sha2", + "sha3", + "substrait 0.63.0", + "url", + "xxhash-rust", +] + +[[package]] +name = "incql_tutorial_book" +version = "0.3.0" +dependencies = [ + "incan_derive", + "incan_stdlib", + "incql", +] + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "integer-encoding" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02" + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jobserver" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" +dependencies = [ + "getrandom 0.4.3", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "keccak" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" +dependencies = [ + "cpufeatures 0.2.17", +] + +[[package]] +name = "lexical-core" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d8d125a277f807e55a77304455eb7b1cb52f2b18c143b60e766c120bd64a594" +dependencies = [ + "lexical-parse-float", + "lexical-parse-integer", + "lexical-util", + "lexical-write-float", + "lexical-write-integer", +] + +[[package]] +name = "lexical-parse-float" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52a9f232fbd6f550bc0137dcb5f99ab674071ac2d690ac69704593cb4abbea56" +dependencies = [ + "lexical-parse-integer", + "lexical-util", +] + +[[package]] +name = "lexical-parse-integer" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a7a039f8fb9c19c996cd7b2fcce303c1b2874fe1aca544edc85c4a5f8489b34" +dependencies = [ + "lexical-util", +] + +[[package]] +name = "lexical-util" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2604dd126bb14f13fb5d1bd6a66155079cb9fa655b37f875b3a742c705dbed17" + +[[package]] +name = "lexical-write-float" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50c438c87c013188d415fbabbb1dceb44249ab81664efbd31b14ae55dabb6361" +dependencies = [ + "lexical-util", + "lexical-write-integer", +] + +[[package]] +name = "lexical-write-integer" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "409851a618475d2d5796377cad353802345cba92c867d9fbcde9cf4eac4e14df" +dependencies = [ + "lexical-util", +] + +[[package]] +name = "libbz2-rs-sys" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34b357333733e8260735ba5894eb928c02ecc69c78715f01a8019e7fa7f2db4c" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "liblzma" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45aec2360b3933207e27908049d8e4df4e476b58180afb1e56b2a4fb72efe4ba" +dependencies = [ + "liblzma-sys", +] + +[[package]] +name = "liblzma-sys" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a046c7f353ba30f810545151e04f63545833803f5b86ee3ddf1517247fe560a5" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litemap" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" + +[[package]] +name = "lz4_flex" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef0d4ed8669f8f8826eb00dc878084aa8f253506c4fd5e8f58f5bce72ddb97e" +dependencies = [ + "twox-hash", +] + +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d65c71f1ce40ab09135ce117d742b9f8a19ff91a41a8b57ed50bc2de59c427" +dependencies = [ + "libc", + "wasi", + "windows-sys", +] + +[[package]] +name = "multimap" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" + +[[package]] +name = "num-bigint" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +dependencies = [ + "memchr", +] + +[[package]] +name = "object_store" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622acbc9100d3c10e2ee15804b0caa40e55c933d5aa53814cd520805b7958a49" +dependencies = [ + "async-trait", + "bytes", + "chrono", + "futures-channel", + "futures-core", + "futures-util", + "http", + "humantime", + "itertools", + "parking_lot", + "percent-encoding", + "thiserror", + "tokio", + "tracing", + "url", + "walkdir", + "wasm-bindgen-futures", + "web-time", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "parquet" +version = "58.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dafa7d01085b62a47dd0c1829550a0a36710ea9c4fe358a05a85477cec8a908" +dependencies = [ + "ahash", + "arrow-array", + "arrow-buffer", + "arrow-data", + "arrow-ipc", + "arrow-schema", + "arrow-select", + "base64", + "brotli", + "bytes", + "chrono", + "flate2", + "futures", + "half", + "hashbrown 0.17.1", + "lz4_flex", + "num-bigint", + "num-integer", + "num-traits", + "object_store", + "paste", + "seq-macro", + "simdutf8", + "snap", + "thrift", + "tokio", + "twox-hash", + "zstd", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pbjson" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "898bac3fa00d0ba57a4e8289837e965baa2dee8c3749f3b11d45a64b4223d9c3" +dependencies = [ + "base64", + "serde", +] + +[[package]] +name = "pbjson-build" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af22d08a625a2213a78dbb0ffa253318c5c79ce3133d32d296655a7bdfb02095" +dependencies = [ + "heck", + "itertools", + "prost", + "prost-types", +] + +[[package]] +name = "pbjson-types" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e748e28374f10a330ee3bb9f29b828c0ac79831a32bab65015ad9b661ead526" +dependencies = [ + "bytes", + "chrono", + "pbjson", + "pbjson-build", + "prost", + "prost-build", + "serde", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "petgraph" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" +dependencies = [ + "fixedbitset", + "hashbrown 0.15.5", + "indexmap", + "serde", +] + +[[package]] +name = "phf" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "913273894cec178f401a31ec4b656318d95473527be05c0752cc41cdc32be8b7" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_shared" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06005508882fb681fd97892ecff4b7fd0fee13ef1aa569f8695dae7ab9099981" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "potential_utf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +dependencies = [ + "zerovec", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.119", +] + +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prost" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "528ac67416ff8646872a3c02cad9cc4ee5dc9f9540c9b10771855c95cb2e5ae1" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03da047801ff44bb6a4d407d4860c05fd70bb81714e6b2f3812603d5b145b042" +dependencies = [ + "heck", + "itertools", + "log", + "multimap", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "regex", + "syn 2.0.119", + "tempfile", +] + +[[package]] +name = "prost-derive" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b570b25f7617e43d59005d0990ccb79e950a423952cea19671b7a876da390adf" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "prost-types" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f94967dc7688f3054c7fac87473ffae4cc4c3904800e2d9f5b857246d8963b0a" +dependencies = [ + "prost", +] + +[[package]] +name = "protobuf-src" +version = "2.1.1+27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6217c3504da19b85a3a4b2e9a5183d635822d83507ba0986624b5c05b83bfc40" +dependencies = [ + "cmake", +] + +[[package]] +name = "psm" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645dbe486e346d9b5de3ef16ede18c26e6c70ad97418f4874b8b1889d6e761ea" +dependencies = [ + "ar_archive_writer", + "cc", +] + +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "recursive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0786a43debb760f491b1bc0269fe5e84155353c67482b9e60d0cfb596054b43e" +dependencies = [ + "recursive-proc-macro-impl", + "stacker", +] + +[[package]] +name = "recursive-proc-macro-impl" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76009fbe0614077fc1a2ce255e3a1881a2e3a3527097d5dc6d8212c585e7e38b" +dependencies = [ + "quote", + "syn 2.0.119", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" + +[[package]] +name = "regress" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2057b2325e68a893284d1538021ab90279adac1139957ca2a74426c6f118fb48" +dependencies = [ + "hashbrown 0.16.1", + "memchr", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schemars" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" +dependencies = [ + "dyn-clone", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 2.0.119", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "seq-macro" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc" + +[[package]] +name = "serde" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.0", +] + +[[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "indexmap", + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_tokenstream" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c49585c52c01f13c5c2ebb333f14f6885d76daa768d8a037d28017ec538c69" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "syn 2.0.119", +] + +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] +name = "sha1" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest", +] + +[[package]] +name = "sha3" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77fd7028345d415a4034cf8777cd4f8ab1851274233b45f84e3d955502d93874" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "simd-adler32" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + +[[package]] +name = "siphasher" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" + +[[package]] +name = "snap" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "199905e6153d6405f9728fe44daace35f8f837bbf830bb6e85fbd5828709a886" + +[[package]] +name = "socket2" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "sqlparser" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf5ea8d4d7c808e1af1cbabebca9a2abe603bcefc22294c5b95018d53200cb7" +dependencies = [ + "log", + "recursive", + "sqlparser_derive", +] + +[[package]] +name = "sqlparser_derive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6dd45d8fc1c79299bfbb7190e42ccbbdf6a5f52e4a6ad98d92357ea965bd289" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "stacker" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "640c8cdd92b6b12f5bcb1803ca3bbf5ab96e5e6b6b96b9ab77dabe9e880b3190" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "windows-sys", +] + +[[package]] +name = "substrait" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62fc4b483a129b9772ccb9c3f7945a472112fdd9140da87f8a4e7f1d44e045d0" +dependencies = [ + "heck", + "pbjson", + "pbjson-build", + "pbjson-types", + "prettyplease", + "prost", + "prost-build", + "prost-types", + "protobuf-src", + "regress", + "schemars", + "semver", + "serde", + "serde_json", + "serde_yaml", + "syn 2.0.119", + "typify", + "walkdir", +] + +[[package]] +name = "substrait" +version = "0.63.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e620ff4d5c02fd6f7752931aa74b16a26af66a63022cc1ad412c77edbe0bab47" +dependencies = [ + "heck", + "indexmap", + "prettyplease", + "prost", + "prost-build", + "prost-types", + "protobuf-src", + "regress", + "schemars", + "semver", + "serde", + "serde_json", + "serde_yaml", + "syn 2.0.119", + "typify", + "walkdir", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fac314a64dc9a36e61a9eb4261a5e9bbfbc922b27e518af97bc32b926cf967" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.3", + "once_cell", + "rustix", + "windows-sys", +] + +[[package]] +name = "thiserror" +version = "2.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.0", +] + +[[package]] +name = "thrift" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e54bc85fc7faa8bc175c4bab5b92ba8d9a3ce893d0e9f42cc455c8ab16a9e09" +dependencies = [ + "byteorder", + "integer-encoding", + "ordered-float", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinystr" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tokio" +version = "1.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d988bcd52dbe076d3d46903332f58c912b87a2c49b1428419a5845154762ffee" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6328af13490e73a9b4694030fafd93f8c8c6a9dede33e821c3fc63eddf8042ba" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "tokio-stream" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "twox-hash" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8464ec13c3691491391d9fce00f6416c9a48e46972f72d7865688be2080192c9" + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "typify" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5bcc6f62eb1fa8aa4098f39b29f93dcb914e17158b76c50360911257aa629" +dependencies = [ + "typify-impl", + "typify-macro", +] + +[[package]] +name = "typify-impl" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1eb359f7ffa4f9ebe947fa11a1b2da054564502968db5f317b7e37693cb2240" +dependencies = [ + "heck", + "log", + "proc-macro2", + "quote", + "regress", + "schemars", + "semver", + "serde", + "serde_json", + "syn 2.0.119", + "thiserror", + "unicode-ident", +] + +[[package]] +name = "typify-macro" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "911c32f3c8514b048c1b228361bebb5e6d73aeec01696e8cc0e82e2ffef8ab7a" +dependencies = [ + "proc-macro2", + "quote", + "schemars", + "semver", + "serde", + "serde_json", + "serde_tokenstream", + "syn 2.0.119", + "typify-impl", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "uuid" +version = "1.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239" +dependencies = [ + "getrandom 0.4.3", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8" +dependencies = [ + "cfg-if", + "futures-util", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.119", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" + +[[package]] +name = "writeable" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" + +[[package]] +name = "xxhash-rust" +version = "0.8.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985eec839aaf2a1270af8f4ebcf63cf9401cfd90f0902f97c28d9f104ffbde72" + +[[package]] +name = "yoke" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7cbbc0a705a0fd05cc3676525980d2bf5a9bc4adac6d6475209a7887cf59d19" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "zerofrom" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", + "synstructure", +] + +[[package]] +name = "zerotrie" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "zlib-rs" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b142a20ec14a91d5bc708c1dc21b080c550113d8aa77afa29635673a65dd02c5" + +[[package]] +name = "zmij" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" + +[[package]] +name = "zstd" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.16+zstd.1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" +dependencies = [ + "cc", + "pkg-config", +] +""" diff --git a/examples/tutorial_book/incan.toml b/examples/tutorial_book/incan.toml new file mode 100644 index 00000000..b645854e --- /dev/null +++ b/examples/tutorial_book/incan.toml @@ -0,0 +1,9 @@ +[project] +name = "incql_tutorial_book" +version = "0.1.0" + +[dependencies] +incql = { path = "../.." } + +[project.scripts] +main = "src/main.incn" diff --git a/examples/tutorial_book/orders.csv b/examples/tutorial_book/orders.csv new file mode 100644 index 00000000..0fb6c2a5 --- /dev/null +++ b/examples/tutorial_book/orders.csv @@ -0,0 +1,5 @@ +order_id,customer_id,status,amount +1001,C-001,paid,120.50 +1002,C-002,pending,64.00 +1003,C-001,paid,89.25 +1004,C-003,cancelled,42.75 diff --git a/examples/tutorial_book/src/chapter_01.incn b/examples/tutorial_book/src/chapter_01.incn new file mode 100644 index 00000000..39c20782 --- /dev/null +++ b/examples/tutorial_book/src/chapter_01.incn @@ -0,0 +1,17 @@ +"""Chapter 1: bind a typed row contract to a CSV-backed lazy carrier.""" + +from domain import Order +from pub::incql import LazyFrame, Session, SessionError, report_session_error + + +def read_orders(mut session: Session) -> Result[LazyFrame[Order], SessionError]: + """Register the CSV source and return its typed lazy carrier.""" + orders: LazyFrame[Order] = session.read_csv("tutorial_orders", "orders.csv")? + return Ok(orders) + + +def main() -> None: + mut session = Session.default() + match read_orders(session): + Ok(_) => println("Chapter 1: created a LazyFrame[Order] without executing it") + Err(error) => report_session_error("tutorial.chapter_01", error) diff --git a/examples/tutorial_book/src/chapter_02.incn b/examples/tutorial_book/src/chapter_02.incn new file mode 100644 index 00000000..9858e05e --- /dev/null +++ b/examples/tutorial_book/src/chapter_02.incn @@ -0,0 +1,17 @@ +"""Chapter 2: add a relational step while keeping execution deferred.""" + +from domain import Order +from pub::incql import LazyFrame, Session, SessionError, report_session_error + + +def plan_orders(mut session: Session) -> Result[LazyFrame[Order], SessionError]: + """Return a bounded three-row plan over the typed CSV source.""" + orders: LazyFrame[Order] = session.read_csv("tutorial_orders", "orders.csv")? + return Ok(orders.limit(3)) + + +def main() -> None: + mut session = Session.default() + match plan_orders(session): + Ok(_) => println("Chapter 2: planned ReadNamedTable -> Limit; execution is still deferred") + Err(error) => report_session_error("tutorial.chapter_02", error) diff --git a/examples/tutorial_book/src/chapter_03.incn b/examples/tutorial_book/src/chapter_03.incn new file mode 100644 index 00000000..a24fb49b --- /dev/null +++ b/examples/tutorial_book/src/chapter_03.incn @@ -0,0 +1,24 @@ +"""Chapter 3: inspect the Prism plan and lineage before execution.""" + +from domain import Order +from pub::incql import LazyFrame, Session, SessionError, inspect_lineage, inspect_plan, report_session_error + + +def inspect_orders(mut session: Session) -> Result[None, SessionError]: + """Build and inspect the tutorial plan without collecting rows.""" + orders: LazyFrame[Order] = session.read_csv("tutorial_orders", "orders.csv")? + plan = orders.limit(3) + inspection = inspect_plan(plan.clone()) + lineage = inspect_lineage(plan) + + println(f"plan id: {inspection.plan_id}") + println(f"authored nodes: {inspection.authored_node_count}") + println(f"lineage edges: {len(lineage.edges)}") + return Ok(None) + + +def main() -> None: + mut session = Session.default() + match inspect_orders(session): + Ok(_) => println("Chapter 3: inspection completed without executing the plan") + Err(error) => report_session_error("tutorial.chapter_03", error) diff --git a/examples/tutorial_book/src/chapter_04.incn b/examples/tutorial_book/src/chapter_04.incn new file mode 100644 index 00000000..d974a226 --- /dev/null +++ b/examples/tutorial_book/src/chapter_04.incn @@ -0,0 +1,34 @@ +"""Chapter 4: collect through DataFusion and preserve execution evidence.""" + +from domain import Order +from pub::incql import LazyFrame, Session, SessionError, inspect_lineage, inspect_plan, report_session_error + + +def collect_orders(mut session: Session) -> Result[None, SessionError]: + """Inspect, execute, and print the structured materialization surface.""" + orders: LazyFrame[Order] = session.read_csv("tutorial_orders", "orders.csv")? + plan = orders.limit(3) + inspection = inspect_plan(plan.clone()) + lineage = inspect_lineage(plan.clone()) + observed = session.collect_observed(plan) + + println(f"plan id: {inspection.plan_id}") + println(f"lineage edges: {len(lineage.edges)}") + println(f"backend: {observed.observation.backend_name}") + match observed.data: + Some(frame) => + println(f"columns: {frame.resolved_columns():?}") + println(f"rows: {frame.row_count()}") + println(frame.preview_text()) + None => + match observed.error: + Some(error) => return Err(error) + None => println("collect_observed returned no data and no typed error") + return Ok(None) + + +def main() -> None: + mut session = Session.default() + match collect_orders(session): + Ok(_) => println("Chapter 4: DataFusion collection completed") + Err(error) => report_session_error("tutorial.chapter_04", error) diff --git a/examples/tutorial_book/src/chapter_05.incn b/examples/tutorial_book/src/chapter_05.incn new file mode 100644 index 00000000..bcfa368e --- /dev/null +++ b/examples/tutorial_book/src/chapter_05.incn @@ -0,0 +1,39 @@ +"""Chapter 5: compare the inspected plan with adapter coverage evidence.""" + +from domain import Order +from pub::incql import LazyFrame, Session, SessionError, inspect_lineage, inspect_plan, report_session_error + + +def check_orders(mut session: Session) -> Result[None, SessionError]: + """Collect the plan, then inspect DataFusion coverage for its requirements.""" + orders: LazyFrame[Order] = session.read_csv("tutorial_orders", "orders.csv")? + plan = orders.limit(3) + inspection = inspect_plan(plan.clone()) + lineage = inspect_lineage(plan.clone()) + observed = session.collect_observed(plan.clone()) + + println(f"plan id: {inspection.plan_id}") + println(f"lineage edges: {len(lineage.edges)}") + println(f"backend: {observed.observation.backend_name}") + match observed.data: + Some(frame) => + println(f"columns: {frame.resolved_columns():?}") + println(f"rows: {frame.row_count()}") + println(frame.preview_text()) + None => + match observed.error: + Some(error) => return Err(error) + None => println("collect_observed returned no data and no typed error") + + coverage = session.check_plan_coverage(plan) + println(f"coverage records: {len(coverage)}") + for record in coverage: + println(f"- {record.requirement.capability.value()}: {record.state.value()}") + return Ok(None) + + +def main() -> None: + mut session = Session.default() + match check_orders(session): + Ok(_) => println("Chapter 5: adapter coverage remained explicit") + Err(error) => report_session_error("tutorial.chapter_05", error) diff --git a/examples/tutorial_book/src/chapter_06.incn b/examples/tutorial_book/src/chapter_06.incn new file mode 100644 index 00000000..3c9b6d23 --- /dev/null +++ b/examples/tutorial_book/src/chapter_06.incn @@ -0,0 +1,62 @@ +"""Chapter 6: observe quality and leave the handling decision with the caller.""" + +from domain import Order +from pub::incql import ( + LazyFrame, + QualityObservation, + QualityObservationStatus, + Session, + SessionError, + inspect_lineage, + inspect_plan, + report_session_error, + row_count, +) + + +def caller_accepts(observations: list[QualityObservation]) -> bool: + """Apply this tutorial caller's policy without changing IncQL evidence.""" + for observation in observations: + if observation.status != QualityObservationStatus.Passed: + return false + return true + + +def evaluate_orders(mut session: Session) -> Result[None, SessionError]: + """Collect, check coverage, and demonstrate passing and failing quality evidence.""" + orders: LazyFrame[Order] = session.read_csv("tutorial_orders", "orders.csv")? + plan = orders.limit(3) + inspection = inspect_plan(plan.clone()) + lineage = inspect_lineage(plan.clone()) + observed = session.collect_observed(plan.clone()) + + println(f"plan id: {inspection.plan_id}") + println(f"lineage edges: {len(lineage.edges)}") + println(f"backend: {observed.observation.backend_name}") + match observed.data: + Some(frame) => + println(f"columns: {frame.resolved_columns():?}") + println(f"rows: {frame.row_count()}") + println(frame.preview_text()) + None => + match observed.error: + Some(error) => return Err(error) + None => println("collect_observed returned no data and no typed error") + + coverage = session.check_plan_coverage(plan.clone()) + println(f"coverage records: {len(coverage)}") + + passing = session.observe_quality(plan.clone(), [row_count(min_count=Some(1), max_count=Some(3))]) + failing = session.observe_quality(plan, [row_count(min_count=Some(4))]) + println(f"passing observation: {passing[0].status.value()}") + println(f"deliberate failing observation: {failing[0].status.value()}") + println(f"caller accepts passing policy: {caller_accepts(passing)}") + println(f"caller accepts failing policy: {caller_accepts(failing)}") + return Ok(None) + + +def main() -> None: + mut session = Session.default() + match evaluate_orders(session): + Ok(_) => println("Chapter 6: quality produced evidence; the caller made the decision") + Err(error) => report_session_error("tutorial.chapter_06", error) diff --git a/examples/tutorial_book/src/chapter_07.incn b/examples/tutorial_book/src/chapter_07.incn new file mode 100644 index 00000000..d9ee9c1e --- /dev/null +++ b/examples/tutorial_book/src/chapter_07.incn @@ -0,0 +1,76 @@ +"""Chapter 7: write an accepted plan and preserve write evidence.""" + +from domain import Order +from pub::incql import ( + LazyFrame, + QualityObservation, + QualityObservationStatus, + Session, + SessionError, + csv_sink, + inspect_lineage, + inspect_plan, + report_session_error, + row_count, +) + + +def caller_accepts(observations: list[QualityObservation]) -> bool: + """Return true only when every observation satisfies the caller's policy.""" + for observation in observations: + if observation.status != QualityObservationStatus.Passed: + return false + return true + + +pub def run_chapter(mut session: Session) -> Result[None, SessionError]: + """Run the complete typed plan, evidence, decision, and write flow.""" + orders: LazyFrame[Order] = session.read_csv("tutorial_orders", "orders.csv")? + plan = orders.limit(3) + inspection = inspect_plan(plan.clone()) + lineage = inspect_lineage(plan.clone()) + observed = session.collect_observed(plan.clone()) + + println(f"plan id: {inspection.plan_id}") + println(f"lineage edges: {len(lineage.edges)}") + println(f"backend: {observed.observation.backend_name}") + match observed.data: + Some(frame) => + println(f"columns: {frame.resolved_columns():?}") + println(f"rows: {frame.row_count()}") + println(frame.preview_text()) + None => + match observed.error: + Some(error) => return Err(error) + None => println("collect_observed returned no data and no typed error") + + coverage = session.check_plan_coverage(plan.clone()) + println(f"coverage records: {len(coverage)}") + for record in coverage: + println(f"- {record.requirement.capability.value()}: {record.state.value()}") + + required_quality = session.observe_quality(plan.clone(), [row_count(min_count=Some(1), max_count=Some(3))]) + deliberate_probe = session.observe_quality(plan.clone(), [row_count(min_count=Some(4))]) + println(f"required quality: {required_quality[0].status.value()}") + println(f"deliberate strict probe: {deliberate_probe[0].status.value()}") + println(f"caller would accept strict probe: {caller_accepts(deliberate_probe)}") + + if not caller_accepts(required_quality): + println("Caller decision: do not write") + return Ok(None) + + output_uri = "target/tutorial-orders.csv" + written = session.write_observed(plan, csv_sink(output_uri)) + match written.error: + Some(error) => return Err(error) + None => + println("Caller decision: write the plan accepted by the required policy") + println(f"wrote: {output_uri}") + return Ok(None) + + +def main() -> None: + mut session = Session.default() + match run_chapter(session): + Ok(_) => println("Chapter 7: observed write completed") + Err(error) => report_session_error("tutorial.chapter_07", error) diff --git a/examples/tutorial_book/src/chapter_08.incn b/examples/tutorial_book/src/chapter_08.incn new file mode 100644 index 00000000..bae5c0bf --- /dev/null +++ b/examples/tutorial_book/src/chapter_08.incn @@ -0,0 +1,34 @@ +"""Chapter 8: filter and shape typed data with IncQL query clauses.""" + +import pub::incql + +from domain import Order, PaidOrderReview +from pub::incql import LazyFrame, Session, SessionError, desc, report_session_error + + +def query_paid_orders(mut session: Session) -> Result[None, SessionError]: + """Run a SQL-familiar query block without using relational method chains.""" + orders: LazyFrame[Order] = session.read_csv("tutorial_orders_query", "orders.csv")? + paid_orders: LazyFrame[PaidOrderReview] = query { + FROM orders + WHERE .status == "paid" + SELECT + .order_id as order_id, + .customer_id as customer_id, + .amount as amount, + ORDER BY desc(.amount) + LIMIT 10 + } + + result = session.collect(paid_orders)? + println(f"columns: {result.resolved_columns():?}") + println(f"rows: {result.row_count()}") + println(result.preview_text()) + return Ok(None) + + +def main() -> None: + mut session = Session.default() + match query_paid_orders(session): + Ok(_) => println("Chapter 8: query block completed") + Err(error) => report_session_error("tutorial.chapter_08", error) diff --git a/examples/tutorial_book/src/chapter_09.incn b/examples/tutorial_book/src/chapter_09.incn new file mode 100644 index 00000000..d5f842a1 --- /dev/null +++ b/examples/tutorial_book/src/chapter_09.incn @@ -0,0 +1,33 @@ +"""Chapter 9: group and summarize typed data with IncQL query clauses.""" + +import pub::incql + +from domain import Order, StatusSummary +from pub::incql import LazyFrame, Session, SessionError, count, desc, report_session_error, sum + + +def summarize_statuses(mut session: Session) -> Result[None, SessionError]: + """Group order rows and collect a typed summary without method chains.""" + orders: LazyFrame[Order] = session.read_csv("tutorial_orders_summary", "orders.csv")? + summaries: LazyFrame[StatusSummary] = query { + FROM orders + GROUP BY .status + SELECT + .status as status, + count() as order_count, + sum(.amount) as total_amount, + ORDER BY desc(.total_amount) + } + + result = session.collect(summaries)? + println(f"columns: {result.resolved_columns():?}") + println(f"rows: {result.row_count()}") + println(result.preview_text()) + return Ok(None) + + +def main() -> None: + mut session = Session.default() + match summarize_statuses(session): + Ok(_) => println("Chapter 9: grouped query block completed") + Err(error) => report_session_error("tutorial.chapter_09", error) diff --git a/examples/tutorial_book/src/domain.incn b/examples/tutorial_book/src/domain.incn new file mode 100644 index 00000000..48485761 --- /dev/null +++ b/examples/tutorial_book/src/domain.incn @@ -0,0 +1,29 @@ +"""Typed row contracts shared by every tutorial-book checkpoint.""" + + +@derive(Clone) +pub model Order: + """The intended row shape of the tutorial CSV source.""" + + pub order_id: int + pub customer_id: str + pub status: str + pub amount: float + + +@derive(Clone) +pub model PaidOrderReview: + """Projected row shape for the query-block beginner route.""" + + pub order_id: int + pub customer_id: str + pub amount: float + + +@derive(Clone) +pub model StatusSummary: + """Grouped result shape for the query-block beginner route.""" + + pub status: str + pub order_count: int + pub total_amount: float diff --git a/examples/tutorial_book/src/main.incn b/examples/tutorial_book/src/main.incn new file mode 100644 index 00000000..b19c44e7 --- /dev/null +++ b/examples/tutorial_book/src/main.incn @@ -0,0 +1,11 @@ +"""Default entry point for the complete IncQL tutorial-book example.""" + +from chapter_07 import run_chapter +from pub::incql import Session, report_session_error + + +def main() -> None: + mut session = Session.default() + match run_chapter(session): + Ok(_) => println("IncQL tutorial book complete") + Err(error) => report_session_error("tutorial.main", error) diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 00000000..d929ca44 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,141 @@ +site_name: IncQL +site_description: Typed relational logic for governed data systems +site_url: https://encero-systems.github.io/IncQL/ +repo_url: https://github.com/encero-systems/IncQL +repo_name: encero-systems/IncQL +docs_dir: docs +site_dir: site +strict: true + +theme: + name: material + logo: shared/brand/incql-mark.png + font: false + highlightjs: false + pygments_style: default + palette: + - scheme: default + primary: black + accent: cyan + features: + - navigation.instant + - navigation.tabs + - navigation.tabs.sticky + - navigation.sections + - navigation.indexes + - content.code.annotate + - content.code.copy + +plugins: + - search + +hooks: + - mkdocs_hooks.py + +markdown_extensions: + - admonition + - attr_list + - def_list + - md_in_html + - pymdownx.details + - pymdownx.highlight: + use_pygments: true + pygments_lang_class: true + extend_pygments_lang: + - name: incan + lang: incan + - pymdownx.snippets: + base_path: + - . + check_paths: true + - pymdownx.superfences + - pymdownx.tasklist: + custom_checkbox: true + +extra_css: + - stylesheets/prismplane.css?v=20260719-query-block-part-34 + +extra_javascript: + - javascripts/prismplane.js?v=20260719-book-parts-24 + +nav: + - Home: index.md + - Learn: + - Overview: language/README.md + - The IncQL Book: + - Book overview: language/tutorials/book/index.md + - Part I · Model the work: + - 1. Read a typed relation: language/tutorials/book/01_typed_relation.md + - 2. Build deferred work: language/tutorials/book/02_deferred_plan.md + - Part II · Explain and execute it: + - 3. Inspect the plan: language/tutorials/book/03_inspect_plan.md + - 4. Collect an observed result: language/tutorials/book/04_collect_observed.md + - 5. Check adapter coverage: language/tutorials/book/05_adapter_coverage.md + - Part III · Decide what happens next: + - 6. Observe data quality: language/tutorials/book/06_quality_observations.md + - 7. Make the write decision: language/tutorials/book/07_governed_write.md + - Part IV · Query blocks: + - 8. Write your first query: language/tutorials/book/08_first_query_block.md + - 9. Summarize and order results: language/tutorials/book/09_summarize_query.md + - Concepts: + - Dataset carriers: language/explanation/dataset_carriers.md + - Execution context: language/explanation/execution_context.md + - Guides: + - Overview: language/how-to/README.md + - Dataset transformations: language/how-to/dataset_transformations.md + - Normalize semi-structured fields: language/how-to/normalize_semistructured_fields.md + - Nested row values: language/how-to/nested_row_values.md + - Generator rows: language/how-to/generator_rows.md + - Window columns: language/how-to/window_columns.md + - Approximate metrics: language/how-to/approximate_metrics.md + - Typed HLL sketches: language/how-to/typed_hll_sketches.md + - Variant payloads: language/how-to/variant_payloads.md + - Execution observations: language/how-to/execution_observations.md + - Quality observations: language/how-to/quality_observations.md + - Governed evidence: language/how-to/governed_evidence.md + - Inspect plan lineage: language/how-to/inspect_plan_lineage.md + - Reference: + - Overview: language/reference/README.md + - Core: + - Dataset carriers: language/reference/dataset_carriers.md + - Dataset methods: language/reference/dataset_methods.md + - Query blocks: language/reference/query_blocks.md + - Execution context: language/reference/execution_context.md + - Inspection: language/reference/inspection.md + - Quality: language/reference/quality.md + - Governance: language/reference/governance.md + - Builders: + - Filters: language/reference/builders/filters.md + - Projections: language/reference/builders/projections.md + - Aggregates: language/reference/builders/aggregates.md + - Functions: + - Index: language/reference/functions/index.md + - Approximate: language/reference/functions/approximate.md + - Format: language/reference/functions/format.md + - Generators: language/reference/functions/generators.md + - Nested: language/reference/functions/nested.md + - Sketches: language/reference/functions/sketches.md + - Variants: language/reference/functions/variants.md + - Windows: language/reference/functions/windows.md + - Substrait: + - Conformance: language/reference/substrait/conformance.md + - Operator catalog: language/reference/substrait/operator_catalog.md + - Read root binding contract: language/reference/substrait/read_root_binding_contract.md + - Revision and extension policy: language/reference/substrait/revision_and_extension_policy.md + - Architecture: architecture.md + - Design records: + - Overview: design_records.md + - RFCs: rfcs/README.md + - Whitepapers: + - Overview: whitepapers/README.md + - IncQL-DB: whitepapers/incql_db.md + - Project: + - Overview: project.md + - Docs map: docs_map.md + - Release notes: + - v0.1: release_notes/v0_1.md + - Contributing: + - Contributor architecture: contributing/architecture.md + - Writing RFCs: contributing/writing_rfcs.md + - RFC template: rfcs/TEMPLATE.md + - Prismplane docs theme: contributing/prismplane_docs_theme.md diff --git a/mkdocs_hooks.py b/mkdocs_hooks.py new file mode 100644 index 00000000..5dafddb7 --- /dev/null +++ b/mkdocs_hooks.py @@ -0,0 +1,45 @@ +from __future__ import annotations + +import sys +from pathlib import Path + + +def _validate_rfc_catalog(root: Path) -> None: + """Keep the generated RFC index in lockstep with the source records.""" + from mkdocs.exceptions import ConfigurationError + + from utils.rfc_catalog import ( + CatalogError, + build_catalog, + render_index_block, + replace_generated_block, + ) + + rfc_dir = root / "docs" / "rfcs" + readme = rfc_dir / "README.md" + tags = rfc_dir / "catalog.json" + try: + current = readme.read_text(encoding="utf-8") + records = build_catalog(rfc_dir, tags=tags) + expected = replace_generated_block(current, render_index_block(records)) + except (CatalogError, OSError) as error: + raise ConfigurationError(f"RFC catalog validation failed: {error}") from error + + if current != expected: + raise ConfigurationError( + "The generated RFC catalog is stale. Run `make rfc-index`, " + "review the resulting index, and commit it with the RFC changes." + ) + + +def on_config(config): # noqa: D401 - MkDocs hook signature + """Register custom rendering support and validate generated docs data.""" + root = Path(__file__).resolve().parent + if str(root) not in sys.path: + sys.path.insert(0, str(root)) + + from utils.incan_pygments import register_incan_lexer + + register_incan_lexer() + _validate_rfc_catalog(root) + return config diff --git a/requirements-docs.txt b/requirements-docs.txt new file mode 100644 index 00000000..38dda21f --- /dev/null +++ b/requirements-docs.txt @@ -0,0 +1,2 @@ +mkdocs==1.6.1 +mkdocs-material==9.5.49 diff --git a/tests/docs/test_rfc_catalog.py b/tests/docs/test_rfc_catalog.py new file mode 100644 index 00000000..f817f0ad --- /dev/null +++ b/tests/docs/test_rfc_catalog.py @@ -0,0 +1,504 @@ +from __future__ import annotations + +from contextlib import redirect_stderr, redirect_stdout +from dataclasses import FrozenInstanceError +from io import StringIO +import json +from pathlib import Path +from tempfile import TemporaryDirectory +import unittest + +from utils.rfc_catalog import ( + BEGIN_MARKER, + END_MARKER, + CatalogTag, + CatalogError, + build_catalog, + catalog_data, + discover_rfc_files, + load_tag_mapping, + main, + parse_rfc, + render_index_block, + replace_generated_block, +) + + +def rfc_text( + number: int, + *, + project_label: str = "IncQL", + title: str = "A test RFC", + status: str = "Draft", + created: str = "2026-07-18", + related: str = "—", + issue: str = "[IncQL #1](https://example.test/issues/1)", + rfc_pr: str = "—", + summary: str = "A concise summary of the proposed behavior.", + motivation: str = "This matters because the current behavior is incomplete.", +) -> str: + return f"""# {project_label} RFC {number:03d}: {title} + +- **Status:** {status} +- **Created:** {created} +- **Author(s):** Test Author (@test) +- **Related:** {related} +- **Issue:** {issue} +- **RFC PR:** {rfc_pr} +- **Written against:** Incan v0.4 +- **Shipped in:** — + +## Summary + +{summary} + +## Motivation + +{motivation} +""" + + +class RfcCatalogTests(unittest.TestCase): + def setUp(self) -> None: + self.temporary_directory = TemporaryDirectory() + self.addCleanup(self.temporary_directory.cleanup) + self.rfc_dir = Path(self.temporary_directory.name) / "rfcs" + self.rfc_dir.mkdir() + + def write_rfc( + self, + number: int, + *, + directory: str = "", + slug: str = "test_rfc", + **text_options: str, + ) -> Path: + parent = self.rfc_dir / directory + parent.mkdir(parents=True, exist_ok=True) + path = parent / f"{number:03d}_{slug}.md" + path.write_text(rfc_text(number, **text_options), encoding="utf-8") + return path + + def test_discovers_recursively_and_excludes_non_rfc_documents(self) -> None: + first = self.write_rfc(0) + implemented = self.write_rfc( + 1, + directory="closed/implemented", + slug="implemented", + status="Implemented", + ) + (self.rfc_dir / "README.md").write_text("# RFCs\n", encoding="utf-8") + (self.rfc_dir / "TEMPLATE.md").write_text("# Template\n", encoding="utf-8") + (self.rfc_dir / "notes.md").write_text("# Notes\n", encoding="utf-8") + + self.assertEqual(discover_rfc_files(self.rfc_dir), [first, implemented]) + records = build_catalog(self.rfc_dir) + + self.assertEqual([record.id for record in records], ["000", "001"]) + self.assertEqual(records[0].lifecycle, "active") + self.assertEqual(records[1].lifecycle, "implemented") + self.assertEqual(records[1].href, "closed/implemented/001_implemented/") + + def test_parses_metadata_related_items_and_first_summary_paragraph(self) -> None: + path = self.write_rfc( + 0, + title="`query {}` blocks", + related="\n - IncQL RFC 001 (`DataSet[T]`)\n - IncQL RFC 002 (Substrait)", + summary=( + "The **typed** query surface spans\n" + "multiple source lines.\n\nA second paragraph." + ), + ) + + record = parse_rfc(path, self.rfc_dir) + + self.assertEqual(record.title, "query {} blocks") + self.assertEqual(record.authors, "Test Author (@test)") + self.assertEqual( + record.related, + ("IncQL RFC 001 (DataSet[T])", "IncQL RFC 002 (Substrait)"), + ) + self.assertEqual(record.related_ids, ("001", "002")) + self.assertEqual(record.issue_label, "IncQL #1") + self.assertEqual(record.issue_url, "https://example.test/issues/1") + self.assertEqual(record.summary, "The typed query surface spans multiple source lines.") + self.assertEqual( + record.motivation, + "This matters because the current behavior is incomplete.", + ) + + def test_allows_the_deliberate_049_gap(self) -> None: + self.write_rfc(48, slug="before_gap") + self.write_rfc(50, slug="after_gap") + + self.assertEqual([record.id for record in build_catalog(self.rfc_dir)], ["048", "050"]) + + def test_project_label_and_allowed_gaps_are_configurable(self) -> None: + self.write_rfc(0, project_label="Incan") + self.write_rfc(2, slug="after_gap", project_label="Incan") + + with self.assertRaisesRegex( + CatalogError, + "first line must be '# IncQL RFC NNN: Title'", + ): + build_catalog(self.rfc_dir, allowed_gaps=frozenset({1})) + + records = build_catalog( + self.rfc_dir, + project_label="Incan", + allowed_gaps=frozenset({1}), + ) + self.assertEqual([record.id for record in records], ["000", "002"]) + + def test_rejects_an_unexpected_sequence_gap(self) -> None: + self.write_rfc(0) + self.write_rfc(2, slug="unexpected_gap") + + with self.assertRaisesRegex(CatalogError, r"unexpected gap\(s\): 001"): + build_catalog(self.rfc_dir) + + def test_rejects_duplicate_ids_across_lifecycle_folders(self) -> None: + self.write_rfc(0, slug="active") + self.write_rfc( + 0, + directory="closed/implemented", + slug="done", + status="Implemented", + ) + + with self.assertRaisesRegex(CatalogError, "duplicate RFC id 000"): + build_catalog(self.rfc_dir) + + def test_rejects_filename_and_h1_disagreement(self) -> None: + path = self.write_rfc(0) + path.write_text(rfc_text(1), encoding="utf-8") + + with self.assertRaisesRegex(CatalogError, "filename RFC 000 disagrees with H1 RFC 001"): + parse_rfc(path, self.rfc_dir) + + def test_rejects_missing_metadata_and_invalid_created_date(self) -> None: + path = self.write_rfc(0) + path.write_text( + rfc_text(0).replace("- **RFC PR:** —\n", ""), + encoding="utf-8", + ) + with self.assertRaisesRegex(CatalogError, "missing required metadata: RFC PR"): + parse_rfc(path, self.rfc_dir) + + path.write_text(rfc_text(0, created="18-07-2026"), encoding="utf-8") + with self.assertRaisesRegex(CatalogError, "Created must use YYYY-MM-DD"): + parse_rfc(path, self.rfc_dir) + + def test_requires_a_source_derived_motivation_paragraph(self) -> None: + path = self.write_rfc(0, motivation="") + + with self.assertRaisesRegex(CatalogError, "'## Motivation' must start with a paragraph"): + parse_rfc(path, self.rfc_dir) + + def test_rejects_unknown_status_and_lifecycle_mismatch(self) -> None: + path = self.write_rfc(0, status="Accepted") + with self.assertRaisesRegex(CatalogError, "unsupported status 'Accepted'"): + parse_rfc(path, self.rfc_dir) + + path.write_text(rfc_text(0, status="Implemented"), encoding="utf-8") + with self.assertRaisesRegex(CatalogError, "does not agree with lifecycle folder"): + parse_rfc(path, self.rfc_dir) + + def test_accepts_documented_status_variants_in_matching_folders(self) -> None: + blocked = self.write_rfc(0, status="Blocked (by RFC 001)") + superseded = self.write_rfc( + 1, + directory="closed/superseded", + status="Superseded by IncQL RFC 002", + ) + + self.assertEqual(parse_rfc(blocked, self.rfc_dir).status_key, "blocked") + self.assertEqual(parse_rfc(superseded, self.rfc_dir).lifecycle, "superseded") + + def test_custom_project_label_applies_to_superseded_status(self) -> None: + superseded = self.write_rfc( + 0, + directory="closed/superseded", + status="Superseded by Incan RFC 001", + project_label="Incan", + ) + + record = parse_rfc(superseded, self.rfc_dir, project_label="Incan") + self.assertEqual(record.status_key, "superseded") + + def test_normalizes_all_source_links_and_rejects_malformed_links(self) -> None: + path = self.write_rfc( + 0, + rfc_pr=( + "[IncQL #10](https://example.test/pull/10); " + "[IncQL #11](https://example.test/pull/11) (follow-up)" + ), + ) + + record = parse_rfc(path, self.rfc_dir) + self.assertEqual(record.rfc_pr_label, "IncQL #10") + self.assertEqual(record.rfc_pr_url, "https://example.test/pull/10") + self.assertEqual( + [(link.label, link.url) for link in record.rfc_pr_links], + [ + ("IncQL #10", "https://example.test/pull/10"), + ("IncQL #11", "https://example.test/pull/11"), + ], + ) + + path.write_text(rfc_text(0, issue="[broken](not-a-url)"), encoding="utf-8") + with self.assertRaisesRegex(CatalogError, "Issue must be a Markdown link"): + parse_rfc(path, self.rfc_dir) + + def test_validates_related_ids_against_the_discovered_corpus(self) -> None: + self.write_rfc(0, related="IncQL RFC 001 (missing)") + + with self.assertRaisesRegex(CatalogError, r"Related references unknown RFC id\(s\): 001"): + build_catalog(self.rfc_dir) + + def tag_catalog( + self, + records: dict[object, list[object]], + *, + definitions: dict[object, object] | None = None, + ) -> dict[str, object]: + return { + "definitions": definitions + or { + "authoring": "Authoring", + "planning": "Planning", + "runtime": "Runtime", + }, + "records": records, + } + + def test_tag_catalog_supports_many_to_many_assignments_and_json_files(self) -> None: + self.write_rfc(0) + self.write_rfc(1, slug="second") + + tags = build_catalog( + self.rfc_dir, + tags=self.tag_catalog( + { + "000": ["planning", "authoring"], + 1: ["runtime"], + } + ), + ) + self.assertEqual( + tags[0].tags, + ( + CatalogTag(key="authoring", label="Authoring"), + CatalogTag(key="planning", label="Planning"), + ), + ) + self.assertEqual(tags[1].tags, (CatalogTag(key="runtime", label="Runtime"),)) + self.assertIsInstance(tags[0].tags, tuple) + with self.assertRaises(FrozenInstanceError): + tags[0].tags[0].label = "Changed" # type: ignore[misc] + + tag_file = self.rfc_dir.parent / "tags.json" + tag_file.write_text( + json.dumps(self.tag_catalog({"000": ["authoring"], "001": ["runtime"]})), + encoding="utf-8", + ) + from_file = build_catalog(self.rfc_dir, tags=tag_file) + self.assertEqual( + [[tag.key for tag in record.tags] for record in from_file], + [["authoring"], ["runtime"]], + ) + + def test_tag_catalog_requires_every_discovered_rfc_and_rejects_unknown_rfcs(self) -> None: + self.write_rfc(0) + self.write_rfc(1, slug="second") + + with self.assertRaisesRegex(CatalogError, "missing RFCs: 001"): + build_catalog(self.rfc_dir, tags=self.tag_catalog({"000": ["planning"]})) + with self.assertRaisesRegex(CatalogError, "unknown RFCs: 002"): + build_catalog( + self.rfc_dir, + tags=self.tag_catalog( + { + "000": ["planning"], + "001": ["runtime"], + "002": ["authoring"], + } + ), + ) + + def test_tag_catalog_validates_sections_definitions_and_stable_keys(self) -> None: + with self.assertRaisesRegex(CatalogError, "missing section.*records"): + load_tag_mapping({"definitions": {"planning": "Planning"}}) + with self.assertRaisesRegex(CatalogError, "unknown section.*topics"): + load_tag_mapping( + {"definitions": {}, "records": {}, "topics": {}} # type: ignore[dict-item] + ) + with self.assertRaisesRegex(CatalogError, "definitions must be a JSON object"): + load_tag_mapping({"definitions": [], "records": {}}) + with self.assertRaisesRegex(CatalogError, "records must be a JSON object"): + load_tag_mapping({"definitions": {}, "records": []}) + with self.assertRaisesRegex(CatalogError, "lowercase kebab-case"): + load_tag_mapping(self.tag_catalog({}, definitions={"Data Access": "Data access"})) + with self.assertRaisesRegex(CatalogError, "non-empty label"): + load_tag_mapping(self.tag_catalog({}, definitions={"planning": " "})) + with self.assertRaisesRegex(CatalogError, "duplicate label 'planning'"): + load_tag_mapping( + self.tag_catalog( + {}, + definitions={"planning": "Planning", "plan": " planning "}, + ) + ) + + def test_tag_catalog_rejects_invalid_record_tag_sets(self) -> None: + with self.assertRaisesRegex(CatalogError, "must have at least one tag"): + load_tag_mapping(self.tag_catalog({"000": []})) + with self.assertRaisesRegex(CatalogError, "tags must be an array"): + load_tag_mapping(self.tag_catalog({"000": "planning"})) # type: ignore[dict-item] + with self.assertRaisesRegex(CatalogError, "non-string tag key"): + load_tag_mapping(self.tag_catalog({"000": [1]})) + with self.assertRaisesRegex(CatalogError, "duplicate tag 'planning'"): + load_tag_mapping(self.tag_catalog({"000": ["planning", "planning"]})) + with self.assertRaisesRegex(CatalogError, "more than 4 tags"): + load_tag_mapping( + self.tag_catalog( + {"000": ["one", "two", "three", "four", "five"]}, + definitions={ + "one": "One", + "two": "Two", + "three": "Three", + "four": "Four", + "five": "Five", + }, + ) + ) + with self.assertRaisesRegex(CatalogError, "unknown tag 'unknown'"): + load_tag_mapping(self.tag_catalog({"000": ["unknown"]})) + with self.assertRaisesRegex(CatalogError, "defines RFC 000 more than once"): + load_tag_mapping(self.tag_catalog({"0": ["planning"], "000": ["runtime"]})) + + def test_catalog_data_and_rendered_block_are_deterministic_and_safe(self) -> None: + self.write_rfc(0, summary="Avoid termination & preserve safety.") + self.write_rfc(1, slug="second") + records = build_catalog( + self.rfc_dir, + tags=self.tag_catalog( + { + "000": ["planning", "authoring"], + "001": ["runtime"], + } + ), + ) + + data = catalog_data(reversed(records)) + block = render_index_block(reversed(records)) + + self.assertEqual([item["id"] for item in data], ["000", "001"]) + self.assertIsInstance(data[0]["related"], list) + self.assertIsInstance(data[0]["related_ids"], list) + self.assertEqual( + data[0]["issue_links"], + [{"label": "IncQL #1", "url": "https://example.test/issues/1"}], + ) + self.assertEqual( + data[0]["tags"], + [ + {"key": "authoring", "label": "Authoring"}, + {"key": "planning", "label": "Planning"}, + ], + ) + self.assertTrue(block.startswith(BEGIN_MARKER)) + self.assertTrue(block.endswith(END_MARKER)) + self.assertIn('type="application/json" data-rfc-catalog', block) + self.assertIn('class="pp-rfc-fallback" data-rfc-fallback markdown="1"', block) + self.assertNotIn(" termination", block) + self.assertIn(r"\u003c/script\u003e termination \u0026 preserve safety", block) + self.assertIn( + "| [000](000_test_rfc.md) | Draft | Authoring, Planning | A test RFC |", + block, + ) + + def test_replaces_exactly_one_generated_marker_pair(self) -> None: + original = f"Before\n\n{BEGIN_MARKER}\nold\n{END_MARKER}\n\nAfter\n" + generated = f"{BEGIN_MARKER}\nnew\n{END_MARKER}" + + self.assertEqual( + replace_generated_block(original, generated), + f"Before\n\n{generated}\n\nAfter\n", + ) + with self.assertRaisesRegex(CatalogError, "exactly one"): + replace_generated_block("No markers", generated) + with self.assertRaisesRegex(CatalogError, "end marker must follow"): + replace_generated_block(f"{END_MARKER}\n{BEGIN_MARKER}", generated) + + def test_cli_write_and_check_share_the_same_generated_output(self) -> None: + self.write_rfc(0) + tags = self.rfc_dir / "catalog.json" + tags.write_text( + json.dumps(self.tag_catalog({"000": ["planning", "authoring"]})), + encoding="utf-8", + ) + readme = self.rfc_dir / "README.md" + readme.write_text( + f"# RFCs\n\n{BEGIN_MARKER}\nstale\n{END_MARKER}\n", + encoding="utf-8", + ) + arguments = ["--rfc-dir", str(self.rfc_dir), "--readme", str(readme)] + + with redirect_stdout(StringIO()), redirect_stderr(StringIO()): + self.assertEqual(main([*arguments, "--write"]), 0) + self.assertEqual(main([*arguments, "--check"]), 0) + + current = readme.read_text(encoding="utf-8") + self.assertIn("| RFC | Status | Tags | Title |", current) + self.assertIn("| Draft | Authoring, Planning | A test RFC |", current) + readme.write_text(current.replace("A test RFC", "A stale RFC", 1), encoding="utf-8") + errors = StringIO() + with redirect_stdout(StringIO()), redirect_stderr(errors): + self.assertEqual(main([*arguments, "--check"]), 1) + self.assertIn("is out of date", errors.getvalue()) + self.assertIn(f"--tags {tags}", errors.getvalue()) + self.assertIn("--write", errors.getvalue()) + self.assertNotIn("python utils/rfc_catalog.py --write", errors.getvalue()) + + def test_cli_accepts_custom_project_label_and_sequence_gaps(self) -> None: + self.write_rfc(0, project_label="Incan") + self.write_rfc(2, slug="after_gap", project_label="Incan") + readme = self.rfc_dir / "README.md" + readme.write_text( + f"# RFCs\n\n{BEGIN_MARKER}\nstale\n{END_MARKER}\n", + encoding="utf-8", + ) + + arguments = [ + "--rfc-dir", + str(self.rfc_dir), + "--readme", + str(readme), + "--project-label", + "Incan", + "--allow-gap", + "001", + "--write", + ] + with redirect_stdout(StringIO()), redirect_stderr(StringIO()): + self.assertEqual(main(arguments), 0) + + self.assertIn("| [002]", readme.read_text(encoding="utf-8")) + + def test_current_incql_corpus_satisfies_the_catalog_contract(self) -> None: + repository_root = Path(__file__).resolve().parents[2] + records = build_catalog( + repository_root / "docs" / "rfcs", + tags=repository_root / "docs" / "rfcs" / "catalog.json", + ) + + self.assertEqual(len(records), 50) + self.assertEqual(records[0].id, "000") + self.assertEqual(records[-1].id, "050") + self.assertNotIn("049", {record.id for record in records}) + self.assertTrue(all(1 <= len(record.tags) <= 4 for record in records)) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/docs/test_rfc_reader.cjs b/tests/docs/test_rfc_reader.cjs new file mode 100644 index 00000000..392cd13e --- /dev/null +++ b/tests/docs/test_rfc_reader.cjs @@ -0,0 +1,144 @@ +const test = require("node:test"); +const assert = require("node:assert/strict"); + +const { rfcReaderContract } = require("../../docs/javascripts/prismplane.js"); + +const defaults = { + query: "", + scope: "all", + status: "", + sort: "", + tags: [], + selectedId: "000", + selectedExplicitly: false, +}; + +const known = { + statuses: new Set(["planned", "implemented"]), + tags: new Set(["evidence", "planning", "types"]), + records: new Set(["000", "030"]), +}; + +test("adjacent RFC selection skips hidden records and stops at the boundaries", () => { + const records = [{ id: "007" }, { id: "030" }, { id: "045" }]; + + assert.equal(rfcReaderContract.adjacentRecordId(records, "007", 1), "030"); + assert.equal(rfcReaderContract.adjacentRecordId(records, "030", -1), "007"); + assert.equal(rfcReaderContract.adjacentRecordId(records, "007", -1), "007"); + assert.equal(rfcReaderContract.adjacentRecordId(records, "045", 1), "045"); + assert.equal(rfcReaderContract.adjacentRecordId([], "007", 1), ""); + assert.equal(rfcReaderContract.adjacentRecordId(records, "unknown", 1), "007"); +}); + +test("filtered results preserve a visible selection and otherwise repair to the first result", () => { + const records = [{ id: "007" }, { id: "030" }]; + + assert.equal(rfcReaderContract.resolvedRecordId(records, "030"), "030"); + assert.equal(rfcReaderContract.resolvedRecordId(records, "045"), "007"); + assert.equal(rfcReaderContract.resolvedRecordId([], "045"), ""); +}); + +test("tag selections are unique, sorted, and toggleable", () => { + let tags = rfcReaderContract.setTag([], "planning", true); + tags = rfcReaderContract.setTag(tags, "evidence", true); + tags = rfcReaderContract.setTag(tags, "planning", true); + assert.deepEqual(tags, ["evidence", "planning"]); + + tags = rfcReaderContract.setTag(tags, "planning", false); + assert.deepEqual(tags, ["evidence"]); +}); + +test("multiple tag filters use intersection semantics", () => { + const normalize = (value) => value.toLowerCase().trim(); + const record = { + lifecycle: "active", + status_key: "planned", + tags: [{ key: "evidence" }, { key: "planning" }], + searchText: "prism lineage graph", + }; + const state = { ...defaults, query: "lineage", scope: "active", status: "planned", tags: ["evidence", "planning"] }; + + assert.equal(rfcReaderContract.matches(record, state, normalize), true); + assert.equal(rfcReaderContract.matches(record, { ...state, tags: ["evidence", "types"] }, normalize), false); +}); + +test("status sorting is stable and uses RFC number as its tie break", () => { + const records = [ + { id: "030", status: "In Progress" }, + { id: "003", status: "Implemented" }, + { id: "007", status: "In Progress" }, + { id: "000", status: "Planned" }, + ]; + + assert.deepEqual( + rfcReaderContract.sortRecords(records, "status-asc").map((record) => record.id), + ["003", "007", "030", "000"], + ); + assert.deepEqual( + rfcReaderContract.sortRecords(records, "status-desc").map((record) => record.id), + ["000", "007", "030", "003"], + ); + assert.deepEqual( + rfcReaderContract.sortRecords(records, "").map((record) => record.id), + ["000", "003", "007", "030"], + ); +}); + +test("URL state uses sorted repeated tag parameters and removes legacy topic state", () => { + const params = new URLSearchParams("topic=planning&tag=types&rfc=000"); + const state = { + ...defaults, + sort: "status-asc", + tags: ["planning", "evidence"], + selectedId: "030", + selectedExplicitly: true, + }; + + rfcReaderContract.writeParams(params, state); + + assert.equal(params.has("topic"), false); + assert.deepEqual(params.getAll("tag"), ["evidence", "planning"]); + assert.equal(params.get("sort"), "status-asc"); + assert.equal(params.get("rfc"), "030"); +}); + +test("URL state round-trips and ignores unknown or duplicate filters", () => { + const params = new URLSearchParams("q=lineage&scope=active&status=planned&sort=status-desc&tag=planning&tag=evidence&tag=planning&tag=unknown&rfc=030"); + + const state = rfcReaderContract.readParams(params, defaults, known); + + assert.deepEqual(state, { + query: "lineage", + scope: "all", + status: "planned", + sort: "status-desc", + tags: ["evidence", "planning"], + selectedId: "030", + selectedExplicitly: true, + }); + + const restoredAfterHistoryNavigation = rfcReaderContract.readParams( + new URLSearchParams("tag=types&rfc=000"), + defaults, + known, + ); + assert.deepEqual(restoredAfterHistoryNavigation.tags, ["types"]); + assert.equal(restoredAfterHistoryNavigation.selectedId, "000"); + assert.equal(restoredAfterHistoryNavigation.selectedExplicitly, true); + + const unknownSort = rfcReaderContract.readParams( + new URLSearchParams("sort=recent"), + defaults, + known, + ); + assert.equal(unknownSort.sort, ""); +}); + +test("clearing tags removes every tag parameter while preserving other filters", () => { + const params = new URLSearchParams("q=prism&tag=evidence&tag=planning"); + + rfcReaderContract.writeParams(params, { ...defaults, query: "prism" }); + + assert.deepEqual(params.getAll("tag"), []); + assert.equal(params.get("q"), "prism"); +}); diff --git a/utils/__init__.py b/utils/__init__.py new file mode 100644 index 00000000..f2232b12 --- /dev/null +++ b/utils/__init__.py @@ -0,0 +1 @@ +"""MkDocs helper modules for the IncQL documentation site.""" diff --git a/utils/incan_pygments.py b/utils/incan_pygments.py new file mode 100644 index 00000000..0fb395a7 --- /dev/null +++ b/utils/incan_pygments.py @@ -0,0 +1,315 @@ +from __future__ import annotations + +import re +from pathlib import Path +from typing import Iterable, Sequence + +from pygments.lexers import _mapping +from pygments.lexers.python import PythonLexer +from pygments.token import Keyword, Name, Operator, Token + + +_FALLBACK_STDLIB_FUNCTIONS = { + "abs", + "avg", + "ceil", + "coalesce", + "col", + "concat", + "count", + "date_add", + "datediff", + "desc", + "eq", + "floor", + "from_csv", + "from_json", + "ge", + "gt", + "is_array", + "is_json", + "is_null", + "le", + "lower", + "lt", + "make_date", + "make_timestamp", + "max", + "min", + "neq", + "round", + "sqrt", + "substring", + "sum", + "trim", + "try_cast", + "typeof", + "upper", +} + +_FALLBACK_STDLIB_TYPES = { + "Any", + "Bool", + "DataFrame", + "DataSet", + "Float", + "Int", + "LazyFrame", + "None", + "Option", + "Result", + "Session", + "Str", + "String", + "Union", + "bool", + "dict", + "float", + "int", + "list", + "set", + "str", + "tuple", +} + + +_INFO_WITH_ALIASES = re.compile( + r'info\([^,]+,\s*"([^"]+)"\s*,\s*&\[(.*?)\]', + re.DOTALL, +) +_INFO_CANONICAL = re.compile(r'info\([^,]+,\s*"([^"]+)"', re.DOTALL) + + +def _extract_lang_items(text: str) -> tuple[set[str], set[str]]: + canonicals: set[str] = set() + aliases: set[str] = set() + + for match in _INFO_WITH_ALIASES.finditer(text): + canonicals.add(match.group(1)) + aliases.update(re.findall(r'"([^"]+)"', match.group(2))) + + for match in _INFO_CANONICAL.finditer(text): + canonicals.add(match.group(1)) + + return canonicals, aliases + + +def _load_lang_items(paths: Sequence[Path]) -> tuple[set[str], set[str]]: + canonicals: set[str] = set() + aliases: set[str] = set() + for path in paths: + if not path.exists(): + continue + text = path.read_text(encoding="utf-8", errors="replace") + loaded_canonicals, loaded_aliases = _extract_lang_items(text) + canonicals.update(loaded_canonicals) + aliases.update(loaded_aliases) + return canonicals, aliases + + +def _incan_core_lang_root() -> Path | None: + """Return a local Incan core language registry when this repo is vendored beside Incan.""" + root = Path(__file__).resolve().parents[1] + candidates = [ + root / "incan" / "crates" / "incan_core" / "src" / "lang", + root.parent / "incan" / "crates" / "incan_core" / "src" / "lang", + root.parent.parent / "incan" / "crates" / "incan_core" / "src" / "lang", + ] + for candidate in candidates: + if candidate.exists(): + return candidate + return None + + +def _load_keywords_from_registry() -> list[str]: + lang_root = _incan_core_lang_root() + if lang_root is None: + return [] + + registry_path = lang_root / "keywords.rs" + if not registry_path.exists(): + return [] + + text = registry_path.read_text(encoding="utf-8", errors="replace") + pattern = re.compile( + r'info(?:_with_aliases)?\(\s*KeywordId::[A-Za-z_]+,\s*"([^"]+)"', + re.DOTALL, + ) + return sorted({match.group(1) for match in pattern.finditer(text)}) + + +def _load_stdlib_functions() -> set[str]: + lang_root = _incan_core_lang_root() + if lang_root is None: + return set(_FALLBACK_STDLIB_FUNCTIONS) + + paths = [ + lang_root / "builtins.rs", + lang_root / "surface" / "functions.rs", + lang_root / "surface" / "constructors.rs", + lang_root / "surface" / "math.rs", + lang_root / "surface" / "methods.rs", + ] + canonicals, aliases = _load_lang_items(paths) + return canonicals.union(aliases, _FALLBACK_STDLIB_FUNCTIONS) + + +def _load_stdlib_types() -> set[str]: + lang_root = _incan_core_lang_root() + if lang_root is None: + return set(_FALLBACK_STDLIB_TYPES) + + paths = [ + lang_root / "types" / "numerics.rs", + lang_root / "types" / "collections.rs", + lang_root / "types" / "stringlike.rs", + lang_root / "surface" / "types.rs", + lang_root / "derives.rs", + lang_root / "errors.rs", + ] + canonicals, aliases = _load_lang_items(paths) + return canonicals.union(aliases, _FALLBACK_STDLIB_TYPES) + + +def _fallback_keywords() -> list[str]: + return [ + "and", + "as", + "async", + "await", + "break", + "case", + "class", + "const", + "continue", + "crate", + "def", + "elif", + "else", + "enum", + "false", + "for", + "from", + "if", + "import", + "in", + "is", + "let", + "match", + "model", + "mut", + "newtype", + "none", + "not", + "or", + "pass", + "pub", + "python", + "return", + "rust", + "self", + "super", + "trait", + "true", + "type", + "while", + "with", + "yield", + ] + + +def _keywords() -> Iterable[str]: + # IncQL query blocks are Incan vocab syntax, so include the docs-facing clause keywords here + # instead of relying on generic Python highlighting for uppercase words. + extras = { + "derive", + "module", + "test", + "tests", + "FROM", + "HAVING", + "SELECT", + "WHERE", + "GROUP", + "BY", + "ORDER", + "WINDOW", + "LIMIT", + "JOIN", + "ON", + "OVER", + "PARTITION", + "ROWS", + "RANGE", + "CURRENT", + "PRECEDING", + "FOLLOWING", + "UNBOUNDED", + "ASC", + "DESC", + "INNER", + "LEFT", + "RIGHT", + "FULL", + "OUTER", + "quality", + "query", + } + return sorted(set(_load_keywords_from_registry()).union(_fallback_keywords(), extras)) + + +class IncanLexer(PythonLexer): + """Pygments lexer for Incan and IncQL code examples.""" + + name = "Incan" + aliases = ["incan", "incn"] + filenames = ["*.incn"] + mimetypes = ["text/x-incan"] + + flags = re.MULTILINE + + def __init__(self, **options): + super().__init__(**options) + self._incan_keywords = set(_keywords()) + self._incan_types = _load_stdlib_types() + self._incan_functions = _load_stdlib_functions() + + def get_tokens_unprocessed(self, text, stack=("root",)): + for index, token, value in super().get_tokens_unprocessed(text, stack=stack): + if token is Name and value in self._incan_keywords: + yield index, Keyword, value + continue + if token is Name and value in self._incan_types: + yield index, Keyword.Type, value + continue + if token is Name and value in self._incan_functions: + yield index, Name.Builtin, value + continue + if token is Name and value.startswith("assert_"): + yield index, Name.Function, value + continue + if token is Token.Error and value == "?": + yield index, Operator, value + continue + if token is Name and value[:1].isupper(): + yield index, Name.Class, value + continue + yield index, token, value + + +def register_incan_lexer() -> None: + """Register Incan lexer with Pygments for MkDocs builds.""" + + if "IncanLexer" in _mapping.LEXERS: + return + + _mapping.LEXERS["IncanLexer"] = ( + __name__, + IncanLexer.name, + tuple(IncanLexer.aliases), + tuple(IncanLexer.filenames), + tuple(IncanLexer.mimetypes), + ) + + +__all__ = ["IncanLexer"] diff --git a/utils/rfc_catalog.py b/utils/rfc_catalog.py new file mode 100644 index 00000000..63a9e66f --- /dev/null +++ b/utils/rfc_catalog.py @@ -0,0 +1,784 @@ +"""Build and validate the portable RFC catalog used by the documentation site. + +The module intentionally depends only on the Python standard library. It can +therefore be copied to another repository without bringing MkDocs (or a MkDocs +plugin) into that repository's validation path. +""" + +from __future__ import annotations + +import argparse +from collections.abc import Mapping, Sequence +from dataclasses import asdict, dataclass, replace +from datetime import date +import json +from pathlib import Path +import re +import shlex +import sys +from typing import Any +from urllib.parse import urlparse + + +BEGIN_MARKER = "" +END_MARKER = "" + +REQUIRED_METADATA = ( + "Status", + "Created", + "Author(s)", + "Related", + "Issue", + "RFC PR", + "Written against", + "Shipped in", +) + +ACTIVE_STATUSES = frozenset({"Draft", "Planned", "In Progress", "Blocked", "Deferred"}) +TERMINAL_STATUSES = frozenset({"Implemented", "Superseded", "Rejected", "Withdrawn"}) +DEFAULT_PROJECT_LABEL = "IncQL" +ALLOWED_GAPS = frozenset({49}) +MAX_TAGS_PER_RECORD = 4 + +_RFC_FILENAME_RE = re.compile(r"^(?P\d{3})_(?P[a-z0-9][a-z0-9_]*)\.md$") +_METADATA_RE = re.compile(r"^-\s+\*\*(?P[^*]+?):\*\*\s*(?P.*)$") +_BLOCKED_STATUS_RE = re.compile(r"^Blocked(?:\s+\(.+\))?$") +_MARKDOWN_LINK_RE = re.compile(r"!?\[([^]]*)\]\([^)]+\)") +_MARKDOWN_REFERENCE_LINK_RE = re.compile(r"\[([^]]+)\]\[[^]]*\]") +_METADATA_LINK_RE = re.compile(r"\[([^]]+)\]\((https?://[^\s)]+)\)") +_BARE_URL_RE = re.compile(r"https?://\S+") +_RELATED_RFC_RE = re.compile(r"(?:IncQL\s+)?RFC\s+(\d{3})", flags=re.IGNORECASE) +_TAG_KEY_RE = re.compile(r"^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$") + + +class CatalogError(ValueError): + """Raised when the RFC corpus cannot form a valid catalog.""" + + +@dataclass(frozen=True, slots=True) +class CatalogLink: + """A validated source link exposed without client-side Markdown parsing.""" + + label: str + url: str + + +@dataclass(frozen=True, slots=True, order=True) +class CatalogTag: + """One stable tag key and its repository-defined display label. + + RFC records store tags as a sorted tuple of these frozen values. This + keeps the Python representation immutable and makes serialized output + independent of JSON object ordering. + """ + + key: str + label: str + + +@dataclass(frozen=True, slots=True) +class RfcRecord: + """One validated RFC entry, ready for deterministic serialization.""" + + id: str + title: str + status: str + status_key: str + lifecycle: str + created: str + authors: str + related: tuple[str, ...] + related_ids: tuple[str, ...] + issue: str + issue_label: str | None + issue_url: str | None + issue_links: tuple[CatalogLink, ...] + rfc_pr: str + rfc_pr_label: str | None + rfc_pr_url: str | None + rfc_pr_links: tuple[CatalogLink, ...] + written_against: str + shipped_in: str + summary: str + motivation: str + source_path: str + href: str + tags: tuple[CatalogTag, ...] = () + + def to_dict(self) -> dict[str, Any]: + """Return a JSON-compatible representation with stable field names.""" + + result = asdict(self) + result["related"] = list(self.related) + result["related_ids"] = list(self.related_ids) + result["issue_links"] = [asdict(link) for link in self.issue_links] + result["rfc_pr_links"] = [asdict(link) for link in self.rfc_pr_links] + result["tags"] = [asdict(tag) for tag in self.tags] + return result + + +def discover_rfc_files(rfc_dir: Path | str) -> list[Path]: + """Recursively discover RFC files in deterministic path order. + + Only files following ``NNN_slug.md`` are RFC inputs. README and TEMPLATE + documents are consequently excluded even if their names change case. + """ + + root = Path(rfc_dir) + if not root.is_dir(): + raise CatalogError(f"RFC directory does not exist: {root}") + + files = [ + path + for path in root.rglob("*.md") + if path.is_file() and _RFC_FILENAME_RE.fullmatch(path.name) + ] + return sorted(files, key=lambda path: path.relative_to(root).as_posix()) + + +def _plain_text(value: str) -> str: + value = _MARKDOWN_LINK_RE.sub(r"\1", value) + value = _MARKDOWN_REFERENCE_LINK_RE.sub(r"\1", value) + value = value.replace("`", "").replace("**", "").replace("__", "") + return " ".join(value.split()) + + +def _metadata_value(lines: list[str]) -> str: + cleaned: list[str] = [] + for line in lines: + value = line.strip() + if not value: + continue + if value.startswith("- "): + value = value[2:].strip() + cleaned.append(value) + return "\n".join(cleaned).strip() + + +def _parse_metadata(lines: list[str], source: Path) -> dict[str, str]: + metadata: dict[str, str] = {} + current_name: str | None = None + current_lines: list[str] = [] + + def commit() -> None: + nonlocal current_name, current_lines + if current_name is None: + return + if current_name in metadata: + raise CatalogError(f"{source}: duplicate metadata field {current_name!r}") + metadata[current_name] = _metadata_value(current_lines) + current_name = None + current_lines = [] + + for line in lines: + match = _METADATA_RE.match(line) + if match: + commit() + current_name = match.group("name").strip() + current_lines = [match.group("value")] + elif current_name is not None: + current_lines.append(line) + commit() + + missing = [name for name in REQUIRED_METADATA if not metadata.get(name, "").strip()] + if missing: + fields = ", ".join(missing) + raise CatalogError(f"{source}: missing required metadata: {fields}") + return metadata + + +def _normalise_project_label(value: str) -> str: + label = " ".join(value.split()) + if not label: + raise CatalogError("project label must not be empty") + return label + + +def _status_kind(status: str, source: Path, project_label: str) -> str: + if status in ACTIVE_STATUSES or status in TERMINAL_STATUSES: + return status + if _BLOCKED_STATUS_RE.fullmatch(status): + return "Blocked" + superseded_status_re = re.compile( + rf"^Superseded(?:\s+by\s+{re.escape(project_label)}\s+RFC\s+\d{{3}})?$" + ) + if superseded_status_re.fullmatch(status): + return "Superseded" + allowed = ", ".join(sorted(ACTIVE_STATUSES | TERMINAL_STATUSES)) + raise CatalogError(f"{source}: unsupported status {status!r}; expected one of: {allowed}") + + +def _lifecycle_for_path(relative_path: Path, status_kind: str, source: Path) -> str: + parent = relative_path.parent.as_posix() + if parent == ".": + expected = ACTIVE_STATUSES + lifecycle = "active" + elif parent == "closed/implemented": + expected = frozenset({"Implemented"}) + lifecycle = "implemented" + elif parent == "closed/superseded": + expected = frozenset({"Superseded"}) + lifecycle = "superseded" + elif parent == "closed/rejected": + expected = frozenset({"Rejected", "Withdrawn"}) + lifecycle = "rejected" + else: + raise CatalogError( + f"{source}: unsupported lifecycle folder {parent!r}; " + "use the RFC root or closed/{implemented,superseded,rejected}" + ) + + if status_kind not in expected: + allowed = ", ".join(sorted(expected)) + raise CatalogError( + f"{source}: status {status_kind!r} does not agree with lifecycle folder " + f"{parent!r} (expected {allowed})" + ) + return lifecycle + + +def _parse_related(raw: str) -> tuple[str, ...]: + if raw.strip() in {"-", "—"}: + return () + values = tuple(_plain_text(line) for line in raw.splitlines() if line.strip()) + return tuple(value for value in values if value not in {"-", "—"}) + + +def _related_ids(raw: str) -> tuple[str, ...]: + return tuple(dict.fromkeys(_RELATED_RFC_RE.findall(raw))) + + +def _parse_metadata_links(raw: str, field: str, source: Path) -> tuple[CatalogLink, ...]: + value = raw.strip() + if value in {"-", "—"}: + return () + + markdown_matches = list(_METADATA_LINK_RE.finditer(value)) + links: list[CatalogLink] = [] + if markdown_matches: + residual = _METADATA_LINK_RE.sub("", value) + if "[" in residual or "]" in residual: + raise CatalogError(f"{source}: {field} contains malformed Markdown link syntax") + for match in markdown_matches: + label = _plain_text(match.group(1)) + url = match.group(2) + if not label: + raise CatalogError(f"{source}: {field} link label must not be empty") + parsed = urlparse(url) + if parsed.scheme not in {"http", "https"} or not parsed.netloc: + raise CatalogError(f"{source}: {field} contains an invalid URL: {url!r}") + links.append(CatalogLink(label=label, url=url)) + return tuple(links) + + if _BARE_URL_RE.fullmatch(value): + parsed = urlparse(value) + if parsed.scheme in {"http", "https"} and parsed.netloc: + return (CatalogLink(label=value, url=value),) + + raise CatalogError( + f"{source}: {field} must be a Markdown link, an HTTP(S) URL, or an explicit —/-" + ) + + +def _parse_section_paragraph(lines: list[str], heading: str, source: Path) -> str: + try: + heading_index = next(i for i, line in enumerate(lines) if line.strip() == heading) + except StopIteration as error: + raise CatalogError(f"{source}: missing required {heading!r} section") from error + + paragraph: list[str] = [] + for line in lines[heading_index + 1 :]: + stripped = line.strip() + if not stripped: + if paragraph: + break + continue + if stripped.startswith("#"): + break + paragraph.append(stripped) + + summary = _plain_text(" ".join(paragraph)) + if not summary: + raise CatalogError(f"{source}: {heading!r} must start with a paragraph") + return summary + + +def parse_rfc( + path: Path | str, + rfc_dir: Path | str, + *, + project_label: str = DEFAULT_PROJECT_LABEL, +) -> RfcRecord: + """Parse and validate one RFC file.""" + + source = Path(path) + root = Path(rfc_dir) + project_label = _normalise_project_label(project_label) + try: + relative_path = source.relative_to(root) + except ValueError as error: + raise CatalogError(f"RFC is outside the configured RFC directory: {source}") from error + + filename_match = _RFC_FILENAME_RE.fullmatch(source.name) + if filename_match is None: + raise CatalogError(f"{source}: RFC filename must match NNN_slug.md") + + lines = source.read_text(encoding="utf-8").splitlines() + if not lines: + raise CatalogError(f"{source}: RFC file is empty") + heading_re = re.compile( + rf"^#\s+{re.escape(project_label)}\s+RFC\s+" + r"(?P\d{3}):\s*(?P.+?)\s*$" + ) + heading_match = heading_re.fullmatch(lines[0]) + if heading_match is None: + raise CatalogError( + f"{source}: first line must be '# {project_label} RFC NNN: Title'" + ) + + filename_id = filename_match.group("id") + heading_id = heading_match.group("id") + if filename_id != heading_id: + raise CatalogError( + f"{source}: filename RFC {filename_id} disagrees with H1 RFC {heading_id}" + ) + + try: + summary_index = next(i for i, line in enumerate(lines) if line.strip() == "## Summary") + except StopIteration as error: + raise CatalogError(f"{source}: missing required '## Summary' section") from error + metadata = _parse_metadata(lines[1:summary_index], source) + + created = metadata["Created"] + try: + parsed_created = date.fromisoformat(created) + except ValueError as error: + raise CatalogError(f"{source}: Created must use YYYY-MM-DD, got {created!r}") from error + if parsed_created.isoformat() != created: + raise CatalogError(f"{source}: Created must use zero-padded YYYY-MM-DD, got {created!r}") + + authors = _plain_text(metadata["Author(s)"]) + if authors in {"-", "—"}: + raise CatalogError(f"{source}: Author(s) must name at least one author") + + status = _plain_text(metadata["Status"]) + status_kind = _status_kind(status, source, project_label) + lifecycle = _lifecycle_for_path(relative_path, status_kind, source) + title = _plain_text(heading_match.group("title")) + if not title: + raise CatalogError(f"{source}: RFC title must not be empty") + + source_path = relative_path.as_posix() + href = relative_path.with_suffix("").as_posix() + "/" + related_ids = _related_ids(metadata["Related"]) + issue_links = _parse_metadata_links(metadata["Issue"], "Issue", source) + rfc_pr_links = _parse_metadata_links(metadata["RFC PR"], "RFC PR", source) + return RfcRecord( + id=filename_id, + title=title, + status=status, + status_key=status_kind.lower().replace(" ", "-"), + lifecycle=lifecycle, + created=created, + authors=authors, + related=_parse_related(metadata["Related"]), + related_ids=related_ids, + issue=metadata["Issue"], + issue_label=issue_links[0].label if issue_links else None, + issue_url=issue_links[0].url if issue_links else None, + issue_links=issue_links, + rfc_pr=metadata["RFC PR"], + rfc_pr_label=rfc_pr_links[0].label if rfc_pr_links else None, + rfc_pr_url=rfc_pr_links[0].url if rfc_pr_links else None, + rfc_pr_links=rfc_pr_links, + written_against=_plain_text(metadata["Written against"]), + shipped_in=_plain_text(metadata["Shipped in"]), + summary=_parse_section_paragraph(lines, "## Summary", source), + motivation=_parse_section_paragraph(lines, "## Motivation", source), + source_path=source_path, + href=href, + ) + + +def _normalise_rfc_id(value: object, context: str) -> str: + if isinstance(value, bool): + raise CatalogError(f"{context}: boolean is not an RFC id") + text = str(value).strip() + if not text.isdigit() or len(text) > 3: + raise CatalogError(f"{context}: RFC id must be a number from 000 to 999, got {value!r}") + return f"{int(text):03d}" + + +def load_tag_mapping( + source: Mapping[object, object] | Path | str, +) -> dict[str, tuple[CatalogTag, ...]]: + """Load a controlled many-to-many RFC tag catalog. + + The configuration shape is ``{"definitions": {key: label}, "records": + {rfc_id: [key, ...]}}``. A path is interpreted as a UTF-8 JSON file. + Returned record tags are frozen tuples sorted by stable key. + """ + + raw: object + if isinstance(source, Mapping): + raw = source + else: + path = Path(source) + try: + raw = json.loads(path.read_text(encoding="utf-8")) + except (OSError, json.JSONDecodeError) as error: + raise CatalogError(f"could not load tag catalog {path}: {error}") from error + + if not isinstance(raw, Mapping): + raise CatalogError("tag catalog must be a JSON object or a mapping") + + expected_sections = {"definitions", "records"} + section_names = set(raw) + missing_sections = sorted(expected_sections - section_names) + unknown_sections = sorted(str(name) for name in section_names - expected_sections) + if missing_sections or unknown_sections: + problems: list[str] = [] + if missing_sections: + problems.append("missing section(s): " + ", ".join(missing_sections)) + if unknown_sections: + problems.append("unknown section(s): " + ", ".join(unknown_sections)) + raise CatalogError("tag catalog has " + "; ".join(problems)) + + raw_definitions = raw["definitions"] + raw_records = raw["records"] + if not isinstance(raw_definitions, Mapping): + raise CatalogError("tag catalog definitions must be a JSON object or a mapping") + if not isinstance(raw_records, Mapping): + raise CatalogError("tag catalog records must be a JSON object or a mapping") + + definitions: dict[str, CatalogTag] = {} + label_to_key: dict[str, str] = {} + for raw_key, raw_label in raw_definitions.items(): + if not isinstance(raw_key, str) or not _TAG_KEY_RE.fullmatch(raw_key): + raise CatalogError( + "tag key must use lowercase kebab-case and start with a letter, " + f"got {raw_key!r}" + ) + if not isinstance(raw_label, str) or not raw_label.strip(): + raise CatalogError(f"tag definition {raw_key!r} must have a non-empty label") + label = " ".join(raw_label.split()) + normalised_label = label.casefold() + if normalised_label in label_to_key: + other_key = label_to_key[normalised_label] + raise CatalogError( + f"tag definitions {other_key!r} and {raw_key!r} have duplicate label {label!r}" + ) + label_to_key[normalised_label] = raw_key + definitions[raw_key] = CatalogTag(raw_key, label) + + id_to_tags: dict[str, tuple[CatalogTag, ...]] = {} + for raw_id, raw_tag_keys in raw_records.items(): + rfc_id = _normalise_rfc_id(raw_id, "tag catalog records") + if rfc_id in id_to_tags: + raise CatalogError(f"tag catalog defines RFC {rfc_id} more than once") + if not isinstance(raw_tag_keys, Sequence) or isinstance( + raw_tag_keys, (str, bytes, bytearray) + ): + raise CatalogError(f"tag catalog: RFC {rfc_id} tags must be an array") + if not raw_tag_keys: + raise CatalogError(f"tag catalog: RFC {rfc_id} must have at least one tag") + if len(raw_tag_keys) > MAX_TAGS_PER_RECORD: + raise CatalogError( + f"tag catalog: RFC {rfc_id} has more than {MAX_TAGS_PER_RECORD} tags" + ) + + seen_keys: set[str] = set() + record_tags: list[CatalogTag] = [] + for raw_key in raw_tag_keys: + if not isinstance(raw_key, str): + raise CatalogError(f"tag catalog: RFC {rfc_id} has a non-string tag key") + if raw_key in seen_keys: + raise CatalogError( + f"tag catalog: RFC {rfc_id} contains duplicate tag {raw_key!r}" + ) + seen_keys.add(raw_key) + try: + record_tags.append(definitions[raw_key]) + except KeyError as error: + raise CatalogError( + f"tag catalog: RFC {rfc_id} references unknown tag {raw_key!r}" + ) from error + id_to_tags[rfc_id] = tuple(sorted(record_tags)) + + return dict(sorted(id_to_tags.items())) + + +def _validate_sequence(records: list[RfcRecord], allowed_gaps: frozenset[int]) -> None: + if not records: + return + numbers = {int(record.id) for record in records} + missing = sorted(set(range(min(numbers), max(numbers) + 1)) - numbers - set(allowed_gaps)) + if missing: + formatted = ", ".join(f"{number:03d}" for number in missing) + raise CatalogError(f"RFC sequence has unexpected gap(s): {formatted}") + + +def build_catalog( + rfc_dir: Path | str, + *, + tags: Mapping[object, object] | Path | str | None = None, + allowed_gaps: frozenset[int] = ALLOWED_GAPS, + project_label: str = DEFAULT_PROJECT_LABEL, +) -> list[RfcRecord]: + """Discover, validate, and return RFCs sorted by numeric id.""" + + root = Path(rfc_dir) + project_label = _normalise_project_label(project_label) + records = [ + parse_rfc(path, root, project_label=project_label) + for path in discover_rfc_files(root) + ] + records.sort(key=lambda record: int(record.id)) + + seen: dict[str, str] = {} + for record in records: + if record.id in seen: + raise CatalogError( + f"duplicate RFC id {record.id}: {seen[record.id]} and {record.source_path}" + ) + seen[record.id] = record.source_path + _validate_sequence(records, allowed_gaps) + + discovered_ids = set(seen) + for record in records: + unknown_related = sorted(set(record.related_ids) - discovered_ids) + if unknown_related: + formatted = ", ".join(unknown_related) + raise CatalogError( + f"{record.source_path}: Related references unknown RFC id(s): {formatted}" + ) + + if tags is not None: + tag_mapping = load_tag_mapping(tags) + discovered = discovered_ids + assigned = set(tag_mapping) + missing = sorted(discovered - assigned) + unknown = sorted(assigned - discovered) + problems: list[str] = [] + if missing: + problems.append("missing RFCs: " + ", ".join(missing)) + if unknown: + problems.append("unknown RFCs: " + ", ".join(unknown)) + if problems: + details = "; ".join(problems) + raise CatalogError( + f"tag catalog must assign at least one tag to every discovered RFC ({details})" + ) + records = [replace(record, tags=tag_mapping[record.id]) for record in records] + + return records + + +def catalog_data(records: Sequence[RfcRecord]) -> list[dict[str, Any]]: + """Convert records to deterministic JSON-compatible catalog objects.""" + + return [record.to_dict() for record in sorted(records, key=lambda record: int(record.id))] + + +def _escape_table_cell(value: str) -> str: + return value.replace("|", "\\|").replace("\n", " ") + + +def render_index_block(records: Sequence[RfcRecord]) -> str: + """Render generated JSON data and an accessible Markdown-table fallback.""" + + ordered = sorted(records, key=lambda record: int(record.id)) + payload = json.dumps(catalog_data(ordered), ensure_ascii=False, indent=2, sort_keys=True) + # Source prose containing an HTML end tag must not be able to close the script. + payload = payload.replace("<", "\\u003c").replace(">", "\\u003e").replace("&", "\\u0026") + with_tags = any(record.tags for record in ordered) + + lines = [ + BEGIN_MARKER, + '<script type="application/json" data-rfc-catalog>', + payload, + "</script>", + "", + '<div class="pp-rfc-fallback" data-rfc-fallback markdown="1">', + "", + ] + if with_tags: + lines.extend(("| RFC | Status | Tags | Title |", "| ---: | --- | --- | --- |")) + else: + lines.extend(("| RFC | Status | Title |", "| ---: | --- | --- |")) + + for record in ordered: + rfc_link = f"[{record.id}]({record.source_path})" + status = _escape_table_cell(record.status) + title = _escape_table_cell(record.title) + if with_tags: + tag_labels = _escape_table_cell(", ".join(tag.label for tag in record.tags)) + lines.append(f"| {rfc_link} | {status} | {tag_labels} | {title} |") + else: + lines.append(f"| {rfc_link} | {status} | {title} |") + + lines.extend(("", "</div>", END_MARKER)) + return "\n".join(lines) + + +def replace_generated_block(readme_text: str, generated_block: str) -> str: + """Replace the single marked generated region in an RFC README.""" + + begin_count = readme_text.count(BEGIN_MARKER) + end_count = readme_text.count(END_MARKER) + if begin_count != 1 or end_count != 1: + raise CatalogError( + "README must contain exactly one generated RFC catalog marker pair: " + f"{BEGIN_MARKER} ... {END_MARKER}" + ) + begin = readme_text.index(BEGIN_MARKER) + try: + end = readme_text.index(END_MARKER, begin) + len(END_MARKER) + except ValueError as error: + raise CatalogError("README RFC catalog end marker must follow its begin marker") from error + return readme_text[:begin] + generated_block + readme_text[end:] + + +def _parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(description="Validate and generate the RFC catalog") + action = parser.add_mutually_exclusive_group(required=True) + action.add_argument("--write", action="store_true", help="update the marked README block") + action.add_argument("--check", action="store_true", help="fail if the README block is stale") + parser.add_argument("--rfc-dir", type=Path, help="RFC directory (defaults to docs/rfcs)") + parser.add_argument("--readme", type=Path, help="README containing generated markers") + parser.add_argument( + "--tags", + type=Path, + help="JSON tag catalog (defaults to catalog.json beside the RFC README when present)", + ) + parser.add_argument( + "--project-label", + default=DEFAULT_PROJECT_LABEL, + help=f"project name used in RFC H1 headings (default: {DEFAULT_PROJECT_LABEL})", + ) + gaps = parser.add_mutually_exclusive_group() + gaps.add_argument( + "--allow-gap", + "--allowed-gap", + dest="allowed_gaps", + action="append", + type=_rfc_number, + metavar="NNN", + help="allowed missing RFC number; repeat for multiple gaps", + ) + gaps.add_argument( + "--no-allowed-gaps", + action="store_true", + help="require an unbroken RFC number sequence", + ) + return parser + + +def _rfc_number(value: str) -> int: + try: + number = int(value) + except ValueError as error: + raise argparse.ArgumentTypeError("RFC number must be an integer from 000 to 999") from error + if number < 0 or number > 999: + raise argparse.ArgumentTypeError("RFC number must be an integer from 000 to 999") + return number + + +def _write_remediation( + args: argparse.Namespace, + *, + rfc_dir: Path, + readme: Path, + tags: Path | None, + allowed_gaps: frozenset[int], +) -> str: + uses_repository_defaults = ( + args.rfc_dir is None + and args.readme is None + and args.tags is None + and args.project_label == DEFAULT_PROJECT_LABEL + and args.allowed_gaps is None + and not args.no_allowed_gaps + ) + if uses_repository_defaults: + return "make rfc-index" + + command = [ + "python3", + "utils/rfc_catalog.py", + "--rfc-dir", + str(rfc_dir), + "--readme", + str(readme), + ] + if tags is not None: + command.extend(("--tags", str(tags))) + if args.project_label != DEFAULT_PROJECT_LABEL: + command.extend(("--project-label", args.project_label)) + if allowed_gaps != ALLOWED_GAPS: + if allowed_gaps: + for number in sorted(allowed_gaps): + command.extend(("--allow-gap", f"{number:03d}")) + else: + command.append("--no-allowed-gaps") + command.append("--write") + return shlex.join(command) + + +def main(argv: Sequence[str] | None = None) -> int: + """Run the catalog command-line interface.""" + + args = _parser().parse_args(argv) + repository_root = Path(__file__).resolve().parents[1] + rfc_dir = args.rfc_dir or repository_root / "docs" / "rfcs" + readme = args.readme or rfc_dir / "README.md" + tags = args.tags + default_tags = rfc_dir / "catalog.json" + if tags is None and default_tags.is_file(): + tags = default_tags + if args.no_allowed_gaps: + allowed_gaps = frozenset() + elif args.allowed_gaps is not None: + allowed_gaps = frozenset(args.allowed_gaps) + else: + allowed_gaps = ALLOWED_GAPS + try: + records = build_catalog( + rfc_dir, + tags=tags, + allowed_gaps=allowed_gaps, + project_label=args.project_label, + ) + block = render_index_block(records) + current = readme.read_text(encoding="utf-8") + expected = replace_generated_block(current, block) + except (CatalogError, OSError) as error: + print(f"rfc catalog: error: {error}", file=sys.stderr) + return 2 + + if args.check: + if current != expected: + remediation = _write_remediation( + args, + rfc_dir=rfc_dir, + readme=readme, + tags=tags, + allowed_gaps=allowed_gaps, + ) + print( + f"rfc catalog: {readme} is out of date; run `{remediation}`", + file=sys.stderr, + ) + return 1 + return 0 + + if current != expected: + readme.write_text(expected, encoding="utf-8") + print(f"rfc catalog: updated {readme}") + else: + print(f"rfc catalog: {readme} is already up to date") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())