From 5287d322e439a0d8632815134057605d2ac1ff13 Mon Sep 17 00:00:00 2001 From: Brad Edwards Date: Mon, 3 Aug 2026 03:25:44 +0200 Subject: [PATCH] docs: add Ground Control requirement specs as repo-local files Import this project's 57 requirement(s) from Ground Control as repo-local files under docs/requirements//requirement.md (specs-as-code). --- docs/requirements/PUL-A001/requirement.md | 28 +++++++++++++++ docs/requirements/PUL-A002/requirement.md | 28 +++++++++++++++ docs/requirements/PUL-A003/requirement.md | 27 ++++++++++++++ docs/requirements/PUL-A004/requirement.md | 27 ++++++++++++++ docs/requirements/PUL-A005/requirement.md | 28 +++++++++++++++ docs/requirements/PUL-A006/requirement.md | 27 ++++++++++++++ docs/requirements/PUL-A007/requirement.md | 31 ++++++++++++++++ docs/requirements/PUL-A008/requirement.md | 30 ++++++++++++++++ docs/requirements/PUL-A009/requirement.md | 31 ++++++++++++++++ docs/requirements/PUL-A010/requirement.md | 28 +++++++++++++++ docs/requirements/PUL-F001/requirement.md | 28 +++++++++++++++ docs/requirements/PUL-F002/requirement.md | 28 +++++++++++++++ docs/requirements/PUL-F003/requirement.md | 28 +++++++++++++++ docs/requirements/PUL-F004/requirement.md | 28 +++++++++++++++ docs/requirements/PUL-F005/requirement.md | 31 ++++++++++++++++ docs/requirements/PUL-F006/requirement.md | 29 +++++++++++++++ docs/requirements/PUL-F007/requirement.md | 31 ++++++++++++++++ docs/requirements/PUL-F008/requirement.md | 34 ++++++++++++++++++ docs/requirements/PUL-F009/requirement.md | 33 +++++++++++++++++ docs/requirements/PUL-F010/requirement.md | 33 +++++++++++++++++ docs/requirements/PUL-F011/requirement.md | 33 +++++++++++++++++ docs/requirements/PUL-F012/requirement.md | 34 ++++++++++++++++++ docs/requirements/PUL-F013/requirement.md | 37 +++++++++++++++++++ docs/requirements/PUL-F014/requirement.md | 40 +++++++++++++++++++++ docs/requirements/PUL-F015/requirement.md | 35 ++++++++++++++++++ docs/requirements/PUL-F016/requirement.md | 36 +++++++++++++++++++ docs/requirements/PUL-F017/requirement.md | 44 +++++++++++++++++++++++ docs/requirements/PUL-F018/requirement.md | 41 +++++++++++++++++++++ docs/requirements/PUL-F019/requirement.md | 29 +++++++++++++++ docs/requirements/PUL-F020/requirement.md | 35 ++++++++++++++++++ docs/requirements/PUL-F021/requirement.md | 35 ++++++++++++++++++ docs/requirements/PUL-F022/requirement.md | 30 ++++++++++++++++ docs/requirements/PUL-F023/requirement.md | 29 +++++++++++++++ docs/requirements/PUL-F024/requirement.md | 33 +++++++++++++++++ docs/requirements/PUL-F025/requirement.md | 33 +++++++++++++++++ docs/requirements/PUL-F026/requirement.md | 32 +++++++++++++++++ docs/requirements/PUL-F027/requirement.md | 31 ++++++++++++++++ docs/requirements/PUL-F028/requirement.md | 32 +++++++++++++++++ docs/requirements/PUL-F029/requirement.md | 33 +++++++++++++++++ docs/requirements/PUL-F030/requirement.md | 36 +++++++++++++++++++ docs/requirements/PUL-F031/requirement.md | 31 ++++++++++++++++ docs/requirements/PUL-P001/requirement.md | 32 +++++++++++++++++ docs/requirements/PUL-P002/requirement.md | 30 ++++++++++++++++ docs/requirements/PUL-P003/requirement.md | 27 ++++++++++++++ docs/requirements/PUL-P004/requirement.md | 26 ++++++++++++++ docs/requirements/PUL-P005/requirement.md | 26 ++++++++++++++ docs/requirements/PUL-Q001/requirement.md | 28 +++++++++++++++ docs/requirements/PUL-Q002/requirement.md | 35 ++++++++++++++++++ docs/requirements/PUL-Q003/requirement.md | 28 +++++++++++++++ docs/requirements/PUL-Q004/requirement.md | 34 ++++++++++++++++++ docs/requirements/PUL-Q005/requirement.md | 30 ++++++++++++++++ docs/requirements/PUL-Q006/requirement.md | 30 ++++++++++++++++ docs/requirements/PUL-Q007/requirement.md | 25 +++++++++++++ docs/requirements/PUL-Q008/requirement.md | 31 ++++++++++++++++ docs/requirements/PUL-Q009/requirement.md | 29 +++++++++++++++ docs/requirements/PUL-Q010/requirement.md | 32 +++++++++++++++++ docs/requirements/SEC-001/requirement.md | 25 +++++++++++++ 57 files changed, 1775 insertions(+) create mode 100644 docs/requirements/PUL-A001/requirement.md create mode 100644 docs/requirements/PUL-A002/requirement.md create mode 100644 docs/requirements/PUL-A003/requirement.md create mode 100644 docs/requirements/PUL-A004/requirement.md create mode 100644 docs/requirements/PUL-A005/requirement.md create mode 100644 docs/requirements/PUL-A006/requirement.md create mode 100644 docs/requirements/PUL-A007/requirement.md create mode 100644 docs/requirements/PUL-A008/requirement.md create mode 100644 docs/requirements/PUL-A009/requirement.md create mode 100644 docs/requirements/PUL-A010/requirement.md create mode 100644 docs/requirements/PUL-F001/requirement.md create mode 100644 docs/requirements/PUL-F002/requirement.md create mode 100644 docs/requirements/PUL-F003/requirement.md create mode 100644 docs/requirements/PUL-F004/requirement.md create mode 100644 docs/requirements/PUL-F005/requirement.md create mode 100644 docs/requirements/PUL-F006/requirement.md create mode 100644 docs/requirements/PUL-F007/requirement.md create mode 100644 docs/requirements/PUL-F008/requirement.md create mode 100644 docs/requirements/PUL-F009/requirement.md create mode 100644 docs/requirements/PUL-F010/requirement.md create mode 100644 docs/requirements/PUL-F011/requirement.md create mode 100644 docs/requirements/PUL-F012/requirement.md create mode 100644 docs/requirements/PUL-F013/requirement.md create mode 100644 docs/requirements/PUL-F014/requirement.md create mode 100644 docs/requirements/PUL-F015/requirement.md create mode 100644 docs/requirements/PUL-F016/requirement.md create mode 100644 docs/requirements/PUL-F017/requirement.md create mode 100644 docs/requirements/PUL-F018/requirement.md create mode 100644 docs/requirements/PUL-F019/requirement.md create mode 100644 docs/requirements/PUL-F020/requirement.md create mode 100644 docs/requirements/PUL-F021/requirement.md create mode 100644 docs/requirements/PUL-F022/requirement.md create mode 100644 docs/requirements/PUL-F023/requirement.md create mode 100644 docs/requirements/PUL-F024/requirement.md create mode 100644 docs/requirements/PUL-F025/requirement.md create mode 100644 docs/requirements/PUL-F026/requirement.md create mode 100644 docs/requirements/PUL-F027/requirement.md create mode 100644 docs/requirements/PUL-F028/requirement.md create mode 100644 docs/requirements/PUL-F029/requirement.md create mode 100644 docs/requirements/PUL-F030/requirement.md create mode 100644 docs/requirements/PUL-F031/requirement.md create mode 100644 docs/requirements/PUL-P001/requirement.md create mode 100644 docs/requirements/PUL-P002/requirement.md create mode 100644 docs/requirements/PUL-P003/requirement.md create mode 100644 docs/requirements/PUL-P004/requirement.md create mode 100644 docs/requirements/PUL-P005/requirement.md create mode 100644 docs/requirements/PUL-Q001/requirement.md create mode 100644 docs/requirements/PUL-Q002/requirement.md create mode 100644 docs/requirements/PUL-Q003/requirement.md create mode 100644 docs/requirements/PUL-Q004/requirement.md create mode 100644 docs/requirements/PUL-Q005/requirement.md create mode 100644 docs/requirements/PUL-Q006/requirement.md create mode 100644 docs/requirements/PUL-Q007/requirement.md create mode 100644 docs/requirements/PUL-Q008/requirement.md create mode 100644 docs/requirements/PUL-Q009/requirement.md create mode 100644 docs/requirements/PUL-Q010/requirement.md create mode 100644 docs/requirements/SEC-001/requirement.md diff --git a/docs/requirements/PUL-A001/requirement.md b/docs/requirements/PUL-A001/requirement.md new file mode 100644 index 0000000..9cf833d --- /dev/null +++ b/docs/requirements/PUL-A001/requirement.md @@ -0,0 +1,28 @@ +--- +id: PUL-A001 +title: "Timeline library encapsulation" +status: ACTIVE +type: CONSTRAINT +priority: MUST +wave: 0 +created_at: 2026-04-30T19:18:12.240742Z +updated_at: 2026-05-12T21:26:30.999476Z +--- + +# PUL-A001 — Timeline library encapsulation + +## Statement + +Scene modules SHALL NOT import the timeline library directly. Scene timelines SHALL be constructed via the timeline utilities exposed on the scene context. + +## Rationale + +Encapsulating the timeline library lets the runtime swap implementations without breaking scenes (ADR-003). + +## Traceability + +- CONSTRAINS → ADR `ADR-003` (GSAP as the Timeline Engine) +- CONSTRAINS → ADR `ADR-008` (Agent-Native Authoring as a First-Class Architectural Constraint) +- TESTS → TEST `tests/runtime/policy-a001-timeline-encapsulation.test.ts` (PUL-A001 source-policy gate (Vitest)) +- IMPLEMENTS → CODE_FILE `tests/runtime/source-policy.ts` +- IMPLEMENTS → GITHUB_ISSUE `50` diff --git a/docs/requirements/PUL-A002/requirement.md b/docs/requirements/PUL-A002/requirement.md new file mode 100644 index 0000000..8bec6af --- /dev/null +++ b/docs/requirements/PUL-A002/requirement.md @@ -0,0 +1,28 @@ +--- +id: PUL-A002 +title: "Audio library encapsulation" +status: ACTIVE +type: CONSTRAINT +priority: MUST +wave: 0 +created_at: 2026-04-30T19:18:16.076099Z +updated_at: 2026-05-12T21:26:32.142508Z +--- + +# PUL-A002 — Audio library encapsulation + +## Statement + +Scene modules SHALL access audio playback only via the runtime audio context. Scenes SHALL NOT instantiate `HTMLAudioElement` or directly import the audio library, except where a scene drops down to raw Web Audio with a documented justification and registers cleanup with the runtime. + +## Rationale + +Per-scene cleanup and master mute depend on routed audio access (ADR-004, ADR-008). + +## Traceability + +- CONSTRAINS → ADR `ADR-004` (Howler.js as the Audio Engine) +- CONSTRAINS → ADR `ADR-008` (Agent-Native Authoring as a First-Class Architectural Constraint) +- TESTS → TEST `tests/runtime/policy-a002-audio-encapsulation.test.ts` (PUL-A002 source-policy gate (Vitest)) +- IMPLEMENTS → CODE_FILE `tests/runtime/source-policy.ts` +- IMPLEMENTS → GITHUB_ISSUE `51` diff --git a/docs/requirements/PUL-A003/requirement.md b/docs/requirements/PUL-A003/requirement.md new file mode 100644 index 0000000..957012e --- /dev/null +++ b/docs/requirements/PUL-A003/requirement.md @@ -0,0 +1,27 @@ +--- +id: PUL-A003 +title: "Optional rendering libraries are scene-local" +status: ACTIVE +type: CONSTRAINT +priority: MUST +wave: 0 +created_at: 2026-04-30T19:18:19.087658Z +updated_at: 2026-05-12T21:26:33.394301Z +--- + +# PUL-A003 — Optional rendering libraries are scene-local + +## Statement + +The runtime core SHALL NOT import PixiJS, Three.js, or Phaser. Adoption of any of these libraries SHALL be scene-local. + +## Rationale + +Keeps the core lightweight and lets specialized rendering be opt-in per scene (ADR-005). + +## Traceability + +- CONSTRAINS → ADR `ADR-005` (DOM/CSS as the Default Rendering Surface) +- TESTS → TEST `tests/runtime/policy-a003-rendering-libraries.test.ts` (PUL-A003 source-policy gate (Vitest)) +- IMPLEMENTS → CODE_FILE `tests/runtime/source-policy.ts` +- IMPLEMENTS → GITHUB_ISSUE `52` diff --git a/docs/requirements/PUL-A004/requirement.md b/docs/requirements/PUL-A004/requirement.md new file mode 100644 index 0000000..582c9f1 --- /dev/null +++ b/docs/requirements/PUL-A004/requirement.md @@ -0,0 +1,27 @@ +--- +id: PUL-A004 +title: "Live runtime is independent of the export pipeline" +status: ACTIVE +type: CONSTRAINT +priority: MUST +wave: 0 +created_at: 2026-04-30T19:18:22.241247Z +updated_at: 2026-05-12T21:26:34.152746Z +--- + +# PUL-A004 — Live runtime is independent of the export pipeline + +## Statement + +The runtime core SHALL NOT import Remotion or any video-rendering library. Export functionality SHALL live in a separate codebase that consumes the same scene metadata and composition manifests. + +## Rationale + +Separation of concerns between live playback and headless export (ADR-006). + +## Traceability + +- CONSTRAINS → ADR `ADR-006` (Remotion as a Parallel Export Path, Not the Live Runtime) +- TESTS → TEST `tests/runtime/policy-a004-export-pipeline.test.ts` (PUL-A004 source-policy gate (Vitest)) +- IMPLEMENTS → CODE_FILE `tests/runtime/source-policy.ts` +- IMPLEMENTS → GITHUB_ISSUE `53` diff --git a/docs/requirements/PUL-A005/requirement.md b/docs/requirements/PUL-A005/requirement.md new file mode 100644 index 0000000..7f1d8d6 --- /dev/null +++ b/docs/requirements/PUL-A005/requirement.md @@ -0,0 +1,28 @@ +--- +id: PUL-A005 +title: "Composition is declarative" +status: ACTIVE +type: CONSTRAINT +priority: MUST +wave: 0 +created_at: 2026-04-30T19:18:25.743385Z +updated_at: 2026-05-12T21:26:35.304298Z +--- + +# PUL-A005 — Composition is declarative + +## Statement + +Compositions SHALL be expressed as declarative manifests of scene ids. The runtime SHALL NOT support imperative dispatch (e.g., `if/else` branching or position-based dispatch in a control script) as the source of truth for composition order. + +## Rationale + +Manifests-over-flow-control is a binding constraint of the agent-native model (ADR-002, ADR-008). + +## Traceability + +- CONSTRAINS → ADR `ADR-002` (Scene Registry and Composition Manifests as the Core Abstraction) +- CONSTRAINS → ADR `ADR-008` (Agent-Native Authoring as a First-Class Architectural Constraint) +- TESTS → TEST `tests/runtime/policy-a005-declarative-composition.test.ts` (PUL-A005 source-policy gate (Vitest)) +- IMPLEMENTS → CODE_FILE `src/compositions/default.ts` (Reference declarative composition manifest) +- IMPLEMENTS → GITHUB_ISSUE `54` diff --git a/docs/requirements/PUL-A006/requirement.md b/docs/requirements/PUL-A006/requirement.md new file mode 100644 index 0000000..d3b519e --- /dev/null +++ b/docs/requirements/PUL-A006/requirement.md @@ -0,0 +1,27 @@ +--- +id: PUL-A006 +title: "Live runtime is independent of slide frameworks" +status: ACTIVE +type: CONSTRAINT +priority: MUST +wave: 0 +created_at: 2026-04-30T19:18:33.320260Z +updated_at: 2026-05-12T21:26:36.251439Z +--- + +# PUL-A006 — Live runtime is independent of slide frameworks + +## Statement + +The runtime core SHALL NOT import or depend on slide-framework primitives (e.g., reveal.js or Spectacle). Slide frameworks MAY be used in companion projects, separate from the runtime core. + +## Rationale + +Avoids the two-runtime coordination cost rejected in ADR-001. + +## Traceability + +- CONSTRAINS → ADR `ADR-001` (Custom Experience Runtime, Not a Slide Framework) +- TESTS → TEST `tests/runtime/policy-a006-slide-frameworks.test.ts` (PUL-A006 source-policy gate (Vitest)) +- IMPLEMENTS → CODE_FILE `tests/runtime/source-policy.ts` +- IMPLEMENTS → GITHUB_ISSUE `55` diff --git a/docs/requirements/PUL-A007/requirement.md b/docs/requirements/PUL-A007/requirement.md new file mode 100644 index 0000000..4c15c92 --- /dev/null +++ b/docs/requirements/PUL-A007/requirement.md @@ -0,0 +1,31 @@ +--- +id: PUL-A007 +title: "Scene id format" +status: ACTIVE +type: CONSTRAINT +priority: MUST +wave: 0 +created_at: 2026-04-30T19:18:36.039619Z +updated_at: 2026-05-03T02:28:47.712651Z +--- + +# PUL-A007 — Scene id format + +## Statement + +Scene ids SHALL be lowercase ASCII consisting of letters, digits, and hyphens. Ids SHALL NOT be reused across scenes. + +## Rationale + +Stable, URL-safe, kebab-case ids are the agent-addressable identity contract (ADR-002, ADR-008). + +## Traceability + +- IMPLEMENTS → CODE_FILE `src/runtime/identifier.ts` (Kebab-case identifier predicate — KEBAB_IDENTIFIER_PATTERN + isKebabIdentifier) +- CONSTRAINS → ADR `ADR-002` (Scene Registry and Composition Manifests as the Core Abstraction) +- CONSTRAINS → ADR `ADR-008` (Agent-Native Authoring as a First-Class Architectural Constraint) +- IMPLEMENTS → CODE_FILE `src/runtime/scene.ts` (Scene id format guard — SCENE_ID_PATTERN, isSceneId, isSceneIdOrNull) +- IMPLEMENTS → CODE_FILE `src/runtime/registry.ts` (Scene id uniqueness enforcement — createSceneRegistry duplicate-id detection) +- TESTS → TEST `tests/runtime/scene.test.ts` (Scene id format spec — 32 cases under "scene id format (PUL-A007)" + 9 defaultNext cases) +- TESTS → TEST `tests/runtime/registry.test.ts` (Scene id uniqueness spec — duplicate-id describe block in registry.test.ts) +- IMPLEMENTS → GITHUB_ISSUE `10` (PUL-A007: Scene id format) diff --git a/docs/requirements/PUL-A008/requirement.md b/docs/requirements/PUL-A008/requirement.md new file mode 100644 index 0000000..b00960d --- /dev/null +++ b/docs/requirements/PUL-A008/requirement.md @@ -0,0 +1,30 @@ +--- +id: PUL-A008 +title: "Mode dispatch in core" +status: ACTIVE +type: CONSTRAINT +priority: MUST +wave: 1 +created_at: 2026-04-30T19:18:39.441198Z +updated_at: 2026-05-17T21:55:03.440234Z +--- + +# PUL-A008 — Mode dispatch in core + +## Statement + +Workbench mode dispatch SHALL be implemented in the runtime core. Scene modules SHALL NOT contain mode-specific branches except where they must respond to mode hints (e.g., suppressing audio in `mode=screenshot`). + +## Rationale + +Mode behavior must be uniform across scenes (ADR-007, ADR-008). + +## Traceability + +- CONSTRAINS → ADR `ADR-007` (Browser as the Default Workbench and Agent Collaboration Surface) +- CONSTRAINS → ADR `ADR-008` (Agent-Native Authoring as a First-Class Architectural Constraint) +- IMPLEMENTS → CODE_FILE `src/runtime/navigation.ts` (URL navigation grammar — NAVIGATION_MODES allowlist, parseNavigationSearch, effectiveMode (pure URL-derived mode dispatch boundary for PUL-A008 clause 1)) +- IMPLEMENTS → CODE_FILE `src/runtime/scene-loader.ts` (Scene loader — per-navigation mode dispatch: validateModeGrammar, audioOutputPolicyFor(mode), mode→runner-input hints, buildPresenterPipe, ctx.mode threading (PUL-A008 clause 1 in the runtime core)) +- IMPLEMENTS → DOCUMENTATION `docs/design/pul-a008-mode-dispatch-core-preflight.md` (PUL-A008 architectural preflight — consolidated repo-wide guardrails for mode-dispatch boundary, required reuse, cross-cutting layers, extensibility seam, and anti-patterns) +- IMPLEMENTS → GITHUB_ISSUE `56` (Issue #56 — PUL-A008: Mode dispatch in core) +- TESTS → TEST `tests/runtime/policy-a008-mode-dispatch.test.ts` (PUL-A008 source-policy gate — scene-local mode-literal branch detector (binary equality, switch/case, .includes membership) with const-alias resolution) diff --git a/docs/requirements/PUL-A009/requirement.md b/docs/requirements/PUL-A009/requirement.md new file mode 100644 index 0000000..8557760 --- /dev/null +++ b/docs/requirements/PUL-A009/requirement.md @@ -0,0 +1,31 @@ +--- +id: PUL-A009 +title: "Captions and prompter share metadata source" +status: ACTIVE +type: CONSTRAINT +priority: MUST +wave: 2 +created_at: 2026-04-30T19:18:42.131179Z +updated_at: 2026-05-18T01:10:18.552804Z +--- + +# PUL-A009 — Captions and prompter share metadata source + +## Statement + +The prompter view SHALL be derived from the same caption metadata used by the runtime. There SHALL NOT be a separate authoring source for prompter content. + +## Rationale + +Single source of truth (ADR-008). + +## Traceability + +- CONSTRAINS → ADR `ADR-008` (Agent-Native Authoring as a First-Class Architectural Constraint) +- IMPLEMENTS → CODE_FILE `src/runtime/scene.ts` (Caption interface + SceneModule.captions field + assertSceneModule schema gate — the canonical single-source authoring slot for prompter content (PUL-A009)) +- IMPLEMENTS → CODE_FILE `src/runtime/prompter.ts` (buildPrompterScript — single derivation seam reading exclusively from SceneModule.captions (PUL-A009 clause 1)) +- IMPLEMENTS → CODE_FILE `src/runtime/scene-loader.ts` (mode=prompter loader dispatch — routes navigation through buildPrompterScript(target) so prompter view is derived from caption metadata (PUL-A009 clause 1)) +- IMPLEMENTS → DOCUMENTATION `docs/design/pul-a009-captions-prompter-single-source-preflight.md` (PUL-A009 design preflight — consolidates binding guardrails for the single-source contract) +- TESTS → TEST `tests/runtime/policy-a009-captions-single-source.test.ts` (PUL-A009 source-policy gate — 131 scanner self-tests + 4 runtime-tree assertions covering four sub-rules (interface fields, parallel schema declarations, prompter Caption import boundary, scene-module object literals)) +- TESTS → TEST `tests/runtime/prompter.test.ts` (Prompter derivation behavior tests — pin buildPrompterScript reading exclusively from scene.captions (PUL-A009 clause 1, pre-existing)) +- IMPLEMENTS → GITHUB_ISSUE `57` (PUL-A009: Captions and prompter share metadata source) diff --git a/docs/requirements/PUL-A010/requirement.md b/docs/requirements/PUL-A010/requirement.md new file mode 100644 index 0000000..a046f71 --- /dev/null +++ b/docs/requirements/PUL-A010/requirement.md @@ -0,0 +1,28 @@ +--- +id: PUL-A010 +title: "Live and export share scene metadata" +status: ACTIVE +type: CONSTRAINT +priority: SHOULD +wave: 3 +created_at: 2026-04-30T19:18:44.804081Z +updated_at: 2026-05-18T03:20:51.029768Z +--- + +# PUL-A010 — Live and export share scene metadata + +## Statement + +Any export pipeline SHALL consume the same scene metadata and composition manifests as the live runtime. Export-specific metadata SHALL NOT replace live-runtime metadata. + +## Rationale + +Shared metadata keeps the scene library as the single source of truth across surfaces (ADR-006). + +## Traceability + +- CONSTRAINS → ADR `ADR-006` (Remotion as a Parallel Export Path, Not the Live Runtime) +- TESTS → TEST `tests/runtime/policy-a010-export-metadata-share.test.ts` (PUL-A010 export-metadata-share source-policy gate) +- IMPLEMENTS → DOCUMENTATION `docs/design/pul-a010-live-export-metadata-preflight.md` (PUL-A010 design preflight) +- IMPLEMENTS → GITHUB_ISSUE `58` (PUL-A010: Live and export share scene metadata) +- IMPLEMENTS → PULL_REQUEST `121` (feat: add PUL-A010 live/export shared scene metadata gate) diff --git a/docs/requirements/PUL-F001/requirement.md b/docs/requirements/PUL-F001/requirement.md new file mode 100644 index 0000000..2e6eee5 --- /dev/null +++ b/docs/requirements/PUL-F001/requirement.md @@ -0,0 +1,28 @@ +--- +id: PUL-F001 +title: "Scene module shape" +status: ACTIVE +type: FUNCTIONAL +priority: MUST +wave: 0 +created_at: 2026-04-30T19:16:11.856896Z +updated_at: 2026-05-03T00:43:09.810404Z +--- + +# PUL-F001 — Scene module shape + +## Statement + +A scene module SHALL export an object containing the fields `id`, `title`, `tags`, `assets`, `captions`, `defaultNext`, `standalone`, `trailerSafe`, `create`, `timeline`, and `cleanup`. The `duration` field SHALL be present and SHALL be either a non-negative integer in milliseconds or `null` for an open-ended/interrupt-driven scene. + +## Rationale + +Defines the contract every scene satisfies. Required by the scene/composition model (ADR-002) and the agent-native authoring constraint (ADR-008). + +## Traceability + +- DOCUMENTS → ADR `ADR-002` (Scene Registry and Composition Manifests as the Core Abstraction) +- DOCUMENTS → ADR `ADR-008` (Agent-Native Authoring as a First-Class Architectural Constraint) +- IMPLEMENTS → CODE_FILE `src/runtime/scene.ts` (SceneModule contract + assertSceneModule + isSceneModule) +- TESTS → TEST `tests/runtime/scene.test.ts` (SceneModule contract Vitest spec — 62 tests covering every clause) +- IMPLEMENTS → GITHUB_ISSUE `6` (PUL-F001: Scene module shape) diff --git a/docs/requirements/PUL-F002/requirement.md b/docs/requirements/PUL-F002/requirement.md new file mode 100644 index 0000000..3fe2d0e --- /dev/null +++ b/docs/requirements/PUL-F002/requirement.md @@ -0,0 +1,28 @@ +--- +id: PUL-F002 +title: "Scene registry" +status: ACTIVE +type: FUNCTIONAL +priority: MUST +wave: 0 +created_at: 2026-04-30T19:16:14.315443Z +updated_at: 2026-05-03T01:28:03.625542Z +--- + +# PUL-F002 — Scene registry + +## Statement + +The runtime SHALL register scenes by their `id` field in a single registry. The registry SHALL be the only mechanism by which scenes are addressable for navigation. + +## Rationale + +Stable scene identity is the basis of compositions, URL navigation, and agent edits. + +## Traceability + +- DOCUMENTS → ADR `ADR-002` (Scene Registry and Composition Manifests as the Core Abstraction) +- DOCUMENTS → ADR `ADR-008` (Agent-Native Authoring as a First-Class Architectural Constraint) +- IMPLEMENTS → CODE_FILE `src/runtime/registry.ts` (Scene registry — createSceneRegistry / SceneRegistry) +- TESTS → TEST `tests/runtime/registry.test.ts` (Scene registry contract spec (35 tests)) +- IMPLEMENTS → GITHUB_ISSUE `8` (PUL-F002: Scene registry) diff --git a/docs/requirements/PUL-F003/requirement.md b/docs/requirements/PUL-F003/requirement.md new file mode 100644 index 0000000..80fefa4 --- /dev/null +++ b/docs/requirements/PUL-F003/requirement.md @@ -0,0 +1,28 @@ +--- +id: PUL-F003 +title: "Composition manifest format" +status: ACTIVE +type: FUNCTIONAL +priority: MUST +wave: 0 +created_at: 2026-04-30T19:16:17.718055Z +updated_at: 2026-05-03T03:52:17.839316Z +--- + +# PUL-F003 — Composition manifest format + +## Statement + +A composition manifest SHALL be a declarative ordered list of scene id references. Each entry MAY be a bare scene id string or an object containing a scene id and per-entry overrides for sub-range or behavior. + +## Rationale + +Compositions are first-class artifacts separate from scenes; they enable recomposition (full talk, short cut, trailer) without forking scenes. + +## Traceability + +- IMPLEMENTS → CODE_FILE `src/runtime/composition.ts` (Composition manifest format — types + assertCompositionManifest + isCompositionManifest) +- TESTS → TEST `tests/runtime/composition.test.ts` (Composition manifest format Vitest spec — clauses C1-C3 + AC1-AC2 + ADR-002 fixtures) +- IMPLEMENTS → GITHUB_ISSUE `12` (PUL-F003: Composition manifest format) +- DOCUMENTS → ADR `ADR-002` (Scene Registry and Composition Manifests as the Core Abstraction) +- DOCUMENTS → ADR `ADR-008` (Agent-Native Authoring as a First-Class Architectural Constraint) diff --git a/docs/requirements/PUL-F004/requirement.md b/docs/requirements/PUL-F004/requirement.md new file mode 100644 index 0000000..f81b349 --- /dev/null +++ b/docs/requirements/PUL-F004/requirement.md @@ -0,0 +1,28 @@ +--- +id: PUL-F004 +title: "Composition resolution" +status: ACTIVE +type: FUNCTIONAL +priority: MUST +wave: 0 +created_at: 2026-04-30T19:16:21.159163Z +updated_at: 2026-05-03T05:09:06.257026Z +--- + +# PUL-F004 — Composition resolution + +## Statement + +Given a composition manifest, the runtime SHALL: (a) verify every referenced scene id exists in the registry; (b) preload assets declared by each scene; (c) mount each scene in order via `create(ctx)`; (d) run its timeline; (e) tear it down via `cleanup(ctx)` before mounting the next scene. + +## Rationale + +Defines the runtime's lifecycle for playing a composition. + +## Traceability + +- IMPLEMENTS → CODE_FILE `src/runtime/composition-resolver.ts` (Composition resolver — resolveComposition orchestrator) +- TESTS → TEST `tests/runtime/composition-resolver.test.ts` (Composition resolver Vitest spec — 35 cases covering every PUL-F004 clause) +- DOCUMENTS → ADR `ADR-011` (Composition Resolver as a Pure Orchestrator with Injected Adapters) +- IMPLEMENTS → GITHUB_ISSUE `13` (PUL-F004: Composition resolution) +- DOCUMENTS → ADR `ADR-002` (Scene Registry and Composition Manifests as the Core Abstraction) diff --git a/docs/requirements/PUL-F005/requirement.md b/docs/requirements/PUL-F005/requirement.md new file mode 100644 index 0000000..4ecd29b --- /dev/null +++ b/docs/requirements/PUL-F005/requirement.md @@ -0,0 +1,31 @@ +--- +id: PUL-F005 +title: "Per-scene asset declaration and preload" +status: ACTIVE +type: FUNCTIONAL +priority: MUST +wave: 0 +created_at: 2026-04-30T19:16:24.194542Z +updated_at: 2026-05-03T06:16:32.805167Z +--- + +# PUL-F005 — Per-scene asset declaration and preload + +## Statement + +Each scene SHALL declare its required assets in metadata. The runtime SHALL preload declared assets for the active composition before the scene mounts. + +## Rationale + +Predictable asset behavior enables reasoning by humans and agents and enables deterministic screenshot rendering. + +## Traceability + +- DOCUMENTS → ADR `ADR-002` (Scene Registry and Composition Manifests as the Core Abstraction) +- DOCUMENTS → ADR `ADR-008` (Agent-Native Authoring as a First-Class Architectural Constraint) +- IMPLEMENTS → CODE_FILE `src/runtime/asset-preloader.ts` (Asset preloader — createAssetPreloader factory (clause b)) +- IMPLEMENTS → CODE_FILE `src/runtime/scene.ts` (SceneModule.assets field declaration + assertSceneModule field-guard (clause a)) +- TESTS → TEST `tests/runtime/asset-preloader.test.ts` (Asset preloader Vitest spec — 34 cases covering every PUL-F005 clause-(b) behavior) +- DOCUMENTS → ADR `ADR-012` (Asset Preloader — Warm Bytes via Fetch + Drain; Decode-Complete is Future Work) +- IMPLEMENTS → GITHUB_ISSUE `14` (PUL-F005: Per-scene asset declaration and preload) +- DOCUMENTS → DOCUMENTATION `docs/asset-url-policy.md` (Asset URL and credential policy (production)) diff --git a/docs/requirements/PUL-F006/requirement.md b/docs/requirements/PUL-F006/requirement.md new file mode 100644 index 0000000..be27e67 --- /dev/null +++ b/docs/requirements/PUL-F006/requirement.md @@ -0,0 +1,29 @@ +--- +id: PUL-F006 +title: "Per-scene cleanup invocation" +status: ACTIVE +type: FUNCTIONAL +priority: MUST +wave: 0 +created_at: 2026-04-30T19:16:26.871770Z +updated_at: 2026-05-03T16:13:26.887100Z +--- + +# PUL-F006 — Per-scene cleanup invocation + +## Statement + +The runtime SHALL invoke `cleanup(ctx)` on every scene exit, including normal advance, presenter skip, runtime error within the scene, and composition end. + +## Rationale + +Cleanup is the runtime's lifecycle guarantee; without it, scoped agent edits cannot be safe. + +## Traceability + +- DOCUMENTS → ADR `ADR-002` (Scene Registry and Composition Manifests as the Core Abstraction) +- DOCUMENTS → ADR `ADR-008` (Agent-Native Authoring as a First-Class Architectural Constraint) +- IMPLEMENTS → CODE_FILE `src/runtime/composition-resolver.ts` (Composition resolver — runScene cleanup-always invariant + AbortSignal contract) +- TESTS → TEST `tests/runtime/composition-resolver.test.ts` (Composition resolver Vitest spec — PUL-F006 describe block (10 tests covering normal advance, presenter skip via AbortSignal, runtime error, composition end, exactly-once invariant)) +- DOCUMENTS → ADR `ADR-011` (Composition Resolver as a Pure Orchestrator with Injected Adapters — risk-table entry updated for the AbortSignal seam landing at PUL-F006) +- IMPLEMENTS → GITHUB_ISSUE `15` (PUL-F006: Per-scene cleanup invocation) diff --git a/docs/requirements/PUL-F007/requirement.md b/docs/requirements/PUL-F007/requirement.md new file mode 100644 index 0000000..d409200 --- /dev/null +++ b/docs/requirements/PUL-F007/requirement.md @@ -0,0 +1,31 @@ +--- +id: PUL-F007 +title: "URL navigation grammar" +status: ACTIVE +type: FUNCTIONAL +priority: MUST +wave: 1 +created_at: 2026-04-30T19:16:29.545448Z +updated_at: 2026-05-03T17:12:34.426448Z +--- + +# PUL-F007 — URL navigation grammar + +## Statement + +The runtime SHALL accept the URL parameters `scene`, `composition`, `index`, `beat`, and `mode`. Parameter combinations SHALL be parsed at startup and on `popstate`. + +## Rationale + +URL grammar is the agent contract for inspection (ADR-007). + +## Traceability + +- DOCUMENTS → ADR `ADR-002` (Scene Registry and Composition Manifests as the Core Abstraction) +- DOCUMENTS → ADR `ADR-007` (Browser as the Default Workbench and Agent Collaboration Surface) +- IMPLEMENTS → CODE_FILE `src/runtime/navigation.ts` (URL navigation grammar parser, popstate subscriber, and runtime-entry bootstrap) +- IMPLEMENTS → CODE_FILE `src/main.ts` (Runtime entry calls bootstrapNavigation(globalThis) so URL params are parsed at startup and on popstate) +- TESTS → TEST `tests/runtime/navigation.test.ts` (Vitest suite covering parser, subscribeNavigation, bootstrapNavigation, and ADR-013 invariants) +- DOCUMENTS → ADR `ADR-013` (URL Navigation Grammar Boundary) +- IMPLEMENTS → GITHUB_ISSUE `16` (PUL-F007: URL navigation grammar) +- IMPLEMENTS → PULL_REQUEST `69` (Add URL navigation grammar parser (PUL-F007)) diff --git a/docs/requirements/PUL-F008/requirement.md b/docs/requirements/PUL-F008/requirement.md new file mode 100644 index 0000000..7b90b81 --- /dev/null +++ b/docs/requirements/PUL-F008/requirement.md @@ -0,0 +1,34 @@ +--- +id: PUL-F008 +title: "URL parameter — scene" +status: ACTIVE +type: FUNCTIONAL +priority: MUST +wave: 1 +created_at: 2026-04-30T19:16:32.220250Z +updated_at: 2026-05-03T22:55:21.997950Z +--- + +# PUL-F008 — URL parameter — scene + +## Statement + +When the `scene` URL parameter is present, the runtime SHALL load the addressed scene as the navigation target. + +## Rationale + +Direct scene addressing is the basis of agent verification and reviewer use. + +## Traceability + +- DOCUMENTS → ADR `ADR-007` (Browser as the Default Workbench and Agent Collaboration Surface) +- IMPLEMENTS → CODE_FILE `src/runtime/scene-navigation.ts` (Scene navigation dispatch — resolveSceneNavigation + loadSceneNavigationTarget bridge) +- IMPLEMENTS → CODE_FILE `src/runtime/scene-loader.ts` (Scene loader — createSceneLoader state machine + WorkbenchSceneCtx) +- IMPLEMENTS → CODE_FILE `src/runtime/composition-registry.ts` (Composition registry — addressability path the `composition` URL parameter consults) +- IMPLEMENTS → CODE_FILE `src/main.ts` (Workbench entry — wires F007's bootstrapNavigation to F008's SceneLoader so URL targets drive the lifecycle) +- DOCUMENTS → ADR `ADR-014` (Loading the Addressed Scene as the Runtime Navigation Target) +- TESTS → TEST `tests/runtime/scene-navigation.test.ts` (Scene navigation dispatch tests — locator kinds, slice immutability, identity guarantees, lifecycle integration) +- TESTS → TEST `tests/runtime/scene-loader.test.ts` (Scene loader tests — queue supersession, cleanup-before-handoff, error surfacing, abort coordination) +- TESTS → TEST `tests/runtime/composition-registry.test.ts` (Composition registry tests — id validation, manifest validation, deep-frozen snapshot, generator inputs) +- IMPLEMENTS → GITHUB_ISSUE `17` (PUL-F008: URL parameter — scene) +- IMPLEMENTS → PULL_REQUEST `70` (Add scene URL parameter resolver (PUL-F008)) diff --git a/docs/requirements/PUL-F009/requirement.md b/docs/requirements/PUL-F009/requirement.md new file mode 100644 index 0000000..e614f3c --- /dev/null +++ b/docs/requirements/PUL-F009/requirement.md @@ -0,0 +1,33 @@ +--- +id: PUL-F009 +title: "URL parameter — composition" +status: ACTIVE +type: FUNCTIONAL +priority: MUST +wave: 1 +created_at: 2026-04-30T19:16:34.529380Z +updated_at: 2026-05-04T21:59:05.145881Z +--- + +# PUL-F009 — URL parameter — composition + +## Statement + +When the `composition` URL parameter is present, the runtime SHALL resolve the addressed composition manifest and use it as the navigation context. + +## Rationale + +Required for presenting and inspecting scenes within a specific composition. + +## Traceability + +- DOCUMENTS → ADR `ADR-007` (Browser as the Default Workbench and Agent Collaboration Surface) +- IMPLEMENTS → CODE_FILE `src/runtime/navigation.ts` (URL grammar parser — recognizes `composition` parameter and emits composition locator kinds) +- IMPLEMENTS → CODE_FILE `src/runtime/scene-navigation.ts` (Scene navigation dispatcher — resolves the addressed composition manifest as navigation context) +- IMPLEMENTS → CODE_FILE `src/runtime/scene-loader.ts` (Scene loader — drives the resolved composition through lifecycle, records `data-pulsar-composition-target`) +- IMPLEMENTS → CODE_FILE `src/runtime/composition-registry.ts` (Composition registry — id-keyed composition lookup that the `?composition=` URL parameter consults) +- IMPLEMENTS → CODE_FILE `src/main.ts` (Workbench entry — wires `bootstrapNavigation` → loader so `?composition=` is honored at startup and on popstate) +- TESTS → TEST `tests/runtime/navigation.test.ts` (URL parser tests — pin parser handling of `composition`, `composition+scene`, `composition+index`) +- TESTS → TEST `tests/runtime/scene-navigation.test.ts` (Dispatcher tests — pin composition-manifest resolution, slice snapshot, missing/empty/range error paths) +- TESTS → TEST `tests/runtime/scene-loader.test.ts` (Loader tests — pin PUL-F009 canonical cases (composition-only, composition-index) at the loader boundary) +- IMPLEMENTS → GITHUB_ISSUE `18` (PUL-F009: URL parameter — composition) diff --git a/docs/requirements/PUL-F010/requirement.md b/docs/requirements/PUL-F010/requirement.md new file mode 100644 index 0000000..f04d9a4 --- /dev/null +++ b/docs/requirements/PUL-F010/requirement.md @@ -0,0 +1,33 @@ +--- +id: PUL-F010 +title: "URL parameter — index" +status: ACTIVE +type: FUNCTIONAL +priority: MUST +wave: 1 +created_at: 2026-04-30T19:16:37.197252Z +updated_at: 2026-05-04T22:19:50.990001Z +--- + +# PUL-F010 — URL parameter — index + +## Statement + +When the `index` URL parameter is present alongside a `composition`, the runtime SHALL position playback at the given zero-based index within the composition. + +## Rationale + +Compatibility shim for positional navigation; scene id remains the source of truth for identity. + +## Traceability + +- DOCUMENTS → ADR `ADR-007` (Browser as the Default Workbench and Agent Collaboration Surface) +- IMPLEMENTS → CODE_FILE `src/runtime/navigation.ts` (URL grammar parser — accepts `composition` + `index` and emits `composition-index` locator) +- IMPLEMENTS → CODE_FILE `src/runtime/scene-navigation.ts` (Scene navigation dispatcher — `composition-index` resolves head to `manifest[index]`, slices forward, range-checks the index) +- IMPLEMENTS → CODE_FILE `src/runtime/scene-loader.ts` (Scene loader — drives the `composition-index` snapshot through the lifecycle and records `data-pulsar-scene-target`/`data-pulsar-composition-target`) +- TESTS → TEST `tests/runtime/navigation.test.ts` (URL parser tests — pin `index` parsing (zero-based, base-10, safe-integer) and `composition+index` combination) +- TESTS → TEST `tests/runtime/scene-navigation.test.ts` (Dispatcher tests — pin `composition-index` slice from index, range/non-integer/empty-composition error paths) +- TESTS → TEST `tests/runtime/scene-loader.test.ts` (Loader-boundary regression anchors for PUL-F010 — index>1 selects manifest[index]; varying-index sequence resolves to different heads) +- DOCUMENTS → ADR `ADR-013` (URL Navigation Grammar Boundary — names `composition`+`index` as the positional locator, defines `index` as composition-scoped non-identity) +- DOCUMENTS → ADR `ADR-014` (Scene Navigation Dispatch — names `composition`+`index` as PUL-F010 positional behavior, head is `manifest[index]`) +- IMPLEMENTS → GITHUB_ISSUE `19` (PUL-F010: URL parameter — index) diff --git a/docs/requirements/PUL-F011/requirement.md b/docs/requirements/PUL-F011/requirement.md new file mode 100644 index 0000000..6bdde1a --- /dev/null +++ b/docs/requirements/PUL-F011/requirement.md @@ -0,0 +1,33 @@ +--- +id: PUL-F011 +title: "URL parameter — beat" +status: ACTIVE +type: FUNCTIONAL +priority: MUST +wave: 1 +created_at: 2026-04-30T19:16:40.223481Z +updated_at: 2026-05-18T18:48:08.228349Z +--- + +# PUL-F011 — URL parameter — beat + +## Statement + +When the `beat` URL parameter is present, the runtime SHALL position the active scene's timeline at the named timeline label. If the label does not exist, the runtime SHALL surface an error and remain at the scene's first beat. + +## Rationale + +Named beats are the agent-friendly time grammar (ADR-003, ADR-008). + +## Traceability + +- DOCUMENTS → ADR `ADR-007` (Browser as the Default Workbench and Agent Collaboration Surface) +- DOCUMENTS → ADR `ADR-003` (GSAP as the Timeline Engine) +- DOCUMENTS → CODE_FILE `src/runtime/composition-resolver.ts` (Composition resolver — `headBeat` / `onBeatMissing` forwarding to plan[0]'s run input only (PUL-F011 runtime contract)) +- DOCUMENTS → CODE_FILE `src/runtime/scene-navigation.ts` (Scene navigation bridge — beat / onBeatMissing forwarded as headBeat / onBeatMissing to resolveComposition (PUL-F011 runtime contract)) +- DOCUMENTS → CODE_FILE `src/runtime/scene-loader.ts` (Scene loader — extracts target.beat, builds non-fatal onBeatMissing closure with abort/dispose/once-only guards, defense-in-depth grammar checks (PUL-F011 runtime contract)) +- DOCUMENTS → TEST `tests/runtime/composition-resolver.test.ts` (Resolver tests — head-only beat/onBeatMissing forwarding + paired-required precondition (PUL-F011 runtime contract)) +- DOCUMENTS → TEST `tests/runtime/scene-navigation.test.ts` (Bridge tests — beat / onBeatMissing forwarded only to head scene of slice + paired-required precondition (PUL-F011 runtime contract)) +- DOCUMENTS → ADR `ADR-015` (URL Beat Positioning as Timeline-Runner State — names beat as runner-boundary state, missing-label is non-fatal, no parallel beat schema, current-state note explaining DRAFT status pending GSAP runner) +- DOCUMENTS → GITHUB_ISSUE `20` (PUL-F011: URL parameter — beat (forward-looking; runtime contract delivered, end-to-end seek pending PUL-F022/F023 GSAP runner)) +- DOCUMENTS → TEST `tests/runtime/scene-loader-beat-mode.test.ts` (Loader tests — beat positioning (PUL-F011): pending-runner mounted-during-diagnostic, abort/dispose suppression, once-only, throwing-onError non-fatal, defense-in-depth grammar. Split from scene-loader.test.ts (ADR-025).) diff --git a/docs/requirements/PUL-F012/requirement.md b/docs/requirements/PUL-F012/requirement.md new file mode 100644 index 0000000..e7e134d --- /dev/null +++ b/docs/requirements/PUL-F012/requirement.md @@ -0,0 +1,34 @@ +--- +id: PUL-F012 +title: "URL parameter — mode" +status: ACTIVE +type: FUNCTIONAL +priority: MUST +wave: 1 +created_at: 2026-04-30T19:16:42.530916Z +updated_at: 2026-05-05T21:37:36.354958Z +--- + +# PUL-F012 — URL parameter — mode + +## Statement + +When the `mode` URL parameter is present, the runtime SHALL select the corresponding workbench mode. If `mode` is absent, the runtime SHALL default to `present`. + +## Rationale + +Mode is selected by URL only; not by stored state. + +## Traceability + +- IMPLEMENTS → PULL_REQUEST `75` (PUL-F012: URL parameter `mode`) +- DOCUMENTS → ADR `ADR-007` (Browser as the Default Workbench and Agent Collaboration Surface) +- IMPLEMENTS → CODE_FILE `src/runtime/navigation.ts` (effectiveMode helper — pure URL-only mode dispatch boundary (target?.mode ?? 'present')) +- IMPLEMENTS → CODE_FILE `src/runtime/scene-loader.ts` (SceneLoader — runtime-core seam: buildCtx(effectiveMode(target)) per navigation, validateModeGrammar defense-in-depth, abort-on-builder-throw, WorkbenchSceneCtx.mode field) +- IMPLEMENTS → CODE_FILE `src/main.ts` (Workbench entry — buildCtx: (mode) => ({ stage, mode }) wires per-navigation effective mode into WorkbenchSceneCtx) +- IMPLEMENTS → CODE_FILE `src/scenes/placeholder.ts` (Placeholder isWorkbenchCtx predicate — validates ctx.mode against NAVIGATION_MODES and ctx.stage shape so PUL-F012's mode-aware ctx contract is enforced at the scene boundary) +- TESTS → TEST `tests/runtime/navigation.test.ts` (effectiveMode tests — per-mode round-trip + URL-only-source spy installation on localStorage/sessionStorage/document.cookie/history.state) +- TESTS → TEST `tests/scenes/placeholder.test.ts` (Placeholder ctx-shape tests — pin no-op contract on malformed mode and malformed stage shapes) +- DOCUMENTS → ADR `ADR-013` (URL Navigation Grammar Boundary — names the parser-vs-runtime-core boundary for PUL-F012 (parser preserves absent mode; runtime core derives effective `present` and exposes ctx.mode)) +- IMPLEMENTS → GITHUB_ISSUE `21` (PUL-F012: URL parameter — mode) +- TESTS → TEST `tests/runtime/scene-loader-beat-mode.test.ts` (SceneLoader mode-dispatch tests — buildCtx input + ctx.mode threading + no-leak across navigations + defense-in-depth + abort-on-builder-throw. Split from scene-loader.test.ts (ADR-025).) diff --git a/docs/requirements/PUL-F013/requirement.md b/docs/requirements/PUL-F013/requirement.md new file mode 100644 index 0000000..7a788f3 --- /dev/null +++ b/docs/requirements/PUL-F013/requirement.md @@ -0,0 +1,37 @@ +--- +id: PUL-F013 +title: "Workbench mode — present" +status: ACTIVE +type: FUNCTIONAL +priority: MUST +wave: 1 +created_at: 2026-04-30T19:16:45.215253Z +updated_at: 2026-05-18T21:21:55.903870Z +--- + +# PUL-F013 — Workbench mode — present + +## Statement + +In `mode=present`, the runtime SHALL render full chrome, audio, and inter-scene transitions, and SHALL respond to presenter input. + +## Rationale + +Default presentation behavior. + +## Traceability + +- DOCUMENTS → ADR `ADR-007` (Browser as the Default Workbench and Agent Collaboration Surface) +- DOCUMENTS → ADR `ADR-016` (ADR-016: Workbench Mode `present` — Contract Boundary and Adapter Seams) +- DOCUMENTS → DOCUMENTATION `docs/design/pul-f013-present-mode-preflight.md` (PUL-F013 Present Mode Preflight (codex architecture preflight design context)) +- DOCUMENTS → GITHUB_ISSUE `22` (PUL-F013: Workbench mode — present) +- IMPLEMENTS → CODE_FILE `src/main.ts` (Workbench composition root — wires chrome, audio, transition registry + overlay, and keyboard + bridge presenter sources into the scene loader) +- IMPLEMENTS → CODE_FILE `src/runtime/timeline.ts` (Timeline engine — Transition/TransitionRegistry contract; applySegmentTransition invokes inter-scene transitions while composing the GSAP master timeline) +- IMPLEMENTS → CODE_FILE `src/runtime/workbench-chrome.ts` (Workbench chrome surface — mode-governed chrome rendering for mode=present) +- IMPLEMENTS → CODE_FILE `src/runtime/audio.ts` (Audio service — Howler-backed per-navigation AudioService rendering audio under mode=present) +- IMPLEMENTS → CODE_FILE `src/runtime/presenter.ts` (Presenter command schema + boundary validator — the runtime seam that responds to presenter input) +- IMPLEMENTS → CODE_FILE `src/runtime/scene-loader.ts` (Scene loader — present-mode dispatch building per-navigation audio service, presenter controller, and chrome dispatch) +- IMPLEMENTS → CODE_FILE `src/system/transitions/registry.ts` (Default inter-scene transition implementations — cut, dissolve, hard-slam, hold-on-black, push) +- TESTS → TEST `tests/runtime/scene-loader-present.test.ts` (Present-mode scene-loader tests — chrome, audio, transitions, and presenter seams under mode=present) +- TESTS → TEST `tests/system/transitions.test.ts` (Inter-scene transition contract tests — default transition implementations) +- TESTS → TEST `tests-e2e/pulsar-intro.spec.ts` (Present-mode e2e — chrome surface, transition overlay, present playback, keyboard advance under chromium/firefox/webkit) diff --git a/docs/requirements/PUL-F014/requirement.md b/docs/requirements/PUL-F014/requirement.md new file mode 100644 index 0000000..3099285 --- /dev/null +++ b/docs/requirements/PUL-F014/requirement.md @@ -0,0 +1,40 @@ +--- +id: PUL-F014 +title: "Workbench mode — standalone" +status: ACTIVE +type: FUNCTIONAL +priority: MUST +wave: 1 +created_at: 2026-04-30T19:16:47.883215Z +updated_at: 2026-05-18T21:22:09.246924Z +--- + +# PUL-F014 — Workbench mode — standalone + +## Statement + +In `mode=standalone`, the runtime SHALL render a single scene with surrounding chrome, inter-scene transitions, and audio bed suppressed; the scene SHALL run as if no surrounding composition existed. + +## Rationale + +Single-scene authoring/inspection without surrounding talk. + +## Traceability + +- DOCUMENTS → ADR `ADR-007` (Browser as the Default Workbench and Agent Collaboration Surface) +- IMPLEMENTS → CODE_FILE `src/runtime/audio.ts` (Audio service — composition audio bed seam (AudioBedDeclaration, bed, bedSuppressed)) +- IMPLEMENTS → CODE_FILE `src/runtime/scene-loader.ts` (Scene loader — bed wiring + mode=standalone bed suppression) +- IMPLEMENTS → CODE_FILE `src/runtime/composition-registry.ts` (Composition registry — audioBed registration boundary) +- DOCUMENTS → ADR `ADR-017` (ADR-017: Workbench Mode `standalone` — Single-Scene Execution at the Loader) +- DOCUMENTS → DOCUMENTATION `docs/design/pul-f014-standalone-mode-preflight.md` (PUL-F014 Standalone Mode Preflight (codex architecture preflight design context)) +- DOCUMENTS → GITHUB_ISSUE `23` (PUL-F014: Workbench mode — standalone) +- IMPLEMENTS → CODE_FILE `src/runtime/scene-navigation.ts` (Scene-navigation resolver — threads audioBed onto the composition context) +- IMPLEMENTS → CODE_FILE `src/runtime/validation.ts` (Runtime validation — audioBed shape check (composition-audio-bed-invalid finding)) +- TESTS → TEST `tests/runtime/audio.test.ts` (Audio service tests — composition audio bed + assertAudioBedDeclaration) +- TESTS → TEST `tests/runtime/scene-loader-audio.test.ts` (Loader audio tests — composition audio bed play + standalone suppression) +- TESTS → TEST `tests/runtime/composition-registry.test.ts` (Composition registry tests — audioBed storage, deep-freeze, validation) +- TESTS → TEST `tests/runtime/scene-navigation.test.ts` (Scene-navigation tests — audioBed threaded into composition context) +- TESTS → TEST `tests/runtime/validation.test.ts` (Validation tests — composition-audio-bed-invalid findings) +- TESTS → TEST `tests/system/standalone-audio-bed.test.ts` (System test — composition audio bed end-to-end (play vs standalone suppression)) +- IMPLEMENTS → GITHUB_ISSUE `127` (PUL-F014: standalone-mode chrome and audio-bed suppression) +- TESTS → TEST `tests/runtime/scene-loader-standalone-loop.test.ts` (Loader tests — standalone-mode single-scene execution (PUL-F014): slice-truncation mechanism, ctx.mode seam, and the three suppression surfaces (chrome / audio-bed / inter-scene transitions) now all shipped) diff --git a/docs/requirements/PUL-F015/requirement.md b/docs/requirements/PUL-F015/requirement.md new file mode 100644 index 0000000..a418629 --- /dev/null +++ b/docs/requirements/PUL-F015/requirement.md @@ -0,0 +1,35 @@ +--- +id: PUL-F015 +title: "Workbench mode — loop" +status: ACTIVE +type: FUNCTIONAL +priority: MUST +wave: 1 +created_at: 2026-04-30T19:16:49.907011Z +updated_at: 2026-05-18T21:22:19.926385Z +--- + +# PUL-F015 — Workbench mode — loop + +## Statement + +In `mode=loop`, the runtime SHALL run the addressed scene's timeline and restart it on completion. + +## Rationale + +Repeated visual/audio inspection during authoring. + +## Traceability + +- DOCUMENTS → ADR `ADR-007` (Browser as the Default Workbench and Agent Collaboration Surface) +- DOCUMENTS → ADR `ADR-018` (ADR-018: Workbench Mode `loop` — Runner Repeat Hint at the Loader/Runner Seam) +- DOCUMENTS → DOCUMENTATION `docs/design/pul-f015-loop-mode-preflight.md` (PUL-F015 Loop Mode Preflight (codex architecture preflight design context)) +- DOCUMENTS → GITHUB_ISSUE `24` (PUL-F015: Workbench mode — loop) +- DOCUMENTS → TEST `tests/runtime/composition-resolver.test.ts` (Composition-resolver tests — URL loop-mode runner repeat-hint forwarding describe block (PUL-F015 resolver layer: headRepeat plumbed to plan[0]'s run input only, key-presence semantics, no-op interpretation, independent from headBeat)) +- DOCUMENTS → TEST `tests/runtime/scene-navigation.test.ts` (Scene-navigation tests — URL loop-mode repeat forwarding describe block (PUL-F015 bridge layer: repeat → headRepeat plumbing, bridge-level slice truncation under repeat as structural defense, independence from beat)) +- IMPLEMENTS → CODE_FILE `src/runtime/timeline.ts` (GSAP timeline adapter — positionMaster() sets master.repeat(-1) for headRepeat==='until-aborted' and runMasterUntilDone() plays the looping master (PUL-F015 restart-on-completion, artifact of record)) +- TESTS → TEST `tests/runtime/timeline.test.ts` (Timeline adapter tests — observes the master completing >=2 distinct iterations under headRepeat (PUL-F015 restart-on-completion unit verification)) +- TESTS → TEST `tests-e2e/loop-mode.spec.ts` (Loop-mode Playwright spec — boots ?scene=loop-fixture&mode=loop and polls data-pulsar-loop-iteration to >=2 across chromium/firefox/webkit (PUL-F015 restart-on-completion e2e verification)) +- TESTS → TEST `tests/scenes/loop-fixture.test.ts` (Loop fixture scene unit tests — per-iteration counter increment and fresh-counter-per-navigation (PUL-F015 loop verification harness)) +- IMPLEMENTS → GITHUB_ISSUE `128` (PUL-F015: loop-mode GSAP runner — actual timeline restart on completion) +- DOCUMENTS → TEST `tests/runtime/scene-loader-standalone-loop.test.ts` (Loader tests — loop-mode repeat-hint forwarding (PUL-F015): loader→adapter repeat hint, slice truncation, ctx.mode seam. Split from scene-loader.test.ts (ADR-025).) diff --git a/docs/requirements/PUL-F016/requirement.md b/docs/requirements/PUL-F016/requirement.md new file mode 100644 index 0000000..8960255 --- /dev/null +++ b/docs/requirements/PUL-F016/requirement.md @@ -0,0 +1,36 @@ +--- +id: PUL-F016 +title: "Workbench mode — paused" +status: ACTIVE +type: FUNCTIONAL +priority: MUST +wave: 1 +created_at: 2026-04-30T19:16:56.297381Z +updated_at: 2026-05-18T21:22:29.570206Z +--- + +# PUL-F016 — Workbench mode — paused + +## Statement + +In `mode=paused`, the runtime SHALL mount the addressed scene and hold it at its first frame without advancing the timeline. + +## Rationale + +Layout/styling review without motion. + +## Traceability + +- DOCUMENTS → ADR `ADR-007` (Browser as the Default Workbench and Agent Collaboration Surface) +- DOCUMENTS → ADR `ADR-019` (ADR-019: Workbench Mode `paused` — Runner Hold-at-First-Frame Hint at the Loader/Runner Seam) +- DOCUMENTS → DOCUMENTATION `docs/design/pul-f016-paused-mode-preflight.md` (PUL-F016 Paused Mode Preflight (codex architecture preflight design context)) +- DOCUMENTS → GITHUB_ISSUE `25` (PUL-F016: Workbench mode — paused) +- DOCUMENTS → TEST `tests/runtime/composition-resolver.test.ts` (Composition-resolver tests — URL paused-mode runner hold-hint forwarding describe block (PUL-F016 resolver layer: headHold plumbed to plan[0]'s run input only, key-presence semantics, no-op interpretation, independent from headBeat and headRepeat)) +- DOCUMENTS → TEST `tests/runtime/scene-navigation.test.ts` (Scene-navigation tests — URL paused-mode hold forwarding describe block (PUL-F016 bridge layer: hold → headHold plumbing, bridge-level slice truncation under hold as structural defense via shared truncateToHead helper, independence from beat and repeat)) +- DOCUMENTS → TEST `tests/runtime/scene-loader-paused-scrub.test.ts` (Loader tests — paused-mode hold-hint forwarding (PUL-F016): loader→adapter hold hint, slice truncation, ctx.mode seam, pending-until-abort runner contract. Split from scene-loader.test.ts (ADR-025).) +- IMPLEMENTS → CODE_FILE `src/runtime/timeline.ts` (GSAP composition timeline adapter — positionMaster() holds the composed master at frame 0 (seek(0) + pause()) for headHold === 'first-frame'; runMasterUntilDone() parks the held master without advancing (PUL-F016 hold-at-first-frame mechanic)) +- IMPLEMENTS → CODE_FILE `src/runtime/scene-loader.ts` (Scene loader — maps effectiveMode === 'paused' to the runner hold hint hold: 'first-frame' and mounts the addressed scene through the resolver lifecycle (PUL-F016 mode dispatch)) +- TESTS → TEST `tests/runtime/timeline.test.ts` (GSAP timeline adapter tests — createGsapCompositionTimeline behavioral test: a scene tween's animated value stays at frame 0 under headHold: 'first-frame', including after wall-clock time elapses (PUL-F016 acceptance criterion 2)) +- TESTS → TEST `tests/scenes/paused-fixture.test.ts` (Paused verification fixture unit tests — PUL-F001 contract assertions, the onUpdate tween-value->attribute mapping, and ctx-defensiveness coverage for the PUL-F016 paused-mode verification fixture scene) +- TESTS → TEST `tests-e2e/paused-mode.spec.ts` (Playwright paused-mode spec — boots the paused fixture under mode=paused and asserts data-pulsar-paused-progress holds at "0" past the tween duration, with a mode=loop control (PUL-F016 browser acceptance gate)) +- IMPLEMENTS → GITHUB_ISSUE `129` (PUL-F016: paused-mode GSAP runner — actual hold at first frame) diff --git a/docs/requirements/PUL-F017/requirement.md b/docs/requirements/PUL-F017/requirement.md new file mode 100644 index 0000000..f141543 --- /dev/null +++ b/docs/requirements/PUL-F017/requirement.md @@ -0,0 +1,44 @@ +--- +id: PUL-F017 +title: "Workbench mode — scrub" +status: ACTIVE +type: FUNCTIONAL +priority: MUST +wave: 1 +created_at: 2026-04-30T19:16:59.346844Z +updated_at: 2026-05-21T09:05:04.571682Z +--- + +# PUL-F017 — Workbench mode — scrub + +## Statement + +In `mode=scrub`, the runtime SHALL display timeline controls allowing the user to scrub forward, backward, and to named beats. Audio cues SHALL fire only on monotonic forward playback. + +## Rationale + +Timing and beat-alignment inspection. + +## Traceability + +- DOCUMENTS → ADR `ADR-007` (Browser as the Default Workbench and Agent Collaboration Surface) +- IMPLEMENTS → GITHUB_ISSUE `130` (PUL-F017: scrub-mode timeline controls + monotonic-forward cue gating) +- IMPLEMENTS → CODE_FILE `src/runtime/timeline.ts` (GSAP timeline adapter — scrub run mode (held live master), MasterTimeline.reverse(), direction-driven audio cue gate toggling) +- IMPLEMENTS → CODE_FILE `src/runtime/audio.ts` (Audio service — dynamic cue-eligibility gate (createCueGate); play() suppresses cues while the gate is closed) +- IMPLEMENTS → CODE_FILE `src/system/chrome/scrub.ts` (Workbench scrub-mode transport controls — play/pause/reverse, drag scrubber, named-beat jump buttons) +- IMPLEMENTS → CODE_FILE `src/runtime/scene-loader.ts` (Scene loader — builds the shared cue gate under mode=scrub and wires it into the audio service and timeline adapter) +- IMPLEMENTS → CODE_FILE `src/main.ts` (Workbench bootstrap — mounts the scrub controls and attaches the master via the timeline adapter onMaster hook under mode=scrub) +- IMPLEMENTS → CODE_FILE `src/scenes/scrub-fixture.ts` (Scrub verification fixture scene — observable progress + named beat for mode=scrub) +- DOCUMENTS → ADR `ADR-020` (ADR-020: Workbench Mode `scrub` — Runner Cue-Gate Hint at the Loader/Runner Seam) +- DOCUMENTS → DOCUMENTATION `docs/design/pul-f017-scrub-mode-preflight.md` (PUL-F017 Scrub Mode Preflight (codex architecture preflight design context)) +- DOCUMENTS → GITHUB_ISSUE `26` (PUL-F017: Workbench mode — scrub) +- DOCUMENTS → TEST `tests/runtime/composition-resolver.test.ts` (Composition-resolver tests — URL scrub-mode runner cue-gate-hint forwarding describe block (PUL-F017 resolver: headCueGate to plan[0] only, key-presence semantics, no-op interpretation, independence)) +- DOCUMENTS → TEST `tests/runtime/scene-navigation.test.ts` (Scene-navigation tests — URL scrub-mode cue-gate forwarding describe block (PUL-F017 bridge: cueGate → headCueGate plumbing, bridge-level slice truncation under cueGate via shared truncateToHead helper, independence from beat / repeat / hold)) +- IMPLEMENTS → CODE_FILE `src/runtime/composition-resolver.ts` (Composition resolver — forwards the audioCueGate control opaquely to the timeline adapter (mode-opaque per ADR-011)) +- IMPLEMENTS → CODE_FILE `src/runtime/scene-navigation.ts` (Scene-navigation bridge — forwards audioCueGate from the loader through to resolveComposition) +- TESTS → TEST `tests/runtime/scrub-cue-gating.test.ts` (Monotonic-forward cue gating — a cue at master time T fires on a forward crossing and is suppressed on a reverse crossing) +- TESTS → TEST `tests/system/scrub-controls.test.ts` (Scrub transport controls — play/pause/reverse/seek drive the master, named-beat jump, sync, attach/detach/dispose) +- TESTS → TEST `tests/scenes/scrub-fixture.test.ts` (Scrub verification fixture scene — contract shape, progress projection, midpoint beat, ctx defensiveness) +- TESTS → TEST `tests/runtime/timeline.test.ts` (Timeline adapter — scrub run mode holds the master live, reverse(), and direction-driven cue-gate toggling) +- TESTS → TEST `tests/runtime/audio.test.ts` (Audio service — createCueGate and play() suppression while the cue gate is closed (post-validation)) +- TESTS → TEST `tests/runtime/scene-loader-paused-scrub.test.ts` (Scene loader — scrub-mode cue-gate wiring (shared instance into the audio service and timeline adapter) plus the cue-gate-hint forwarding seam) diff --git a/docs/requirements/PUL-F018/requirement.md b/docs/requirements/PUL-F018/requirement.md new file mode 100644 index 0000000..d900b9d --- /dev/null +++ b/docs/requirements/PUL-F018/requirement.md @@ -0,0 +1,41 @@ +--- +id: PUL-F018 +title: "Workbench mode — screenshot" +status: ACTIVE +type: FUNCTIONAL +priority: MUST +wave: 1 +created_at: 2026-04-30T19:17:02.849756Z +updated_at: 2026-05-21T17:01:55.363946Z +--- + +# PUL-F018 — Workbench mode — screenshot + +## Statement + +In `mode=screenshot`, the runtime SHALL render the addressed scene at the addressed beat (or first frame if no beat) with all asset preloads resolved, no animation in progress, all audio suppressed, and any randomness sourced from a deterministic seed. + +## Rationale + +Deterministic visual regression hooks for agents and reviewers. + +## Traceability + +- TESTS → TEST `tests/scenes/screenshot-rng-fixture.test.ts` (Screenshot RNG fixture scene: same-seed identical projection, different-seed divergence, contract shape) +- TESTS → TEST `tests-e2e/screenshot-mode.spec.ts` (Playwright e2e: two loads of the same screenshot URL replay a byte-identical random sequence) +- DOCUMENTS → ADR `ADR-007` (Browser as the Default Workbench and Agent Collaboration Surface) +- DOCUMENTS → ADR `ADR-008` (Agent-Native Authoring as a First-Class Architectural Constraint) +- DOCUMENTS → GITHUB_ISSUE `27` (PUL-F018: Workbench mode — screenshot) +- DOCUMENTS → ADR `ADR-021` (ADR-021: Workbench Mode `screenshot` — Runner Capture-Bundle Hint at the Loader/Runner Seam) +- DOCUMENTS → DOCUMENTATION `docs/design/pul-f018-screenshot-mode-preflight.md` (PUL-F018 Screenshot Mode Preflight (codex architecture preflight design context)) +- DOCUMENTS → TEST `tests/runtime/composition-resolver.test.ts` (Composition-resolver tests — URL screenshot-mode runner capture-hint forwarding describe block (PUL-F018 resolver: headScreenshot to plan[0] only, key-presence, no-op, independence)) +- DOCUMENTS → TEST `tests/runtime/scene-navigation.test.ts` (Scene-navigation tests — URL screenshot-mode capture forwarding describe block (PUL-F018 bridge: screenshot → headScreenshot plumbing, slice truncation under screenshot, independence from beat / repeat / hold / cueGate)) +- IMPLEMENTS → GITHUB_ISSUE `131` (PUL-F018: screenshot-mode runtime (frame freeze, preload await, deterministic seed)) +- IMPLEMENTS → CODE_FILE `src/runtime/rng.ts` (Deterministic seeded PRNG (mulberry32 + xmur3) — the sanctioned randomness source for PUL-F018) +- DOCUMENTS → TEST `tests/runtime/scene-loader-screenshot-prompter.test.ts` (Loader tests — screenshot-mode runner capture-hint forwarding describe block (PUL-F018 boundary: loader → adapter screenshot, slice truncation, ctx.mode seam, beat-alongside-screenshot, range/behavior preserved). Split from scene-loader.test.ts (ADR-025).) +- IMPLEMENTS → CODE_FILE `src/runtime/scene-loader.ts` (Loader: WorkbenchSceneCtx.rng, deriveNavigationSeed, per-occurrence RNG threading, screenshot mode dispatch) +- IMPLEMENTS → CODE_FILE `src/runtime/timeline.ts` (GSAP composition timeline: screenshot frame freeze (positionMaster seek-to-beat-or-0 + pause)) +- IMPLEMENTS → CODE_FILE `src/runtime/audio.ts` (Audio service: screenshot audio suppression via AudioOutputPolicy 'silent' (muted sounds)) +- IMPLEMENTS → CODE_FILE `src/runtime/composition-resolver.ts` (Composition resolver: asset-preload fence — every declared preload awaited before create/timeline/master playback) +- TESTS → TEST `tests/runtime/rng.test.ts` (PRNG determinism: same seed → identical sequence, distinct seeds diverge, draws bounded [0,1)) +- TESTS → TEST `tests/runtime/scene-loader-screenshot-seed.test.ts` (deriveNavigationSeed purity/distinctness + loader ctx.rng determinism and per-occurrence independence) diff --git a/docs/requirements/PUL-F019/requirement.md b/docs/requirements/PUL-F019/requirement.md new file mode 100644 index 0000000..384bdc7 --- /dev/null +++ b/docs/requirements/PUL-F019/requirement.md @@ -0,0 +1,29 @@ +--- +id: PUL-F019 +title: "Workbench mode — prompter" +status: ACTIVE +type: FUNCTIONAL +priority: MUST +wave: 1 +created_at: 2026-04-30T19:17:05.438511Z +updated_at: 2026-05-18T18:48:18.112074Z +--- + +# PUL-F019 — Workbench mode — prompter + +## Statement + +In `mode=prompter`, the runtime SHALL render a script/caption view derived from the captions metadata of the addressed scene or composition. Visual rendering of the scene SHALL be suppressed. + +## Rationale + +Prompter and reviewer caption review. + +## Traceability + +- DOCUMENTS → ADR `ADR-007` (Browser as the Default Workbench and Agent Collaboration Surface) +- DOCUMENTS → ADR `ADR-008` (Agent-Native Authoring as a First-Class Architectural Constraint) +- DOCUMENTS → GITHUB_ISSUE `28` (PUL-F019: Workbench mode — prompter) +- DOCUMENTS → ADR `ADR-022` (ADR-022: Workbench Mode `prompter` — Loader-Side Lifecycle Bypass with Captions Aggregation Seam) +- DOCUMENTS → TEST `tests/runtime/prompter.test.ts` (Prompter pure-function tests — buildPrompterScript (PUL-F019 captions aggregation: single-scene, full composition slice, composition+scene non-head, structural caption clone, manifest-metadata passthrough, deep-frozen output, source-isolation invariants)) +- DOCUMENTS → TEST `tests/runtime/scene-loader-screenshot-prompter.test.ts` (Loader tests — prompter-mode caption-view dispatch describe block (PUL-F019 boundary: structural lifecycle suppression, full-slice captions aggregation, renderer dispatch + abort lifecycle). Split from scene-loader.test.ts (ADR-025).) diff --git a/docs/requirements/PUL-F020/requirement.md b/docs/requirements/PUL-F020/requirement.md new file mode 100644 index 0000000..5c8967e --- /dev/null +++ b/docs/requirements/PUL-F020/requirement.md @@ -0,0 +1,35 @@ +--- +id: PUL-F020 +title: "Presenter controls — advance, hold, skip" +status: ACTIVE +type: FUNCTIONAL +priority: MUST +wave: 1 +created_at: 2026-04-30T19:17:08.111959Z +updated_at: 2026-05-18T21:22:41.156959Z +--- + +# PUL-F020 — Presenter controls — advance, hold, skip + +## Statement + +In `mode=present`, the runtime SHALL accept presenter input to advance to the next beat, hold the current beat, skip forward, and skip backward. Beat progression SHALL be interruptible without breaking timeline state. + +## Rationale + +Required for live presentation control. + +## Traceability + +- DOCUMENTS → ADR `ADR-007` (Browser as the Default Workbench and Agent Collaboration Surface) +- DOCUMENTS → GITHUB_ISSUE `29` (PUL-F020: Presenter controls — advance, hold, skip) +- DOCUMENTS → ADR `ADR-023` (ADR-023: Presenter Controls — Per-Navigation Command Source at the Loader/Runner Seam) +- DOCUMENTS → DOCUMENTATION `docs/design/pul-f020-presenter-controls-preflight.md` (PUL-F020 Presenter Controls Preflight (codex architecture preflight design context)) +- DOCUMENTS → TEST `tests/runtime/presenter.test.ts` (Presenter pure-module tests (PUL-F020 boundary: command kinds + validator + per-navigation controller — abort teardown, post-abort/post-unsubscribe emission guards, idempotent source unsubscribe, frozen defensive copy, sibling isolation)) +- DOCUMENTS → TEST `tests/runtime/scene-navigation.test.ts` (Scene-navigation tests — URL present-mode presenter forwarding describe block (PUL-F020 bridge: presenter to runner single + composition, every-scene NOT head-only, no-truncation under presenter, key-presence omission)) +- DOCUMENTS → TEST `tests/runtime/scene-loader-present.test.ts` (Loader tests — presenter-controls dispatch (PUL-F020): mode=present forwarding, per-mode negatives, every-kind delivery, abort-detaches, cleanup-before-handoff, onError. Split from scene-loader.test.ts; updated for ADR-025 single timeline.run forwarding.) +- IMPLEMENTS → CODE_FILE `src/runtime/timeline.ts` (GSAP composition timeline runner — applyPresenterCommandToMaster translates advance / hold / skip-forward / skip-backward into master transport (PUL-F020); per-activation held / explicitlyPaused state distinguishes a beat hold from an explicit pause.) +- IMPLEMENTS → CODE_FILE `src/system/presenter/keyboard-source.ts` (Keyboard presenter source — DEFAULT_KEYBOARD_BINDINGS maps ArrowRight/Space (advance), KeyP (hold), ArrowLeft/PageUp (skip-backward), PageDown (skip-forward) into PresenterCommands under mode=present.) +- TESTS → TEST `tests/runtime/timeline.test.ts` (Timeline tests — presenter command transport describe block: advance releases a hold and seeks to the next beat; hold is idempotent (not a toggle); skip-forward/skip-backward seek segments.) +- TESTS → TEST `tests/system/presenter-keyboard.test.ts` (Keyboard presenter tests — PageDown emits skip-forward, PageUp emits skip-backward; end-to-end dispatch: a real keydown drives the master timeline to the next segment.) +- IMPLEMENTS → GITHUB_ISSUE `132` (PUL-F020 / PUL-F021 / PUL-F025: wire presenter keyboard input + GSAP pause/resume) diff --git a/docs/requirements/PUL-F021/requirement.md b/docs/requirements/PUL-F021/requirement.md new file mode 100644 index 0000000..74135b9 --- /dev/null +++ b/docs/requirements/PUL-F021/requirement.md @@ -0,0 +1,35 @@ +--- +id: PUL-F021 +title: "Pause and resume" +status: ACTIVE +type: FUNCTIONAL +priority: MUST +wave: 1 +created_at: 2026-04-30T19:17:10.386106Z +updated_at: 2026-05-18T21:22:51.494576Z +--- + +# PUL-F021 — Pause and resume + +## Statement + +The runtime SHALL accept presenter input to pause the active timeline and SHALL accept input to resume from the same point. + +## Rationale + +Presenter recovery during interruptions (questions, technical pauses). + +## Traceability + +- DOCUMENTS → ADR `ADR-007` (Browser as the Default Workbench and Agent Collaboration Surface) +- DOCUMENTS → GITHUB_ISSUE `30` (PUL-F021: Pause and resume) +- DOCUMENTS → ADR `ADR-024` (ADR-024: Presenter Pause/Resume — Runner-Owned Transport State on the Existing Presenter Command Seam (extends ADR-023 with the pause/resume command kinds; pins the cross-command precedence contract; PUL-F021 stays DRAFT until the GSAP runner lands)) +- DOCUMENTS → DOCUMENTATION `docs/design/pul-f021-pause-resume-preflight.md` (PUL-F021 Pause and Resume Preflight (codex architecture preflight design context: boundary, required reuse of the ADR-023 incumbents, cross-cutting layer table, guardrails incl. cross-command precedence, extensibility point, non-goals, anti-patterns)) +- DOCUMENTS → TEST `tests/runtime/presenter.test.ts` (Presenter pure-module tests — PUL-F021 seam coverage: PRESENTER_COMMAND_KINDS pins the six-kind list; isPresenterCommand admits `pause` / `resume` (and rejects `paused`); createPresenterController forwards `pause` then `resume` to the runner in order) +- DOCUMENTS → CODE_FILE `src/runtime/presenter.ts` (Presenter command module — PRESENTER_COMMAND_KINDS extended with the PUL-F021 `pause` / `resume` kinds; ADR-024 contract recorded in the module docstring. Forward-looking: DRAFT until the GSAP runner proves same-point behavior.) +- DOCUMENTS → TEST `tests/runtime/scene-loader-present.test.ts` (Loader tests — presenter-controls dispatch describe block: PUL-F021 `pause` / `resume` delivered to the runner through the mode=present loader dispatch alongside the PUL-F020 four kinds. Split from scene-loader.test.ts (ADR-025).) +- IMPLEMENTS → CODE_FILE `src/runtime/timeline.ts` (GSAP composition timeline runner — pause freezes the master at the current playhead and resume continues from the same point; ADR-024 cross-command precedence (only resume unfreezes an explicit pause).) +- IMPLEMENTS → CODE_FILE `src/system/presenter/keyboard-source.ts` (Keyboard presenter source — DEFAULT_KEYBOARD_BINDINGS maps KeyK (pause) and KeyL (resume) into PresenterCommands, the presenter input surface for PUL-F021.) +- TESTS → TEST `tests/runtime/timeline.test.ts` (Timeline tests — presenter command transport describe block: pause freezes the master and resume continues from the same playhead; beat-pacing commands received while explicitly paused never resume (ADR-024).) +- TESTS → TEST `tests/system/presenter-keyboard.test.ts` (Keyboard presenter tests — KeyK emits pause and KeyL emits resume.) +- IMPLEMENTS → GITHUB_ISSUE `132` (PUL-F020 / PUL-F021 / PUL-F025: wire presenter keyboard input + GSAP pause/resume) diff --git a/docs/requirements/PUL-F022/requirement.md b/docs/requirements/PUL-F022/requirement.md new file mode 100644 index 0000000..5123a64 --- /dev/null +++ b/docs/requirements/PUL-F022/requirement.md @@ -0,0 +1,30 @@ +--- +id: PUL-F022 +title: "Timeline orchestration" +status: ACTIVE +type: FUNCTIONAL +priority: MUST +wave: 2 +created_at: 2026-04-30T19:17:13.492296Z +updated_at: 2026-05-10T15:53:24.356372Z +--- + +# PUL-F022 — Timeline orchestration + +## Statement + +Each scene SHALL produce a timeline that the runtime composes into a master timeline for the active composition. The composed timeline SHALL support play, pause, seek, speed change, and named labels. + +## Rationale + +Timeline composition is the runtime's sequencing spine (ADR-003). + +## Traceability + +- DOCUMENTS → ADR `ADR-003` (GSAP as the Timeline Engine) +- IMPLEMENTS → CODE_FILE `src/runtime/timeline.ts` (GSAP timeline adapter — ctx.gsap, composeMasterTimeline, MasterTimeline, createGsapCompositionTimeline) +- IMPLEMENTS → CODE_FILE `src/runtime/composition-resolver.ts` (Composition resolver — mount-all → compose-master → play → cleanup-all lifecycle (ADR-025)) +- IMPLEMENTS → ADR `ADR-025` (Timeline Adapter and Composition Master — Revising the Resolution Lifecycle) +- TESTS → TEST `tests/runtime/timeline.test.ts` (GSAP timeline adapter tests — engine, assertSceneTimeline, composeMasterTimeline, MasterTimeline transport, createGsapCompositionTimeline) +- TESTS → TEST `tests/runtime/composition-resolver.test.ts` (Composition resolver tests — mount-all → compose-master → play → cleanup-all lifecycle, abort checkpoints, failure semantics) +- IMPLEMENTS → GITHUB_ISSUE `31` (PUL-F022: Timeline orchestration) diff --git a/docs/requirements/PUL-F023/requirement.md b/docs/requirements/PUL-F023/requirement.md new file mode 100644 index 0000000..70918db --- /dev/null +++ b/docs/requirements/PUL-F023/requirement.md @@ -0,0 +1,29 @@ +--- +id: PUL-F023 +title: "Named timeline beats" +status: ACTIVE +type: FUNCTIONAL +priority: MUST +wave: 2 +created_at: 2026-04-30T19:17:16.129340Z +updated_at: 2026-05-10T16:43:05.834955Z +--- + +# PUL-F023 — Named timeline beats + +## Statement + +Scene timelines SHALL support named labels (beats) referenceable by URL, presenter input, and other runtime subsystems. + +## Rationale + +Named beats are the agent-friendly time grammar (ADR-003, ADR-008). + +## Traceability + +- DOCUMENTS → ADR `ADR-003` (GSAP as the Timeline Engine) +- DOCUMENTS → ADR `ADR-008` (Agent-Native Authoring as a First-Class Architectural Constraint) +- IMPLEMENTS → CODE_FILE `src/runtime/timeline.ts` (Named timeline beats — kebab-case beat-label + position validation (assertSceneTimeline), the MasterTimeline beat-query surface (labels/hasLabel/seek/labelFor/beats), parseSceneTimelineLabel) +- TESTS → TEST `tests/runtime/timeline.test.ts` (Named timeline beats tests — beat-label/position validation, leak-on-rejection, parseSceneTimelineLabel, MasterTimeline.beats(), resolver bad-beat envelope, terminal-beat resolve-immediately) +- IMPLEMENTS → ADR `ADR-026` (Named Timeline Beats — Scene-Local Kebab Labels, Validated at Compose Time, Referenced Through the Master) +- DOCUMENTS → DOCUMENTATION `docs/design/pul-f023-named-timeline-beats-preflight.md` (PUL-F023 Named Timeline Beats — codex architecture preflight design note) diff --git a/docs/requirements/PUL-F024/requirement.md b/docs/requirements/PUL-F024/requirement.md new file mode 100644 index 0000000..f8b3d6e --- /dev/null +++ b/docs/requirements/PUL-F024/requirement.md @@ -0,0 +1,33 @@ +--- +id: PUL-F024 +title: "Audio orchestration" +status: ACTIVE +type: FUNCTIONAL +priority: MUST +wave: 2 +created_at: 2026-04-30T19:17:18.796248Z +updated_at: 2026-05-11T00:35:38.890208Z +--- + +# PUL-F024 — Audio orchestration + +## Statement + +The runtime SHALL provide an audio service exposing per-scene playback, fades, sprites, looping, and named groups. Each scene SHALL access audio only via the runtime context. + +## Rationale + +Centralized audio lifecycle and cleanup (ADR-004). + +## Traceability + +- DOCUMENTS → ADR `ADR-004` (Howler.js as the Audio Engine) +- IMPLEMENTS → GITHUB_ISSUE `33` (PUL-F024: Audio orchestration) +- IMPLEMENTS → CODE_FILE `src/runtime/audio.ts` (Runtime audio service (createHowlerAudioEngine + createAudioService + noopAudioEngine)) +- IMPLEMENTS → CODE_FILE `src/runtime/scene-loader.ts` (Scene loader — ctx.audio threading, audioEngine option, onSceneCleaned wiring) +- IMPLEMENTS → CODE_FILE `src/runtime/composition-resolver.ts` (Composition resolver — onSceneCleaned hook for runtime per-scene audio group teardown) +- TESTS → TEST `tests/runtime/audio.test.ts` (createAudioService — load/play/fade/stop/stopGroup/mute/stopAll, sprites, signal binding, error isolation, runtime validators) +- TESTS → TEST `tests/runtime/audio-engine.test.ts` (createHowlerAudioEngine smoke tests (Howler boundary in node noAudio mode)) +- TESTS → TEST `tests/runtime/scene-loader-audio.test.ts` (Scene loader ↔ audio integration — ctx.audio threading, signal binding, silent mode, slice-asset enforcement, prompter bypass, noop fallback) +- DOCUMENTS → DOCUMENTATION `docs/design/pul-f024-audio-orchestration-preflight.md` (PUL-F024 audio orchestration preflight (codex architecture guidance)) +- IMPLEMENTS → PULL_REQUEST `92` (Implement PUL-F024 audio orchestration: ctx.audio + Howler boundary (ADR-004)) diff --git a/docs/requirements/PUL-F025/requirement.md b/docs/requirements/PUL-F025/requirement.md new file mode 100644 index 0000000..7d68865 --- /dev/null +++ b/docs/requirements/PUL-F025/requirement.md @@ -0,0 +1,33 @@ +--- +id: PUL-F025 +title: "Master mute" +status: ACTIVE +type: FUNCTIONAL +priority: MUST +wave: 1 +created_at: 2026-04-30T19:17:21.493234Z +updated_at: 2026-05-18T21:23:02.557254Z +--- + +# PUL-F025 — Master mute + +## Statement + +The runtime SHALL accept presenter input to toggle master mute. Master mute SHALL silence audio without altering timeline state. + +## Rationale + +Presenter audio control during live runs. + +## Traceability + +- DOCUMENTS → ADR `ADR-004` (Howler.js as the Audio Engine) +- DOCUMENTS → GITHUB_ISSUE `34` (PUL-F025: Master mute) +- DOCUMENTS → DOCUMENTATION `docs/design/pul-f025-master-mute-preflight.md` (PUL-F025 master mute preflight — codex architecture guardrails for composing ADR-004 master mute with the ADR-023 presenter command seam (no new ADR; runtime-side handler in scene-loader buildLoad)) +- DOCUMENTS → CODE_FILE `src/runtime/presenter.ts` (Presenter module — PRESENTER_COMMAND_KINDS adds the PUL-F025 toggle-master-mute kind; createPresenterController refactored to centralized validation + contained subscribe-time throws. PUL-F025 stays DRAFT pending presenter UI.) +- DOCUMENTS → CODE_FILE `src/runtime/scene-loader.ts` (Scene loader — buildPresenterPipe subscribes a per-navigation audio handler that calls audio.mute(!audio.isMuted()) on toggle-master-mute; separate presenterAbort signal so the navigation signal stays un-aborted on success.) +- DOCUMENTS → TEST `tests/runtime/presenter.test.ts` (Presenter pure-module tests — PUL-F025 coverage: seven-kind allowlist incl. toggle-master-mute; isPresenterCommand accept/reject coverage; centralized validation (one onError per emission); contained subscribe-time throw via onError.) +- DOCUMENTS → TEST `tests/runtime/scene-loader-present.test.ts` (Loader tests — presenter master-mute dispatch (PUL-F025 / ADR-004): toggle, round-trip, timeline-state-untouched, mode scoping, engine round-trip, cross-scene persistence, abort detach, post-completion teardown.) +- IMPLEMENTS → CODE_FILE `src/system/presenter/keyboard-source.ts` (Keyboard presenter source — DEFAULT_KEYBOARD_BINDINGS maps KeyM to toggle-master-mute, the presenter input surface for PUL-F025.) +- TESTS → TEST `tests/system/presenter-keyboard.test.ts` (Keyboard presenter tests — KeyM emits toggle-master-mute.) +- IMPLEMENTS → GITHUB_ISSUE `132` (PUL-F020 / PUL-F021 / PUL-F025: wire presenter keyboard input + GSAP pause/resume) diff --git a/docs/requirements/PUL-F026/requirement.md b/docs/requirements/PUL-F026/requirement.md new file mode 100644 index 0000000..efe3809 --- /dev/null +++ b/docs/requirements/PUL-F026/requirement.md @@ -0,0 +1,32 @@ +--- +id: PUL-F026 +title: "Rehearsal mode" +status: ACTIVE +type: FUNCTIONAL +priority: SHOULD +wave: 2 +created_at: 2026-04-30T19:17:23.797423Z +updated_at: 2026-05-11T03:00:39.520922Z +--- + +# PUL-F026 — Rehearsal mode + +## Statement + +The runtime SHALL provide a rehearsal mode in which audio is silenced or logged as cues without altering timeline state. + +## Rationale + +Author rehearsal without disturbing audio output. + +## Traceability + +- DOCUMENTS → ADR `ADR-004` (Howler.js as the Audio Engine) +- IMPLEMENTS → CODE_FILE `src/runtime/audio.ts` (Audio service — AudioOutputPolicy union, log-cues emission, AudioCueLogEntry discriminated union, boundary validation) +- IMPLEMENTS → CODE_FILE `src/runtime/scene-loader.ts` (Scene loader — audioOutputPolicyFor(mode) dispatches mode=rehearsal to outputPolicy 'log-cues'; onAudioCue threaded into per-navigation AudioService) +- IMPLEMENTS → CODE_FILE `src/runtime/navigation.ts` (URL grammar — NAVIGATION_MODES includes 'rehearsal' (eighth workbench mode)) +- IMPLEMENTS → ADR `ADR-004` (ADR-004 — Output policy subsection documents rehearsal-mode contract on the audio-service seam) +- TESTS → TEST `tests/runtime/audio.test.ts` (Audio service tests — outputPolicy log-cues emission, validation, freeze, sequence, no-URL invariant; legacy silent rejection) +- TESTS → TEST `tests/runtime/scene-loader-audio.test.ts` (Scene loader rehearsal tests — outputPolicy wiring, FULL slice preservation, head-entry override preservation, ctx.mode reach) +- TESTS → TEST `tests/runtime/navigation.test.ts` (Navigation grammar tests — 'rehearsal' accepted as URL mode value; allowlist contains it) +- IMPLEMENTS → GITHUB_ISSUE `35` (PUL-F026: Rehearsal mode) diff --git a/docs/requirements/PUL-F027/requirement.md b/docs/requirements/PUL-F027/requirement.md new file mode 100644 index 0000000..b69720f --- /dev/null +++ b/docs/requirements/PUL-F027/requirement.md @@ -0,0 +1,31 @@ +--- +id: PUL-F027 +title: "Caption metadata and prompter content" +status: ACTIVE +type: FUNCTIONAL +priority: MUST +wave: 2 +created_at: 2026-04-30T19:17:26.822588Z +updated_at: 2026-05-11T04:23:51.549818Z +--- + +# PUL-F027 — Caption metadata and prompter content + +## Statement + +Each scene SHALL declare its captions in metadata as a list of `{ at, text }` entries, where `at` is a millisecond offset or a beat label. The runtime SHALL derive the prompter view from this same metadata. + +## Rationale + +Single source of truth for prompter and captions (ADR-008). + +## Traceability + +- DOCUMENTS → ADR `ADR-002` (Scene Registry and Composition Manifests as the Core Abstraction) +- DOCUMENTS → ADR `ADR-008` (Agent-Native Authoring as a First-Class Architectural Constraint) +- IMPLEMENTS → CODE_FILE `src/runtime/scene.ts` (Scene schema — Caption.at number|string union, isCaptionAt predicate, describeCaptionFault indexed validator) +- IMPLEMENTS → CODE_FILE `src/runtime/prompter.ts` (Prompter — buildPrompterScript derives view from scene.captions; carries widened Caption.at through structurally) +- IMPLEMENTS → ADR `ADR-027` (ADR-027 — Caption Timestamp Grammar: refines the caption-shape clauses in ADR-002 / ADR-022) +- TESTS → TEST `tests/runtime/scene.test.ts` (Scene schema tests — caption block: numeric+beat-label accept, every reject path, indexed error format pinned) +- TESTS → TEST `tests/runtime/prompter.test.ts` (Prompter tests — mixed numeric + beat-label passthrough pinned) +- IMPLEMENTS → GITHUB_ISSUE `36` (PUL-F027: Caption metadata and prompter content) diff --git a/docs/requirements/PUL-F028/requirement.md b/docs/requirements/PUL-F028/requirement.md new file mode 100644 index 0000000..9cb3dfa --- /dev/null +++ b/docs/requirements/PUL-F028/requirement.md @@ -0,0 +1,32 @@ +--- +id: PUL-F028 +title: "Validation" +status: ACTIVE +type: FUNCTIONAL +priority: MUST +wave: 0 +created_at: 2026-04-30T19:17:30.249579Z +updated_at: 2026-05-11T05:32:27.744008Z +--- + +# PUL-F028 — Validation + +## Statement + +The runtime SHALL provide a validation pass that detects: scene ids referenced in compositions but not present in the registry; assets referenced in scene metadata but not resolvable; duplicate scene ids; scenes that do not export a `cleanup` function. + +## Rationale + +Validation catches structural breakage before agents or humans waste time on it (ADR-008). + +## Traceability + +- DOCUMENTS → ADR `ADR-008` (Agent-Native Authoring as a First-Class Architectural Constraint) +- IMPLEMENTS → CODE_FILE `src/runtime/validation.ts` (validateRuntime — structural validation pass over scenes, compositions, asset policy; the four clauses of PUL-F028) +- IMPLEMENTS → CODE_FILE `src/runtime/id-registry.ts` (createIdRegistry — onDuplicate collector hook lets the validator aggregate duplicate scene-ids without throwing while runtime boot stays fail-fast) +- IMPLEMENTS → CODE_FILE `src/main.ts` (Workbench entry — invokes validateRuntime + assertNoValidationFindings at boot against the same scenes/compositions handed to the registry constructors, halting before lifecycle effects on a broken graph) +- TESTS → TEST `tests/runtime/validation.test.ts` (Vitest suite — clause-by-clause coverage for validateRuntime + assertNoValidationFindings (33 tests)) +- TESTS → TEST `tests/runtime/id-registry.test.ts` (Vitest suite — locks the onDuplicate collector contract validation depends on (5 tests)) +- DOCUMENTS → DOCUMENTATION `CHANGELOG.md` (CHANGELOG — [Unreleased] / Added entry for the runtime validation pass) +- DOCUMENTS → DOCUMENTATION `docs/design/pul-f028-validation-preflight.md` (Design preflight — architectural guardrails for PUL-F028: reuse contracts, no side effects, asset-policy seam) +- IMPLEMENTS → GITHUB_ISSUE `37` (PUL-F028: Validation) diff --git a/docs/requirements/PUL-F029/requirement.md b/docs/requirements/PUL-F029/requirement.md new file mode 100644 index 0000000..45bd104 --- /dev/null +++ b/docs/requirements/PUL-F029/requirement.md @@ -0,0 +1,33 @@ +--- +id: PUL-F029 +title: "Scene-level error isolation" +status: ACTIVE +type: FUNCTIONAL +priority: MUST +wave: 1 +created_at: 2026-04-30T19:17:33.258752Z +updated_at: 2026-05-12T02:53:55.260016Z +--- + +# PUL-F029 — Scene-level error isolation + +## Statement + +When a scene throws or otherwise fails during `create`, `timeline`, or `cleanup`, the runtime SHALL surface the failure with scene context and SHALL NOT halt the active composition. The presenter SHALL be able to advance past the failed scene. + +## Rationale + +Recoverability during live presentation. + +## Traceability + +- DOCUMENTS → ADR `ADR-007` (Browser as the Default Workbench and Agent Collaboration Surface) +- IMPLEMENTS → GITHUB_ISSUE `38` (PUL-F029: Scene-level error isolation) +- IMPLEMENTS → CODE_FILE `src/runtime/composition-resolver.ts` +- IMPLEMENTS → CODE_FILE `src/runtime/scene-loader.ts` +- IMPLEMENTS → CODE_FILE `src/runtime/scene-navigation.ts` +- IMPLEMENTS → ADR `ADR-028` (Scene-Level Error Isolation) +- TESTS → TEST `tests/runtime/composition-resolver.test.ts` +- TESTS → TEST `tests/runtime/scene-loader.test.ts` +- TESTS → TEST `tests/runtime/scene-loader-audio.test.ts` +- TESTS → TEST `tests/runtime/scene-navigation.test.ts` diff --git a/docs/requirements/PUL-F030/requirement.md b/docs/requirements/PUL-F030/requirement.md new file mode 100644 index 0000000..6fbc01d --- /dev/null +++ b/docs/requirements/PUL-F030/requirement.md @@ -0,0 +1,36 @@ +--- +id: PUL-F030 +title: "Audio unlock interaction" +status: ACTIVE +type: FUNCTIONAL +priority: MUST +wave: 2 +created_at: 2026-04-30T19:17:36.159441Z +updated_at: 2026-05-12T04:25:43.563737Z +--- + +# PUL-F030 — Audio unlock interaction + +## Statement + +On loading `mode=present` for a composition that declares audio, the runtime SHALL provide a single explicit user-gesture interaction that satisfies browser autoplay policy before the composition begins. + +## Rationale + +Centralizes the autoplay-policy unlock once, not per scene. + +## Traceability + +- DOCUMENTS → ADR `ADR-004` (Howler.js as the Audio Engine) +- IMPLEMENTS → GITHUB_ISSUE `39` (PUL-F030: Audio unlock interaction) +- IMPLEMENTS → PULL_REQUEST `104` (Implement PUL-F030 present-mode audio unlock interaction (ADR-029)) +- IMPLEMENTS → ADR `ADR-029` (Present-Mode Audio Unlock Gate Before Composition Start) +- IMPLEMENTS → CODE_FILE `src/runtime/scene.ts` (SceneModule.audio field + sceneDeclaresAudio predicate) +- IMPLEMENTS → CODE_FILE `src/runtime/audio.ts` (AudioEngine.unlock — Web Audio + HTML5 fallback) +- IMPLEMENTS → CODE_FILE `src/runtime/scene-loader.ts` (Audio unlock gate (AudioUnlockAdapter seam + resolveUnlockGate)) +- IMPLEMENTS → CODE_FILE `src/runtime/audio-unlock-dom.ts` (Workbench DOM unlock adapter factory) +- IMPLEMENTS → CODE_FILE `src/main.ts` (Production workbench wiring of the unlock adapter) +- TESTS → TEST `tests/runtime/scene.test.ts` (scene.audio schema + sceneDeclaresAudio predicate tests) +- TESTS → TEST `tests/runtime/audio-engine.test.ts` (AudioEngine.unlock branch tests (Web Audio, HTML5, fail-close, idempotency)) +- TESTS → TEST `tests/runtime/scene-loader-audio.test.ts` (PUL-F030 gate trigger / bypass / supersession / fail-loud tests) +- TESTS → TEST `tests/runtime/audio-unlock-dom.test.ts` (Workbench DOM unlock adapter contract tests) diff --git a/docs/requirements/PUL-F031/requirement.md b/docs/requirements/PUL-F031/requirement.md new file mode 100644 index 0000000..45079d1 --- /dev/null +++ b/docs/requirements/PUL-F031/requirement.md @@ -0,0 +1,31 @@ +--- +id: PUL-F031 +title: "Workbench chrome surface" +status: ACTIVE +type: FUNCTIONAL +priority: MUST +wave: 1 +created_at: 2026-05-09T01:43:51.818650Z +updated_at: 2026-05-17T22:44:11.718471Z +--- + +# PUL-F031 — Workbench chrome surface + +## Statement + +The runtime SHALL render a workbench chrome surface around the scene stage. Chrome SHALL be workbench-owned, mounted before the first scene navigation, and SHALL NOT be created or mutated by scene modules. Chrome rendering SHALL be governed by the active workbench mode: `mode=present` renders chrome fully; modes that explicitly suppress chrome (e.g., `mode=standalone`, `mode=screenshot`) SHALL hide it. Chrome SHALL persist across scene navigations within a composition without being torn down between scenes. + +## Rationale + +Per ADR-007 and ADR-016, chrome is the persistent UI a presenter and reviewer rely on to orient navigation and presenter affordances. Chrome must be workbench-owned to keep mode dispatch in the runtime core (PUL-A008) and to prevent scene modules owning UI that should outlive scene cleanup. PUL-F013 ACTIVE depends on this requirement landing. + +## Traceability + +- IMPLEMENTS → GITHUB_ISSUE `77` (PUL-F031: Workbench chrome surface) +- IMPLEMENTS → PULL_REQUEST `119` (feat: add PUL-F031 workbench chrome surface) +- IMPLEMENTS → CODE_FILE `src/runtime/workbench-chrome.ts` +- IMPLEMENTS → CODE_FILE `src/runtime/scene-loader.ts` +- IMPLEMENTS → CODE_FILE `src/main.ts` +- IMPLEMENTS → ADR `docs/adrs/031-workbench-chrome-surface.md` (ADR-031: Workbench Chrome Surface) +- TESTS → TEST `tests/runtime/workbench-chrome.test.ts` +- TESTS → TEST `tests/runtime/scene-loader-chrome.test.ts` diff --git a/docs/requirements/PUL-P001/requirement.md b/docs/requirements/PUL-P001/requirement.md new file mode 100644 index 0000000..c19daf1 --- /dev/null +++ b/docs/requirements/PUL-P001/requirement.md @@ -0,0 +1,32 @@ +--- +id: PUL-P001 +title: "Mandatory cleanup contract" +status: ACTIVE +type: CONSTRAINT +priority: MUST +wave: 0 +created_at: 2026-04-30T19:18:48.252403Z +updated_at: 2026-05-12T16:16:40.928203Z +--- + +# PUL-P001 — Mandatory cleanup contract + +## Statement + +Every scene module MUST export a `cleanup(ctx)` function. The runtime MUST invoke it on scene exit. Cleanup leaks (DOM, listeners, audio, timeline objects) MUST be treated as runtime defects. + +## Rationale + +Cleanup is the runtime invariant that makes scoped agent edits safe (ADR-008). + +## Traceability + +- CONSTRAINS → ADR `ADR-008` (Agent-Native Authoring as a First-Class Architectural Constraint) +- IMPLEMENTS → CODE_FILE `src/runtime/scene.ts` (SceneModule contract + assertSceneModule (cleanup required field)) +- IMPLEMENTS → CODE_FILE `src/runtime/composition-resolver.ts` (Composition resolver — mandatory cleanup invocation on every scene exit (including failure paths)) +- IMPLEMENTS → CODE_FILE `src/runtime/scene-loader.ts` (Scene loader — cleanup-before-handoff for every navigation) +- IMPLEMENTS → CODE_FILE `src/runtime/validation.ts` (Validation pass — clause (d) detects scenes that do not export cleanup) +- TESTS → TEST `tests/runtime/scene.test.ts` (Scene contract tests — cleanup field shape) +- TESTS → TEST `tests/runtime/composition-resolver.test.ts` (Composition resolver tests — mandatory cleanup invocation) +- TESTS → TEST `tests/runtime/validation.test.ts` (Validation tests — clause (d) missing-cleanup detection) +- IMPLEMENTS → GITHUB_ISSUE `59` (Issue #59 — PUL-P001 mandatory cleanup contract) diff --git a/docs/requirements/PUL-P002/requirement.md b/docs/requirements/PUL-P002/requirement.md new file mode 100644 index 0000000..a685e56 --- /dev/null +++ b/docs/requirements/PUL-P002/requirement.md @@ -0,0 +1,30 @@ +--- +id: PUL-P002 +title: "Validation runs in CI" +status: ACTIVE +type: CONSTRAINT +priority: MUST +wave: 0 +created_at: 2026-04-30T19:18:50.946517Z +updated_at: 2026-05-12T16:16:44.803906Z +--- + +# PUL-P002 — Validation runs in CI + +## Statement + +The validation pass MUST run in CI on every pull request. CI MUST fail when validation reports any error. + +## Rationale + +Validation that does not gate merges does not protect the loop (ADR-008). + +## Traceability + +- CONSTRAINS → ADR `ADR-008` (Agent-Native Authoring as a First-Class Architectural Constraint) +- IMPLEMENTS → CODE_FILE `src/workbench-graph.ts` (Canonical workbench-graph module — single source for scenes + compositions consumed by main.ts AND the CI gate) +- IMPLEMENTS → CODE_FILE `src/main.ts` (Workbench bootstrap — imports canonical graph for boot-time validation) +- IMPLEMENTS → CONFIG `.github/workflows/ci.yml` (CI workflow — pnpm test:coverage job runs the validation gate on every PR) +- TESTS → TEST `tests/runtime/workbench-graph.test.ts` (Workbench-graph integration gate — asserts zero findings + fault-injection per PUL-F028 clause) +- IMPLEMENTS → GITHUB_ISSUE `60` (Issue #60 — PUL-P002 validation runs in CI) +- IMPLEMENTS → PULL_REQUEST `106` (PR #106 — bundle PUL-P001..P005 + workbench-graph CI gate) diff --git a/docs/requirements/PUL-P003/requirement.md b/docs/requirements/PUL-P003/requirement.md new file mode 100644 index 0000000..b22d869 --- /dev/null +++ b/docs/requirements/PUL-P003/requirement.md @@ -0,0 +1,27 @@ +--- +id: PUL-P003 +title: "ADR format" +status: ACTIVE +type: CONSTRAINT +priority: MUST +wave: 0 +created_at: 2026-04-30T19:18:53.607786Z +updated_at: 2026-05-12T16:16:48.616768Z +--- + +# PUL-P003 — ADR format + +## Statement + +Architecture decisions MUST be recorded as ADRs in MADR format under `docs/adrs/` and mirrored in Ground Control. + +## Rationale + +Single, machine-readable, versioned source for architectural decisions. + +## Traceability + +- IMPLEMENTS → DOCUMENTATION `docs/adrs/000-template.md` (Local MADR template — the format every ADR follows) +- IMPLEMENTS → DOCUMENTATION `docs/adrs/README.md` (ADR index + MADR declaration + immutability + sequential-numbering principles) +- CONSTRAINS → ADR `ADR-008` (Agent-native authoring — names ADR/GC mirroring discipline as a binding architectural constraint) +- IMPLEMENTS → GITHUB_ISSUE `61` (Issue #61 — PUL-P003 ADR format) diff --git a/docs/requirements/PUL-P004/requirement.md b/docs/requirements/PUL-P004/requirement.md new file mode 100644 index 0000000..aab0b36 --- /dev/null +++ b/docs/requirements/PUL-P004/requirement.md @@ -0,0 +1,26 @@ +--- +id: PUL-P004 +title: "Requirement lifecycle" +status: ACTIVE +type: CONSTRAINT +priority: MUST +wave: 0 +created_at: 2026-04-30T19:18:56.704654Z +updated_at: 2026-05-12T16:16:52.067116Z +--- + +# PUL-P004 — Requirement lifecycle + +## Statement + +Requirements MUST follow the lifecycle DRAFT → ACTIVE → DEPRECATED. A requirement MUST NOT transition to ACTIVE until it is implemented and traceability links to source and tests are in place. + +## Rationale + +Status accurately reflects implementation state. + +## Traceability + +- IMPLEMENTS → DOCUMENTATION `docs/requirements/conventions.md` (Status Lifecycle section — DRAFT→ACTIVE→DEPRECATED rule + implementation+traceability preconditions for ACTIVE) +- CONSTRAINS → ADR `ADR-008` (Agent-native authoring — requirement lifecycle discipline + manifest-over-flow-control) +- IMPLEMENTS → GITHUB_ISSUE `62` (Issue #62 — PUL-P004 requirement lifecycle) diff --git a/docs/requirements/PUL-P005/requirement.md b/docs/requirements/PUL-P005/requirement.md new file mode 100644 index 0000000..08065ef --- /dev/null +++ b/docs/requirements/PUL-P005/requirement.md @@ -0,0 +1,26 @@ +--- +id: PUL-P005 +title: "ADR linkage discipline" +status: ACTIVE +type: CONSTRAINT +priority: MUST +wave: 0 +created_at: 2026-04-30T19:18:58.802715Z +updated_at: 2026-05-12T16:16:55.165490Z +--- + +# PUL-P005 — ADR linkage discipline + +## Statement + +When any ADR motivates or constrains a requirement, the requirement MUST have a traceability link to that ADR with link type DOCUMENTS or CONSTRAINS as appropriate. + +## Rationale + +Bidirectional traceability between ADRs and requirements. + +## Traceability + +- IMPLEMENTS → DOCUMENTATION `docs/requirements/conventions.md` (ADR Linkage section — gc_create_traceability_link with DOCUMENTS/CONSTRAINS for ADR artifacts) +- CONSTRAINS → ADR `ADR-008` (Agent-native authoring — names the bidirectional ADR/requirement linkage discipline) +- IMPLEMENTS → GITHUB_ISSUE `63` (Issue #63 — PUL-P005 ADR linkage discipline) diff --git a/docs/requirements/PUL-Q001/requirement.md b/docs/requirements/PUL-Q001/requirement.md new file mode 100644 index 0000000..e395978 --- /dev/null +++ b/docs/requirements/PUL-Q001/requirement.md @@ -0,0 +1,28 @@ +--- +id: PUL-Q001 +title: "Screenshot determinism" +status: DRAFT +type: NON_FUNCTIONAL +priority: MUST +wave: 1 +created_at: 2026-04-30T19:17:41.993185Z +updated_at: 2026-04-30T19:17:41.993185Z +--- + +# PUL-Q001 — Screenshot determinism + +## Statement + +For a given code revision and a given workbench URL with `mode=screenshot`, the rendered output SHALL be byte-identical across reloads on the same browser engine and platform. + +## Rationale + +Determinism is what makes screenshot mode useful for visual regression and agent verification. + +## Traceability + +- DOCUMENTS → ADR `ADR-007` (Browser as the Default Workbench and Agent Collaboration Surface) +- DOCUMENTS → ADR `ADR-008` (Agent-Native Authoring as a First-Class Architectural Constraint) +- DOCUMENTS → GITHUB_ISSUE `40` (PUL-Q001: Screenshot determinism) +- DOCUMENTS → TEST `tests/runtime/screenshot-determinism-source.test.ts` (PUL-Q001 — screenshot determinism source scan (structural gate)) +- DOCUMENTS → GITHUB_ISSUE `133` (PUL-Q001: empirical byte-equality verification for screenshot mode) diff --git a/docs/requirements/PUL-Q002/requirement.md b/docs/requirements/PUL-Q002/requirement.md new file mode 100644 index 0000000..1a3ffdb --- /dev/null +++ b/docs/requirements/PUL-Q002/requirement.md @@ -0,0 +1,35 @@ +--- +id: PUL-Q002 +title: "Browser support" +status: ACTIVE +type: NON_FUNCTIONAL +priority: MUST +wave: 0 +created_at: 2026-04-30T19:17:44.744805Z +updated_at: 2026-05-13T03:30:30.960495Z +--- + +# PUL-Q002 — Browser support + +## Statement + +The runtime SHALL function in the latest stable releases of Chromium-based browsers, Firefox, and WebKit-based browsers as of the project release. + +## Rationale + +Defines the supported environment without overreaching to legacy browsers. + +## Traceability + +- IMPLEMENTS → CODE_FILE `src/workbench-graph.ts` (Workbench graph registration (placeholder + fixture)) +- IMPLEMENTS → CONFIG `.github/workflows/ci.yml` (CI workflow with browser-support job) +- IMPLEMENTS → CONFIG `package.json` (package.json (@playwright/test dep + test:browsers script)) +- TESTS → TEST `tests-e2e/browser-support.spec.ts` (Playwright cross-engine smoke spec) +- TESTS → TEST `tests/runtime/policy-q002-browser-support.test.ts` (PUL-Q002 source-policy structural gate) +- TESTS → TEST `tests/scenes/browser-support-fixture.test.ts` (Browser support fixture scene unit tests) +- IMPLEMENTS → GITHUB_ISSUE `41` (PUL-Q002: Browser support) +- IMPLEMENTS → PULL_REQUEST `108` (PUL-Q002: Browser support gate) +- IMPLEMENTS → ADR `docs/adrs/030-browser-support.md` (ADR-030: Browser Support Contract) +- IMPLEMENTS → DOCUMENTATION `docs/design/pul-q002-browser-support-preflight.md` (PUL-Q002 Browser Support Preflight (codex)) +- IMPLEMENTS → CONFIG `playwright.config.ts` (Playwright config (3 engine projects + webServer)) +- IMPLEMENTS → CODE_FILE `src/scenes/browser-support-fixture.ts` (Browser support fixture scene (real GSAP timeline)) diff --git a/docs/requirements/PUL-Q003/requirement.md b/docs/requirements/PUL-Q003/requirement.md new file mode 100644 index 0000000..ede3c0a --- /dev/null +++ b/docs/requirements/PUL-Q003/requirement.md @@ -0,0 +1,28 @@ +--- +id: PUL-Q003 +title: "URL state determinism" +status: ACTIVE +type: NON_FUNCTIONAL +priority: MUST +wave: 1 +created_at: 2026-04-30T19:17:48.165212Z +updated_at: 2026-05-16T06:08:17.783953Z +--- + +# PUL-Q003 — URL state determinism + +## Statement + +URL parameters SHALL fully determine the runtime's targeted state. The runtime SHALL NOT use `localStorage`, `sessionStorage`, cookies, or other persisted state to determine which scene, beat, composition, or mode is targeted. + +## Rationale + +Agents and reviewers must be able to share URLs that produce the same target on any machine. + +## Traceability + +- DOCUMENTS → ADR `ADR-007` (Browser as the Default Workbench and Agent Collaboration Surface) +- IMPLEMENTS → TEST `tests/runtime/policy-q003-url-state-determinism.test.ts` (Vitest source-policy gate — structural enforcement of the persisted-state ban across src/**/*.ts) +- IMPLEMENTS → DOCUMENTATION `docs/design/pul-q003-url-state-determinism-preflight.md` (Design preflight — guardrails for URL-only target selection, alias-aware enforcement, line-scoped exemptions) +- TESTS → TEST `tests/runtime/navigation.test.ts` (PUL-Q003 behavioral describe block — seeds host globals with misleading values, asserts parseNavigationSearch/bootstrapNavigation/subscribeNavigation/effectiveMode ignore them) +- IMPLEMENTS → GITHUB_ISSUE `42` (PUL-Q003: URL state determinism) diff --git a/docs/requirements/PUL-Q004/requirement.md b/docs/requirements/PUL-Q004/requirement.md new file mode 100644 index 0000000..ae61e94 --- /dev/null +++ b/docs/requirements/PUL-Q004/requirement.md @@ -0,0 +1,34 @@ +--- +id: PUL-Q004 +title: "Resource cleanup completeness" +status: ACTIVE +type: NON_FUNCTIONAL +priority: MUST +wave: 0 +created_at: 2026-04-30T19:17:51.650358Z +updated_at: 2026-05-16T22:17:19.401929Z +--- + +# PUL-Q004 — Resource cleanup completeness + +## Statement + +After `cleanup(ctx)` has run for a scene, no DOM nodes, event listeners, audio handles, or timeline objects created by the scene SHALL remain attached to the runtime. + +## Rationale + +Cleanup leaks make scoped agent edits unsafe and corrupt subsequent scenes. + +## Traceability + +- DOCUMENTS → ADR `ADR-002` (Scene Registry and Composition Manifests as the Core Abstraction) +- DOCUMENTS → ADR `ADR-008` (Agent-Native Authoring as a First-Class Architectural Constraint) +- IMPLEMENTS → GITHUB_ISSUE `43` (Issue #43: PUL-Q004: Resource cleanup completeness) +- IMPLEMENTS → PULL_REQUEST `110` (PR #110: add PUL-Q004 resource cleanup completeness source-policy gate) +- TESTS → TEST `tests/runtime/policy-q004-resource-cleanup.test.ts` (PUL-Q004 resource cleanup source-policy gate (Vitest)) +- DOCUMENTS → DOCUMENTATION `docs/design/pul-q004-resource-cleanup-preflight.md` (PUL-Q004 resource cleanup preflight design note) +- IMPLEMENTS → CODE_FILE `src/runtime/composition-resolver.ts` (Composition resolver — reverse-mount-order cleanup(ctx) on every exit path (happy, abort, phase failure, per-scene isolation)) +- IMPLEMENTS → CODE_FILE `src/runtime/scene-loader.ts` (Scene loader — navigation completion teardown (audio.stopAll, presenterAbort.abort, controller.abort) on every exit path) +- IMPLEMENTS → CODE_FILE `src/runtime/audio.ts` (Audio service — AudioService.stopAll() on navigation signal abort: stops + unloads every sound registered by the scene) +- IMPLEMENTS → CODE_FILE `src/runtime/timeline.ts` (Timeline adapter — MasterTimeline.kill() on natural completion and abort; composeMasterTimeline kills every scene-returned timeline on compose failure) +- IMPLEMENTS → CODE_FILE `src/runtime/presenter.ts` (Presenter controller — tearDownAll on per-navigation AbortSignal; subscriptions detached and source unsubscribed once) diff --git a/docs/requirements/PUL-Q005/requirement.md b/docs/requirements/PUL-Q005/requirement.md new file mode 100644 index 0000000..782c69e --- /dev/null +++ b/docs/requirements/PUL-Q005/requirement.md @@ -0,0 +1,30 @@ +--- +id: PUL-Q005 +title: "Validation actionability" +status: ACTIVE +type: NON_FUNCTIONAL +priority: MUST +wave: 0 +created_at: 2026-04-30T19:17:54.279739Z +updated_at: 2026-05-17T05:32:34.781854Z +--- + +# PUL-Q005 — Validation actionability + +## Statement + +Each error reported by the validation pass SHALL identify the offending entity (scene id, asset path, composition id) and the failing condition in human-readable form. + +## Rationale + +Validation that does not produce actionable errors is a runtime smell, not a runtime feature. + +## Traceability + +- DOCUMENTS → ADR `ADR-008` (Agent-Native Authoring as a First-Class Architectural Constraint) +- IMPLEMENTS → GITHUB_ISSUE `44` (Issue #44: PUL-Q005: Validation actionability) +- IMPLEMENTS → CODE_FILE `src/runtime/validation.ts` (Runtime validation pass — sceneIndex carry-through, scenes[index] message rewrites for no-usable-id branches, programmatic entryIndex via CompositionManifestError) +- IMPLEMENTS → CODE_FILE `src/runtime/composition.ts` (Composition manifest validator — CompositionManifestError exposes offending entry index programmatically so validation findings carry it without message parsing) +- TESTS → TEST `tests/runtime/validation.test.ts` (PUL-Q005 actionability suite (Vitest) — scene-record position locators, duplicate-id occurrence index, asset record-position prefix, programmatic entryIndex, AggregateError self-containment) +- DOCUMENTS → DOCUMENTATION `docs/design/pul-q005-validation-actionability-preflight.md` (PUL-Q005 validation actionability preflight design note) +- IMPLEMENTS → PULL_REQUEST `113` (PR #113: Add PUL-Q005 validation actionability locators) diff --git a/docs/requirements/PUL-Q006/requirement.md b/docs/requirements/PUL-Q006/requirement.md new file mode 100644 index 0000000..9b533b2 --- /dev/null +++ b/docs/requirements/PUL-Q006/requirement.md @@ -0,0 +1,30 @@ +--- +id: PUL-Q006 +title: "Error surfacing context" +status: ACTIVE +type: NON_FUNCTIONAL +priority: MUST +wave: 1 +created_at: 2026-04-30T19:17:57.358657Z +updated_at: 2026-05-17T18:02:11.621921Z +--- + +# PUL-Q006 — Error surfacing context + +## Statement + +When the runtime surfaces an error, the error message SHALL include the scene id and (where applicable) the beat label or the timeline phase (`create`, `timeline`, `cleanup`). + +## Rationale + +Scoped diagnosis is essential for the agent loop and for live presenter recovery. + +## Traceability + +- DOCUMENTS → ADR `ADR-007` (Browser as the Default Workbench and Agent Collaboration Surface) +- IMPLEMENTS → CODE_FILE `src/runtime/error.ts` (Shared error helpers — formatSceneContext canonical scene-error context renderer (PUL-Q006)) +- IMPLEMENTS → CODE_FILE `src/runtime/scene-loader.ts` (Scene loader — buildOnSceneFailed.renderMessage + buildOnBeatMissing compose scene-error context via formatSceneContext (PUL-Q006)) +- TESTS → TEST `tests/runtime/error.test.ts` (formatSceneContext unit tests — empty/scene-only/scene+phase/scene+beat output shapes (PUL-Q006)) +- TESTS → TEST `tests/runtime/pul-q006-error-context.test.ts` (PUL-Q006 cross-surface invariant suite — every runtime-error surface pinned for scene id + (phase or beat)) +- DOCUMENTS → DOCUMENTATION `docs/design/pul-q006-error-surfacing-context-preflight.md` (PUL-Q006 architecture preflight design note — guardrails, cross-cutting layers, intended design) +- IMPLEMENTS → GITHUB_ISSUE `45` (PUL-Q006: Error surfacing context) diff --git a/docs/requirements/PUL-Q007/requirement.md b/docs/requirements/PUL-Q007/requirement.md new file mode 100644 index 0000000..26fce37 --- /dev/null +++ b/docs/requirements/PUL-Q007/requirement.md @@ -0,0 +1,25 @@ +--- +id: PUL-Q007 +title: "No remote code execution" +status: ACTIVE +type: NON_FUNCTIONAL +priority: MUST +wave: 0 +created_at: 2026-04-30T19:18:00.409884Z +updated_at: 2026-05-12T21:26:30.134542Z +--- + +# PUL-Q007 — No remote code execution + +## Statement + +The runtime SHALL NOT use `eval`, `new Function`, dynamic `import()` of remote URLs at runtime, or any equivalent mechanism that would execute code not present in the published bundle. + +## Rationale + +Bounded code surface; CSP-compatible; no surprise execution paths. + +## Traceability + +- TESTS → TEST `tests/runtime/policy-q007-remote-code-execution.test.ts` (PUL-Q007 source-policy gate (Vitest)) +- IMPLEMENTS → CODE_FILE `tests/runtime/source-policy.ts` (Shared source-policy scanner module) diff --git a/docs/requirements/PUL-Q008/requirement.md b/docs/requirements/PUL-Q008/requirement.md new file mode 100644 index 0000000..22f61c7 --- /dev/null +++ b/docs/requirements/PUL-Q008/requirement.md @@ -0,0 +1,31 @@ +--- +id: PUL-Q008 +title: "Accessibility of DOM/CSS scenes" +status: ACTIVE +type: NON_FUNCTIONAL +priority: MUST +wave: 2 +created_at: 2026-04-30T19:18:03.891358Z +updated_at: 2026-05-17T18:58:11.029152Z +--- + +# PUL-Q008 — Accessibility of DOM/CSS scenes + +## Statement + +Scenes rendered via DOM/CSS SHALL preserve the browser's native accessibility tree: text remains selectable, focus order follows DOM order, and ARIA attributes are not stripped by the runtime. + +## Rationale + +DOM/CSS as the default surface (ADR-005) only pays off if the runtime does not undermine it. + +## Traceability + +- DOCUMENTS → ADR `ADR-005` (DOM/CSS as the Default Rendering Surface) +- IMPLEMENTS → CODE_FILE `src/scenes/dom-css-accessibility-fixture.ts` (DOM/CSS accessibility fixture scene) +- IMPLEMENTS → CODE_FILE `src/workbench-graph.ts` (Workbench graph registration of the accessibility fixture) +- IMPLEMENTS → DOCUMENTATION `docs/design/pul-q008-dom-css-accessibility-preflight.md` (PUL-Q008 architecture preflight design note) +- TESTS → TEST `tests/runtime/policy-q008-dom-css-accessibility.test.ts` (PUL-Q008 source-policy gate (Vitest)) +- TESTS → TEST `tests/scenes/dom-css-accessibility-fixture.test.ts` (PUL-Q008 fixture scene unit tests) +- TESTS → TEST `tests-e2e/dom-css-accessibility.spec.ts` (PUL-Q008 Playwright spec (chromium/firefox/webkit)) +- IMPLEMENTS → GITHUB_ISSUE `47` (Issue #47 — PUL-Q008 implementation) diff --git a/docs/requirements/PUL-Q009/requirement.md b/docs/requirements/PUL-Q009/requirement.md new file mode 100644 index 0000000..75212d7 --- /dev/null +++ b/docs/requirements/PUL-Q009/requirement.md @@ -0,0 +1,29 @@ +--- +id: PUL-Q009 +title: "Asset failure surfacing" +status: ACTIVE +type: NON_FUNCTIONAL +priority: MUST +wave: 0 +created_at: 2026-04-30T19:18:06.915217Z +updated_at: 2026-05-17T07:28:53.653512Z +--- + +# PUL-Q009 — Asset failure surfacing + +## Statement + +When an asset declared by a scene fails to load during preload, the runtime SHALL surface the failure with the scene id and asset path before mounting the scene. + +## Rationale + +Silent asset failures cause unexplained visual breakage at presentation time. + +## Traceability + +- DOCUMENTS → ADR `ADR-002` (Scene Registry and Composition Manifests as the Core Abstraction) +- IMPLEMENTS → CODE_FILE `src/runtime/error.ts` (Shared error helpers — describeErrorDetailed bounded multi-cause renderer (PUL-Q009 public-surface seam)) +- IMPLEMENTS → CODE_FILE `src/runtime/scene-loader.ts` (Scene loader — surfaceError renders via describeErrorDetailed so scene id + every failing asset path land on data-pulsar-navigation-error and onError (PUL-Q009)) +- TESTS → TEST `tests/runtime/error.test.ts` (describeErrorDetailed unit tests — AggregateError/cause walking, default unbounded branches, depth bound, cycle guard, PUL-Q009 every-asset-path invariant) +- TESTS → TEST `tests/runtime/scene-loader.test.ts` (Scene loader integration test — PUL-Q009: AggregateError-shaped preload throw surfaces scene id + every asset path via onError and data-pulsar-navigation-error, create(ctx) never called) +- IMPLEMENTS → GITHUB_ISSUE `48` (PUL-Q009: Asset failure surfacing) diff --git a/docs/requirements/PUL-Q010/requirement.md b/docs/requirements/PUL-Q010/requirement.md new file mode 100644 index 0000000..9cc9736 --- /dev/null +++ b/docs/requirements/PUL-Q010/requirement.md @@ -0,0 +1,32 @@ +--- +id: PUL-Q010 +title: "Master mute responsiveness" +status: ACTIVE +type: NON_FUNCTIONAL +priority: SHOULD +wave: 2 +created_at: 2026-04-30T19:18:09.218996Z +updated_at: 2026-05-17T20:54:46.835420Z +--- + +# PUL-Q010 — Master mute responsiveness + +## Statement + +Master mute SHALL silence active audio playback within 100 milliseconds of being engaged. + +## Rationale + +Bounded responsiveness for the presenter's audio control. + +## Traceability + +- DOCUMENTS → ADR `ADR-004` (Howler.js as the Audio Engine) +- IMPLEMENTS → GITHUB_ISSUE `49` (PUL-Q010: Master mute responsiveness) +- IMPLEMENTS → DOCUMENTATION `docs/design/pul-q010-master-mute-responsiveness-preflight.md` (PUL-Q010 master-mute responsiveness preflight — runtime seam, required reuse, guardrails, extensibility) +- IMPLEMENTS → CODE_FILE `src/runtime/scene-loader.ts` (Scene loader — buildPresenterPipe subscribes the audio handler ahead of any runner subscriber so toggle-master-mute synchronously calls audio.mute(!audio.isMuted()) before any other listener runs (PUL-Q010 critical path).) +- IMPLEMENTS → CODE_FILE `src/runtime/audio.ts` (Runtime audio service — AudioService.mute(...) synchronously calls engine.setMasterMute(...); createHowlerAudioEngine().setMasterMute(b) synchronously calls Howler.mute(b) (PUL-Q010 engine boundary).) +- IMPLEMENTS → CODE_FILE `src/runtime/presenter.ts` (Presenter controller — synchronous centralWrapped fan-out: validation + per-subscriber try/catch keep a throwing runner subscriber from masking the PUL-Q010 audio flip.) +- TESTS → TEST `tests/runtime/scene-loader-present.test.ts` (Loader presenter-seam PUL-Q010 tests — synchronous engine flip on next statement after emit(), audio handler runs before runner subscriber, slow runner busy-loop cannot delay the flip, throwing runner subscriber cannot prevent the flip.) +- TESTS → TEST `tests/runtime/audio.test.ts` (Audio-service PUL-Q010 tests — engine.setMasterMute is invoked synchronously from AudioService.mute (no microtask hop); mute path adds zero per-sound handle calls even when sounds are loaded and playing.) +- TESTS → TEST `tests/runtime/audio-engine.test.ts` (Audio-engine PUL-Q010 production-Howler-boundary test — createHowlerAudioEngine().setMasterMute(b) forwards synchronously to Howler.mute(b) via monkey-patched spy.) diff --git a/docs/requirements/SEC-001/requirement.md b/docs/requirements/SEC-001/requirement.md new file mode 100644 index 0000000..d7b1dc7 --- /dev/null +++ b/docs/requirements/SEC-001/requirement.md @@ -0,0 +1,25 @@ +--- +id: SEC-001 +title: "Vetted-Author-Only Conversation Surface" +status: ACTIVE +type: NON_FUNCTIONAL +priority: MUST +created_at: 2026-05-17T05:12:14.628620Z +updated_at: 2026-05-17T05:12:20.660900Z +--- + +# SEC-001 — Vetted-Author-Only Conversation Surface + +## Statement + +The repository's issue, pull-request, and discussion surfaces shall accept input only from vetted authors. Mechanism, scope, and lifetime of the control are operational details outside this requirement statement and outside any artifact change-controlled in this repository. Past content authored outside the vetted set shall be reviewed and curated where appropriate. + +## Rationale + +The repository's issue, pull-request, and discussion threads are consumed by both human maintainers and agent-assisted workflows that load historical thread content as context when responding to current work. Unrestricted authorship on those surfaces is a path for arbitrary external input to influence those downstream consumers; the requirement is the policy boundary that closes it. + +## Traceability + +- IMPLEMENTS → ADR `docs/adrs/009-conversation-surface-hardening.md` (ADR-009: Conversation Surface Hardening) +- IMPLEMENTS → GITHUB_ISSUE `111` +- IMPLEMENTS → PULL_REQUEST `112`