Repository navigation
ARCHITECTURE
The canonical, living description of how RISE is built and why it is built that way. There is one of these. If something here disagrees with the tree, the tree is right and this file is a bug —
src/core/system-design.test.jsexists to make that bug fail a build rather than mislead a reader.
Scope. Three things, in order: what the system is (§1–§6), the contracts that constrain it (§7), and every significant design decision with the alternatives rejected and the tradeoff that decided it (§8). §9 records what this design costs, and §10 says how the document is kept true.
When to update this file. In the same change that alters a boundary, adds or removes a room, changes a contract, or settles a decision in §8. Not after.
A browser-based audiovisual reader. Text is broken into atoms — a word, a phrase, a line — each carrying a duration. A Player advances atoms against a clock. A Chamber paints them over procedural or sourced imagery with a bed of sound. The same compiled session can instead be projected into Page, a spatial typographic composition. An Experience Program can author what appears when.
Around that engine sit rooms: Portal, Library, Chapel and Rosarium, Workshop, Vault, Scriptorium, Curia, Journeys, Via, Keystones, Settings.
Cloudflare serves the app shell and same-origin decision routes. The Library's optional recommendation route reads a curated Standard Ebooks catalog from PostgreSQL, caches that public catalog and short-lived decisions in Redis, and asks JEV to choose one book on a decision-cache miss. The reader's source text, proposal validation, and reading pipeline remain in the browser.
Every decision in §8 is downstream of these. They are the axioms; everything else is a recommendation.
- Reader material stays local by default. Source text, reading history and personal media stay in the browser. When the reader explicitly routes a Scriptorium request with JEV, only the intent they entered and target word count are sent to the RISE function and TypeSafe. The reader supplies the TypeSafe key for that request; RISE does not persist it. When the reader asks for a Library recommendation, only their entered intent is sent to the RISE Worker and, on a decision-cache miss, OpenRouter. PostgreSQL holds public catalog metadata. Redis holds that catalog and validated choices for five minutes; its decision key is a keyed digest of the intent and catalog, and it does not store the raw intent.
- Reverent degradation. A work, image or sound that will not resolve is absent — never a broken frame, never a substitute. Silence outranks approximation.
- Provenance travels with the work. A reader should always be able to tell a received text from one written here, and every visual carries its rights.
- Structure is read, never inferred. An ingest may not destroy a distinction the source made, and may not re-guess one it discarded.
╔═══════════════════════════════════════════════════════════════════════════════╗
║ RISE — SYSTEM DESIGN ║
╚═══════════════════════════════════════════════════════════════════════════════╝
BUILD PLANE — run deliberately, by a person, never on a reader's request
┌─────────────────────────────────────────────────────────────────────────┐
│ scripts/ (ingest · harvest · audit · catalog · voice · render · gate) │
│ │
│ Standard Ebooks ─┐ │
│ Gutenberg ───────┼─▶ *-ingest.mjs ─▶ words-in == words-out ─▶ sha256 │
│ Douay-Rheims ────┘ │ (refuses on mismatch) │
│ ▼ │
│ Met · AIC · NASA ─▶ *-harvest.mjs ─▶ contact sheet ─▶ HUMAN PIN │
│ │
│ Kokoro TTS ───────▶ build-voice-pack.mjs ─▶ recitation WAV + manifest │
│ │
│ check-release-readiness.mjs ── fails closed while any gate is open │
└────────────────────────────────┬────────────────────────────────────────┘
│ emits JS modules + public/ assets
▼
BUILD ┌──────────────────┐
│ vite build │ Rollup follows dynamic
│ ~3 s │ imports; no manualChunks
└────────┬─────────┘
▼
DELIVERY ┌──────────────────────────────────────────────────────────┐
│ Cloudflare Worker · SPA rewrite · /assets/* immutable │
│ index.html no-cache — it names the hashed chunks │
│ CSP: self + named museum/text origins; no third-party JS │
└────────────────────────┬─────────────────────────────────┘
▼
╔═══════════════════════════════════════════════════════════════════════════════╗
║ BROWSER — reading runtime; optional same-origin decision requests. ║
║ ║
║ index.html ─▶ src/app.js — composition root: boots app-scoped services, ║
║ injects operations into the route manifest ║
║ ┌──────────────┬───────────────┬──────────────┬─────────────────┐ ║
║ ▼ ▼ ▼ ▼ ▼ ║
║ ┌──────────┐ ┌────────────┐ ┌───────────┐ ┌────────────┐ ┌─────────────┐║
║ │ Router │ │ Session │ │ Player │ │ Audio │ │ Visual │║
║ │ in-memory│ │ Compiler │ │ the clock │ │ Engine │ │ Cortex │║
║ │ backstack│ │ THE ONLY │ │ 5 states │ │ Web Audio │ │ the ONLY │║
║ │ crossfade│ │ WAY IN │ │ │ │ recitation │ │ flash │║
║ └────┬─────┘ └─────┬──────┘ └─────┬─────┘ └─────┬──────┘ │ dispatcher │║
║ │ │ │ │ └──────┬──────┘║
║ │ │ │ │ │ ║
║ ▼ │ ▼ ▼ ▼ ║
║ ┌─────────────┐ │ ┌────────────────────────────────────────────┐ ║
║ │ ROOMS │ │ │ SURFACES │ ║
║ │ Portal │ └───────▶│ Chamber (stream, in time) │ ║
║ │ Library │ │ Page (spatial, same Session) │ ║
║ │ Chapel/Via │ └────────────────────────────────────────────┘ ║
║ │ Rosarium │ ║
║ │ Workshop │ each room is lazily imported with its own stylesheet ║
║ │ Vault │ ║
║ │ Scriptorium│ ┌──────────────────────────────────────────────────────┐ ║
║ │ Curia │ │ SOURCES registry + providers + IndexedDB cache │ ║
║ │ Journeys │ │ archive · local · gutenberg · sacred · arxiv · │ ║
║ │ Keystones │ │ generated · wikimedia (failure degrades one, not │ ║
║ │ Settings │ │ the app) │ ║
║ └─────────────┘ └──────────────────────────────────────────────────────┘ ║
║ ║
║ STORAGE localStorage (settings, journals, blueprints, images, orbital) · ║
║ IndexedDB (workshop media, personal swells, local works, source cache) · ║
║ flash consent is a ONE-USE IN-MEMORY capability, deliberately not ║
║ persisted (§8.18). src/core/user-data.js is the export and erase ║
║ inventory — a store missing from it cannot be carried out or cleared. ║
╚═══════════════════════════════════════════════════════════════════════════════╝
│ anonymous · no-referrer · abort + timeout ▲
▼ │ failure ⇒ stillness
┌────────────────────────────────────────────────────────────────────────┐
│ THIRD PARTIES Met · Art Institute · Cleveland · Rijksmuseum · │
│ Wikimedia · Gutenberg · arXiv. Pinned catalogs are preferred to live │
│ search; a live call is a convenience, never a dependency. │
└────────────────────────────────────────────────────────────────────────┘
OFFLINE RENDER — a separate path, deliberately not the live one
┌────────────────────────────────────────────────────────────────────────┐
│ Experience Program ─▶ compileRenderPlan ─▶ src/core/render/clock.js │
│ (rational frame index, NOT rAF) ─▶ Playwright Chromium paints the │
│ Chamber stage at explicit presentation times ─▶ ffmpeg H.264/AAC │
└────────────────────────────────────────────────────────────────────────┘
The diagram above is drawn: it says what the design is, and §10 checks the
claims in it that can be checked. This one is derived — npm run docs:diagram
reads every non-test module under src/, resolves its relative imports, and
writes what follows. It cannot disagree with the code, because the code writes
it, and CI fails when the committed copy is not what src/ produces.
flowchart LR
app["app<br/>composition root<br/>8 modules"]
audio["audio<br/>Web Audio, recitation<br/>10 modules"]
components["components<br/>routed views<br/>45 modules"]
content["content<br/>texts, imagery, journeys<br/>231 modules"]
core["core<br/>session, player, router<br/>153 modules"]
enterprise["enterprise<br/>talk program, speaker rail<br/>28 modules"]
oracle["oracle<br/>2 modules"]
page["page<br/>spatial projection<br/>4 modules"]
sources["sources<br/>text and visual providers<br/>22 modules"]
vendor["vendor<br/>SyberLabs design kit<br/>2 modules"]
visuals["visuals<br/>procedural generation<br/>61 modules"]
app -.-> |3 lazy| audio
app --> |1| components
app --> |5| content
app --> |36| core
app -.-> |1 lazy| sources
app -.-> |1 lazy| visuals
audio --> |1| content
audio --> |5| core
components -.-> |1 lazy| app
components --> |2| audio
components --> |24| content
components --> |166| core
components -.-> |1 lazy| page
components --> |4| sources
components -.-> |2 lazy| vendor
components --> |18| visuals
content --> |3| audio
content --> |16| core
content --> |17| sources
content --> |1| visuals
core --> |6| audio
core --> |14| content
core --> |4| sources
core --> |23| visuals
page --> |2| core
page --> |3| visuals
sources --> |2| content
sources -.-> |3 lazy| visuals
visuals -.-> |5 lazy| content
visuals --> |18| core
visuals --> |4| sources
Solid is a static import and travels in the first load; dashed is reached
only through import() and is deferred. The number on an edge is how many
imports it stands for. Generated by npm run docs:diagram — edit the
generator, not the diagram.
Layering is described here, not enforced: an edge is a fact, not a permission. A dashed edge is the shape §8.24 argues for — deferred rather than deleted — and it is the same set the first-load measurement prices.
| Plane | Holds | Changes when | Shipped to a reader |
|---|---|---|---|
| Control |
src/ code — engine, rooms, surfaces, providers |
a behaviour changes | yes, as hashed chunks |
| Data | works, chapel books, catalogs, recitation audio, pinned imagery | an editorial act occurs | yes; currently through the control plane (see §8.2) |
| Build |
scripts/ — ingest, harvest, audit, render, release gate |
a process changes | no |
The build plane is the one that enforces the §2 constraints. Its refusals — word-count mismatch, missing rights basis, an uncertified work on a public shelf — are the reason those constraints are properties rather than intentions.
src/app.js is the composition root. It owns application-scoped services
and translates component events into navigation or session compilation.
src/app/route-manifest.js is the only module allowed to know every routed
room; it lazy-loads each room and passes only named capabilities. The immersive
session factory is a separate lazy module, so route extraction does not add its
player or visual machinery to first load.
Production exposes no application singleton on window. Development and the
browser suite may install the frozen seven-operation window.__RISE_TEST__
bridge from src/app/test-bridge.js; an ordinary production build does not
install it.
src/core/router.js owns crossfade transitions, view activation and
deactivation, the back stack, and failure restoration. Routed components must
make document-level listeners lifecycle-aware with activate(),
deactivate(), destroy(). A rejected async initializer must not leave the
transition lock held or the previous view hidden.
src/core/session-compiler.js is the only way a reading is built. Every
launch surface calls it. Do not recreate chunk or pacing logic in a component.
The Current is what every entrance arrives at: a compiled Session and the
Player that runs it. It is not a further type. new Session appears only in
the compiler and new Player only in src/app/chamber-session-factory.js,
and src/core/current.test.js fails if either appears anywhere else. The
reading that follows a division of a work is not copied field by field: every
field of a Session is classified in src/core/session-successor.js as
identity, source, or reading, and a field that is none of them fails a test.
The Current has one place: the head of the Stream, player.sessionState .currentIndex. A projection is another way of looking at it. The Page opens
on the paragraph that holds the head (PageReader.showAtom, fed by the atom
range each text block carries), and nothing done in a projection moves the head,
because there is no seeking (LATERAL-TRAVERSAL-SPEC §1). The names
Constellation and Stage are reserved for projections that do not exist
yet.
Under a span of the reading's words there may be a thread: a gloss (written, by
the program's authority) or an echo (received: it stores no words and names
where the edition's are), both anchored to source text in the canonical
program's thread track, plus the image and sound the score already anchors
there. src/core/undercurrent.js gathers what lies under one atom. A dive
looks there: a hold is a glance, a tap an anchor (src/core/dive.js). It holds
the Player as pausing does and never moves the head, and it is offered only on a
reading that has threads.
A reading's pace has one vocabulary of profiles, PACE_CURVE_IDS in
src/core/pacing.js, and the compiler, the Reader Setup, the Workshop, saved
projects and the settings a reader keeps are each held to it by
src/core/pace-profiles.test.js. Jev's list is a smaller contract with the
Worker and must stay inside it. breath is the newest: atoms swell and ease
about every ten seconds, a whole number of cycles per reading, phased from the
authored clock so the reading is as long as it was. An atom at or near the
shortest an atom can be does not swell, so a reading too fast to swell is left
exactly as it was. It measures nothing about the reader.
src/core/player.js owns the authoritative reading clock and the playback
state machine: idle, playing, paused, interlocuting, complete.
src/components/Chamber.js renders and does not own the clock.
src/visuals/visual-cortex.js is the only flash dispatcher. It owns active
visual selection, decoded image pools, abort ownership on config change, the
execution-time consent and photosensitivity checks, and the presence lifecycle.
src/sources/registry.js owns provider discovery and initialization.
Provider initialization is retryable; registry initialization is idempotent; a
provider failure degrades that provider, not startup.
Layering, checked by §10: src/core and src/visuals never import from
src/components, statically or dynamically. Rooms communicate with the
application through callbacks passed in at construction.
src/enterprise/ is a sibling of the reader, not a room. The reader does
not import it, and it imports nothing outside itself.
src/enterprise/boundary.test.js fails if either side reaches across. What a
speaker may see is admitted by the talk-program gate in that directory, not by
the Experience Program. §8.30.
Every place a reader can be. This list is checked against src/components/
in both directions by §10, so a room added without a line here, or a line here
outliving its room, fails a build.
| Room | Module | What it is |
|---|---|---|
| Portal | src/components/Portal.js |
the hub, and the first screen |
| Create | src/components/Create.js |
original personal readings, private revisions, and portable text |
| Keystones | src/components/Keystones.js |
the public entry corridor |
| Mint | src/components/Mint.js |
the door a minted sequence opens onto |
| Chamber | src/components/Chamber.js |
a reading, in time |
| ChamberOrbital | src/components/ChamberOrbital.js |
tuning a reading before it starts |
| Library | src/components/Library.js |
the prepared editions |
| Chapel | src/components/Chapel.js |
the scripture corpus |
| Rosarium | src/components/Rosarium.js |
the Rosary, on the liturgy engine |
| Via | src/components/Via.js |
the Stations of the Cross |
| Workshop | src/components/Workshop.js |
authoring a composition |
| Visual Lab | src/components/VisualLab.js |
exploring, saving, and reusing Living Flame scenes |
| Vault | src/components/Vault.js |
saved compositions and archetypes |
| Scriptorium | src/components/Scriptorium.js |
a model composes; a gate refuses |
| Curia | src/components/Curia.js |
the source and rights record |
| Journeys | src/components/Journeys.js |
authored long-form experiences |
| Settings | src/components/Settings.js |
preferences, export and erase |
| Guide | src/components/Guide.js |
onboarding, as an overlay rather than a route |
| BetaGate | src/components/BetaGate.js |
invitation UX; not a security boundary (§7) |
Seven modules in src/components/ are deliberately not rooms; they support
routed rooms: src/components/Admit.js,
src/components/NamingModal.js, src/components/SourceBrowser.js and
src/components/VisualNavigator.js, plus the Jev voice input helper
src/components/jev-dictation.js, the shared room frame
src/components/room-chrome.js (header, icons, Alert), and the SyberLabs
chrome helper src/components/atlas.js, which lazily imports the vendored
design-system kit in src/vendor/syber/ (the ambient atmosphere behind Home
and the gate, and the RISE sigil) so neither engine is part of first load.
The Navigator's columns, text material, preview, and Chapel trays live in src/components/visual-navigator/ so the
shell stays a mount point. Chamber mounts a Fit-mask runtime from
src/core/fit-mask-runtime.js rather than owning the glyph-mask state machine.
a work is chosen
▼
provider.get(id) ──▶ payload (a dynamic import today — see §8.2)
▼
session-compiler.js
│ validate and bound input 50–1000 wpm · 2,000,000 chars/source
│ chunk each source MAX_CHUNK_WORDS 16 · PHRASE_FLOOR 5
│ verse is READ from the edition
│ attach source name and id to every atom
│ insert timing-locked source boundaries
│ apply the pacing curve timingLocked atoms are exempt
▼
Session { atoms[], totalDuration } ── the source of truth for duration
▼
player.js ── advances atoms; a visual presence PAUSES the reading clock
│ and is awaited; a rejected visual resumes without entering
│ visible-duration accounting
▼
Chamber (stream) │ PageReader (spatial) — same Session, two projections
- Visual safety is an execution-time veto, including when photosensitivity mode is enabled during a running session. Never auto-grant consent from a preset or a saved configuration.
-
VisualFlashGateadmits a presentation only within a visible-duty ceiling over a rolling window, and only after the previous presence has rested. A gate reservation is committed only after the source renders, so unavailable content consumes no cadence budget. -
Treat as untrusted at every HTML or URL sink: remote metadata, uploaded
filenames, pasted text, saved browser data. Prefer
textContent; useescapeHtmlandsafeUrlonly where templating is unavoidable. - Network and worker failure must produce bounded stillness or a local fallback, never an unbounded playback stall.
-
A new personal store is added to
src/core/user-data.jsin the same change that introduces it. A store missing from that inventory is data export cannot carry out and erase cannot clear. - The BetaGate is invitation UX, not an authorization boundary. Invite data and codes ship to the browser. Real access control would require a server-side identity service, which §8.1 rejects.
The format is fixed and checked by src/core/system-design.test.js: every
entry states Chosen, Rejected, Why, and Status. Status is one
of settled, open, deferred, or reversed.
- Chosen: the browser is the entire runtime. Static files on a CDN.
- Rejected: a server tier with accounts, sync, and server-side identity.
- Why: the tradeoff is unusually lopsided. A backend buys cross-device sync, real access control, server-side rate limiting toward museum APIs, and telemetry. It costs the §2.1 constraint outright — "nothing leaves" stops being a property of the architecture and becomes a promise about conduct — and it imports availability, consistency, replication, authentication, authorization and an operational budget into a project that currently has none of those problems. A CDN already scales to any readership without a design change. The one thing genuinely lost is real access control, and that loss is accepted and named in §7 rather than hidden.
- Status: settled.
-
Chosen: a work is a content-addressed asset.
content/manifest.json(schemarise.content-manifest.v1) names every work; the payload lives atcontent/works/<sha256>.jsonand is fetched at read time bysrc/core/content-store.js, which verifies the digest on arrival. The hash is the URL, so the address an object was fetched by is the digest it must have. -
Rejected: generated
.jsmodules undersrc/content/, reached by dynamicimport()from a catalog — which is what this was until it was cut. - Why: compiling books bought one real thing — Rollup hashed, split and cache-busted payloads for free, and a missing import was a build error rather than a 404. It cost far more. The JavaScript compiler parsed novels; the repository carried the corpus; a test fork needed a raised heap ceiling to compile a single book; and a withheld work had to be unlinked from the catalog or the bundler shipped it anyway — the defect that once built and deployed 82 MB of unreachable books. Measured at the cut: shipped JavaScript fell from 18.7 MB to 3.25 MB with no book text left in it at all, and first load fell to 58.8 KB brotli over three requests. What the old design could not buy at any price is what this one gets for nothing: a payload is re-verified in the reader's browser on every read, so a silently corrupted object is unreadable rather than readable-and-wrong. A work that will not verify is absent, per §2.2 — never substituted.
-
Status: settled. Recorded as
openwhen this register was written, and closed by the change that cut the seam.
-
Chosen: a withheld work appears in
content/manifest.jsonwithshelved: falseand itswithheldReason, and its payload is simply not addressed. -
Rejected: the earlier mechanism, where a withheld work carried metadata
but no
loadfunction, and a test asserted that correspondence in both directions. -
Why: that test —
reachable-payloads.test.js— existed to stop the bundler shipping something a runtime filter could not remove. Once content is not built, the bundler is not on the path, and the defect it guarded is not merely fixed but impossible. Deleting a test because its subject ceased to exist is the strongest available form of a fix, and is why the deletion appears in the same change as the seam.content-manifest.test.jsreplaces it, asserting what is now true: every shelved work resolves, and every withheld one states a reason. - Status: settled.
- Chosen: a withheld work keeps its metadata, provenance and a stated reason. The payload stays on disk and in git.
- Rejected: deleting the payload.
- Why: a withholding is an editorial act, and it must stay reversible and legible to a future curator — "withheld, never deleted," with every withholding stating a reason, enforced by test. The mechanism for keeping a withheld work off the wire has changed twice and is recorded in §8.2a; the rule about not destroying the payload has not changed at all.
- Status: settled.
- Chosen: Rollup's default splitting, which follows the dynamic imports we write.
- Rejected: named cache groups for large subsystems.
-
Why: it was a grouping directive read as a deferral one. Naming a module
there makes it a dependency of the entry, so the shell emits a
modulepreloadand the browser fetches it before the reader has chosen anything — the audio engine kept being preloaded afterapp.jswas changed to import it dynamically, because the list still named it. It also suppressed Rollup's own "dynamic import will not move module into another chunk" warnings, which are the report that a deferral has been defeated. Measured worth of the whole mechanism: three kilobytes. - Status: settled.
- Chosen: Kokoro runs at build time; the deployed app plays same-origin audio addressed by normalized phrase text.
-
Rejected: running the model in the reader's browser — and this one was
measured before it was rejected, not assumed. The browser path was built
and tried:
speechSynthesisis a formant synthesiser and was never a candidate; q8 WebAssembly ran at a real-time factor of 2.6–3.0 against a budget of 0.75; q4f16 produced non-finite samples; the WebGPU path produced numeric explosions large enough that the code now rejects WebGPU before encoding. The record is kept indocs/vision/RECITATION-SPEC.mdrather than deleted with the code. - Why: beyond the measurements, runtime inference would need a model host, a WebAssembly policy exception and a large first-run download, and would make a reading depend on a third party. Pre-building keeps the CSP free of any script or WASM exception and makes recitation byte-identical and certifiable — the acoustic ledger binds a human verdict to exact audio bytes, which runtime synthesis could not support. The governing rule was written as "treat speech as unavailable rather than choosing a backend by feature detection alone," which is §2.2 applied to sound. The cost is size: the packs ship uncompressed, and that is the second-largest known cost (§9).
- Status: settled for the mechanism; the delivery format is open.
- Chosen: a plan is compiled, Playwright Chromium paints the Chamber stage at explicit presentation times, ffmpeg encodes.
-
Rejected:
MediaRecorderor WebCodecs capturing a live session. -
Why: capture records whatever the machine managed to draw, so the artifact
depends on the recording machine's load. The offline path advances a
deterministic rational frame clock (
src/core/render/clock.js, explicitly notrequestAnimationFrameand notAudioContext.currentTime), so the same program yields the same frames. The cost is that render is not available to a reader in the browser: the production build carries no write path, and the Workshop hands out a job description for the CLI instead. - Status: settled.
-
Chosen, for now:
Playerowns the reading clock; each persistent visual field runs its ownrequestAnimationFrameloop. -
Rejected so far: one scheduler with subscribers and a single
sessionTime. - Why: each engine was written to own its own animation, and nothing forced a shared timeline. The cost is that nothing can answer "what time is it" for a session as a whole, and a rendered MP4 and a live reading of the same session run on different notions of time. The deterministic clock the live path lacks already exists in the render path.
- Status: open.
- Chosen: imagery is harvested offline, reviewed on a contact sheet, and pinned. Live API calls are a convenience.
- Rejected: live search against museum APIs as the primary path.
- Why: live search cannot be rate-limited across readers, cannot be rights-checked before display, and puts a third party on the reading path. Harvest-and-pin means a human approved every image and its rights before a reader could meet it, which §2.3 requires. Two rejections are recorded with their evidence: the Wikimedia category registry is empty by design after an audit found a category silently returning nothing for its whole life — "a searched source can rot invisibly, and a pinned one cannot" — and the Met provider was retired because its public API serves roughly 750-pixel derivatives over pools too shallow to hold a reading. The cost is a smaller, slower-moving collection.
- Status: settled.
-
Loose end:
netlify.tomlstill grantsconnect-srctocorsproxy.io, and no module undersrc/,scripts/ore2e/calls it. It is a stale allowance rather than a live dependency — a CSP grant nothing needs is a surface with no purpose, and it should be removed.
- Chosen: a small shelf chosen as acceptance fixtures for textual forms — epic, drama, lyric, wisdom, essay, novel — each ingested structure-preserving and certified end to end.
- Rejected: the accumulate-then-clean loop that produced eighty-eight Gutenberg works.
- Why: the old loop was acquire → ingest → find garbage → write a detector → clean → find different garbage. It could not terminate, because a detector registry finds only what a signature already describes: "every detector reports zero" gets weaker as you learn more. A canon of favourites proves nothing; a canon of forms proves the instrument. The cost is a catalogue small enough to look unfinished, accepted deliberately.
- Status: settled.
-
Chosen: direct DOM construction and template strings, one bespoke module
per room, six production dependencies:
sql.jsfor browser-local work,@neondatabase/serverlessand@upstash/redisfor the Worker catalog path, and@ai-ecoverse/kev.js,onnxruntime-weband@huggingface/tokenizersfor on-device Kev, imported only by the EnterpRise worker that runs it (§8.32). The tokenizer already shipped inside kev.js; it is named because the worker builds Kev's session itself. - Rejected: React, Vue, Svelte or any virtual-DOM library.
-
Why: the tradeoff is real in both directions. A framework would give
declarative rendering, diffing, and would largely remove the
innerHTMLsurface that currently requiresescapeHtmldiscipline at every sink. It would cost a dependency, a rewrite of every room, and a rendering model between the author and the paint — in a project whose whole subject is precise control of what appears when. The recorded defects in this codebase have been structural, not rendering bugs. -
Status: settled, and revisited if the
innerHTMLsurface ever produces a real defect rather than a theoretical one.
- Chosen: plain JavaScript with JSDoc where it helps.
- Rejected: a TypeScript migration.
- Why: types would catch a class of error this project has not been making. Its expensive defects have been a vocabulary living in two places, a guard that could not fail, structure destroyed at import, and a build-time dependency a runtime filter could not remove — none of which a type system sees. The cost of migrating is a six-figure line change across the tree.
- Status: settled for now; the reasoning is about observed defect classes, so new evidence should reopen it.
-
Chosen: a view registry with a back stack. Three things have real
addresses: the Keystone corridor, the rosary door, and
/p/<slug>for a minted sequence. - Rejected so far: URL-addressable rooms.
-
Why: no reason recorded in the tree — the router was built for
transitions, and addresses were never required. The cost is that most of RISE
cannot be linked to, browser Back does not meaningfully work inside a room,
and a reload lands on the Portal.
handleNavigatealready pushes history for one corridor, so the mechanism exists. -
A MINTED SEQUENCE IS AN ADDRESS BECAUSE IT HAS TO BE. It exists to be
printed on a card and scanned, which is a URL and nothing else. It is a
third narrow case rather than the general regime this entry still rejects:
the register in
src/content/programs/is an allowlist, so the address names a mint rather than naming a file. - AND IT RESOLVES TO A THRESHOLD, NOT TO A READING. The same rule the Keystone corridor follows, for a reason worth stating: a reading begun from a cold address bar begins with no user activation, so the browser refuses the audio and the first phrase is silent, and every safety notice is stepped over on the way. The button on the threshold is the gesture the audio lifecycle is waiting for.
- Status: open.
- Chosen: Vitest on jsdom for the unit suite, plus a Playwright browser suite, plus two unit tests that use real system tools — one hands bytes to a real ffmpeg, one paints a live Chamber stage in real Chromium.
- Rejected: a stub for either of those two.
- Why: "a stub would only prove we can satisfy our own stub." Those two tests are the reason CI can claim an MP4 can be produced and a frame can be painted at all. The cost is that the runner must install ffmpeg and Chromium, and that jsdom remains a weak substrate for Web Audio, IndexedDB, device pixel ratio and real animation frames.
- Status: settled; broadening the real-browser layer is open.
-
Chosen: test forks start with an explicit
--max-old-space-size. - Rejected: scaling worker count against system memory.
- Why: it could not have worked — what kills a fork is its own V8 old-space limit, and no number of workers changes that limit. The suite passed on a workstation and died on CI over nothing either machine was short of, because Node 20 and Node 22 default that ceiling differently. Naming it removes the dependence on which Node picked the number.
-
What this entry predicted, and got wrong. It said the cap was raised
around a cause that was still there: V8 died in
CompilationCache::LookupScript → String::SlowFlatten, flattening a module's source text to compile it, and the modules that size were the books — so the ceiling was expected to come down once §8.2 removed them from the module graph. §8.2 resolved, the books left, and the ceiling is still needed. Measured rather than assumed: a fork capped at 2048 still dies, and the stack is now an ordinary incremental-marking failure under the event loop. Removing the books removed one file's ability to fill a heap by itself; it did not change what a fork accumulates across the files it is handed. - Status: settled. Do not delete this on the theory that §8.2 made it unnecessary — that theory was tested and is false. The test is one command: cap a green run at 2048 and see whether it is still green.
-
Chosen:
npm run release:checkis a single admission report that exits nonzero while any machine-verifiable or declared human gate is open. Certification is bound to bytes and withdrawn automatically by re-ingest. - Rejected: treating a green build as sufficient.
- Why: "a green build is necessary, never sufficient." Editorial, acoustic, device and comprehension judgements cannot be automated, and a suite that passed would otherwise imply they had been made. The cost is that the public debut is blocked on human throughput, which is the real critical path today and is stated as such rather than engineered around.
- Status: settled. Do not weaken the checker to obtain green; close the named evidence gap.
-
Chosen: a
vite:preloadErrorlistener and a router check treat a failed chunk import as a stale build and reload once, guarded by a sentinel;index.htmlis servedmust-revalidate. - Rejected: letting the tab break, and reloading unguarded.
-
Why:
index.htmlnames the hashed chunks, so a tab left open across a release asks for a file the new deploy replaced, gets a 404, and can no longer reach any view it had not already loaded. A stale chunk is not a transient network error and retrying cannot fix it. The sentinel exists because an unguarded reload turns a real network failure into a loop. - Status: settled.
-
Chosen:
scripts/build-division-index.mjsprecomputes division structure; withheld divisions go to a separate file nothing shipping imports. - Rejected: deriving divisions in every browser.
- Why: it lets a card say how many chapters a work has without downloading the work. Labels are verified against the divided text rather than against counts, because a count-only check passed while chapter labels drifted and broke Scriptorium navigation.
- Status: settled.
- Chosen: consent to flashing imagery is an in-memory capability for one presentation.
-
Rejected: the former browser-session grant held in
sessionStorage. -
Why: a persisted grant means a reader who consented once meets flashing
imagery later without being asked, including in a room they did not consent
in.
sessionStorageis now only ever cleared, never written. Consent is also never auto-granted from a preset or a saved configuration (§7). - Status: settled.
-
Chosen: when a room is deleted, its persisted keys and data namespaces
stay. The Solarium is gone and
rise_sol_plan_v1remains insrc/core/user-data.js; the Atrium room is gone and itsatr-accession ids andatriumCollectionsfield remain. - Rejected: deleting the keys with the room.
- Why: "a key dropped from that registry is data that export cannot carry out and erase cannot clear" — a reader who planned a day in the Solarium still has one saved. Renaming a persisted key is a migration, not a cleanup, and doing it inside a deletion is how a deletion becomes an outage. The key is removed when nobody can still be holding one, which is not the same day the room goes.
- Status: settled.
-
Chosen:
src/core/reading-limits.jsholds the pace bounds, and every surface imports them. - Rejected: each surface carrying its own slider range.
- Why: a reader who chose 60 wpm was silently overridden to 100, because a narrow window was the min and max of one modal's slider, copied twice. This is the defect class that recurs most in this codebase — a vocabulary living in two places where only one learns a new word — and the standing rule is to prefer deleting one copy to synchronising two. Where duplication is unavoidable, a test asserts the two agree, which turns silent drift into loud failure.
- Status: settled.
-
Chosen:
RELEASE_SERVES_UNCERTIFIEDistrue, written in source rather than a build flag, and the shelf tells the reader a work is a candidate. - Rejected: failing closed until certifications land.
- Why: failing closed was the right default and the wrong outcome — nothing has ever been certified, so the public shelf served nothing while development served everything. Putting the override in source rather than in an environment variable means it is visible in review and cannot be set accidentally by a deploy.
-
Status: open, and explicitly temporary. Set it to
falsethe day the certifications land.
- Chosen: a reader who has expressed no preference gets Gallery — continuous imagery behind the text.
- Rejected: rhythmic full-frame flashing as the default.
- Why: Gallery is the only surface that never flashes and never goes black, so it is what an unasked reader should meet, and it needs no consent prompt. Raising a photosensitivity warning over a surface that does not carry the risk asks a reader to accept a danger that is not there. A domain that authors its own surface — Chapel, a Vault program — still wins, per the three-layer law: content authors, the runtime follows, the cortex renders.
- Status: settled.
-
Chosen: the Curia apply endpoint and the MP4 export endpoint are Vite
plugins with
apply: 'serve', so they exist only on the dev server. - Rejected: shipping write endpoints with the static build.
- Why: a static deploy with no write path cannot be made to write. The cost is that the Workshop's export hands a reader a job description for the CLI instead of a file, which §8.6 already accepts.
- Status: settled. Production also ships no source maps, to keep the bundle opaque.
- Chosen: Journeys are on ice — the published list is empty while the scores, tests and compiler stay. RISE Chain stays out of production.
- Rejected: re-anchoring Journey scores quickly, and shipping Chain under release pressure.
- Why: Journey scores quote editions the canon no longer serves, so their anchors broke — correctly. Re-authoring someone's score against a new translation is an editorial act, not a repair. Chain was held back because production pressure would immediately impose questions of attribution, moderation, and whether generation could accidentally reproduce long source passages — questions better answered before readers than after.
- Status: deferred.
- Chosen: code is Apache 2.0; authored strings, curated selections and names are reserved; visual engine output is not reserved separately from the engine.
- Rejected: splitting the licence by directory, and reserving procedural output as composition.
- Why: the directory split "got two answers, which is the same as getting none," because a directory holds both code and authored text. Reserving engine output was dropped on the reasoning that a grant to run and modify the engine is a grant to the images it draws — claiming otherwise while licensing the engine Apache would be incoherent.
- Status: settled.
-
Chosen: a stance (
src/core/stances.js) is a named partial of the configuration the Orbital already builds. It writes fields in the visual, audio and temporal orbits, and what it emits takes the same road as a hand-built configuration: the Orbital's persistence normalizers, thennormalizeVisualConfigin the session compiler. Which stance a reader is standing in is derived from the configuration, never stored. - Rejected: a simplified reading mode with its own path to the cortex; and hiding the orbits behind the stance row.
-
Why: the parameters are not the problem — meeting forty of them with no
orientation is. A second path would double the validation surface that
§7 depends on, for a layer whose entire job is to name points in the space
the validators already police. Storing the chosen stance was rejected for a
smaller reason with the same shape: a remembered choice would keep claiming a
posture the reader had adjusted away from, so the row would lie. Deriving it
cannot. There is no
studystance yet; it is the entry to Page mode, which is sequenced after this step. - Status: settled.
-
Chosen:
src/app.jsowns application state,src/app/route-manifest.jsowns the fixed lazy route table, and rooms receive the exact callbacks or instances they use. Browser automation gets a frozen seven-operation bridge only in development or an explicitly flagged test build. -
Rejected: a production
window.risesingleton, room code reaching back throughglobalThis, and a generic service bag passed into every room. - Why: a room that discovers its host through a global hides its inputs, makes isolated tests lie, and lets unrelated code mutate the whole application. Named capabilities make ownership visible at construction. The narrow test bridge preserves observability without making automation access part of the production product surface.
- Status: settled.
-
Chosen: the Scriptorium offers an optional JEV route to choose between
the two proposal formats RISE already accepts:
rise.experience-program.v1andrise.agent-operation-set.v1. The core session puts that choice into the curator prompt. A same-origin Cloudflare Worker route forwards only the reader's intent and target word count to TypeSafe's JEV API; the reader supplies the API key for the request. - Rejected: putting the TypeSafe key in browser code, adding a second proposal format, or letting JEV accept or execute the proposal.
- Why: proposal format is a real next-operation choice already understood by the Scriptorium parser and producer. This places optional JEV routing in the authoring flow while keeping its decision bounded by RISE's existing schemas and validation. JEV's choice is a routing recommendation; deterministic parsing, source resolution, producer checks and the reader's Begin action retain their existing authority.
- Data boundary: no source text, Library records, personal media, reading history or generated proposal is sent to JEV by this route. The user-entered intent may itself contain personal information and is sent only after the reader presses Route with JEV. The TypeSafe key is held in page memory and forwarded in the authorization header; RISE does not store it.
- Status: open. The route exists in the Worker; each reader must supply a TypeSafe API key, and the production path still needs direct verification.
- Chosen: an optional Library form sends the reader's intent to the same-origin Cloudflare Worker. The Worker reads an exact-edition Standard Ebooks catalog from PostgreSQL, caches that public catalog in Redis for 30 seconds, and asks JEV through OpenRouter to choose one work ID on a decision-cache miss. Redis caches the validated decision for five minutes under a keyed digest of the intent and catalog, without storing raw intent. The browser opens that held edition through the existing Library path.
- Rejected: sending book text or personal reading history to JEV, storing raw intents or decisions in PostgreSQL, inventing a recommendation from local heuristics when JEV fails, and accepting a model-selected unheld edition.
- Why: a recommendation is useful only when it leads to a book the reader can actually open. PostgreSQL owns the catalog, Redis reduces repeat reads, and JEV makes a bounded choice on the first matching request. Exact edition and source revision checks keep the model inside the release inventory. The brief description shown after the decision is curated catalog copy; JEV does not generate prose.
- Status: open. The same-origin production request and book opening were verified; the five-minute decision cache still requires production verification.
-
Chosen: the live room lives in
src/enterprise/. One deck, an in-memory corpus of documents and tables, cards prepared before the talk, one speaker rail, and one stage. Promote re-checks the talk-program gate for that room's audience. An audience final that misses the program may retrieve a permitted sentence onto the rail. Listed presenters share the rail. A decision sees the transcript window plus candidate ids, titles, scores, and layouts. A chart names a table and columns; the renderer copies cells. Promote, Dismiss, and Retract are the speaker's. - Rejected: forking the reader into a second app; extracting Chamber, the Experience Program, and the Worker into a shared package; putting the rail inside a reader route; mounting Chamber on the stage; a second rail; an external file-host connector; an OpenRouter call on the enterprise decision route.
-
Why: the failure that matters is a confidential document, or a number
that was not in the source, in front of the room. The gate, the id-only
decision, and the cell renderer make that failure loud. The phases in
docs/superpowers/specs/2026-09-27-enterprise-room-design.mdare implemented insrc/enterprise/andworker/enterprise-decision.mjs, fromdocs/superpowers/plans/2026-09-27-enterprise-room.md. The reader's lack of access control (§8.1) is unchanged: this audience check belongs to the sibling, and the sibling is not on the reader's first load. - Status: settled, except the rejected provider call on the enterprise decision route, which §8.31 reverses. The suite's latency ceilings are the product targets on this fixture, not a measurement of a live recognizer.
-
Chosen: the room listens through the browser recognizer. Interim speech
warms the lexical tier; a final, or the presenter's typed Ask, asks for a
decision.
session.preparebuilds arise.enterprise-context.v1(src/enterprise/context.js): evidence (window, speaker, mode), structure (candidate ids, titles, scores, layouts; rail ids and titles), and authority (the actions this turn allows, never promotion). The live loop (src/enterprise/live.js) keeps one decision in flight per channel (speech, ask), cancels it only when a newer turn on the same channel arrives, and bounds it with a timeout./api/enterprise-decisionjoins the other decision routes behinddecisionProviderand the limiter and asks the provider one choice question whose options are opaque keys.session.resolveaccepts an answer only for a turn it issued, once, while no later turn on its channel is pending. The trace (src/enterprise/trace.js) records every step without the transcript. The rule decider remains for tests and an explicitly chosen local mode. - Rejected: falling back from a failed JEV decision to rules; letting the provider name a card id or write text; sending documents, tenants, or the audience to the route; deciding on interim speech; a speech vendor SDK; a server-side trace store; a vector store.
-
Why: the model is useful for choosing which permitted card fits what
was just said, and harmful anywhere else. Every failure mode — timeout,
cancellation, a late answer, a malformed answer, an outage — resolves to
the rail as it was, and the stage still moves only on a presenter's tap
after the gate runs again. Spec:
docs/superpowers/specs/2026-09-28-enterprise-live-loop-design.md. - Status: open. The loop, route, and page are verified with a scripted recognizer and routed decisions. A live Kev or Jev decision on the deployed room, and field latency from a real microphone, are not yet measured.
-
Chosen: a third decider, "Kev (device)", runs Kev through WebGPU with
@ai-ecoverse/kev.jsandonnxruntime-webin a dedicated worker (src/enterprise/kev-worker.js). It asks the same question and reads the answer the same way as the server route (src/enterprise/rail-question.js).src/enterprise/device-model.jspins what may load: a manifest naming any checkpoint but the pinned one is refused before weights are fetched, and the runtime binary must match the digest of the lockfile's copy. The model downloads only when a presenter chooses it. Until it is ready, or after it fails, decisions hold. The worker script alone may fetch model hosts and compile WebAssembly: the Cloudflare Worker serves it with its own policy (worker/enterprise-decision.mjs), and every page keeps the site policy inpublic/_headers.kev-check.htmlmeasures load, latency, and agreement on a real device. The weights never sit whole in the worker: each file streams into Cache Storage (src/enterprise/kev-store.js) and reaches the runtime as a disk-backed Blob, which the JSPI build ofonnxruntime-webreads one tensor at a time on its way to the GPU. A browser without JSPI loads nothing. -
Rejected: a local Python service for Windows users (CUDA, WSL2, and a
localhost port every site could reach); falling back to the server or the
rules when the device cannot run Kev; the CPU WebAssembly path, too slow for
a live rail; serving the runtime binary from this site, which is over the
static asset size limit; kev.js's own
loadKev, which reads every weight file into memory before the session exists and crashed the tab loading Kev-4B's 4.7 GB on a 16 GB Windows machine. -
Why: the transcript and the decision stay on the presenter's machine,
with nothing to install. Kev-4B on the device is pinned to the checkpoint
the server Kev serves (
deploy/kev/modal_app.py), and a test keeps the two pins equal. - Status: open. Kev-0.8B loads and decides on Chrome 153 for Windows with an AMD RX 5700 (30 of 30 questions, 214 ms median). While a model is loaded, Chrome's GPU process holds system memory about 1.4 times the model's size, so Kev-4B needs roughly 6 GB of free memory; it has not yet been run on Windows. Hosts other than the Cloudflare Worker serve the worker script with the site policy, so Kev (device) fails closed there.
-
Chosen: RISE is one instrument that turns any source into a compiled,
paced, time-based reading, the Current. A room is an entrance (it chooses a
source, a pace and layers and hands them to
compileSession), a contributor (it adds a source, a layer, a projection or a pace), or a rail beside the reading (Curia, Settings). The two constructors are guarded by a test, every entrance's output is checked against one contract, and the reading that follows a division is derived from the Current by classifying each Session field rather than by copying a list. - Rejected: a new Current type beside Session (a second vocabulary for the same object); folding the Rosarium and Via into it, which run their own fixed clock by covenant and must not gain a layer, a dive or an affect signal; keeping the hand-copied field list, which drops any field the Session learns later without saying so.
- Why: every entrance already converged on one compiler, so the work was to make that convergence something a test can break. The successor list was the one place the reading was rebuilt by hand, which is the defect of a vocabulary in two places where only one learns a new word.
-
Status: open. Guards, the successor, one place for the reading, threads
under a passage, dive and surface, and the breath pace are built. Emotions, as
a projection, and Confluence follow. The plan is
docs/plans/CURRENT-CONSOLIDATION.md.
Stated plainly so it is never rediscovered as a surprise.
- The corpus is still versioned in the application repository, even though it no longer travels through the module graph. §8.2 removed the build-time cost; where the bytes live is a separate question and is still open.
- Recitation ships uncompressed, and is now by a very wide margin the largest thing a deploy contains — the audio is roughly seventy times the size of all the JavaScript. §8.5. With the content seam cut, this is the single biggest remaining cost in the design.
- There is no single timeline. §8.7.
- Most rooms have no address. §8.12.
- Access control does not exist, by choice. §8.1, §7.
- The public shelf serves uncertified candidates under an override that is explicitly temporary and should not become permanent by neglect. §8.21.
- The CSP grants an origin nothing calls. §8.8.
- The release is gated on people, and cannot be hurried by engineering. §8.15.
Good intentions rot; this file already had to be rewritten once because it
described rooms that no longer existed. So the claims that can be checked
are checked by src/core/system-design.test.js, which fails a build when:
- a
src/…orscripts/…path named here does not exist on disk; - a module in
src/components/is not mentioned here, or this file mentions a component that is gone — asserted in both directions, because either half failing is silent; - a decision in §8 is missing Chosen, Rejected, Why or Status, or uses a status outside the fixed vocabulary;
- the production dependency list in §8.10 disagrees with
package.json; - §5's layering claim stops being true —
src/coreorsrc/visualsreaches intosrc/components, statically or dynamically.
The import graph in §3 is not checked, it is generated:
npm run docs:diagram writes it out of src/, and CI fails when the committed
copy is not what the tree produces. A claim that writes itself cannot drift.
CI runs that guard and that generator in a job of their own, because both are about this file and both must run for a change that touches only this file — the unit suite, where the guard lives, is skipped for a prose-only change.
What the test cannot check — whether the reasoning is still true — is why §8 records reasons rather than conclusions. A reason that has stopped applying is visible to a reader; a conclusion is not.
npm run test:run # includes the guard above
npm run build
npm run test:e2e # CI shards this four ways
npm run test:e2e:gate # the corridor only, for a fast local loop
npm run docs:diagram # must leave this file unchanged
npm run measure:first-load # what a first visit costs, against its budget
npm run release:check # fails closed; that is correctPublished from docs/specs/ARCHITECTURE.md. Edits made here are overwritten on the next push to main.
Start here
- COMPOSER-FIRST-ROADMAP
- VISION
- PROJECT-KNOWLEDGE
- ARCHITECTURE
- SYSTEM-DESIGN-REVIEW-2026-08-22
- ACCOUNT-SAVES
- RELEASING
- PLUS-CUSTOMER-LIFECYCLE
- AGENTS
Release
Pilot
- LIVE-CURRENT
- LIVE-HANDOFF
- LIVE-MCP
- DIRECTORY-SCREENSHOTS
- INSPECTOR-RUN-2026-10-10
- CLAUDE-RUN-2026-10-10
- CREATIVE-CONTROL-EVALS
- PERCEPTION-EVAL
- SOUND-LEVELS
- WORDING
- RUN-2026-10-08
- COMPOSER-INSTRUMENTS
- CHATGPT-DEMO
- EMBED-WITNESS
- RISE-GATE0
- CHATGPT-HOST-2026-10-04
- LIVE-002-HOST-2026-10-05
- EMBED-STAGE-HOST-2026-10-07
- RELEASE-GATES-2026-10-07
- EMBED-SAFETY-2026-10-05
- LIVE-GEMINI
- LIVE-NVIDIA
- LIVE-IMAGERY
- LIVE-EVALUATION
- LIVE-RED-TEAM
- CURRENT-CONSOLIDATION
- RISE-CHANGES
The Archive - texts and their editing
- ARCHIVE-CANON-SPEC
- ARCHIVE-CLEANSING-SPEC
- CORPUS-REVIEWER-PROMPT
- TYPESETTING-CANON
- PHRASE-CHUNKING-STUDY
- CHUNKER-AWARENESS-STUDY
- LIBRARY-SPEC
- SOL-PD-ACQUISITIONS-DOSSIER-LITERATURE-2026-07-28
Authoring - scores, rooms, and rendering
- EXPERIENCE-PROGRAM-SPEC
- LIVE-CURRENT-EVENTS-V1
- RISE-CURRENT-V1-SLICE
- RISE-SDK
- SCRIPTORIUM-SPEC
- WORKSHOP-COMPOSITION-STUDIO-SPEC
- AGENT-COMPOSITION-AND-RENDER-SPEC
- NARRATION-LANE-SPEC
- RECITATION-SPEC
- SCRIPTORIUM-STRENGTHENING-SPEC
Reading surfaces
- PAGE-MODE-SPEC
- RHYTHMIC-VISUAL-PRESENCE-SPEC
- IMAGE-STREAM-SPEC
- LATERAL-TRAVERSAL-SPEC
- CONTINUOUS-FIELD-SPEC
- JEV-VARIANCE
- INVOCATION-SKINS
- PHASE-2-SAFETY-SPEC
- SPATIAL-CHAMBER-SPEC
- Premium_Mobile_Chamber
Imagery - what may be shown, and on whose authority
- MUSEUM-ATLAS
- SOURCE-CURATION-SPEC
- SOURCE-EXPANSION-SPEC
- ATRIUM-IMAGERY-SPEC
- PERICOPE-IMAGERY-SPEC
- icon-museum-request-draft
- TEXT-ATTUNED-IMAGERY-SPEC
The Chapel
Direction and unbuilt work
Affect and the Emotions room
Reading decision service and retired gate
Product direction and team tracking
- README
- READER-DIRECTION
- CONSOLIDATED-READER
- COMPOSER-FIRST-ROADMAP
- TRACKER-DESIGN
- 2026-10-03-direction-decisions
- 2026-10-04-composer-decision
- 2026-10-05-consolidated-reader-decisions
- 2026-10-05-embed-stage-decision
- 2026-10-06-score-speech-service-roadmap
- 2026-10-07-current-with-a-look
- 2026-10-07-reader-control-and-replay
- 2026-10-05-one-colour-vocabulary-design
- 2026-10-07-chrome-follows-theme
- 2026-10-07-gallery-follows-with-works
- 2026-10-05-canonical-home-design
- 2026-10-04-reader-evidence
- 2026-10-original-performance-roadmap
- 2026-10-08-voice-hardening-2-findings
- 2026-10-09-feelings-sounds-parked
- 2026-10-10-connector-directory-readiness
- 2026-10-10-connector-submission-packet
- 2026-10-16-mystery-school-complete-library
- 2026-10-13-mystery-school-plan