diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 46c3039..4dfde4e 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -8,7 +8,6 @@ on: description: "GitHub-verified signed release tag" type: string required: true - default: v0.0.2 confirmation: description: "Enter STAGE-, for example STAGE-v0.0.2" type: string @@ -93,7 +92,7 @@ jobs: assert.equal(pkg.homepage, "https://docs.0xbuff.com/docs/throughline"); assert.equal(pkg.repository?.url, "git+https://github.com/0xbuffdev/throughline.git"); assert.deepEqual(Object.keys(pkg.exports).sort(), - [".", "./react", "./examples", "./styles/throughline.css"].sort()); + [".", "./remark", "./react", "./examples", "./styles/throughline.css"].sort()); NODE - name: Install frozen dependencies @@ -102,6 +101,10 @@ jobs: run: pnpm audit:source - name: Typecheck run: pnpm typecheck + - name: Typecheck and bundle local playground + run: | + pnpm preview:typecheck + pnpm preview:build - name: Compiler tests run: pnpm test:run - name: Install Chromium @@ -117,6 +120,11 @@ jobs: - name: Verify packed Vite/React browser hydration run: pnpm audit:browser + - name: Verify framework integration consumers + run: pnpm audit:frameworks + - name: Verify Markdown host consumers + run: pnpm audit:markdown-hosts + - name: Pack reviewed tarball and checksum shell: bash run: | diff --git a/AGENTS.md b/AGENTS.md index 52ae57e..35b1c4b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -18,33 +18,10 @@ documentation before changing observable behavior. The source and ## Development and verification -Use Node.js 24+, pnpm **11.2.2**, and the committed `pnpm-lock.yaml`. -This is a **single package**, not a workspace. Do not introduce workspace -filters or application-specific build steps. - -```sh -pnpm install --frozen-lockfile -pnpm audit:source -pnpm typecheck -pnpm test:run -pnpm exec playwright install chromium -pnpm test:browser:run -pnpm build -pnpm audit:licenses -pnpm audit:pack -pnpm audit:browser -pnpm audit:frameworks -pnpm audit:markdown-hosts -``` - -Run the checks relevant to each change and the full sequence before -submitting a release-related PR. `audit:licenses` requires a build; -`audit:pack` and `audit:browser` exercise the packed package in -independent Node, TypeScript, React SSR, and Vite/browser consumers. -The framework and Markdown-host audits install the freshly packed package in disposable projects using committed npm lockfiles and `npm ci`. Registry access is required. Refresh intentional host-version updates with `node scripts/fixtures/update-integration-locks.mjs` and review the four generated lockfiles. -Some consumer checks install public dependencies. Report any skipped or -failed checks accurately; do not suppress failures with focused or -skipped tests. +Use Node.js 24+, pnpm 11.2.2 and the committed lockfile in this +single-package repository. Run the relevant tests for each change and +follow [verification and release procedures](docs/releasing.md) for a +full release candidate. Never misrepresent skipped or failed checks. ## Repository map @@ -63,9 +40,9 @@ skipped tests. - `src/examples.ts`: public examples and acceptance fixtures. - `src/**/__tests__/`, `scripts/`: regression, browser, packaging, source-boundary, and license checks. -- `docs/`: grammar, authoring, domain, and architecture documentation. +- `docs/`: grammar, authoring, integration and architecture documentation. - `.github/workflows/ci.yml`: read-only quality checks. -- `.github/workflows/publish.yml`, `RELEASING.md`: manual npm +- `.github/workflows/publish.yml`, `docs/releasing.md`: manual npm staging, provenance, and maintainer approval procedures. ## Public API and compatibility @@ -111,9 +88,9 @@ Preserve deterministic compilation and stable seeking through frames. Document purpose, compatibility impact, implementation, and checks actually run. Keep `README.md`, relevant `docs/`, tests, and `CHANGELOG.md` in sync with user-visible changes. -- This project has one maintainer. Final review, required signing, - merges, Git tags, and npm release approvals are maintainer actions; - never invent other reviewers or claim those actions happened. +- Final review, any required signing, merges, Git tags and npm release + approvals require maintainer authorization. Never invent approvals or + claim unperformed actions are complete. - Do not publish, stage, approve, move release tags, change repository visibility, or rewrite Git history without explicit authorization. - Publication uses an immutable, GitHub-verified signed tag and the @@ -121,11 +98,6 @@ Preserve deterministic compilation and stable seeking through frames. `npm-publish` staging job receives `id-token: write`. No npm token or OIDC write permission belongs in quality CI. npm staging is not a public release; final npm 2FA approval is separate. -- The repository's public Git history includes a historical reference - to private planning material. Treat this as an unresolved exposure; - inspect historical refs and credentials when relevant. Removing a - reference from the current tree does not erase already-public history. - Review rights and third-party attribution before distributing changes. - -See `CONTRIBUTING.md`, `SECURITY.md`, and `RELEASING.md` for the -corresponding contributor, vulnerability-reporting, and release details. + +See `CONTRIBUTING.md`, `SECURITY.md` and `docs/releasing.md` for +contribution, security reporting and release procedures. diff --git a/CHANGELOG.md b/CHANGELOG.md index 31f8369..bb605f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,196 +1,63 @@ # Changelog -Notable changes to Throughline are recorded here. Package versions follow -Semantic Versioning independently of the version-1 trace and program schemas. +All notable changes to Throughline are recorded here. Package versions follow +[Semantic Versioning](https://semver.org/) independently of version-1 +program and trace schemas. Changes are grouped using +[Keep a Changelog](https://keepachangelog.com/). -## Unreleased +## [Unreleased] -### Fixed (cross-browser toolbar) - -- Keep the wide playback toolbar compact in WebKit without constraining native selector height; verify the compact bar still encloses every control across browser engines. - -### Fixed (narration review) - -- Coordinate browser-global speech synthesis between multiple Throughline players; newer narration takes over while displaced players pause and stop owning cancellation. -- Pause with an accessible error on unexpected speech interruption instead of stalling autoplay or advancing a failed utterance. -- Restart narration for repeated seeks to the same step and replace stale speech when authored guidance changes. -- Keep the dual-audio 360px breakpoint while restoring a generic three-row fallback at <=300px content widths, covering players with no audio, only narration, only step tones, or both. Verify embedded layouts with actual browser element bounds. - -### Added (opt-in caption narration) - -- Offer muted-by-default browser caption narration separately from step tones, using speech-synchronized playback without new runtime dependencies. -- Pause/resume speaking with playback; cancel on seek, scenario change and unmount; preserve the original timer when narration is off. - - -### Changed (development preview sound) - -- Expose the existing opt-in Web Audio speaker control in every local playground example and scenario comparison, initially muted. No default audio in the published React player and no new audio dependencies. - - -### Changed (state hierarchy and motion) - -- Give the present its own color. `--thl-active` (new) marks the current send, its label and the Play button; `--thl-trail` becomes the same hue washed out for history; `--thl-change` becomes the single warm color for changed values. Previously focus and change were near-identical neutrals (about 0.02-0.03 apart in OKLab) and history was the only colored mark. -- Keep trail color on strokes and arrowheads only; visited and revealed edge labels stay `--thl-muted` so 10px text keeps at least 4.5:1 contrast. -- Reserve dashes for structure. Selected nodes and keyboard focus are an outer ring (`.thl-ring`) instead of a dashed outline and a 3px stroke that matched authored focus; the ring is the first-rect-safe sibling drawn after the node body. -- Animate only a forward single step. Seeking the timeline, rewinding and replacing the trace settle on the final frame instead of replaying the send. The decision is latched per step in the player. -- Sequence motion within a step: the arrow draws, its arrowhead and label fade in, then a changed value flashes (waiting for the arrow when the step has a send). Durations derive from `--thl-step-ms`, which the player sets from the playback speed. - -### Fixed (reduced motion) - -- Reduced motion did not disable the send animation: the opt-out selector `.thl-transferred .thl-edge` was less specific than the rule that set the animation, so the animation still ran under `prefers-reduced-motion: reduce`. Animations are now declared only under `prefers-reduced-motion: no-preference`, and a test fails on any unguarded animation rule. - -### Changed (responsive player controls) - -- Add a small consistent gap between the visualization and its optional title and bottom controls. - -- Place chapter and diagram navigation beside transport controls in one compact adaptive footer, with 14px visible icons, slimmer native inputs and 40px mobile hit targets. -- Move variable-length captions below the footer as accessible step disclosures, retaining a one-line preview and a bounded independently scrollable detailed explanation. -- Keep the diagram and playback controls stationary as authored notes and evidence change between steps. - - -### Improved (workflow playback visual review) - -- Retain authored node and connector activity across later steps; seeking - backwards reconstructs the earlier trail without leaking future state. -- Animate each current send directly on its existing arrow path instead of - drawing a second line; repeated sends restart the same-path animation. -- Keep shown-but-unsent links visibly distinct, preserve partial - collapsed-group provenance and announce cumulative progress to screen readers. - Neither transfers nor tinted endpoints imply acknowledgment or persistence. - - -### Changed (pre-release visual review) - -- Add a source-backed Vite preview for browser inspection before publishing or deploying. -- Compact group/node/cell geometry, reduce panel whitespace, set 2px corners and 1.5rem player padding, and replace boxed icon actions with accessible borderless hit targets. -- Move chapter, viewport and hierarchy navigation below the caption into a common footer with playback controls; keep the authored narrative in the caption. -- Size the SVG stage from stable layout content rather than a 52vh placeholder; cap only dense content and retain mobile fit/zoom semantics. -- Place visible relation labels away from node values and competing labels; when space is insufficient, keep the full relation in its SVG title rather than drawing over nodes. - - -### Hardened (TL-I16 candidate) - -- Reject unsupported trace versions and missing frame arrays at the React reader boundary; this is not full validation of untrusted serialized state. -- Reuse a shared node-box set during dense edge routing and record cyclic-layout diagnostic benchmarks. -- Exercise hostile authored text in Chromium and rejected reader versions in isolated packed React SSR. -- Record source verification and outstanding manual, documentation-app and release gates. - -### Added (TL-I15) - -- React-only guided chapters and authored causal explanations with explicit, - validated compiled-event provenance and accessible text parity. -- Independent scenario selector and deterministic checkout success, terminal - failure and timeout/retry/recovery fixtures. -- Unit and Chromium regressions for evidence, chapter seeking, isolated - scenario switching and safe/narrow-screen guidance rendering. - -### Fixed (TL-I15 review follow-up) - -- Provide a dedicated Restart chapter button to seek to the current chapter\'s - first frame even when its option is already selected. -- Reconcile removed scenario IDs to the fallback instead of silently selecting - a previously removed option on reinsertion; test empty/populated transitions. - -### Compatibility - -- Only additive optional React props/exports, no version-1 schema, grammar - or legacy player-default changes. - - -### Fixed (TL-I14 review follow-up) - -- Give undeclared lanes separate internal identities even when their names equal an explicit group ID; resolve geometry by lane name, not an ambiguous ID fallback. -- Highlight a collapsed boundary when a real constituent relation sends, without animating the entire aggregate or implying a destination write. -- Show and announce authored focus and changed state inside collapsed summaries; compile the actual published hierarchy fence as a regression fixture. - -### Added (TL-I14, compatibility proposal pending) - -- Optional hierarchical group IDs, explicit parent references and validated `group "Name" id foo [under parent] as row|column|tree` syntax; legacy flat groups are unchanged. -- Read-only collapsed summaries, exact provenance of aggregated cross-boundary links, nested deterministic layouts, drill-down/breadcrumb controls and regression coverage. - -### Compatibility - -- Program and trace schemas remain version 1; old compilers reject the new opt-in syntax, and old players may show hierarchical traces as flat groups. -- Merge of the source changes is blocked on approval of the separate group-model compatibility ADR in `context`. - - - -### Fixed - -- Correct two-finger pinch translation when zoom and midpoint movement occur - together; focus now centers edge-positioned entities accurately. -- Bound the physical SVG height for dense, narrow diagrams while preserving - fit-to-view aspect ratio. Let page scrolling continue at zoom limits and - preserve native Ctrl/Meta+wheel browser zoom. -- Add unit and Chromium regressions for each TL-I13 review finding. +## [0.1.0] ### Added -- TL-I13 guided viewport: bounded pan, zoom and fit-to-view with wheel, keyboard - and touch gestures, selected-entity inspection and explicit follow-active mode. -- Manual camera precedence, trace replacement reset and corresponding unit and - browser regressions without changing the version-1 trace schema. +- React-independent scene projection with deterministic entity, link and + transfer provenance, including focus filtering and stable seeking. +- Optional nested groups, validated parent references, collapsible summaries, + drill-down controls and breadcrumbs; existing flat groups remain valid. +- Bounded pan, zoom, fit-to-view and follow-active navigation with mouse, + keyboard and touch controls, plus selected-entity inspection. +- Authored guided chapters, evidence-backed explanations and independent + scenario comparisons, including deterministic distributed-checkout examples. +- Separate opt-in browser caption narration synchronized with playback; + step-tone sonification remains optional and off by default. +- Build-time Remark validation via `@0xbuff/throughline/remark` and opt-in + literal-source MDX conversion with the React `ThroughlineEmbed` component. +- Native fullscreen support and a source-backed Vite playground for visually + inspecting examples and responsive playback during development. +- Tested integration patterns for Fumadocs Unified, Next.js App Router, Astro, + react-markdown, mdx-bundler and markdown-to-jsx. +- Expanded keyed React reconciliation and distributed checkout reference traces. ### Changed -- Introduce a React-independent, view-only scene projection with stable node, - edge and transfer provenance, selection/focus filtering, and deterministic - fallback layout clipping. Hidden/stale focus targets retain overview, - projection-owned data is detached and frozen, and React preserves authored - group layout preferences. Existing playback and trace schemas are unchanged. - -### Tested - -- Add packed package integration fixtures for Fumadocs Unified, Next.js App Router, Astro React islands, react-markdown, mdx-bundler and markdown-to-jsx, with frozen npm consumer locks, browser hydration/interaction checks, unchanged-document regressions, and a release-gated integration guide. - -### Added - -- Add opt-in Remark MDX JSX output with literal source attributes and validated - component identifiers, plus a memoized `ThroughlineEmbed` React bridge. -- Verify MDX escaping, atomic multi-fence validation, registered-component - rendering, stable-source playback and independent packed consumers. +- Redesigned the optional React 19 player with responsive step-based controls, + accessible detail disclosures, a state inspector and light/dark/auto themes. +- Updated player CSS tokens to distinguish current activity, accumulated + history and changed values; selection uses a separate focus ring. +- Playback now animates only a single forward step; seeking, rewinding and + trace replacement settle directly on the target frame. +- Persistent activity trails and collapsing groups retain the underlying + authored relation and event provenance without implying durable writes. ### Fixed -- Use verified source coordinates for Remark body diagnostics in nested Markdown; - do not falsely attribute unpositioned semantic errors to the fence header. - -### Added - -- Added dependency-free `@0xbuff/throughline/remark` validation subpath for - parser-owned `thl` fences, source-positioned build failures and unchanged - Markdown ASTs; MDX rendering remains a separate integration. - -### Fixed - -- Announce native fullscreen request failures without interrupting in-page playback; - expose player keyboard shortcuts and descriptive focus instructions. -- Make ended sonification voices safe to clean up after step changes or unmount, - and stop/restart the cue when replacing a trace with an equal frame count. -- Add keyboard/slider, trace replacement during playback, reduced-motion CSS, - fullscreen refusal and sound lifecycle regression coverage. - -### Changed - -- Redesigned the React player with an optional unbranded title, centered - visualization, borderless caption/count, and bottom playback controls. -- Introduced neutral OKLCH light/dark/auto CSS tokens and default `0.325rem` - radius while preserving existing trace/sonification contracts. - -### Added - -- Native fullscreen maximize/minimize toggle with safe unavailable-state - handling, browser state synchronization, and responsive controls. -- Browser regression tests for shell, theme overrides, fullscreen and sound. - -### Added - -- Expanded the keyed reconciliation reference to include its commit beat; - added a distributed checkout timeout, idempotent retry and recovery trace. -- Added deterministic seeking, transfer correctness, version/value-boundary, - and browser rendering regressions for sophisticated reference examples. -- Documented teaching questions, current player limitations and the - unsupported serialized trace-version reader gap. +- Prevented a compact playback-toolbar height discrepancy in WebKit while + preserving visible, usable controls across engines and narrow widths. +- Corrected reduced-motion behavior so send animations are disabled when + the operating system requests reduced motion. +- Improved narration ownership across multiple players, speech interruption + handling, repeated seeking and stale-utterance cancellation. +- Restored responsive control layouts at 300px and smaller widths across + combinations of narration and sonification options. +- Corrected two-finger pinch translation, edge-positioned camera focus and + scroll behavior at zoom limits. +- Fixed collapsed-group summaries, name collisions between lanes and explicit + group IDs, and authored focus/changed-state announcements. +- Fixed chapter restart, removed-scenario fallback, fullscreen failure + announcements, and audio cleanup during trace replacement or unmount. +- Improved Remark diagnostics for nested Markdown and validated input + boundaries for unsupported serialized trace versions. ## [0.0.2] — 2026-11-08 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8f93588..9d9a4cd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,9 +1,5 @@ # Contributing to Throughline -Throughline is maintained by one project maintainer. Bug reports, -documentation corrections, test improvements and narrowly scoped code -contributions are welcome. Opening a PR does not guarantee acceptance. - ## Before you start - Check the [README](README.md), [authoring docs](docs/grammar.md), @@ -23,6 +19,8 @@ Prerequisites: Node.js 24+, pnpm 11.2.2 and a Chromium-capable system. pnpm install --frozen-lockfile pnpm audit:source pnpm typecheck +pnpm preview:typecheck +pnpm preview:build pnpm test:run pnpm exec playwright install chromium pnpm test:browser:run @@ -30,6 +28,8 @@ pnpm build pnpm audit:licenses pnpm audit:pack pnpm audit:browser +pnpm audit:frameworks +pnpm audit:markdown-hosts ~~~ This repository is a standalone package. The root compiler must stay @@ -49,7 +49,7 @@ compiler-to-player boundary. 5. Open the PR with a concise purpose, implementation notes, potential compatibility risks, test results and any remaining work. -The maintainer performs final review, any required signing and merge. +Maintainers perform final review, any required signing and merge. Do not publish npm packages, create release tags or change repository visibility as part of a contribution without explicit authorization. @@ -62,3 +62,23 @@ issue with exploitation details. By submitting a contribution, you agree it may be distributed under the repository's [Apache-2.0 license](LICENSE); do not submit code or content that you lack rights to contribute. + +## Support + +For usage questions, consult the [README](README.md), +[grammar reference](docs/grammar.md), +[domain catalog](docs/catalog.md), and +[architecture notes](docs/architecture.md). + +For a reproducible bug, use the +[bug report template](https://github.com/0xbuffdev/throughline/issues/new/choose) +and include the version, +minimal authored `thl` block, expected/actual behavior and relevant +runtime (Node, browser, React). Avoid disclosing secrets or sensitive +content. + +For an API or feature idea, use the feature request template and +explain the underlying problem and compatibility impact. Support and review are provided as available, without a guaranteed response time. + +**Security vulnerabilities:** use [SECURITY.md](SECURITY.md) rather than +publishing exploitation details in an issue. diff --git a/README.md b/README.md index efb47c5..374cb68 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Throughline +[![CI](https://github.com/0xbuffdev/throughline/actions/workflows/ci.yml/badge.svg)](https://github.com/0xbuffdev/throughline/actions/workflows/ci.yml) [![npm version](https://img.shields.io/npm/v/%400xbuff%2Fthroughline)](https://www.npmjs.com/package/@0xbuff/throughline) [![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue)](LICENSE) [![Node.js](https://img.shields.io/badge/node-%3E%3D24-blue)](package.json) + **Readable state over time, authored in Markdown.** Throughline compiles small, explanatory state traces into deterministic, @@ -29,52 +31,6 @@ or application-specific runtime. - **Complex reference traces:** inspect keyed React reconciliation and a distributed checkout with a lost acknowledgement, retry and recovery. -## Preview the current React player locally - -The development-only Vite playground loads **this checkout's source files**, -not the last published npm package. It lets maintainers inspect real behavior -before approving a release. The app is not included in the npm archive and -does not require deployment or an account. - -Use Node.js 24+ and pnpm 11.2.2: - -~~~sh -pnpm install --frozen-lockfile -pnpm dev -~~~ - -Open **http://localhost:5173** in a browser. Choose Reconciliation, -Shared references, Matrix, Distributed checkout, or **Compare checkout -outcomes**; switch the player between light/dark/system, then between full -width and a 375px mobile viewport. The checkout comparison starts with the -retry/recovery scenario and offers successful and terminal-failure outcomes. -Expand **View authored trace source** to check the exact events on display. -The preview provides the player's opt-in sound control for **every example -and scenario comparison**. Select the speaker icon labeled **Sonify trace** -to unmute, then press Play or Step forward: each step produces a short -Web Audio oscillator tone, not speech. Sound starts **muted** until clicked; -you can mute it again at any time. Some browsers block audio until a user -gesture, and audio availability and output volume depend on the browser -and device. This opt-in is scoped to the development preview: package -consumers still choose whether to supply `sonification` at all. - -For a remote development environment (e.g. GitHub Codespaces), start -\`pnpm dev --host 0.0.0.0\` and use the environment's forwarded port 5173 -rather than treating localhost on your own computer as the remote server. -Keep forwarded access private while reviewing traces. If running from the -unmerged preview branch, check out \`feat/local-player-preview\` first. - -~~~sh -pnpm preview:typecheck -pnpm preview:build -~~~ - -These checks typecheck and bundle the *preview*, independently of the library -build and normal compiler/browser tests. Nothing here creates a Git tag, -publishes to npm, or deploys documentation. A browser visual/manual -accessibility review is still required; build and automated browser tests -cannot attest to visual correctness. - ## Installation Install the package from npm: @@ -137,405 +93,24 @@ export function TraceDemo() { } ~~~ -Import the optional stylesheet once in the host application. The player -accepts an optional `title` (omitted when absent), and `light`, `dark` or -`auto` themes. The borderless, pale-light/charcoal-dark shell centers the -visualization above a caption with an exact `Step N of M` indicator and a -bottom control bar. Playback is **step-based**: there is no invented media -time. The optional state inspector and authored causal explanations remain in the caption; chapter navigation and all player navigation/transport controls sit below it. - -The shell, group boxes, nodes, cells and form inputs use a compact default -`--thl-radius: 0.125rem` (2px at a 16px root font size), with 0.5rem inner -spacing and 1.5rem player padding. Node strokes default to -`--thl-node-border-width: 1.25px`. Icon-only buttons have no visible boxes, -but retain at least 2.5rem interactive targets and visible keyboard focus. - Host overrides are scoped to the -player; for example, with `className="my-trace"` and a stylesheet loaded -after the package CSS: - -~~~css -.thl.my-trace { - --thl-radius: 0.5rem; - --thl-bg: light-dark(oklch(0.98 0 0), oklch(0.14 0 0)); -} -~~~ - -The `--thl-bg`, `--thl-panel`, `--thl-surface`, `--thl-text`, -`--thl-muted`, `--thl-line`, `--thl-focus`, `--thl-change`, `--thl-change-bg`, -`--thl-active`, `--thl-trail`, `--thl-trail-bg` and control tokens belong to the player, not the host's `:root` or `.dark`. - -The shell and its text are neutral; only state-carrying marks take a hue. -`--thl-active` colors what happens in the current step (the sending arrow, -its label and the Play button), `--thl-trail` is the same hue family washed -out for history, and `--thl-change` is the only warm color, reserved for -changed values. Dashed outlines mean structure (collapsed, revealed or -aggregate), never selection: a selected node and the keyboard-focused node -are marked by an outer ring. - -Motion timing follows the step interval. The player sets `--thl-step-ms` on -its root (1500ms at 1x, 750ms at 2x) and the draw and flash durations derive -from it. A host can override `--thl-draw-ms` and `--thl-flash-ms` on `.thl` -like any other token. - -The bottom-right **Maximize player** button enters native browser fullscreen -and changes to **Minimize player** while active, including when fullscreen -changes via Esc. It is disabled where the API is unavailable or blocked. -Sound controls appear only when the host supplies `sonification`. The -inspector remains optional, the trace reference should remain stable while -stepping, and complete custom stylesheets must retain accessibility. - -### Playback accessibility - -Focus the **Visualization player** to use Left/Right arrows for stepping, -Home/End to jump to the first/last step, and Space to play or pause. Native -buttons, the labeled timeline slider and the playback-speed selector keep -their own keyboard interactions; shortcuts do not steal keystrokes from -focused controls. Each frame has an accessible text summary. **Inspect -state** is a native disclosure exposing complete visible values. - -Playback starts paused at the first frame. Changing to a different trace -object resets the step, playback and speed; passing a stable trace object -during ordinary renders avoids unnecessary resets. The timeline describes -**steps**, not elapsed media time. At narrow widths the controls rearrange -without a separate horizontal scroll area. Motion is declared only under -`prefers-reduced-motion: no-preference` and plays only when a step is reached -by one forward step (play, or Step forward): the sending arrow draws, its -arrowhead lands, then a changed value flashes. Seeking the timeline, -rewinding or replacing the trace shows the settled frame immediately. - -Sound is off by default, and the host must provide a `sonification` option -before the user can enable it. A host can explicitly start enabled using -`sonification={{ enabled: true }}`. A new trace object restarts its initial -sound cue and stops the previous one, even when both traces have the same -number of frames; rerendering with the same trace does not replay audio. -A refused fullscreen request is -announced to assistive technology; playback remains available in-page. -Browser support and host policies determine whether fullscreen can start. - -## Trace reader compatibility - -`ThroughlinePlayer` expects a compiler-produced version-1 `Trace` and rejects -unsupported versions or a missing `frames` array at runtime. This is **not** -a complete validator for arbitrary serialized snapshots. Treat external JSON -as untrusted and validate it at the host boundary; prefer compiling bounded -source with `compile`. - -The hardening test and release-evidence scope are documented in -[release-hardening](docs/release-hardening.md). - -## Responsive controls and step detail - -The optional title has 0.75rem of space before the visualization, and the -footer adds a small 0.25rem offset after the stage's existing gap. These -separations keep the graph distinct from both its heading and controls. - - -The React player keeps the navigation and transport immediately beneath -the diagram in one aligned toolbar on wide layouts. Container queries -reflow the toolbar into two rows at intermediate widths and compact -touch-sized controls at mobile widths. Desktop icons render at 14px, -while the icon-button hit area stays 34px, increasing to 40px on -mobile. Native selects use 0.125rem corners and 0.5rem padding. - -The authored caption now follows the toolbar as a native **Details** -disclosure. Its closed header shows the step title, a single-line -preview of the note, and the step counter. Open Details to read the -complete note, transfers, optional causal evidence, and the state -inspector. The detailed content has its own bounded scroll area so -long author-provided explanations cannot push the controls away -from the diagram. The screen-reader step announcement remains active -regardless of whether the disclosure is expanded. Details remain open -between steps until closed, without changing playback state. - -## Opt-in caption narration - -The optional React-only `narration` prop exposes a **Narrate captions** -button next to the independent `sonification` sound-effects toggle. -The button is **off initially**, even when a host offers it. Its first -activation speaks the current authored step from a user gesture using -the browser's Speech Synthesis API. It reads the authored step label, -notes, matching chapter and causal explanation, and transfer summaries. -It does not invent responses, state changes or causal explanations. -Very long utterances are shortened with an explicit spoken notice; the -full authored text remains available in Details. - -With narration enabled, **Play advances only after speech finishes**. -Pause pauses the current voice; Play resumes it. Manual seeking or -stepping cancels the previous voice and narrates the selected step. -Switching scenarios cancels the old voice and starts muted. Disabling -narration returns to timed playback and leaves step tones independent. -Playback speed applies to the next utterance, not the current one. -A manual seek to the **same** step (including Restart chapter) restarts its -narration. If an author updates guidance while the current frame is shown, -the old utterance is invalidated and the corrected explanation is spoken. - -Speech voices, available hardware, and user-activation policies depend -on the browser and OS. The button is disabled where speech synthesis is -unavailable; speech errors pause synchronized playback and are announced. -No text is uploaded by Throughline, but the browser may use a network -speech service. The core stays React- and browser-independent. - -**Multiple embedded players:** Browser speech synthesis has one global queue. -Throughline coordinates narration across its player instances: when B begins -speaking, B takes over and A is paused, unmutes no further audio, and reports -that another player is speaking. A's later seek/unmount does not interrupt B. -A may explicitly re-enable narration to take over again. External scripts -calling the browser speech API are not under Throughline's control; an -unexpected `interrupted` or `canceled` utterance is treated as an error: -narration-driven playback pauses and shows a recoverable message. This -behavior does not imply exclusive ownership of third-party speech. - -## Persistent workflow progress - -The React player retains a cumulative trail through the **selected** step. -Focused or explicitly written nodes and endpoints of authored sends stay -highlighted, along with arrows that have actually transferred an authored -message. The **current arrow itself** draws when a send occurs; there is -no second path painted on top of it. The newest focus and data changes -still have stronger emphasis than previous activity. - -Only an explicit `send` highlights a message path. For instance, setting -a server status **does not** highlight the response link until a separate -authored response send occurs. A `show link` marks a structural connection -with a dashed style, not a delivery. Highlighting an endpoint means it -participated in the authored sequence; it does not imply that a request -was accepted or that the destination persisted data. - -The trail is derived from the compiled trace at each seek position. -Rewinding removes future progress; replaying restores it. Hidden nodes and -links stay hidden; collapsed groups retain original relation provenance -and distinguish partial aggregates. Switching scenarios or replacing -the trace resets the progression. Reduced-motion preferences retain -a fully highlighted arrow without drawing animation or value flashes. Screen readers -get an authored-progress count with the current step. - -## Guided viewport navigation - -The React player opens with a fit-to-view overview. The compact bottom toolbar -provides Zoom in, Zoom out (1× to 4×), Fit to view, a native Select entity menu, -and an explicit Follow active entity toggle. Selecting an entity in the diagram -or menu centers it and exposes its value as text; this never changes the trace. - -- Mouse: drag to pan; wheel over the SVG to zoom about the pointer. Wheel - scrolling remains native at the zoom bounds and Ctrl/Meta+wheel is reserved - for browser-level zoom. -- Touch: one-finger drag to pan, two-finger pinch to zoom, or use the toolbar. -- Keyboard: focus the diagram, use arrow keys to pan, + and - to zoom, and 0 - to fit. The native entity menu provides keyboard and screen-reader selection. - Focus the overall Visualization player for playback shortcuts instead. - -Follow-active starts off. When enabled, it centers the first currently visible -authored active entity without synthesizing activity. Manual pan, zoom, fit or -selection always disables following until explicitly re-enabled. Seeking -and autoplay do not erase manual camera choices. Replacing the trace with a -different object returns the camera to overview, clears selection and disables -following alongside the existing playback reset. Resizing the host can change -layout geometry, but cannot mutate frames. Hidden entities remain unselectable. - -The viewport operates only on SVG viewBox presentation state, without a new -trace schema, group hierarchy or inferred events. Its physical SVG height -follows the stable trace-wide layout's aspect ratio so sparse scenes do not -reserve a tall blank viewport. Very dense scenes are capped at 960px; Fit -to view still preserves the complete graph's aspect ratio, and zoom, pan -and selection expose smaller mobile details. The height responds smoothly -to container changes except when reduced motion is requested. Navigation keeps the camera center within -the world bounds, which allows actual centering of edge nodes while retaining -some of the scene on screen. - -## Hierarchical groups and drill-down (TL-I14) - -Legacy `group "Name" as tree` and `node a "A" in "Name" = 0` remain -supported. For opt-in nested groups, give each participating group a stable ID, -refer to a parent by ID, and keep node membership in one immediate named lane: - -~~~thl flow -group "Worker" id worker under system as row -group "System" id system as column -group "Outside" id outside as row -node job "Job" in "Worker" = "ready" -node external "API" in "Outside" = "waiting" -link request job -> external "request" -step "Send" - send request = "hello" -~~~ - -The group toolbar can drill into a group, collapse it into a summary, expand it, -and return through the breadcrumb trail to overview. Collapsing never changes -authored `hide`/`show`, entity values, trace frames or event history. -The summary counts only authored-visible entities. Boundary links retain all -source relation IDs in accessible SVG tooltips and transfer annotations. -An active aggregate edge uses a **static highlight** for one or more actual -`send` events; it does not imply all constituent relations sent or that the -destination stored anything. A collapsed summary highlights authored focus -and changed state in that step, without exposing system-hidden entities. The state -inspector still shows real authored values. - -Group IDs and parent IDs are an additive opt-in version-1 trace extension, -with no new package import path. Old `thl` sources need no migration. Older -Throughline compilers cannot parse `id`/`under` grammar, and older players -receiving a new compiled trace may display it as flat lanes without collapse. -Only author hierarchical source when its consumer uses a compatible compiler. -Static layout uses the existing deterministic engine and does not add an -external graph-layout dependency. On replacing the `trace` object, collapse -and breadcrumb choices reset alongside selection, camera and playback. - -## Supported domains - -| Domain | What it explains | Authoring model | -| --- | --- | --- | -| `flow` | State, relationships and changes | Explicit nodes, links and steps | -| `bindings` | Primitives, references, mutation and rebinding | Small assignment/push language | -| `binary` | Ripple-carry binary addition | Two unsigned binary operands | -| `matrix` | Matrix multiplication | Two small compatible matrices | -| `callstack` | Recursive factorial calls and returns | A bounded factorial input | - -All five domains produce the same version-1 trace shape. Domains are -intentionally limited; Throughline is not a general-purpose JavaScript -interpreter or an automatic execution tracer. - -## Validate Markdown with Remark - -Import `remarkThroughline` from `@0xbuff/throughline/remark` and use it -in a Unified/Remark pipeline. The default `validate` mode checks each -`thl ` fenced code node, fails the build with source-positioned -VFile diagnostics for invalid content, and leaves all Markdown nodes -unchanged. Ordinary fences are unaffected. No React dependency is required. - -~~~ts -import { unified } from "unified"; -import remarkParse from "remark-parse"; -import remarkStringify from "remark-stringify"; -import { remarkThroughline } from "@0xbuff/throughline/remark"; - -const processor = unified() - .use(remarkParse) - .use(remarkThroughline) // or .use(remarkThroughline, { mode: "validate" }) - .use(remarkStringify); - -await processor.process({ path: "lesson.md", value: markdownSource }); -~~~ +The React player and stylesheet are optional. Read the [player and Markdown +guide](https://github.com/0xbuffdev/throughline/blob/main/docs/user-guide.md) for theming, accessible controls, narration, +hierarchy, scenarios and MDX integration. -The code fence header must contain exactly a supported domain, such as -\`\`\`thl flow\`\`\`; additional fence metadata is not supported. The host -parser supplies code-body lines and file positions. The validator locates -body tokens from the original Markdown, including list and blockquote prefixes. -When no trustworthy position exists, the error remains file-level rather -than pointing at an unrelated fence header. A Markdown document -may contain multiple traces, but publishers should also bound total document -size and number of fences. Validate mode never rewrites the AST, renders the -player or emits JSX. MDX mode is explicitly opt-in. +## Preview locally -### Render Throughline fences from MDX +The [Vite playground](https://github.com/0xbuffdev/throughline/tree/main/playground) renders the current checkout's source, not +necessarily the version currently available from npm. With Node.js 24+ and +pnpm 11.2.2: -A host using the Unified-based MDX compiler can enable the transformation -and register the React bridge in its MDX component map: - -~~~tsx -import { remarkThroughline } from "@0xbuff/throughline/remark"; -import { ThroughlineEmbed } from "@0xbuff/throughline/react"; -import "@0xbuff/throughline/styles/throughline.css"; - -// Pass to your MDX compiler's Remark plugin configuration: -const remarkPlugins = [[remarkThroughline, { mode: "mdx" }]]; - -// Pass to your MDX renderer's components map: -const mdxComponents = { ThroughlineEmbed }; -~~~ - -The plugin validates each `thl ` fence first, then replaces it with -an `mdxJsxFlowElement` whose `source` attribute is a **literal string**. -Quotes, braces, backticks and markup in a validated trace stay data: no -JavaScript expressions or untrusted HTML are generated by this transform. -All matching fences must validate before any are replaced. Other fences -and Markdown nodes are unchanged. - -`componentName` can override the default `ThroughlineEmbed` with one -capitalized React identifier, such as `CustomTrace`; the host must register -that exact name. If registration is missing, MDX rendering fails rather than -silently displaying an empty visualization. Plain Markdown pipelines should -use `validate`, not `mdx`, unless their output compiler supports MDX JSX. - -`ThroughlineEmbed` accepts `source` plus the existing player presentation -props (`title`, `theme`, `className`, `sonification`). It memoizes compilation -for unchanged source, but **still compiles at runtime** during SSR and in -clients; build-time validation does not serialize a precompiled trace. The -host owns MDX component registration, trusted-content policy and aggregate -document budgets. An MDX host can execute its *other* authored expressions; -this plugin does not make arbitrary MDX content safe to execute. - -## Guided explanations and scenario comparison (TL-I15) - -Guidance is **optional, host-authored presentation data**, not compiled trace -metadata. Chapters identify inclusive, zero-based frame ranges. Selecting a -chapter seeks to its first frame and pauses playback. The **Restart chapter** -button returns to the beginning of the current chapter (including while -playing). Causal explanations -show the author's **Trigger**, **Observed event**, **State change**, -**Consequence**, and references to real compiled events. Chronology alone -never establishes causation. - -Three checkout outcomes are separately authored and independently compiled: - -~~~tsx -import { compile } from "@0xbuff/throughline"; -import { CHECKOUT_GUIDANCE, CHECKOUT_SCENARIOS } from "@0xbuff/throughline/examples"; -import { ThroughlineScenarios } from "@0xbuff/throughline/react"; -import "@0xbuff/throughline/styles/throughline.css"; - -const scenarios = CHECKOUT_SCENARIOS.map((item) => ({ - id: item.id, - title: item.title, - description: item.description, - trace: compile(item.source), - guidance: item.id === "retry-recovery" ? CHECKOUT_GUIDANCE : undefined, -})); - -export function CheckoutComparison() { - return ; -} +~~~sh +pnpm install --frozen-lockfile +pnpm dev ~~~ -Each scenario has a unique nonempty ID, title and compiled trace. The optional -`initialScenarioId` selects the starting outcome. Switching scenarios -remounts its player, resetting step, playback, speed, camera, selection and -sound voice, without changing any snapshot. Ordinary rerenders of the same -option preserve state. Replacing a trace object uses the existing reset. -If the host removes the selected scenario, selection moves to the first -available outcome and stays there even if the removed option returns later; -an empty list clears selection and a subsequently populated list selects its -first option. `initialScenarioId` is only used on initial mount. -An empty scenario list shows a text empty state. A single player or -`ThroughlineEmbed` can also accept the `guidance` prop. - -`ThroughlineGuidance` accepts ordered, nonoverlapping `chapters` with -`{ id, title, start, end, summary? }`, and `causalLinks` with -`{ id, title, step, trigger, observedEvent, stateChange, consequence, evidence }`. -Each evidence item is `{ step, event }`: a zero-based frame index and an -event index within `trace.frames[step].events`. Evidence must not lie -after the link's display frame and must reference at least one actual -`set`, `show` or `hide` event. A `send` alone does not prove persistence. - -Invalid range, duplicate ID, missing event or unsupported evidence fails -explicitly. Limits: 100 chapters, 200 links, 20 event references per link, -4096 characters per text field; IDs start with an ASCII letter and contain -up to 64 ASCII letters, digits, underscores or hyphens. Evidence proves -provenance, **not the truth of causal prose**: authors must review it against -notes and snapshots. All descriptions render as text, not HTML. No native -branching graph, simulation, network or hidden telemetry is introduced. - -## Public package exports - -| Import path | Contents | -| --- | --- | -| `@0xbuff/throughline` | Core compiler, parser, types, validation, tracer | -| `@0xbuff/throughline/remark` | Build-time `remarkThroughline` validation and optional MDX output (no React dependency) | -| `@0xbuff/throughline/react` | Optional `ThroughlinePlayer`, `ThroughlineEmbed`, `ThroughlineScenarios` and React types | -| `@0xbuff/throughline/examples` | Copyable authored fixtures | -| `@0xbuff/throughline/styles/throughline.css` | Opt-in, namespaced CSS | - -Only these five entry points are supported; importing from package -`src/` or `dist/` paths is not part of the API. The compiler is -React-free, network-free and usable in offline tooling. +Open [http://localhost:5173](http://localhost:5173) to step through examples, +compare scenarios and inspect responsive layouts. The playground is excluded +from the published npm package. ## How it works @@ -556,45 +131,27 @@ program is limited to 200 entities, 1,000 relations, 2,000 steps and 10,000 events, plus work/expansion budgets. These limits are for deterministic work, not a guarantee of exact heap or CPU usage. -For the complete guide, see the [Throughline documentation](https://docs.0xbuff.com/docs/throughline) and the [architecture notes](https://github.com/0xbuffdev/throughline/blob/main/docs/architecture.md). - ## Documentation -- [Throughline documentation](https://docs.0xbuff.com/docs/throughline) -- [Grammar and authored flow](https://github.com/0xbuffdev/throughline/blob/main/docs/grammar.md) -- [Domain catalog and domain-specific limits](https://github.com/0xbuffdev/throughline/blob/main/docs/catalog.md) -- [Architecture, trace format and runtime boundaries](https://github.com/0xbuffdev/throughline/blob/main/docs/architecture.md) -- [Complex reference traces, questions and compatibility boundaries](https://github.com/0xbuffdev/throughline/blob/main/docs/reference-traces.md) -- [Adding a computed domain](https://github.com/0xbuffdev/throughline/blob/main/docs/authoring.md) -- [Changelog](https://github.com/0xbuffdev/throughline/blob/main/CHANGELOG.md) +- [Documentation website](https://docs.0xbuff.com/docs/throughline) +- [User guide: player, themes, audio, navigation and Markdown](https://github.com/0xbuffdev/throughline/blob/main/docs/user-guide.md) +- [Grammar reference](https://github.com/0xbuffdev/throughline/blob/main/docs/grammar.md) and [supported domains](https://github.com/0xbuffdev/throughline/blob/main/docs/catalog.md) +- [Framework integration guide](https://github.com/0xbuffdev/throughline/blob/main/docs/framework-integrations.md) +- [Architecture and trace model](https://github.com/0xbuffdev/throughline/blob/main/docs/architecture.md) +- [Complete documentation index](https://github.com/0xbuffdev/throughline/blob/main/docs/README.md) +- [Changelog](https://github.com/0xbuffdev/throughline/blob/main/CHANGELOG.md), [v0.1.0 release notes](https://github.com/0xbuffdev/throughline/blob/main/docs/release-notes-v0.1.0.md) + and [upgrade guide](https://github.com/0xbuffdev/throughline/blob/main/docs/migration-v0.1.0.md) ## Development -The source is a standalone Node 24 / pnpm 11.2.2 package, not a -multi-package workspace. - -~~~sh -pnpm install --frozen-lockfile -pnpm audit:source -pnpm typecheck -pnpm test:run -pnpm exec playwright install chromium -pnpm test:browser:run -pnpm build -pnpm audit:licenses -pnpm audit:pack -pnpm audit:browser -~~~ - -The packed-consumer audits install the built archive into independent -Node, TypeScript, React SSR and Vite/browser environments. +To build and verify a checkout, use the [development and verification commands](https://github.com/0xbuffdev/throughline/blob/main/docs/releasing.md#verification). ## Community Read [contributing guidelines](https://github.com/0xbuffdev/throughline/blob/main/CONTRIBUTING.md) before opening a PR. Use [GitHub issues](https://github.com/0xbuffdev/throughline/issues) for reproducible bugs and feature proposals; see -[SUPPORT.md](https://github.com/0xbuffdev/throughline/blob/main/SUPPORT.md) for support and [SECURITY.md](https://github.com/0xbuffdev/throughline/blob/main/SECURITY.md) +[support guidance](https://github.com/0xbuffdev/throughline/blob/main/CONTRIBUTING.md#support) for support and [SECURITY.md](https://github.com/0xbuffdev/throughline/blob/main/SECURITY.md) for confidential vulnerability reports. Participation is governed by the [code of conduct](https://github.com/0xbuffdev/throughline/blob/main/CODE_OF_CONDUCT.md). diff --git a/SECURITY.md b/SECURITY.md index 67aaabe..2fc053c 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -17,11 +17,10 @@ If that option is unavailable, open a brief issue asking for a confidential reporting channel **without including the vulnerability details**. Include the affected package version, impact, reproduction steps and -mitigations only through a private channel. The sole maintainer coordinates -investigation and disclosure as availability permits. No response-time or +mitigations only through a private channel. Reports are triaged privately as capacity permits. No response-time or bug-bounty guarantee is offered. Security concerns include unsafe rendering of authored text, unintended code or network execution, resource-exhaustion limits, browser injection, information disclosure and package supply-chain compromise. For ordinary -usage questions, see [SUPPORT.md](SUPPORT.md). +usage questions, see the [support procedure](CONTRIBUTING.md#support). diff --git a/SUPPORT.md b/SUPPORT.md deleted file mode 100644 index 3718d4b..0000000 --- a/SUPPORT.md +++ /dev/null @@ -1,21 +0,0 @@ -# Support - -For usage questions, consult the [README](README.md), -[grammar reference](docs/grammar.md), -[domain catalog](docs/catalog.md), and -[architecture notes](docs/architecture.md). - -For a reproducible bug, use the -[bug report template](https://github.com/0xbuffdev/throughline/issues/new/choose) -and include the version, -minimal authored `thl` block, expected/actual behavior and relevant -runtime (Node, browser, React). Avoid disclosing secrets or sensitive -content. - -For an API or feature idea, use the feature request template and -explain the underlying problem and compatibility impact. The project -has a sole maintainer; support and review are provided as available, -without a guaranteed response time. - -**Security vulnerabilities:** use [SECURITY.md](SECURITY.md) rather than -publishing exploitation details in an issue. diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..b6a0b77 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,30 @@ +# Throughline documentation + +Start with the [README](../README.md) to install Throughline, compile a trace +and render it with the optional React player. + +## Using Throughline + +- [User guide](user-guide.md): player behavior, theme tokens, accessibility, + navigation, narration, scenarios, Remark and MDX. +- [Flow grammar](grammar.md): source syntax, step operations, limits and + diagnostics. +- [Domain catalog](catalog.md): flow, bindings, binary, matrix and callstack. +- [Framework integration](framework-integrations.md): Fumadocs, Next.js, + Astro and other Markdown renderers. +- [Reference traces](reference-traces.md): reconciliation and distributed + checkout examples. + +## Development and releases + +- [Architecture](architecture.md): compiler, trace model, presentation and + trust boundaries. +- [Adapter authoring](authoring.md): building optional computed domains. +- [Release procedure](releasing.md): exact verification commands, signing, + staged publication and post-release checks. +- [v0.1.0 release notes](release-notes-v0.1.0.md) and + [migration guide](migration-v0.1.0.md). + +For contributions and support, use [CONTRIBUTING.md](../CONTRIBUTING.md). +For private vulnerability reports, follow [SECURITY.md](../SECURITY.md). +The development-only [playground](../playground/) runs with `pnpm dev`. diff --git a/docs/architecture.md b/docs/architecture.md index de65d7a..b38af49 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -29,7 +29,7 @@ build pipelines or other offline ESM environments without React or a DOM. form the presentation shell. Browser fullscreen enters and exits for this player element when supported; seeking never invents timing. -## Pure scene projection (TL-I12) +## Pure scene projection The internal `projectScene(snapshot, groups, preferences)` function separates **compiled system state** from **presentation state**. Its overview projects all @@ -51,14 +51,15 @@ annotations—not the trace history—and relies on compiler-enforced JSON depth/work budgets. The original snapshot remains mutable by neither projection nor selection, and may be shared across compiled frames. The React scene receives authored group layout preferences, preserving existing -`tree` and `column` lane semantics; hierarchy and collapse are future work. +`tree` and `column` lane semantics; optional hierarchy and collapse +are implemented in the later group-projection layer described below. `projectLayout` filters the existing deterministic trace-wide fallback geometry by a projection, retaining the stable positions, viewBox, directions, cycles and self-loops across arbitrary seeking and selection. The React scene uses this internal boundary without adding a package export, trace schema, -viewport control, or public selection API. Pan/zoom, automatic camera -following and user-driven collapse are not part of TL-I12. +viewport control, or public selection API. Pan/zoom, automatic camera following and user-driven collapse are separate +presentation layers above projection. ## Optional spoken explanation @@ -140,7 +141,7 @@ as a structural reveal. Sending never claims destination acknowledgment, processing or persistence, even if both endpoints are tinted as participating entities. -## Authored guidance and separate alternatives (TL-I15) +## Authored guidance and separate alternatives The React-only `ThroughlineGuidance` presentation contract is not serialized into a trace or used by the compiler. Validation checks bounded chapter ranges, @@ -192,7 +193,7 @@ The root compiler does not fetch remote data or use runtime application services. External consumers import only the declared public exports; deep imports are deliberately unsupported. -## Group hierarchy and projection (TL-I14) +## Group hierarchy and projection Optional `SceneGroup.id`/`parentId` metadata forms a static tree of uniquely identified groups. The authoring parser validates the graph before compilation, @@ -212,8 +213,8 @@ camera and presentation state; replacing a trace resets group preferences. Hierarchy is an **additive** version-1 metadata extension, not a new domain or public entry point. Older readers can flatten optional metadata but cannot perform hierarchical collapse; old authoring parsers reject the new grammar. -The explicit compatibility strategy remains subject to the separate TL-I14 -context ADR review prior to a source merge or release. +Consumers should coordinate deployment of the new hierarchy grammar with +the installed compiler and player versions. ## Trace format and compatibility @@ -277,13 +278,11 @@ compilation records grades or authenticates learners. ## Build and trust boundaries `tsdown` produces ESM, declarations and JavaScript source maps for -the three JavaScript exports. The stylesheet is shipped separately. +the four JavaScript exports. The stylesheet is shipped separately. The source package uses one Node 24/pnpm CI job to run compiler and browser tests, inspect third-party dependency license files and install a packed tarball in independent Node, TypeScript, SSR and Vite/React consumers. -These checks are necessary but do not establish copyright ownership, -absence of all secrets, or public-release permission. The private -source history must be separately cleared before any public-source -cutover. +Tests and package audits verify specific technical contracts; they do not +replace source-rights, provenance or release authorization reviews. diff --git a/docs/catalog.md b/docs/catalog.md index dad38f4..ee7fdd4 100644 --- a/docs/catalog.md +++ b/docs/catalog.md @@ -85,12 +85,8 @@ See `grammar.md`. No limits beyond the engine caps (200 entities, - Limits: factorial only, depth ≤ 6 (13 beats max). General calls, branches, and prediction are future adapters. -## Planned (tier 2, lesson-on-demand) - -Each is one `parse` + one `build()` on `tracer()`, ~100 lines with -tests — transcription of the `visualizer/` reference computations, never -bespoke SVG: `vectors` (dot-product geometry, SEF6), `gradient` -(descent + finite-difference compare, SEF6/AI3), `search` (binary -search, SEF3), `sort` (bubble/insertion, SEF3), `cache`/`race`/`tcp` -(SYS1–SYS4 simulators). Workflows never need adapters: `flow` + the -grammar covers them. +## Future domains + +Only the five domains above are supported. New computed adapters require a +separate compatibility review, tests and documentation. Exploratory ideas +are not part of the v0.1.0 public API. diff --git a/docs/framework-integrations.md b/docs/framework-integrations.md index b958e35..c513376 100644 --- a/docs/framework-integrations.md +++ b/docs/framework-integrations.md @@ -1,6 +1,8 @@ -# Framework integration checks +# Framework integrations -These examples target the **unreleased** Remark/MDX integration on the Throughline source branch. They are not installation instructions for the published v0.0.2 package. Do not enable these imports in deployed documentation until an approved package release includes them. +These examples describe the opt-in Remark/MDX integrations introduced in +Throughline v0.1.0. Verify the installed version supports these exports before +using them in a consuming application. The package has separate, opt-in exports: the compiler at `@0xbuff/throughline`, the Remark validator/transform at `@0xbuff/throughline/remark`, the React 19 player/source bridge at `@0xbuff/throughline/react`, and the stylesheet at `@0xbuff/throughline/styles/throughline.css`. Core and Remark do not import React. @@ -178,8 +180,10 @@ export function TraceMarkdown({ source }) { This adapter does not pretend to execute Remark, and it does not automatically transform a standard `~~~thl flow` info string. The fixture verifies that the renderer fails on invalid Throughline sources and preserves unrelated fenced code. When source comes from a publishing system, validate it separately before publication. -## Release and test boundary +## Testing integrations `pnpm audit:frameworks` packs the current source into a tarball and installs it into **independent** temporary Fumadocs, Next.js and Astro consumer projects using committed npm lockfiles and `npm ci`. `pnpm audit:markdown-hosts` uses a fourth independently locked consumer. To update dependencies deliberately, run `node scripts/fixtures/update-integration-locks.mjs` and review the resulting lockfile diffs. The framework audit verifies Fumadocs source generation, Unified MDX with the preserved math/Mermaid/KaTeX pipeline, and a Vite-built Fumadocs/React Router page using the real MDX macro. It also builds independent Next.js App Router and Astro Unified/React-island consumers. All three host exports are served to Chromium to exercise real trace stepping, stylesheet loading, mobile sizing, browser errors and unchanged MDX pages. Those checks are not a claim of manual assistive-technology approval or of a production documentation deployment. `pnpm audit:markdown-hosts` packs the same source and checks SSR integration with `react-markdown`, `mdx-bundler`, and `markdown-to-jsx`, including invalid input and documents with no Throughline fences. The existing `audit:pack` covers Node-only, Remark/MDX, source escaping, SSR and root/React import isolation; `audit:browser` additionally checks a packed Vite React hydration boundary. -The production documentation application must continue using the current published package until a reviewed release candidate is approved and published. Release approval, package versioning, signing, npm distribution, site update and deployment are separate maintainer actions. +Fixture checks verify the integration patterns, not an arbitrary host's +configuration or deployment. Test the host's own build, content validation, +styles and browser behavior after upgrading. diff --git a/docs/migration-v0.1.0.md b/docs/migration-v0.1.0.md new file mode 100644 index 0000000..442fc79 --- /dev/null +++ b/docs/migration-v0.1.0.md @@ -0,0 +1,85 @@ +# Upgrading Throughline from v0.0.2 to v0.1.0 + +This guide describes the upgrade from the previously published v0.0.2. +Verify that v0.1.0 is available on npm before installing it. +`Program.version` and `Trace.version` remain `1`, but npm SemVer +is separate from the trace schema and visual/API compatibility still matters. + +## 1. Confirm the package and integration boundary + +For a clean application consumer, use Node.js 24+ and ESM. Keep React 19 +and React DOM 19 installed only where the optional player is rendered. +Pin `@0xbuff/throughline@0.1.0` in the consumer's lockfile **after** +the package is actually available, and verify the five supported public +imports. Do not import `dist/` or `src/` internals. + +The compiler at `@0xbuff/throughline` is still React-free. The +Remark entry point `@0xbuff/throughline/remark` is an opt-in, +React-independent build-time integration, not an automatically enabled +Markdown renderer. The React entry point adds optional +`ThroughlineEmbed` and `ThroughlineScenarios`. + +## 2. Review authored traces and compatibility + +Existing `thl flow` programs can continue to use flat groups and the +same version-1 snapshot format. Optional syntax +`group "System" id system as column` and +`group "Worker" id worker under system as row` introduces hierarchy. +**v0.0.2 compilers do not parse that new syntax**, so do not publish new +hierarchical content to older consumers. + +Old readers can flatten optional group metadata instead of providing +collapse/drill-down. Do not assume that version-1 alone guarantees +visual feature parity across npm package versions. Retest authoring +diagnostics, strict work limits, seeking, visible group structure +and source-to-frame compatibility against production fixtures. + +## 3. Review the React player and CSS + +Import `@0xbuff/throughline/styles/throughline.css` explicitly once. +The default player shell, control layout, caption placement, corners +and visual emphasis have changed. Review any host CSS overrides rather +than copying assumptions from v0.0.2. + +`--thl-active` now marks current activity and `--thl-trail` represents +past participation; `--thl-change` identifies changed values. +Test custom themes, light/dark/system modes, keyboard focus, +reduced-motion preferences, compact embeds and full-screen behavior. +Step details live in a disclosure below the player controls; integration +tests that targeted old DOM/layout structures may require updating +without weakening accessibility checks. + +Sonification is opt-in via the optional `sonification` prop. +Caption narration is separately opt-in via the optional `narration` prop +and depends on the browser Speech Synthesis API. Test audio availability, +permissions, pause/resume, seeking, multiple player instances and +unmount on real target devices; mocked browser tests are insufficient. + +## 4. Integrate Markdown/MDX explicitly + +If using `remarkThroughline`, preserve existing Remark plugin order, +compile-time validation and expected MDX component registration. +For MDX, provide a host-owned React component mapping to +`ThroughlineEmbed` and import the stylesheet. The default Remark +mode validates without replacing Markdown nodes; opt-in MDX mode +creates literal-source component nodes. Other MDX expressions remain +executable host code, so use trusted MDX sources and enforce +document-wide workload limits. + +See [framework integration examples](framework-integrations.md) for +host-specific configuration. Test the actual deployed host separately +from the provided packed fixture builds. + +## 5. Validate security and rollout + +Compiled source is bounded, inert state data; the React player is not a +complete validator for arbitrary external serialized `Trace` objects. +Validate external JSON at the host boundary or compile bounded source. + +Before changing production documentation or other consumers, compare +actual example output and interactions against the pinned v0.0.2 +baseline, review the [release procedure](releasing.md) +and take a rollback snapshot of dependency locks and host configuration. +Keep the previous working package version pinned until v0.1.0 is +explicitly approved and verified. If integration fails, revert the +consumer lockfile/version rather than rewriting an immutable npm release. diff --git a/docs/reference-traces.md b/docs/reference-traces.md index 58fb977..cd5328f 100644 --- a/docs/reference-traces.md +++ b/docs/reference-traces.md @@ -1,4 +1,4 @@ -# Sophisticated reference traces (TL-I06) +# Reference traces The public `EXAMPLES` export contains two hand-authored `thl flow` teaching fixtures. They compile to version-1 traces; they do not execute React or a @@ -85,22 +85,16 @@ At the observed **v0.0.2** implementation baseline: values are valid. Regression tests pin both behaviors independently. - `validateProgram` rejects unsupported `Program.version`; compilation emits `Trace.version = 1`. -- **Reader compatibility boundary (TL-I16):** `ThroughlinePlayer` rejects unsupported `Trace.version` and missing `frames` arrays before reading playback state. This does not completely validate arbitrary untrusted, serialized snapshot content; the host must validate externally supplied trace data. +- **Reader compatibility boundary:** `ThroughlinePlayer` rejects unsupported `Trace.version` and missing `frames` arrays before reading playback state. This does not completely validate arbitrary untrusted, serialized snapshot content; the host must validate externally supplied trace data. -The new deterministic compilation, arbitrary-seeking, message-endpoint -and browser-player regression tests are source-level acceptance evidence -once they run in CI; the presence of tests alone is not a passing result. +The test suites cover deterministic compilation, seeking, relation endpoints +and browser-player rendering. -## Present renderer limitations and follow-up work +## Changes since v0.0.2 -The v0.0.2 player has flat lane/row/column/tree layouts, not nested -collapse, pan/zoom, follow-active camera, or a dedicated scene-projection -boundary. Its large scenarios can be harder to follow on narrow screens. -The minimal player redesign is TL-I07; interaction/accessibility -regressions are TL-I08; projection and guided navigation are later work. -Do not describe these reference examples as evidence that those features -already exist. +The v0.0.2 player used flat layouts without nested collapse, pan/zoom, +follow-active navigation or scene projection. Version 0.1.0 adds those +presentation features while keeping the trace schema at version 1. -These questions are the initial **comprehension baseline**, not results -from a participant study. A measured comparison against equivalent static -material remains the V-10 release evidence gate. +These teaching questions are examples for comprehension testing, not +measured participant-study results. diff --git a/docs/release-hardening.md b/docs/release-hardening.md deleted file mode 100644 index a65d180..0000000 --- a/docs/release-hardening.md +++ /dev/null @@ -1,66 +0,0 @@ -# TL-I16 — Hardening and release evidence - -This is a **source verification record**, not a release authorization. -Use Node.js 24 and pnpm 11.2.2 with the committed lockfile. - -```sh -pnpm install --frozen-lockfile -pnpm audit:source -pnpm typecheck -pnpm test:run -pnpm exec playwright install --with-deps chromium -pnpm test:browser:run -pnpm build -pnpm audit:licenses -pnpm audit:pack -pnpm audit:browser -pnpm audit:frameworks -pnpm audit:markdown-hosts -``` - -The quality-only GitHub Actions workflow runs these gates on pull requests. -It cannot publish the package or attest to a human comprehension study. - -## Automated verification map - -| Gate | Source of evidence | Limit | -| --- | --- | --- | -| V-01/V-02 | Baseline, compiler, reference-trace, seek and Chromium tests | Version-1 invariants, deterministic frames and arbitrary seeking | -| V-03/V-04/V-06 | Player-shell, tokens, fullscreen and timeline tests | Step-only player, scoped styling and no invented media timing | -| V-05 | Player, viewport, hierarchy, guidance, sound and hardening Chromium tests | Keyboard, touch, 375px layouts, text parity, reduced motion and trace reset in Chromium only | -| V-07/V-08 | Projection, hierarchy, layout and hardening unit fixtures | Stable IDs, no snapshot mutation, cycles, self-links and boundary provenance | -| V-09 | Hierarchy-performance and release-hardening unit fixtures | Prints sample compile/layout times and heap deltas; **no calibrated latency threshold** | -| R-01–R-06 | Remark, MDX, parser-boundaries and isolated packed tests | Safe literal MDX, source diagnostics, root/Remark React isolation | -| R-07–R-09 | Framework, hydration and unchanged-Markdown audits | Locked Fumadocs/Next/Astro/Markdown consumer fixtures, not deployed application | -| R-10–R-13 | Pack/license audits and independent host consumers | Shipped exports/types/license/dependencies and supported host adapters | - -Durations and heap changes vary with runner hardware, JIT warm-up and GC. -Correctness invariants are asserted; a time-based CI regression cutoff -requires controlled repeat samples and a recorded baseline. - -## Reader trust boundary - -The React player rejects unsupported trace versions and missing `frames` -arrays before reading playback state. **This is not a full validator for -arbitrary deserialized snapshots.** Hosts should use the bounded public -compiler or separately validate external serialized trace data. Authored -labels, notes and values remain inert React text, not HTML. Untrusted -executable MDX outside these literal source attributes is a separate -host security concern. - -## Gates still open - -- **V-10:** perform a real participant-comprehension comparison against - matched static material (structure, transfer and failure cause) and - record the questions, methodology, answers and findings. -- **TL-I11 / RMK-10:** integrate a released package in the separate - `documentation` app, preserve existing Remark plugins and verify the - actual application. Packed framework fixtures alone cannot close it. -- Non-Chromium browser and assistive-technology manual checks are pending. -- Proposed Remark and hierarchical compatibility decisions in `context` - require explicit review and acceptance. -- Historical public-source rights review, release versioning, signed - maintainer approval, package staging and publication are separate gates. - -**Status:** Source hardening candidate. TL-I16 remains open until the -applicable outstanding acceptance and release gates are resolved. diff --git a/docs/release-notes-v0.1.0.md b/docs/release-notes-v0.1.0.md new file mode 100644 index 0000000..7176203 --- /dev/null +++ b/docs/release-notes-v0.1.0.md @@ -0,0 +1,45 @@ +# Throughline v0.1.0 + +Throughline compiles Markdown-authored state transitions into deterministic, +step-by-step traces. This release expands the optional React player and adds +build-time Markdown integration without introducing a backend or requiring +React for the compiler. + +## Highlights + +- **State visualization:** React-independent scene projection, hierarchical + groups with collapse/drill-down, activity trails, and bounded pan, zoom and + follow-active navigation. +- **Guided playback:** accessible chapters, authored evidence and explanations, + scenario comparison, responsive controls, state inspection and native + fullscreen. +- **Optional audio:** independent step tones and speech narration. Both are + opt-in; narration depends on the host browser's speech synthesis support. +- **Markdown and MDX:** source-positioned Remark validation, optional literal + MDX components and a `ThroughlineEmbed` React bridge. +- **Integration:** independent package-consumer coverage for Fumadocs Unified, + Next.js App Router, Astro and common Markdown renderers. +- **Reliability:** improved reduced-motion handling, WebKit toolbar layout, + narration interruption/ownership, touch gestures and trace validation. + +## Installation + +~~~sh +npm install @0xbuff/throughline@0.1.0 +~~~ + +Use Node.js 24+ and ESM. The optional React player requires React 19 and +React DOM 19; its CSS must be imported explicitly. + +## Compatibility + +The five supported v0.1.0 package exports are `.`, `./remark`, `./react`, +`./examples` and `./styles/throughline.css`. Program and trace schema +versions remain **1**. New optional group hierarchy syntax is not parsed by +the v0.0.2 compiler, and older players may flatten nested groups. Player +layout and CSS defaults have also changed, so hosts with theme overrides or +DOM-dependent tests should review the new presentation. + +See the [v0.0.2 → v0.1.0 migration guide](migration-v0.1.0.md), +[complete changelog](../CHANGELOG.md) and [usage guide](user-guide.md) +for integration instructions and details. diff --git a/docs/release-qa-post-v0.0.2.md b/docs/release-qa-post-v0.0.2.md deleted file mode 100644 index 65e5e97..0000000 --- a/docs/release-qa-post-v0.0.2.md +++ /dev/null @@ -1,104 +0,0 @@ -# Release-candidate verification — changes after v0.0.2 - -**Assessment date:** 2026-11-12 -**Outcome:** **FAIL — one repeatable WebKit layout assertion fails and release acceptance is not established.** -**Execution boundary:** Verification and test tooling only. No production fix, merge, release, npm stage, tag, or approval. - -## 1. Exact scope - -- **Published baseline:** signed `v0.0.2` tag, commit `ab62dd02661701bb2e26014732f765c73bd4e9f7`. -- **Candidate observed:** `main` at `26d4e81756e28a6c28ac9f0c4b3dbc2e86b6f8c9` (post-PR #30). -- GitHub `v0.0.2...main` reports **251 commits**. This is the source comparison, not a claim of 251 independently reviewed commits. -- Includes reference traces, UI shell, scene projection, viewport/zoom, hierarchy, scenarios and explanations, Remark/MDX, packed framework/Markdown integrations, adaptive footer and captions, persistent workflow trail, opt-in step sounds and opt-in speech narration. -- `package.json` remains at version `0.0.2`; this is not a new registry release candidate or published build. - -The source architecture, API, `AGENTS.md`, `RELEASING.md`, `docs/release-hardening.md` and applicable acceptance gates govern this verification. Private planning materials are not copied into this public repository. - -## 2. Checks actually observed - -The exact candidate commit has a **successful GitHub Actions push run**, [Throughline CI #43212453210](https://github.com/0xbuffdev/throughline/actions/runs/43212453210), on 2026-11-12. The run's job and decoded logs were inspected. - -| Check | Observed result | -| --- | --- | -| Node 24, pnpm 11.2.2, frozen lockfile, unchanged lockfile | Passed | -| Public-source boundary audit, workflow YAML parse | Passed | -| TypeScript typecheck; playground typecheck and Vite build | Passed | -| Unit/compiler suite | **235 passed, 33 files** | -| Chromium browser suite | **82 passed, 14 files**, headless Chrome for Testing 158 | -| Distributable exports, declarations, build smoke checks | Passed | -| Runtime and peer licenses | Passed | -| Packed Node, TypeScript, React SSR and MDX consumers | Passed | -| Packed Vite React SSR/hydration, keyboard/touch/mobile | Passed | -| Isolated Fumadocs, Next.js and Astro consumers | Passed | -| `react-markdown`, `mdx-bundler`, `markdown-to-jsx` consumers | Passed | -| Firefox full browser regression suite | **83 passed, 15 files**, including added non-overlap geometry — [QA matrix #43214998502](https://github.com/0xbuffdev/throughline/actions/runs/43214998502); earlier 82/82 twice | -| WebKit full browser regression suite | **82 passed, 1 failed (15 files)** — [QA matrix #43214998502](https://github.com/0xbuffdev/throughline/actions/runs/43214998502); same original compact-height failure reproduced on three executions; added non-overlap geometry test passes | -| QA branch normal unit/Chromium/packed CI | **Passed on earlier QA commit** — [PR CI #43214005254](https://github.com/0xbuffdev/throughline/actions/runs/43214005254); the later geometry commit's [CI #43214998532](https://github.com/0xbuffdev/throughline/actions/runs/43214998532) must be checked separately | -| Real audio output, physical devices, screen readers, real documentation app | **Not executed / not evidenced here** | - -**Provenance:** The checks above were executed by the linked CI runner, not in a separate local checkout. The local execution environment had no route to clone GitHub or install repository dependencies; no unexecuted command is counted as a pass. The QA matrix ran the original 82 tests and subsequently an additional geometry regression in Firefox/WebKit. The WebKit failure reproduced on the rerun and again with the new geometry test; it was not waived. - -## 3. Acceptance coverage and limitations - -| Area | Automated evidence | Limit / remaining work | -| --- | --- | --- | -| Version-1 compatibility, compiler limits, deterministic frames and seek | Unit fixtures, baseline-contract, snapshot/projection tests | Compare known fixture outputs with the prior published package in a clean external consumer if an exact binary-compatibility decision is needed | -| Reference cases and truthful causal/transfer events | Reference and scenario tests | Human comprehension comparison V-10 not done | -| Responsive layout and visual presentation | Chromium DOM geometry, dense graphs, 260–420px embeds, titles/controls, bounded caption details | Human visual review on Firefox/WebKit/Safari, text zoom, real mobile devices | -| Keyboard, touch, focus, inspector, fullscreen and reduced motion | Chromium/browser and packed Vite smoke checks | NVDA/JAWS/VoiceOver/TalkBack validation and keyboard-only manual audit | -| Narration and sound ownership/lifecycle | Browser tests with mocked Web Speech and Web Audio boundaries | Real voice availability, permissions, quality, interruption and speaker output remain unverified | -| React, SSR, Markdown/MDX and deployment compatibility | Locked disposable consumers and built distributable | No validation of the live documentation app or deployed host integration | -| Security, inert source, source-size and work limits | Source audit, parser tests, literal MDX/Remark and pack checks | Serialized external traces are not exhaustively validated by the player; host must validate untrusted serialized inputs | -| Performance | Diagnostic dense-layout samples in existing tests | No calibrated, repeatable thresholds or cross-device memory/latency acceptance | -| Licenses and distribution | CI license/package/source checks | Historical source rights and public Git-history exposure review are separate | - -## 4. Findings (absence of evidence is not a reproduced implementation defect) - -**QA-01 — P1 / release gate:** V-10 real-participant comprehension study is unverified. Compare matched static and interactive material on structure, transfers and failure causes; record recruitment, tasks, scoring, raw anonymized results, and limitations. Do not invent participants or equate visual approval with comprehension. - -**QA-02 — P1 / release gate:** Real speech, screen-reader and physical-device tests are unverified. Mocked `speechSynthesis` verifies control flow but not speech intelligibility, hardware interruption behavior, browser audio permission, queue interaction with third-party players, or mobile operating-system assistive technology. Manually test opt-in, pause/resume, seeking, same-step restart, two simultaneously mounted players, source changes, unmount and silent fallback. - -**QA-03 — P1 / integration gate:** Locked fixture builds do not verify the actual documentation application, its plugin order, rendered MDX pages and React bridge. The separately owned documentation integration is required before claiming end-to-end docs acceptance. - -**QA-04 — P2 / reproduced cross-browser toolbar-layout assertion (failed):** On Linux GitHub Actions WebKit 27.2 with the 1,600 × 900 viewport, `src/react/__tests__/toolbar.browser.test.tsx:30` fails `expect(bottom.getBoundingClientRect().height).toBeLessThan(55)`: **observed 56px**. Steps: (1) checkout QA branch `05fefb6` with Node 24/pnpm 11.2.2, (2) `pnpm install --frozen-lockfile`, (3) `pnpm exec playwright install --with-deps webkit`, (4) `THROUGHLINE_QA_BROWSER=webkit pnpm test:browser:run`. The two initial workflow attempts gave **81/82 passed and the same failure**: [release-browser run #43214005524](https://github.com/0xbuffdev/throughline/actions/runs/43214005524). The subsequent [matrix run #43214998502](https://github.com/0xbuffdev/throughline/actions/runs/43214998502) with an independent bounds/overlap test gave WebKit **82/83 passed** and Firefox **83/83 passed**. The new geometry test passes in WebKit at 1600, 960, 375 and 320px viewport widths: interactive controls are within toolbar bounds and do not overlap. Therefore this finding is **a cross-browser compact-height threshold failure**, not demonstrated control clipping or overlap in the widths checked. This is a real, repeatable **test failure**; its cause may be a WebKit-native control metric vs. intended CSS spacing. The assertion alone does not prove overlap, clipping or an accessibility violation. Inspect the WebKit screenshot and component/control geometry and determine whether CSS should be corrected or the strict pixel assumption is unsupported. Do **not** relax the check merely to make CI green. - -**QA-05 — P2 / performance evidence:** Existing single-run layout/heap diagnostics do not establish repeatable thresholds. Collect warm and cold samples on fixed hardware, at small/moderate/near-limit graph sizes and 320/720px widths. Record median, tail, heap, layout crossings and version deltas; investigate meaningful regressions before setting a gate. - -**QA-06 — P1 / design-decision gate:** Proposed Markdown/Remark and hierarchical group compatibility decisions must receive explicit maintainer acceptance. A merged source PR or a merged proposal does not change the document's own proposal status. - -**QA-07 — P1 / source-history and publication gate:** Historical public-source references and distribution rights must be reviewed separately. Update SemVer/changelog, verify package archive, and follow the signed-tag/manual staged-publication process only after authorized review. The published `0.0.2` version cannot be reused for a new package. - -The WebKit cross-browser check is **reproducibly failing**, while the additional cross-browser control-bounds test **passes** and remaining items are **unclosed verification/release gates**. The source and logs do not yet establish the WebKit discrepancy as a user-visible layout defect rather than a too-strict test assertion; it must be triaged. **Do not infer absence of defects from passing tests.** - -## 5. Reproducible retest instructions - -On the exact reviewed candidate with Node 24+, pnpm 11.2.2 and a clean lockfile: - -```sh -pnpm install --frozen-lockfile -pnpm audit:source -pnpm typecheck -pnpm preview:typecheck -pnpm preview:build -pnpm test:run -pnpm exec playwright install --with-deps chromium firefox webkit -pnpm test:browser:run -THROUGHLINE_QA_BROWSER=firefox pnpm test:browser:run -THROUGHLINE_QA_BROWSER=webkit pnpm test:browser:run -pnpm build -pnpm audit:licenses -pnpm audit:pack -pnpm audit:browser -pnpm audit:frameworks -pnpm audit:markdown-hosts -``` - -In each of Chromium, Firefox, WebKit and on actual devices, review the source-backed playground (`pnpm dev`) at 260, 300, 320, 360, 375, 768, 960 and 1600px available widths. Confirm no clipped toolbar controls, no horizontal overflow, readable node labels, stable diagram/control positions when long explanations expand, consistent contrast, reduced motion and keyboard focus. Test both themes, two audio controls, no audio, narration only and sonification only. Inspect screenshot differences as evidence rather than silently updating snapshots. - -Perform keyboard-only and screen-reader passes on the caption disclosure, timeline, chapter selector, entity selector, fullscreen failure, scenario changes, status announcements and narration errors. Test with 200% and 400% page zoom, high contrast and device-level reduced-motion settings. Document browser/OS/device/assistive-technology versions, exact inputs and observed outcomes. - -For reproducible bugs, record the fixture, preconditions, numbered actions, expected behavior, observed behavior, severity and a screenshot/log or trace reference. Separate regressions from `v0.0.2`, existing issues and infrastructure failures. - -## 6. Decision and next actions - -**Result: FAIL / NOT CLEARED FOR RELEASE.** The candidate's standard CI and Firefox browser suite are green, but **the WebKit 56px vs. <55px check failed three times, even though the separate non-overlap/control-bounds test passed**. The manual/human/integration/performance/governance gates remain unclosed. Triage the discrepancy on its merits, fix confirmed product defects on separate production-fix PRs if needed, and retest the exact new revision. Close or explicitly re-scope the remaining gates through maintainer review before preparing a signed package release. This QA PR does not approve, merge or publish anything. diff --git a/RELEASING.md b/docs/releasing.md similarity index 61% rename from RELEASING.md rename to docs/releasing.md index 7e58e5b..cacdf07 100644 --- a/RELEASING.md +++ b/docs/releasing.md @@ -5,37 +5,71 @@ Each version is published from an immutable, GitHub-verified signed `vX.Y.Z` tag using manual GitHub Actions OIDC staged publishing. **The GitHub workflow only stages a release; npm 2FA approval publishes it.** +## Verification + +Use Node.js 24+, pnpm 11.2.2 and the committed lockfile. Run the full +release suite on the exact reviewed revision: + +~~~sh +pnpm install --frozen-lockfile +pnpm audit:source +pnpm typecheck +pnpm preview:typecheck +pnpm preview:build +pnpm test:run +pnpm exec playwright install chromium +pnpm test:browser:run +pnpm build +pnpm audit:licenses +pnpm audit:pack +pnpm audit:browser +pnpm audit:frameworks +pnpm audit:markdown-hosts +~~~ + +For Firefox and WebKit verification, install the browser binaries and run +`THROUGHLINE_QA_BROWSER=firefox pnpm test:browser:run` and +`THROUGHLINE_QA_BROWSER=webkit pnpm test:browser:run`. Verify the final CI +results and the packed tarball, not just a successful TypeScript build. + +Validate browser/device appearance, keyboard and screen-reader interactions, +200%/400% zoom, reduced motion and opt-in speech/sound on the supported +platforms. Maintain recorded acceptance evidence for any release-specific +performance, comprehension or documentation-application integration +requirements. These checks are not replaced by mocked or fixture-only tests. + ## Release checklist -1. Confirm source ownership, third-party licenses, security reporting, - public Git-history safety and the final changelog. The repository's - earliest Git history contains a historical private planning reference. - This has already been exposed by public visibility; deleting files from - the latest tree does not erase old commits, forks or caches. +1. Verify rights to distribute the source, applicable third-party notices, + security reporting, release acceptance, compatibility and the final + changelog. Review the public history for any sensitive references. + Historical exposure cannot be undone by deleting current-tree files. 2. Update the version in `package.json` and document the changes in - `CHANGELOG.md`. Maintain the four public package exports and keep + `CHANGELOG.md`. Maintain the five public package exports and keep `Program.version` / `Trace.version` independent of npm SemVer. -3. Run the source, TypeScript, unit, browser, build, license and packed - consumer checks described in `AGENTS.md`. Review the exact archive - contents. Merge the release PR after review and ensure main CI is green. +3. Run the source, TypeScript, preview, Chromium/Firefox/WebKit browser, + build, license and packed consumer checks listed above. + Record the exact candidate SHA, CI runs and applicable manual review. + Review the exact archive contents. Merge the release PR after review and + ensure main CI is green. 4. From a clean checkout with a signing key registered with GitHub, create and push an immutable signed tag **only after** the release commit is - merged. For version `0.0.2`: + merged. For version `0.1.0`: ~~~sh git switch main git pull --ff-only - git tag -s v0.0.2 -m "Throughline v0.0.2" - git tag -v v0.0.2 - git push origin v0.0.2 + git tag -s v0.1.0 -m "Throughline v0.1.0" + git tag -v v0.1.0 + git push origin v0.1.0 ~~~ GitHub must show the tag as **Verified**. Protect `v*` tag updates and deletions. Do not move or reuse existing release tags. 5. In [GitHub Actions](https://github.com/0xbuffdev/throughline/actions), run **Throughline npm release (staged with provenance)** from `main`. - Enter the exact signed tag (e.g. `v0.0.2`) and confirmation - `STAGE-v0.0.2`. The read-only verification job checks the signed tag, + Enter the exact signed tag (e.g. `v0.1.0`) and confirmation + `STAGE-v0.1.0`. The read-only verification job checks the signed tag, manifest, build, tests, packed consumers and archive SHA-256. 6. Inspect the GitHub release artifact and approve the protected `npm-publish` environment job. Its **stage-only** Trusted Publisher @@ -53,7 +87,7 @@ Each version is published from an immutable, GitHub-verified signed npm stage download ~~~ - Confirm the exact version, contents and provenance, then approve + Confirm the exact version, contents and provenance, then, with final maintainer authorization, approve **that stage only** with npm account 2FA: ~~~sh @@ -66,7 +100,7 @@ Each version is published from an immutable, GitHub-verified signed ~~~sh npm view @0xbuff/throughline version npm view @0xbuff/throughline dist-tags - npm view @0xbuff/throughline@0.0.2 version + npm view @0xbuff/throughline@0.1.0 version ~~~ Check [npm's package page](https://www.npmjs.com/package/@0xbuff/throughline) diff --git a/docs/user-guide.md b/docs/user-guide.md new file mode 100644 index 0000000..9f7ed50 --- /dev/null +++ b/docs/user-guide.md @@ -0,0 +1,427 @@ +# Throughline user guide + +This guide expands the [README](../README.md) quick start with details about +the optional React player, authored state, narration, groups, scenarios and +Markdown integration. The core compiler can also be used without React. + +## React player appearance and theming + +Import the optional stylesheet once in the host application. The player +accepts an optional `title` (omitted when absent), and `light`, `dark` or +`auto` themes. The borderless, pale-light/charcoal-dark shell centers the +visualization above a caption with an exact `Step N of M` indicator and a +bottom control bar. Playback is **step-based**: there is no invented media +time. The optional state inspector and authored causal explanations remain in the caption; chapter navigation and all player navigation/transport controls sit below it. + +The shell, group boxes, nodes, cells and form inputs use a compact default +`--thl-radius: 0.125rem` (2px at a 16px root font size), with 0.5rem inner +spacing and 1.5rem player padding. Node strokes default to +`--thl-node-border-width: 1.25px`. Icon-only buttons have no visible boxes, +but retain at least 2.5rem interactive targets and visible keyboard focus. + Host overrides are scoped to the +player; for example, with `className="my-trace"` and a stylesheet loaded +after the package CSS: + +~~~css +.thl.my-trace { + --thl-radius: 0.5rem; + --thl-bg: light-dark(oklch(0.98 0 0), oklch(0.14 0 0)); +} +~~~ + +The `--thl-bg`, `--thl-panel`, `--thl-surface`, `--thl-text`, +`--thl-muted`, `--thl-line`, `--thl-focus`, `--thl-change`, `--thl-change-bg`, +`--thl-active`, `--thl-trail`, `--thl-trail-bg` and control tokens belong to the player, not the host's `:root` or `.dark`. + +The shell and its text are neutral; only state-carrying marks take a hue. +`--thl-active` colors what happens in the current step (the sending arrow, +its label and the Play button), `--thl-trail` is the same hue family washed +out for history, and `--thl-change` is the only warm color, reserved for +changed values. Dashed outlines mean structure (collapsed, revealed or +aggregate), never selection: a selected node and the keyboard-focused node +are marked by an outer ring. + +Motion timing follows the step interval. The player sets `--thl-step-ms` on +its root (1500ms at 1x, 750ms at 2x) and the draw and flash durations derive +from it. A host can override `--thl-draw-ms` and `--thl-flash-ms` on `.thl` +like any other token. + +The bottom-right **Maximize player** button enters native browser fullscreen +and changes to **Minimize player** while active, including when fullscreen +changes via Esc. It is disabled where the API is unavailable or blocked. +Sound controls appear only when the host supplies `sonification`. The +inspector remains optional, the trace reference should remain stable while +stepping, and complete custom stylesheets must retain accessibility. + +### Playback accessibility + +Focus the **Visualization player** to use Left/Right arrows for stepping, +Home/End to jump to the first/last step, and Space to play or pause. Native +buttons, the labeled timeline slider and the playback-speed selector keep +their own keyboard interactions; shortcuts do not steal keystrokes from +focused controls. Each frame has an accessible text summary. **Inspect +state** is a native disclosure exposing complete visible values. + +Playback starts paused at the first frame. Changing to a different trace +object resets the step, playback and speed; passing a stable trace object +during ordinary renders avoids unnecessary resets. The timeline describes +**steps**, not elapsed media time. At narrow widths the controls rearrange +without a separate horizontal scroll area. Motion is declared only under +`prefers-reduced-motion: no-preference` and plays only when a step is reached +by one forward step (play, or Step forward): the sending arrow draws, its +arrowhead lands, then a changed value flashes. Seeking the timeline, +rewinding or replacing the trace shows the settled frame immediately. + +Sound is off by default, and the host must provide a `sonification` option +before the user can enable it. A host can explicitly start enabled using +`sonification={{ enabled: true }}`. A new trace object restarts its initial +sound cue and stops the previous one, even when both traces have the same +number of frames; rerendering with the same trace does not replay audio. +A refused fullscreen request is +announced to assistive technology; playback remains available in-page. +Browser support and host policies determine whether fullscreen can start. + +## Trace reader compatibility + +`ThroughlinePlayer` expects a compiler-produced version-1 `Trace` and rejects +unsupported versions or a missing `frames` array at runtime. This is **not** +a complete validator for arbitrary serialized snapshots. Treat external JSON +as untrusted and validate it at the host boundary; prefer compiling bounded +source with `compile`. + +For the reader's trust boundary, see [architecture](architecture.md). + +## Responsive controls and step detail + +The optional title has 0.75rem of space before the visualization, and the +footer adds a small 0.25rem offset after the stage's existing gap. These +separations keep the graph distinct from both its heading and controls. + + +The React player keeps the navigation and transport immediately beneath +the diagram in one aligned toolbar on wide layouts. Container queries +reflow the toolbar into two rows at intermediate widths and compact +touch-sized controls at mobile widths. Desktop icons render at 14px, +while the icon-button hit area stays 34px, increasing to 40px on +mobile. Native selects use 0.125rem corners and 0.5rem padding. + +The authored caption now follows the toolbar as a native **Details** +disclosure. Its closed header shows the step title, a single-line +preview of the note, and the step counter. Open Details to read the +complete note, transfers, optional causal evidence, and the state +inspector. The detailed content has its own bounded scroll area so +long author-provided explanations cannot push the controls away +from the diagram. The screen-reader step announcement remains active +regardless of whether the disclosure is expanded. Details remain open +between steps until closed, without changing playback state. + +## Opt-in caption narration + +The optional React-only `narration` prop exposes a **Narrate captions** +button next to the independent `sonification` sound-effects toggle. +The button is **off initially**, even when a host offers it. Its first +activation speaks the current authored step from a user gesture using +the browser's Speech Synthesis API. It reads the authored step label, +notes, matching chapter and causal explanation, and transfer summaries. +It does not invent responses, state changes or causal explanations. +Very long utterances are shortened with an explicit spoken notice; the +full authored text remains available in Details. + +With narration enabled, **Play advances only after speech finishes**. +Pause pauses the current voice; Play resumes it. Manual seeking or +stepping cancels the previous voice and narrates the selected step. +Switching scenarios cancels the old voice and starts muted. Disabling +narration returns to timed playback and leaves step tones independent. +Playback speed applies to the next utterance, not the current one. +A manual seek to the **same** step (including Restart chapter) restarts its +narration. If an author updates guidance while the current frame is shown, +the old utterance is invalidated and the corrected explanation is spoken. + +Speech voices, available hardware, and user-activation policies depend +on the browser and OS. The button is disabled where speech synthesis is +unavailable; speech errors pause synchronized playback and are announced. +No text is uploaded by Throughline, but the browser may use a network +speech service. The core stays React- and browser-independent. + +**Multiple embedded players:** Browser speech synthesis has one global queue. +Throughline coordinates narration across its player instances: when B begins +speaking, B takes over and A is paused, unmutes no further audio, and reports +that another player is speaking. A's later seek/unmount does not interrupt B. +A may explicitly re-enable narration to take over again. External scripts +calling the browser speech API are not under Throughline's control; an +unexpected `interrupted` or `canceled` utterance is treated as an error: +narration-driven playback pauses and shows a recoverable message. This +behavior does not imply exclusive ownership of third-party speech. + +## Persistent workflow progress + +The React player retains a cumulative trail through the **selected** step. +Focused or explicitly written nodes and endpoints of authored sends stay +highlighted, along with arrows that have actually transferred an authored +message. The **current arrow itself** draws when a send occurs; there is +no second path painted on top of it. The newest focus and data changes +still have stronger emphasis than previous activity. + +Only an explicit `send` highlights a message path. For instance, setting +a server status **does not** highlight the response link until a separate +authored response send occurs. A `show link` marks a structural connection +with a dashed style, not a delivery. Highlighting an endpoint means it +participated in the authored sequence; it does not imply that a request +was accepted or that the destination persisted data. + +The trail is derived from the compiled trace at each seek position. +Rewinding removes future progress; replaying restores it. Hidden nodes and +links stay hidden; collapsed groups retain original relation provenance +and distinguish partial aggregates. Switching scenarios or replacing +the trace resets the progression. Reduced-motion preferences retain +a fully highlighted arrow without drawing animation or value flashes. Screen readers +get an authored-progress count with the current step. + +## Guided viewport navigation + +The React player opens with a fit-to-view overview. The compact bottom toolbar +provides Zoom in, Zoom out (1× to 4×), Fit to view, a native Select entity menu, +and an explicit Follow active entity toggle. Selecting an entity in the diagram +or menu centers it and exposes its value as text; this never changes the trace. + +- Mouse: drag to pan; wheel over the SVG to zoom about the pointer. Wheel + scrolling remains native at the zoom bounds and Ctrl/Meta+wheel is reserved + for browser-level zoom. +- Touch: one-finger drag to pan, two-finger pinch to zoom, or use the toolbar. +- Keyboard: focus the diagram, use arrow keys to pan, + and - to zoom, and 0 + to fit. The native entity menu provides keyboard and screen-reader selection. + Focus the overall Visualization player for playback shortcuts instead. + +Follow-active starts off. When enabled, it centers the first currently visible +authored active entity without synthesizing activity. Manual pan, zoom, fit or +selection always disables following until explicitly re-enabled. Seeking +and autoplay do not erase manual camera choices. Replacing the trace with a +different object returns the camera to overview, clears selection and disables +following alongside the existing playback reset. Resizing the host can change +layout geometry, but cannot mutate frames. Hidden entities remain unselectable. + +The viewport operates only on SVG viewBox presentation state, without a new +trace schema, group hierarchy or inferred events. Its physical SVG height +follows the stable trace-wide layout's aspect ratio so sparse scenes do not +reserve a tall blank viewport. Very dense scenes are capped at 960px; Fit +to view still preserves the complete graph's aspect ratio, and zoom, pan +and selection expose smaller mobile details. The height responds smoothly +to container changes except when reduced motion is requested. Navigation keeps the camera center within +the world bounds, which allows actual centering of edge nodes while retaining +some of the scene on screen. + +## Hierarchical groups and drill-down + +Legacy `group "Name" as tree` and `node a "A" in "Name" = 0` remain +supported. For opt-in nested groups, give each participating group a stable ID, +refer to a parent by ID, and keep node membership in one immediate named lane: + +~~~thl flow +group "Worker" id worker under system as row +group "System" id system as column +group "Outside" id outside as row +node job "Job" in "Worker" = "ready" +node external "API" in "Outside" = "waiting" +link request job -> external "request" +step "Send" + send request = "hello" +~~~ + +The group toolbar can drill into a group, collapse it into a summary, expand it, +and return through the breadcrumb trail to overview. Collapsing never changes +authored `hide`/`show`, entity values, trace frames or event history. +The summary counts only authored-visible entities. Boundary links retain all +source relation IDs in accessible SVG tooltips and transfer annotations. +An active aggregate edge uses a **static highlight** for one or more actual +`send` events; it does not imply all constituent relations sent or that the +destination stored anything. A collapsed summary highlights authored focus +and changed state in that step, without exposing system-hidden entities. The state +inspector still shows real authored values. + +Group IDs and parent IDs are an additive opt-in version-1 trace extension, +with no new package import path. Old `thl` sources need no migration. Older +Throughline compilers cannot parse `id`/`under` grammar, and older players +receiving a new compiled trace may display it as flat lanes without collapse. +Only author hierarchical source when its consumer uses a compatible compiler. +Static layout uses the existing deterministic engine and does not add an +external graph-layout dependency. On replacing the `trace` object, collapse +and breadcrumb choices reset alongside selection, camera and playback. + +## Supported domains + +| Domain | What it explains | Authoring model | +| --- | --- | --- | +| `flow` | State, relationships and changes | Explicit nodes, links and steps | +| `bindings` | Primitives, references, mutation and rebinding | Small assignment/push language | +| `binary` | Ripple-carry binary addition | Two unsigned binary operands | +| `matrix` | Matrix multiplication | Two small compatible matrices | +| `callstack` | Recursive factorial calls and returns | A bounded factorial input | + +All five domains produce the same version-1 trace shape. Domains are +intentionally limited; Throughline is not a general-purpose JavaScript +interpreter or an automatic execution tracer. + +## Validate Markdown with Remark + +Import `remarkThroughline` from `@0xbuff/throughline/remark` and use it +in a Unified/Remark pipeline. The default `validate` mode checks each +`thl ` fenced code node, fails the build with source-positioned +VFile diagnostics for invalid content, and leaves all Markdown nodes +unchanged. Ordinary fences are unaffected. No React dependency is required. + +~~~ts +import { unified } from "unified"; +import remarkParse from "remark-parse"; +import remarkStringify from "remark-stringify"; +import { remarkThroughline } from "@0xbuff/throughline/remark"; + +const processor = unified() + .use(remarkParse) + .use(remarkThroughline) // or .use(remarkThroughline, { mode: "validate" }) + .use(remarkStringify); + +await processor.process({ path: "lesson.md", value: markdownSource }); +~~~ + +The code fence header must contain exactly a supported domain, such as +\`\`\`thl flow\`\`\`; additional fence metadata is not supported. The host +parser supplies code-body lines and file positions. The validator locates +body tokens from the original Markdown, including list and blockquote prefixes. +When no trustworthy position exists, the error remains file-level rather +than pointing at an unrelated fence header. A Markdown document +may contain multiple traces, but publishers should also bound total document +size and number of fences. Validate mode never rewrites the AST, renders the +player or emits JSX. MDX mode is explicitly opt-in. + +### Render Throughline fences from MDX + +A host using the Unified-based MDX compiler can enable the transformation +and register the React bridge in its MDX component map: + +~~~tsx +import { remarkThroughline } from "@0xbuff/throughline/remark"; +import { ThroughlineEmbed } from "@0xbuff/throughline/react"; +import "@0xbuff/throughline/styles/throughline.css"; + +// Pass to your MDX compiler's Remark plugin configuration: +const remarkPlugins = [[remarkThroughline, { mode: "mdx" }]]; + +// Pass to your MDX renderer's components map: +const mdxComponents = { ThroughlineEmbed }; +~~~ + +The plugin validates each `thl ` fence first, then replaces it with +an `mdxJsxFlowElement` whose `source` attribute is a **literal string**. +Quotes, braces, backticks and markup in a validated trace stay data: no +JavaScript expressions or untrusted HTML are generated by this transform. +All matching fences must validate before any are replaced. Other fences +and Markdown nodes are unchanged. + +`componentName` can override the default `ThroughlineEmbed` with one +capitalized React identifier, such as `CustomTrace`; the host must register +that exact name. If registration is missing, MDX rendering fails rather than +silently displaying an empty visualization. Plain Markdown pipelines should +use `validate`, not `mdx`, unless their output compiler supports MDX JSX. + +`ThroughlineEmbed` accepts `source` plus the existing player presentation +props (`title`, `theme`, `className`, `sonification`). It memoizes compilation +for unchanged source, but **still compiles at runtime** during SSR and in +clients; build-time validation does not serialize a precompiled trace. The +host owns MDX component registration, trusted-content policy and aggregate +document budgets. An MDX host can execute its *other* authored expressions; +this plugin does not make arbitrary MDX content safe to execute. + +## Guided explanations and scenario comparison + +Guidance is **optional, host-authored presentation data**, not compiled trace +metadata. Chapters identify inclusive, zero-based frame ranges. Selecting a +chapter seeks to its first frame and pauses playback. The **Restart chapter** +button returns to the beginning of the current chapter (including while +playing). Causal explanations +show the author's **Trigger**, **Observed event**, **State change**, +**Consequence**, and references to real compiled events. Chronology alone +never establishes causation. + +Three checkout outcomes are separately authored and independently compiled: + +~~~tsx +import { compile } from "@0xbuff/throughline"; +import { CHECKOUT_GUIDANCE, CHECKOUT_SCENARIOS } from "@0xbuff/throughline/examples"; +import { ThroughlineScenarios } from "@0xbuff/throughline/react"; +import "@0xbuff/throughline/styles/throughline.css"; + +const scenarios = CHECKOUT_SCENARIOS.map((item) => ({ + id: item.id, + title: item.title, + description: item.description, + trace: compile(item.source), + guidance: item.id === "retry-recovery" ? CHECKOUT_GUIDANCE : undefined, +})); + +export function CheckoutComparison() { + return ; +} +~~~ + +Each scenario has a unique nonempty ID, title and compiled trace. The optional +`initialScenarioId` selects the starting outcome. Switching scenarios +remounts its player, resetting step, playback, speed, camera, selection and +sound voice, without changing any snapshot. Ordinary rerenders of the same +option preserve state. Replacing a trace object uses the existing reset. +If the host removes the selected scenario, selection moves to the first +available outcome and stays there even if the removed option returns later; +an empty list clears selection and a subsequently populated list selects its +first option. `initialScenarioId` is only used on initial mount. +An empty scenario list shows a text empty state. A single player or +`ThroughlineEmbed` can also accept the `guidance` prop. + +`ThroughlineGuidance` accepts ordered, nonoverlapping `chapters` with +`{ id, title, start, end, summary? }`, and `causalLinks` with +`{ id, title, step, trigger, observedEvent, stateChange, consequence, evidence }`. +Each evidence item is `{ step, event }`: a zero-based frame index and an +event index within `trace.frames[step].events`. Evidence must not lie +after the link's display frame and must reference at least one actual +`set`, `show` or `hide` event. A `send` alone does not prove persistence. + +Invalid range, duplicate ID, missing event or unsupported evidence fails +explicitly. Limits: 100 chapters, 200 links, 20 event references per link, +4096 characters per text field; IDs start with an ASCII letter and contain +up to 64 ASCII letters, digits, underscores or hyphens. Evidence proves +provenance, **not the truth of causal prose**: authors must review it against +notes and snapshots. All descriptions render as text, not HTML. No native +branching graph, simulation, network or hidden telemetry is introduced. + +## Public package exports + +| Import path | Contents | +| --- | --- | +| `@0xbuff/throughline` | Core compiler, parser, types, validation, tracer | +| `@0xbuff/throughline/remark` | Build-time `remarkThroughline` validation and optional MDX output (no React dependency) | +| `@0xbuff/throughline/react` | Optional `ThroughlinePlayer`, `ThroughlineEmbed`, `ThroughlineScenarios` and React types | +| `@0xbuff/throughline/examples` | Copyable authored fixtures | +| `@0xbuff/throughline/styles/throughline.css` | Opt-in, namespaced CSS | + +Only these five entry points are supported; importing from package +`src/` or `dist/` paths is not part of the API. The compiler is +React-free, network-free and usable in offline tooling. + +## How it works + +1. **Parse:** `compile` reads one `thl ` block, preserving useful + source-line information for diagnostics. +2. **Build:** `flow` is parsed into an authored program; computed domains + construct the same program model from bounded input parameters. +3. **Validate and compile:** the engine rejects invalid references, JSON + values, conflicting writes and excessive work, then produces an initial + frame plus a snapshot for each step. +4. **Present (optional):** the React player uses the compiled frames to + render a responsive scene, controls, captions and an inspectable state. + Seeking a frame does not execute the source a second time. + +Trace schema version `1` is independent of the npm package version. +Source length is capped at 262,144 UTF-16 code units; the authored +program is limited to 200 entities, 1,000 relations, 2,000 steps and +10,000 events, plus work/expansion budgets. These limits are for +deterministic work, not a guarantee of exact heap or CPU usage. + +For compiler and schema details, see [architecture](architecture.md). diff --git a/package.json b/package.json index da91d7e..1d79b96 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@0xbuff/throughline", - "version": "0.0.2", + "version": "0.1.0", "description": "Readable state over time, authored in Markdown. A deterministic trace compiler with an optional React player.", "private": false, "license": "Apache-2.0", diff --git a/scripts/audit-source.mjs b/scripts/audit-source.mjs index 301aac5..c27ee31 100644 --- a/scripts/audit-source.mjs +++ b/scripts/audit-source.mjs @@ -24,7 +24,6 @@ for (const file of [ "CONTRIBUTING.md", "CODE_OF_CONDUCT.md", "SECURITY.md", - "SUPPORT.md", ]) { const contents = readFileSync(join(root, file), "utf8"); assert(contents.length >= 300, "Incomplete community file: " + file); diff --git a/src/__tests__/readme-quickstart.test.ts b/src/__tests__/readme-quickstart.test.ts index 2290aa1..a53edbc 100644 --- a/src/__tests__/readme-quickstart.test.ts +++ b/src/__tests__/readme-quickstart.test.ts @@ -2,10 +2,10 @@ import { readFileSync } from "node:fs"; import { describe, expect, it } from "vitest"; import { compile } from "../index.js"; -describe("public README quick start", () => { - it("compiles the exact hierarchy fence published in the README", () => { - const readme = readFileSync(new URL("../../README.md", import.meta.url), "utf8"); - const section = readme.split("## Hierarchical groups and drill-down (TL-I14)")[1]?.split("## Supported domains")[0]; +describe("public documentation examples", () => { + it("compiles the exact hierarchy fence in the user guide", () => { + const guide = readFileSync(new URL("../../docs/user-guide.md", import.meta.url), "utf8"); + const section = guide.split("## Hierarchical groups and drill-down")[1]?.split("## Supported domains")[0]; expect(section).toBeDefined(); const source = section?.match(/~~~thl flow\n[\s\S]*?\n~~~/)?.[0]; expect(source).toBeDefined();