open-standards/aac/SPEC.md
Transaction Science 98116df0d4
Some checks failed
no-vanity-metrics / Scan committed Markdown for vanity metrics (push) Successful in 3s
standards workspaces build + test / eoc-rs (push) Successful in 1m51s
standards workspaces build + test / jouleclaw-rs (push) Successful in 2m48s
standards workspaces build + test / jcp-rs (push) Successful in 1m22s
standards workspaces build + test / aac-rs (push) Successful in 1m13s
standards workspaces build + test / joule-code-rs (push) Successful in 41s
standards workspaces build + test / proof-rs (push) Successful in 1m13s
standards workspaces build + test / joulecontract-rs (push) Successful in 40s
standards workspaces build + test / map-rs (push) Successful in 43s
standards workspaces build + test / mesh-rs (push) Successful in 51s
standards workspaces build + test / sandbox-rs (push) Successful in 2m9s
standards workspaces build + test / openpay (push) Successful in 1m53s
standards workspaces build + test / smart-byte-rs (push) Successful in 2m33s
standards workspaces build + test / arl-rs (push) Has been cancelled
standards workspaces build + test / joule-ui-rs (push) Has been cancelled
standards workspaces build + test / jouledesk-rs (push) Has been cancelled
standards workspaces build + test / joulehook-rs (push) Has been cancelled
standards workspaces build + test / sense-rs (push) Has been cancelled
standards workspaces build + test / macOS energy backend (jcp-energy) (push) Has been cancelled
feat(aac-core, aac-web): what the engine learns from a person is kept, and theirs
Suggestions learned from the person's words were lost on every reload,
so they never became theirs.

- aac-core: PredictionMemory, the learned counts per language (words,
  pairs, sentence starts, how the person wrote them, context words) and
  recent and pinned phrases, as plain versioned data.
  PredictionEngine::memory exports it; restore_memory adds it back
  (each language to its own, now or when that language is set; bounded
  as learning is; a newer version is refused); forget_memory clears it
  all. Restoring reproduces the memory exactly.
- aac-web: the memory is kept on the device beside the language packs
  (the store app updates leave alone), restored once at start before
  anything may overwrite it, and saved a moment after each utterance
  (storage full is reported like any failed save). Words spoken from
  text mode now teach the engine too. Settings > Word suggestions shows
  how many words were learned in this language and offers "Forget the
  words I've said", which takes a second press and clears every
  language.
- SPEC 4.2 (proposed): what a client learns from the person stays on
  their device, is forgettable (confirmed, every language) and should be
  portable; the whole word list is chosen per language.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-30 15:07:04 -04:00

267 lines
26 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# AAC — Specification
**Version 0.2.0 (draft) · Status: draft · Licence: CC-BY-4.0**
This document specifies the AAC open standard: the data model for a portable communication board, the access-method abstraction, the energy-tiered communication cascade, canonical serialization, the interchange format, and the receipt. The key words **MUST**, **MUST NOT**, **REQUIRED**, **SHALL**, **SHOULD**, **SHOULD NOT**, **MAY**, and **OPTIONAL** are to be interpreted as described in RFC 2119.
The Rust reference implementation in [`aac-rs/`](aac-rs/) is normative-by-example: where this prose and the reference types disagree, that is a defect to be reconciled, not a license to diverge.
---
## §1. Scope and terms
AAC (augmentative and alternative communication) is technology that lets a person who cannot rely on speech compose and produce utterances. This standard specifies how a **board** — a person's communication surface and the customization invested in it — is represented, accessed, and exchanged so that it is portable across implementations and devices, operable offline, and never gated behind payment.
- **Board** — a surface holding cells, of a given `BoardType` (`Grid | VisualScene | Keyboard | Hybrid | QuickAccess`).
- **Cell** — a single selectable target on a board, at a fixed grid position, with content and an action.
- **Utterance** — the message a user composes by selecting cells.
- **Access method** — the physical channel by which a user selects (`DirectTouch | EyeGaze | HeadTracking | SwitchScan{switches} | Joystick | Bci | PartialVoice | Pointer`).
- **Client** — software that opens, renders, drives, and speaks a board.
- **Cascade level** — one of ten tiers (L0–L9) through which a communication operation is resolved.
This standard does **not** specify rendering pixels, a UI toolkit, a TTS engine, or symbol artwork. It specifies the data and the invariants that make a voice portable.
---
## §2. The board data model
A board **MUST** be representable as the following structure (field names are normative; the reference type is `aac_core::Board`):
- `id` — a `BoardId` (UUID), stable for the life of the board.
- `name` — display string.
- `board_type` — one of the `BoardType` variants.
- `layout` — a `GridLayout { rows: u16, cols: u16 }`.
- `cells` — a list of `Cell`.
A **cell** **MUST** carry:
- `id` — a `CellId` (UUID).
- `position` — a `GridPosition { row: u16, col: u16 }`, 0-indexed.
- `content` — one of: `Symbol { symbol_id, label }`, `Text(String)`, `Photo { image_ref, label }`, or `Blank`.
- `action` — one of: `Speak(text)`, `Navigate(BoardId)`, `Modifier(GrammarModifier)`, `Backspace`, `Clear`, `Predict`, `SpeakAll`, `Buffer(text)`.
- `style` — colors and optional font size; a client **MAY** override style for accessibility (§5) but **MUST NOT** alter `position`.
- `frequency` — usage count, monotonically non-decreasing.
- `locked` — whether the cell is protected from structural edits.
Boards form a **graph**: a `Navigate` action edges from one board to another. The graph is the source of truth; a grid layout is one projection of it. A `Blank` cell is meaningful — it reserves a position to preserve the motor grid (§3) and **MUST NOT** be elided on round-trip.
---
## §3. Motor stability (normative invariant)
A user's motor plan — the learned muscle memory of where each word lives — is the single most valuable property of a configured board. Therefore:
1. A cell's `position` **MUST NOT** change once assigned, for the life of that cell, except by an explicit user/editor edit to an `unlocked` cell. Prediction, context, frequency, language switching, and any L3+ enrichment **MUST NOT** move a cell.
2. A client **MUST NOT** reflow, resort, or auto-arrange cells on a board whose cells are `locked`.
3. Hiding a word for teaching (progressive disclosure) **MUST** be done by rendering its cell as visually inactive **in place** — never by removing it and collapsing the grid.
4. Two openings of the same board on the same or different conforming clients **MUST** present identical cell positions.
An implementation that rearranges a locked board is non-conformant regardless of any other quality.
---
## §4. Symbols
Cell `Symbol` content references a symbol by `symbol_id` and `SymbolSource`, **not** by embedded pixels. A board therefore travels as a small, text-like document; artwork is resolved by the client from a symbol library. A client **MUST** preserve the `symbol_id` and `label` on round-trip even if it cannot render the referenced artwork, and **SHOULD** fall back to rendering the `label` text when artwork is unavailable. This keeps a board legible across implementations that ship different symbol sets.
### §4.1 Language honesty
A board travels between languages as a graph edge (a translation of each label), but a word list translated from one language is not another language's core vocabulary: the words people use most differ from language to language. Therefore:
1. Every language a client offers **MUST** state how its core vocabulary was obtained, as a `CoreStatus`: `ValidatedNativeCorpus` (a published core list derived from speakers of that language), `StakeholderValidated` (nominated and rated by its speakers and communication partners), `PrimesOnly` (a provisional seed from semantic primes and frequency lists), or `GlossOnly` (glosses of another language's core list).
2. A language record without a `CoreStatus` **MUST** be read as `GlossOnly`.
3. A client **MUST NOT** present `GlossOnly` vocabulary as that language's core vocabulary, and **SHOULD** show each language's status where the person chooses a language.
The reference ships 45 languages: English is `ValidatedNativeCorpus`; the other 44 are `GlossOnly` until their speakers validate their own. (Conformance C7.)
### §4.2 What a suggestion may propose *(proposed — draft for the version after 0.2.0; not part of 0.2.0 conformance)*
A client **MAY** suggest words — a next word, a sentence start, the completion of a word being typed — from a language model and from what the person has said. A model built from published text (subtitles, news, the web) proposes whatever that text holds, including rude, sexual and violent words, and a word proposed unprompted is a word put in the person's mouth. Therefore:
1. A client **MUST** propose only words from the person's vocabulary — the words of the boards they use, in their language — and words they have said themselves, unless the person, or someone acting with them, has chosen to include a language's whole word list.
2. The whole word list **MUST** be off unless chosen, and a client **MUST** say where it comes from before it is chosen.
3. A model **MAY** rank the permitted words whatever the choice; ranking a word is not proposing it. Letter predictions (which keys to order or highlight) **MAY** use the whole word list, because a letter proposes no word.
4. None of this limits what the person says: a word they have said is theirs, and is proposed thereafter; typed text is never changed.
5. What a client learns from the person's words is theirs. It **MUST** stay on their device unless they choose otherwise, and **MUST** be forgettable by them, in every language at once, with a confirmation so that no stray selection erases it. It **SHOULD** be portable between devices and clients.
6. The whole word list is chosen for one language at a time: a choice made for one language **MUST NOT** carry to another.
The reference implements this as `SuggestionScope` in `aac-core` (default `Vocabulary`), and the learned memory as `PredictionMemory` (plain data, versioned: `PredictionEngine::memory`, `restore_memory`, `forget_memory`); a conformance vector is to follow.
### §4.3 People in symbols *(proposed — draft for the version after 0.2.0; not part of 0.2.0 conformance)*
A board is used all over the world, so the people its symbols show must not default to one skin, one gender or one age. Following Unicode's emoji model (UTS #51):
1. **Skin.** A client that draws people **MUST** draw them by default in a generic, non-realistic colour — no one's skin — and **MUST** let the person choose one of the five skin tones of the emoji modifiers U+1F3FB–U+1F3FF, applied to every person in every set it can re-tone. A symbol whose skin colour cannot be changed without changing something that is not skin **MAY** be kept as drawn, and the client **SHOULD** list which.
2. **Gender.** A concept that does not name a gender **SHOULD** be drawn with a figure that reads as no particular gender, or with no figure; a gendered figure is for a gendered word (*mother*, *man*, *girl*).
3. **Age.** A concept that does not name an age **SHOULD NOT** be drawn with a child's figure: a board used by an adult shows adults.
4. The skin tone is the person's choice, kept for them rather than for a board, and changing it **MUST NOT** move or resize any cell (§3).
The reference implements skin as `aac_core::symbols::SkinTone`: the neutral is UTS #51 §2.4's own example, #FFCC22, and the five tones are OpenMoji's; hair keeps its colour whatever the tone. Its symbol build writes skin (and OpenMoji's skin shadow) as the CSS custom properties `--aac-skin` and `--aac-skin-shade` over the neutral, and lists every person symbol and its treatment in `tools/symbols/people-review.md`.
---
## §5. Access model
Every access method is a first-class input. A board **MAY** be driven by any method; the method and its tuning travel with the user's profile, not the board.
- **AccessMethod** — as enumerated in §1. `SwitchScan { switches }` carries the switch count (1, 2, or more).
- **MotorProfile** — `fine_motor`, `sustained_press`, `tremor_level`, `min_target_mm`, optional `max_reach_mm`, `preferred_side`. A client **SHOULD** adapt target size and timing from this profile, and **MUST NOT** require a smaller target than `min_target_mm`.
Timing-based methods are normative in their own right:
1. **Switch scanning** — a conforming scanner **MUST** support row-column, linear, and group scan patterns, a configurable scan rate, and configurable acceptance/timing (§5.2).
2. **Dwell / eye-gaze** — selection by sustained gaze **MUST** use a configurable dwell time with visible progress feedback (§5.3).
3. No access method **MAY** be a precondition for opening or reading a board; a board authored for eye-gaze **MUST** still open under touch or switch.
### §5.1 Timing belongs to the person
1. A client **MUST** let the person, or someone acting with them, set the scan rate and the dwell time directly.
2. Automatic adaptation of timing **MUST** be off unless the person opts in.
3. A client **MUST NOT** change timing silently. It **MAY** measure the person's own responses and **propose** a change, which takes effect only when they accept it — or, if they opted in, automatically under the rules of §5.2 and §5.3.
4. A proposal **MUST** be derived from this person's responses, never from a population norm alone.
### §5.2 Scan-rate floor
A scan rate that a client recommends, proposes or applies from measured responses **MUST** sit above the person's typical press, so that the highlight has not moved on when they act:
- it **MUST NOT** be derived from fewer than 10 recorded responses;
- with `M` the mean and `P95` the nearest-rank 95th percentile of the recorded response times, it **MUST** be at least `max(⌊100·M/65⌋, P95)` — the reference rule — clamped to `[300, 5000]` ms;
- a change within 10% of the current rate **SHOULD NOT** be proposed.
(Conformance C5.)
### §5.3 Dwell proposals
A dwell proposal **MAY** be shorter as well as longer — a person whose dwell is too long is slowed down as surely as one whose dwell is too short makes errors:
- it **MUST NOT** be derived from fewer than 10 selections;
- one step **longer** when more than 20% of recent selections were undone;
- one step **shorter** when fewer than 5% were undone and more than 20% of attempts were abandoned late (gaze left after at least half the dwell);
- a shorter proposal **MUST NOT** go below 300 ms; no dwell may be below 150 ms or above the configured maximum;
- an opted-in client applies at most one change per measurement window, then measures afresh.
The reference steps along the ladder `DT(X) = 300·e^(X/12) − 150` ms. (Conformance C6.)
### §5.4 Adaptation from a prior never moves a cell *(proposed — draft for the version after 0.2.0; not part of 0.2.0 conformance)*
A client **MAY** use a prior over the options on a board — a language model, or the person's own usage counts — together with the person's measured press timing. It **MAY** change only these four things:
1. the **order** in which rows, groups, blocks or cells are scanned;
2. the **emphasis** of a highlight;
3. the **dwell** each cell needs;
4. **which option a switch press selects**.
It **MUST NOT** change any cell's position or size, nor which cells a row, group or block contains (§3). The AI may rank, order, highlight or weight; the person chooses the words.
1. **Consent.** Each of these is an adaptation under §5.1. It **MUST** be off unless the person opts in. What it would change **MUST** be available to show them before they opt in and while it is on. A client **MUST NOT** apply anything other than what it shows: an adaptation shown for one board **MUST NOT** apply on another, and turning an opt-in off **MUST** discard what was applied, so that opting in again needs a fresh preview.
2. **Order.** A new order **MUST** take effect only at the start of a pass, so that a highlight never jumps mid-pass. Turning the opt-in off **MUST** restore the fixed order no later than the next pass. The reference visits units by descending prior mass and members by descending weight, with ties broken by position. This minimises the expected number of highlights before the intended one.
3. **Dwell.** A cell's weighted dwell **MUST** lie within `[max(⌈0.8·D⌉, min(D, 300)), max(min(⌊1.5·D⌋, max), D)]` ms, where `D` is the person's dwell and `max` is their ceiling; for any `D` of at least 150 ms the lower bound is at least 150 ms, and a `D` below that is never weighted below itself. A likelier cell **MUST NOT** need a longer dwell than a less likely one. A weighting **MUST NOT** change `D`, propose a change to it, or restart the §5.3 measurement window, and **MUST NOT** change the length of a dwell already in progress. Turning the opt-in off **MUST** restore `D` for every cell at once.
Because it is bounded, keyed to `D`, previewed, and undone at once, a per-cell weighting within these bounds is not a timing proposal under §5.1 item 4 or §5.3: it **MAY** be derived from a population prior such as a language model, and **MAY** be re-applied after every selection.
4. **Selection.** A press may be weighed against the person's press-offset distribution, so that a press landing just after the highlight has moved on selects what they meant. The following rules apply:
- An option **MUST** be identified by its position at its scan level, never by its rank in a pass, so that an order applied between passes cannot make one option name two cells.
- A press **MUST** be measured only from onsets that could have produced it. The reference admits an onset when the press lands between half a scan interval before it and two intervals after it.
- The distribution **MUST** be learned only from selections the person kept. The presses of every level of a selection (the row and the cell) belong to it. A selection counts as kept only after further selections have been made without it being undone — an Undo reached by scanning is itself a selection, and **MUST NOT** confirm the selection it undoes. Presses that led into a row, group or block the person then left without choosing a cell **MUST NOT** be learned, nor presses the distribution does not explain better than a spurious press. The reference learns a selection once three newer selections have been completed, so it survives two consecutive undos.
- The client **MUST NOT** act on the distribution before 10 such presses.
- The prior **MUST NOT** select on its own: the presses themselves **MUST** favour any selected option at least 4:1 over every other option, and a lone option at least 4:1 over the hypothesis that every press was spurious. The reference caps the prior's ratio between any two options at 99:4 and selects only when the leader's posterior odds over the runner-up reach 99:1; a persisted rule that would weaken the 4:1 floor is not used.
- When the evidence is short of that, the scan continues and the next press adds evidence. A level that would end after such a press — a row's single pass, or the last cycle — **SHOULD** be offered again; the reference offers at most two extra passes per visit.
- Until the person opts in and 10 presses have been kept, a press selects what was highlighted.
(Conformance C8–C11, proposed.)
---
## §6. The cascade
A communication operation **MUST** be resolved through the lowest cascade level that can satisfy it. The ten levels and their order are normative:
| Level | Name | Order-of-magnitude energy |
|-------|------|---------------------------|
| L0 | `CacheHit` | ~1 µJ |
| L1 | `SymbolLookup` | ~10 µJ |
| L2 | `LocalPredict` | ~100 µJ |
| L3 | `ContextPredict` | ~1 mJ |
| L4 | `SemanticExpand` | ~10 mJ |
| L5 | `ConversationAi` | ~100 mJ |
| L6 | `GenerativeComm` | ~1 J |
| L7 | `VoiceClone` | ~5 J |
| L8 | `MultimodalGen` | ~10 J |
| L9 | `AdaptiveModel` | ~15 J |
**§6.1 Energy monotonicity.** The estimated energy of level `Lₙ` **MUST** be strictly greater than that of `Lₙ₋₁`. (Conformance suite C2.)
**§6.2 Offline invariant.** Levels L0, L1, and L2 — and **only** these — **MUST** be offline-capable. A conforming client **MUST** serve every L0–L2 operation with no network access, and **MUST NOT** make any L0–L2 operation contingent on connectivity. (Conformance suite C3.) The practical consequence is normative: a network failure **MUST NOT** prevent a user from selecting cells, looking up symbols, or completing words.
**§6.3 Free at every level.** No cascade level **MAY** be gated behind payment, quota, balance, or subscription at the protocol layer. An implementation **MUST NOT** refuse, degrade, or rate-limit a communication operation on the basis of billing state. Hosted enrichment services (L3+) **MAY** be operated commercially, but a conforming client **MUST** remain fully functional through L2 with no account.
**§6.4 Enrichment is optional.** L3 and above add intelligence (context, sentence construction, voice cloning, symbol generation, model adaptation). They **MUST** be reached only when a lower level cannot close the request, and their absence **MUST NOT** degrade L0–L2 behavior.
---
## §7. Canonical serialization and content addressing
A board **MUST** have a canonical serialization. JSON is the interchange encoding; a canonical form (sorted keys, normalized number forms) **MUST** round-trip byte-identically through a conforming serializer. A board's content identity **MAY** be expressed as a content address (e.g. BLAKE3) over its canonical bytes; two boards with equal canonical bytes are the same board.
---
## §8. Interchange (ingest)
A board **MUST** export to the canonical form of §7 and **MUST** be re-importable from it without loss of cell positions, content, actions, or symbol references.
A conforming engine **SHOULD** additionally **ingest** the open board interchange format and common community exports. Ingestion is one-directional and additive: it reads existing work into the substrate so a user does not start over. On ingest:
- Cell positions present in the source **MUST** be preserved (§3); positions absent from the source **MUST** be assigned deterministically (row-major fill) and then fixed.
- Unsupported or unknown fields **MUST** be reported as non-fatal warnings, never silently dropped where they affect meaning.
- A format the engine cannot yet read **MUST** fail explicitly (a named "format not yet supported" error), never partially and silently.
---
## §9. The receipt
Every operation **MAY** report a two-part energy cost (`measured + estimated`) in microjoules, with an honest provenance tag indicating whether a reading came from hardware instrumentation, a model, or an estimator. The receipt exists for **transparency only**. An implementation **MUST NOT** convert a receipt into a charge, a quota, or a gate. "This session used 3.2 mJ" is a fact shown to the user; it is never a bill.
---
## §10. Conformance
An implementation is conformant if it:
1. Round-trips the published board vectors (§7) byte-identically.
2. Preserves the motor-stability invariant (§3) across open/edit/save.
3. Routes operations to the specified cascade levels and satisfies energy monotonicity (§6.1).
4. Serves exactly L0–L2 offline (§6.2) and gates no level behind billing (§6.3).
5. Ingests the interchange format without losing positions, content, or actions (§8).
6. Leaves timing to the person and keeps proposals within §5.1–§5.3.
7. States every language's core status honestly (§4.1).
8. *(Proposed, after 0.2.0.)* Adapts from a prior only by order, emphasis, bounded dwell and selection, never by moving a cell, and only with the person's opt-in (§5.4).
The [`aac-conformance`](aac-rs/crates/aac-conformance) harness checks the cascade routing table (C1), energy monotonicity (C2), the offline invariant (C3), wire round-trips (C4), the scan-rate floor against the published response-time vectors in [`conformance/v1/scan_rate.json`](conformance/v1/scan_rate.json) (C5), dwell proposals against [`conformance/v1/dwell.json`](conformance/v1/dwell.json) (C6), and language honesty (C7). It also checks the proposed adaptation from a prior against [`conformance/v1/adaptive.json`](conformance/v1/adaptive.json) (§5.4, vector version `0.3.0-draft`). These checks are labelled `[proposed]` and counted separately; an implementation of 0.2.0 is not held to them:
- that order never moves a position (C8);
- that every adaptation requires opt-in (C9);
- that weighted dwell stays within its bounds (C10);
- that selection follows the person's presses (C11).
C5, C6 and C8–C11 recompute every expected value from the raw inputs with an independent oracle before comparing the engine against it. Any FAIL makes the run non-zero.
---
## §11. Non-claims
This standard and its reference implementation are **not** a medical device and have **not** been certified as a speech-generating device (SGD) for insurance or Medicaid funding. They make no clinical claim. Choosing an access method, vocabulary, and communication approach is best done by the person with their team, often with a speech-language pathologist — but it is never a prerequisite: no one needs a professional's permission to start communicating with AAC. The standard provides the portable substrate; it does not replace assessment, training, or support.
---
## Proposed (draft, after 0.2.0)
- **§4.2 What a suggestion may propose (new).** Suggestions (next words, sentence starts, completions) come only from the person's vocabulary and their own words, unless a language's whole word list is chosen — off by default, chosen per language, its source stated before it is chosen. A model may rank permitted words, and letter predictions may use the whole list. Nothing limits what the person says, and what is learned from them stays on their device, forgettable and portable.
- **§4.3 People in symbols (new).** People are drawn by default in no one's skin, and in any of the five Unicode skin tones the person chooses; concepts that name no gender or age are drawn with neutral, adult figures.
- **§5.4 Adaptation from a prior never moves a cell (new).** A prior may change only four things: scan order, emphasis, bounded per-cell dwell, and which option a press selects (Bayesian selection over the person's learned press timing, after Nomon). Each is opt-in, shown before it is applied, tied to the board it was shown for, and discarded on opt-out. Per-cell dwell weighting within its bounds is not a §5.1/§5.3 timing proposal. Selection learns only from kept selections — every level of them, never an abandoned row, never a selection an Undo reached by scanning undid, never a press the model does not explain — identifies options by position, and never lets the prior select. Conformance C8–C11, vector `adaptive.json` version `0.3.0-draft`.
## Changes since 0.1.0
- **§4.1 Language honesty (new).** Every language states its `CoreStatus`; an absent status reads as `GlossOnly`; glosses are never presented as a language's core vocabulary. The earlier claim that one ~400-word core covers ~80% of every language is withdrawn. Conformance C7.
- **§5.1 Timing belongs to the person (new).** Scan rate and dwell are set by the person; automatic adaptation is off unless they opt in; changes are proposed, never silent.
- **§5.2 Scan-rate floor (new).** A measured rate sits above the person's typical press (`max(⌊100·M/65⌋, P95)`, ≥ 10 responses). The previous reference rule set it to 80% of the mean press, so on average the highlight had moved on when the person pressed. Conformance C5.
- **§5.3 Dwell proposals (new).** Proposals go both ways, with floors. The previous reference could only lengthen the dwell, including by 50 ms every 30 selections regardless of how the person was doing; §5 item 2 no longer says dwell "should adapt to fatigue". Conformance C6.
- **§11** now says access and vocabulary decisions are never a prerequisite for starting.
- **Reference implementation** (not normative): the demonstrator can be mounted under a sub-path (`AAC_BASE_PATH`, `AAC_SW_URL`, `AAC_APP_TITLE`, `AAC_HIDE_ENERGY`); its Settings gain a Timing section implementing §5.1–§5.3; dwell reads a monotonic clock that works in browsers (`web-time`); 538 gloss corrections across 44 languages (`aac-core/src/gloss_audit_2026-09-28.json`).