Skip to content

Commit 630fc38

Browse files
authored
feat(idef0): onboarding tour (Pillar B) — /onboard + zone-walk camera (#167)
## Summary **PRD-038 Pillar B** — a deterministic, model-free guided tour that walks a newcomer through the composed-map **zone by zone**. Data-driven (reads only the already-loaded map document), NOT flow-dependent (upstream flows reference nodes hidden inside collapsed megas — CM-01 — so the tour is a zone-walk). - **`tour-state.ts`** (pure, rune-free) — `buildTourStops` (placement row-major order, `zones[]` fallback), reducers `start`/`next`(exits past last)/`prev`/`goTo`/`exit`, `currentStop`. Honest narration: `zone.description_ru` or nothing, never fabricated. - **`OnboardTour.svelte`** — narration overlay (title + progress n/N + zone label + RU narration + what's-inside + Exit/Prev/Next). Composes `shared/ui` `Card`/`Badge`/`Button` (rule 24, no re-skin), `role="dialog"` + focus, keyboard delegated to the view's single handler, reduced-motion, dual-theme. - **`ComposedMapView.svelte`** — `fitToRect` reusing the same fit-scale clamp as `fitToView` (no second zoom controller); `$state` tour controller pinned to level 0; camera centres on the current stop + re-centres on reflow; Esc exits the tour before the Phase-1 reset; live-only. - **`routes/onboard/+page.svelte`** — thin route mounting the SAME `ComposedMapView` widget (one widget, two hosts — HomePage/DependencyGraph untouched → zero regression). Route auto-start deferred `// TODO(onboard-route-autostart)`; the in-view "Start tour" affordance ships regardless. ## Why PRD-038's onboarding goal: "look at the map and understand the project fast." The tour makes that a guided camera walk with narration. It degrades gracefully on today's data and lights up richer (per-node narration, project intro, explicit reading order) once the **source-side** map-pack items land — tracked in `docs/MAP-PACK-FINDINGS-FOR-MARKETPLACE.md` (CM-08/09/16). No web change needed then. ## Test plan - `npx vitest run src/widgets/composed-map src/entities/map` → **186/186** (+31: `tour-state` + `OnboardTour.render` + `tour.render`). - `npx svelte-check` → **0 errors** (2 pre-existing a11y warnings on the map `<svg>`, unrelated). - The build workflow's adversarial VERIFY phase died on a StructuredOutput cap; verification was done directly by the orchestrator (ran the suite + svelte-check, reviewed all 4 surfaces: purity/determinism, fit-math reuse, rule-24, route isolation). RFC-033 active via EVID-095. Refs: RFC-033, PRD-038, EVID-095 🤖 Generated with [Claude Code](https://claude.com/claude-code)
2 parents a1ef133 + 3939813 commit 630fc38

11 files changed

Lines changed: 1783 additions & 5 deletions
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
---
2+
depth: standard
3+
id: EVID-095
4+
kind: evidence
5+
last_modified_at: 2026-07-06T12:50:15.411300+00:00
6+
last_modified_by: claude-code/2.1.201
7+
links:
8+
- target: RFC-033
9+
relation: informs
10+
status: active
11+
title: 'Onboarding tour (Pillar B) build checkpoint: 186 tests + svelte-check 0 + 4-surface review PASS'
12+
---
13+
14+
## Status
15+
16+
draft
17+
18+
## Summary
19+
20+
Prove-phase checkpoint for **RFC-033** (Onboarding tour, PRD-038 Pillar B). The deterministic,
21+
model-free, zone-walk camera tour is implemented and green. The tour steps the camera through the
22+
composed-map's zones in reading order, narrating each from its `description_ru`; Next/Prev/Exit +
23+
keyboard drive it; Esc exits to free control. Built via a workflow whose adversarial VERIFY phase
24+
died on a StructuredOutput cap — the verification below was therefore performed **directly by the
25+
orchestrator** (ground-truth: ran the suite + svelte-check, read every changed surface).
26+
27+
## What was built
28+
29+
- **`widgets/composed-map/model/tour-state.ts`** (NEW, pure/rune-free) — `buildTourStops(doc)`
30+
(placement row-major order, `zones[]` fallback), `TourStop`/`TourState`, reducers
31+
`startTour`/`nextStop`(exits past last)/`prevStop`/`goToStop`/`exitTour`, `currentStop`. Honest
32+
narration: `narrationRu = zone.description_ru` (undefined when absent, never fabricated).
33+
- **`widgets/composed-map/ui/OnboardTour.svelte`** (NEW) — narration overlay: project title +
34+
progress badge (n/N) + zone label + RU narration (only when present) + "what's inside"
35+
(`labels + N more`) + Exit/Prev/Next(Done). Composes `shared/ui` `Card`/`Badge`/`Button` (rule 24
36+
— no primitive re-skin), `role="dialog"` + focus-on-new-stop, keyboard delegated to the view's
37+
single window handler (no double-fire), reduced-motion, token-only dual-theme.
38+
- **`widgets/composed-map/ui/ComposedMapView.svelte`** (CHANGED) — `fitToRect(rect, animated)`
39+
reusing the SAME `computeFitTransform` clamp as `fitToView` (no second zoom controller); a
40+
`$state` tour controller; stops derived from `okDoc` (level 0 — tour pins to root); an effect that
41+
centres the camera on the current stop (and re-centres on a layout/version reflow);
42+
`prefers-reduced-motion` respected; Esc routed to `exitTour` before the Phase-1 reset; live-only
43+
(exits on non-live, RFC-031 Invariant 8).
44+
- **`routes/onboard/+page.svelte`** (NEW) — thin route mounting the SAME `ComposedMapView` widget
45+
directly (one widget, two hosts — HomePage/DependencyGraph untouched, zero regression). Route
46+
auto-start deferred with `TODO(onboard-route-autostart)` per RFC-033 SCOPE (the newcomer clicks
47+
"Start tour"); the route never blocks the tour.
48+
49+
## Observations (measured, 2026-07-06)
50+
51+
- `npx vitest run src/widgets/composed-map src/entities/map` -> **14 files / 186 tests PASS**
52+
(+31 vs the pre-tour 155: `tour-state.test.ts` + `OnboardTour.render.test.ts` +
53+
`tour.render.test.ts`).
54+
- `npx svelte-check` -> **0 errors**, 2 warnings (both pre-existing a11y warnings on the map `<svg>`
55+
click handler at `ComposedMapView.svelte:972`, unrelated to the tour).
56+
- **Orchestrator 4-surface review (adversarial substitute):** (1) `tour-state.ts` pure/deterministic,
57+
placement-ordered, honest narration; (2) `fitToRect` reuses the fit-scale math (no fork); (3)
58+
`OnboardTour` rule-24 clean (grep: no `:global()` reaching a primitive class); (4) `/onboard` route
59+
mounts the widget directly (no HomePage/DependencyGraph edit -> 8 legacy views + drill-down
60+
unchanged). Reduced-motion + live-only + Esc-before-reset confirmed in the diff.
61+
62+
## Known scope boundary
63+
64+
- `/onboard` route **auto-start** is deferred (`TODO(onboard-route-autostart)`) — the in-view
65+
"Start tour" affordance ships regardless (RFC-033 SCOPE).
66+
- The tour narrates ZONE-level `description_ru`; richer per-node narration + a project-level intro
67+
(`meta.title`/`description_ru`) + an explicit reading-order field are **source-side** improvements
68+
tracked in the marketplace findings brief (CM-08/CM-09/CM-16). The tour degrades gracefully now and
69+
will light up richer without web changes once the emitter emits them.
70+
71+
## Structured Fields
72+
73+
verdict: supports
74+
congruence_level: 3
75+
evidence_type: test
76+
77+
## Related Artifacts
78+
79+
- **RFC-033** (`informs`) — the tour architecture this proves; activation gated on this checkpoint.
80+
- **PRD-038** — parent PRD (Pillar B, FR-005..FR-009).
81+
- **RFC-030/031** — the composed-map render + drill-down this tour rides on without regression.
82+
- **`docs/MAP-PACK-FINDINGS-FOR-MARKETPLACE.md`** — source-side items (CM-08/09/16) that enrich the
83+
tour narration next map-pack run.
84+
85+

.forgeplan/prds/PRD-038-composed-map-onboarding-tour-live-local-agent-guide-t4-phase-3.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,3 +569,4 @@ Handed to the T4 Phase-3 **RFC** and **ADR**:
569569

570570

571571

572+

.forgeplan/rfcs/RFC-030-composed-map-phase-1-render-proof-isolated-map-entity-pure-grid-widget-read-only-api-map-as-the-9th-view.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,3 +218,4 @@ Purely additive — one revert removes: the `GraphView` union member + `GRAPH_VI
218218

219219

220220

221+

0 commit comments

Comments
 (0)