release: v0.1.12 — F18 time-travel SINGLE + --experimental + mosaic + light theme#65
Merged
Conversation
Per guides/GIT-FLOW-GUIDE.ru.md §6.10. main = npm = v0.1.11. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
PRD-008 (Standard) — Time-travel slider for workspace history. Timeline panel below canvas with scrubber; SINGLE mode shows state at T, COMPARE mode (Alt-drag, 2 scrubbers) overlays diff between T1 and T2. SC-1..SC-10 + 11 FRs. Subsumes the ad-hoc PR-Diff feature. RFC-007 (Standard) — Time-travel snapshot reconstruction + scrubber UI. Pins server algorithm (forgeplan journal --json --until=ISO replay), endpoint contract /api/snapshot, scrubber debounce 200ms, COMPARE diff classes (added/activated/superseded /degraded). Extends READ_ONLY_SUBCOMMANDS allow-list with `journal` (rule 22 compliant). 8 implementation phases F18-T1..T8. PRD-009 (Standard) — Risk overlay for workspace decay surface. Toggle in canvas-toolbar; glow halo on artifacts where R_eff is low or decay imminent (R_eff < 0.6 threshold). New riskScore pure function. Risk anatomy section in ArtifactPanel. SC-1..SC-10 + 10 FRs. Sankey + Sunburst skip overlay (their layouts already encode hierarchy). RFC-008 (Standard) — Risk overlay rendering + riskScore composition. Pins multiplicative riskScore = (1-R_eff) × decay_factor with 90-day decay window. Rendering via SVG drop- shadow filter with N>200 fallback to outer-circle halo. 7 implementation phases F19-T1..T7. All 4 validated: PRD-008/009 PASS (with non-blocking orphan-FR warnings), RFC-007/008 PASS clean. Refs: PRD-008 RFC-007 PRD-009 RFC-008
## Summary Forgeplan artifacts seeding the next two strategic features: ### F18 — Time-travel slider (PRD-008 + RFC-007) - Timeline panel below canvas with scrubber. - SINGLE mode: state at any past T. - COMPARE mode (Alt-drag, 2 scrubbers): diff overlay between T1 and T2. - Subsumes the ad-hoc PR-Diff feature — same backend, two scrubber positions. - Server: new \`/api/snapshot?at=ISO[&compare=ISO]\` endpoint, read-only proxy to \`forgeplan journal --json --until=ISO\`. - Diff classes: \`node-added\` / \`node-activated\` / \`node-superseded\` / \`node-degraded\`. ### F19 — Risk overlay (PRD-009 + RFC-008) - Toggle in canvas-toolbar. - Glow halo on artifacts with composite risk > 0 (R_eff < 0.6 OR decay imminent). - \`riskScore = (1 - R_eff) × decay_factor\`, 90-day decay window. - Risk anatomy section in ArtifactPanel: evidence list with weakest highlighted, decay timer. - Sankey + Sunburst skip overlay (their layouts already encode hierarchy). - Render via SVG drop-shadow with N>200 fallback to outer-circle halo (perf). ## Verify - forgeplan reindex clean: 47 synced, 0 errors. - forgeplan validate: PRD-008/009 PASS (with non-blocking orphan-FR warnings); RFC-007/008 PASS clean. ## Test plan - [ ] PR review of PRD/RFC content for clarity. - [ ] After merge: F18 and F19 implementation can start in parallel feature branches. - [ ] EVID-016 (F18 acceptance) and EVID-017 (F19 acceptance) follow on implementation. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
PRD-010 (Standard) — Workspace pulse: stats dashboard + health score + trends. 4 charts (R_eff histogram, decay calendar, weekly velocity, status transitions Sankey) + 0..100 health score with 30-day sparkline. CRITICAL UX constraint: every chart MUST ship with hover tooltip + static caption + plain-language interpretation badge. SC-1..SC-10 + 15 FRs. RFC-009 (Standard) — Stats dashboard charts + plain-language interpretation. Pins exact chart shapes, axis math, colour mapping; heuristic 🟢/🟡/🔴 thresholds per chart with copy templates; median-based health score formula resistant to single-artifact gaming. /api/pulse endpoint contract. 8 implementation phases F22-T1..T8. PRD-011 (Standard) — Proactive hints engine for workspace anomalies. Top-3 hints panel above HealthBar; 8+ rules covering common forgeplan-anomalies; severity × recency ranking; snooze 1d/1w with TTL auto-cleanup. SC-1..SC-10 + 12 FRs. RFC-010 (Standard) — Hints rule DSL + ranking dispatcher. Pins HintRule shape (single-file extension point), 8 initial rules with trigger + copy templates, ranking algorithm, snooze model, localization-ready copy map. 7 implementation phases F23-T1..T7. NOTE-001 (Tactical) — Web utilities backlog. Captures 12 deferred ideas (F25-F36): "Ask AI" light, RSS feed, view-state permalink, inline structured search, walkthrough mode, GitHub PR cross-link, decision genealogy, snapshot bookmarks, annotation layer, embedded chat (heavy — separate product), coverage section, onboarding hint. Decision criteria + which-not-to-do. All 5 linked via `forgeplan link`: - RFC-009 -refines-> PRD-010, RFC-010 -refines-> PRD-011 - PRD-008 -informs-> PRD-010 (time-travel feeds pulse) - PRD-010 -informs-> PRD-011 (stats feeds hints) - NOTE-001 -informs-> PRD-008/009/010/011 Reindex: 70 synced, 0 errors. forgeplan health: green. Refs: PRD-010 RFC-009 PRD-011 RFC-010 NOTE-001
forgeplan link wrote `links:` arrays into frontmatter of the already-merged F18/F19 artifacts when establishing the cross-feature relations: - PRD-008 -informs-> PRD-010 (time-travel feeds pulse data) - PRD-009 (received NOTE-001 -informs-> PRD-009) - RFC-007 / RFC-008 received NOTE-001 -informs-> them These are pure metadata updates (Lance index already had the edges from the F18/F19 merge; this just syncs markdown frontmatter). Refs: PRD-008 PRD-009 RFC-007 RFC-008
## Summary 5 artifacts seeding the next strategic features after F18/F19. ### F22 — Workspace pulse (PRD-010 + RFC-009) Stats dashboard with 4 charts (R_eff histogram, decay calendar, weekly velocity, status transitions Sankey) + 0..100 health score with 30-day sparkline. **Critical UX constraint**: every chart MUST ship with hover tooltip + static caption + 🟢/🟡/🔴 plain-language interpretation badge. ### F23 — Proactive hints engine (PRD-011 + RFC-010) Top-3 hints panel above HealthBar; 8 initial rules covering forgeplan anomalies; severity × recency ranking; snooze 1d/1w with TTL auto-cleanup. ### NOTE-001 — Backlog 12 deferred ideas (F25-F36) with cost estimates and "do/skip/wait" criteria. Includes "Ask AI" light, RSS feed, permalink, inline search, walkthrough, GitHub cross-link, annotation layer, embedded-chat (heavy — separate product). ## Linked properly via forgeplan link - RFC-009 -refines→ PRD-010 - RFC-010 -refines→ PRD-011 - PRD-008 -informs→ PRD-010 (time-travel feeds pulse data) - PRD-010 -informs→ PRD-011 (stats metrics feed hints) - NOTE-001 -informs→ PRD-008/009/010/011 forgeplan reindex: 70 synced, 0 errors. forgeplan health: green. ## Verify - forgeplan reindex clean. - forgeplan validate: all PASS (PRD-010/011 with non-blocking orphan-FR warnings; RFCs/Note clean). - forgeplan graph shows 8 new edges. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Adds a small bottom-left footer rendering both the @forgeplan/web package
version (baked at vite-build time via a `define` constant) and the host
forgeplan CLI version (resolved on first request via a memoized
`forgeplan --version` spawn). New read-only `/api/version` endpoint serves
both. CLI lookup degrades to `null` on ENOENT / parse failure; UI then
shows `cli ?` instead of erroring.
Rule 22 is amended to make the flag-only `--version` invocation an
explicit, narrowly scoped exception to the subcommand allow-list — it
flows through a dedicated `getForgeplanVersion()` helper that reuses the
same FORGEPLAN_BIN validation, concurrency cap, and timeout as
`runForgeplan`, and never reaches the subcommand check.
Smoke: `npm run check` clean; `curl /api/version` returns
`{ web: "0.1.11", cli: "0.27.0" }` against vite dev with the repo's own
.forgeplan/; spawn-error path empirically reachable via ENOENT.
Refs: PRD-012, RFC-011, EVID-016
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a small bottom-left footer rendering both the @forgeplan/web
package version (baked at vite-build time via a `define` constant) and
the host forgeplan CLI version (resolved on first request via a memoized
`forgeplan --version` spawn). New read-only `/api/version` endpoint
serves both. CLI lookup degrades to `null` on ENOENT / parse failure; UI
then shows `cli ?` instead of erroring.
Rule 22 is amended to make the flag-only `--version` invocation an
explicit, narrowly scoped exception to the subcommand allow-list — it
flows through a dedicated `getForgeplanVersion()` helper that reuses the
same FORGEPLAN_BIN validation, concurrency cap, and timeout as
`runForgeplan`, and never reaches the subcommand check.
Smoke: `npm run check` clean; `curl /api/version` returns `{ web:
"0.1.11", cli: "0.27.0" }` against vite dev with the repo's own
.forgeplan/; spawn-error path empirically reachable via ENOENT.
Refs: PRD-012, RFC-011, EVID-016
Add Button, Code-with-copy, Dialog under template/src/shared/ui/ and a modalManager service under template/src/shared/services/modal/ so widgets can open dialogs without per-call mounting. ModalRoot is mounted once in +layout.svelte. Add /api/update-check endpoint that probes registry.npmjs.org for the latest @forgeplan/web (5-min server-process cache, 5-second timeout, GET only, never throws). VersionFooter polls it once at mount and every 30 minutes; when hasUpdate, an UpdateButton appears above the footer and opens UpdateDialog (current → latest + copyable manual command). Auto-update is intentionally out of scope: running `npx @forgeplan/web update` from the running server would rmSync the very files serving the request. Dialog explains this and offers the manual path only. Rule 22 amended to allow exactly one non-forgeplan endpoint hitting the literal URL https://registry.npmjs.org/@forgeplan/web/latest. Refs: PRD-013, RFC-012, EVID-017 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`npx @forgeplan/web update` has two interactive failure modes: - if the package is not cached, npx prompts "Ok to proceed? (y)" and blocks waiting for Enter — bad UX when the user pasted the command; - if a stale version is cached, npx silently runs the old copy, which copies the OLD dist/ into .forgeplan-web/ — the update is a no-op. Switch the dialog command to `npx -y @forgeplan/web@latest update`: `-y` auto-confirms the install prompt; `@latest` forces npx to fetch the newest tarball. Add a one-line footnote explaining both flags. Refs: PRD-013 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
After `update` runs, the host process keeps serving the OLD code: - macOS/Linux: the running Node process holds the old .forgeplan-web/ files open via open inodes, so rmSync from the new bin doesn't kill the request loop. The browser sees the old version until the user manually restarts `node .forgeplan-web/index.js`. - Windows: the rm step itself can fail with EBUSY because Windows locks open files; update may partially fail, but again the running server is unaffected. Either way the user has to stop+start the server. HMR is not an option here — adapter-node has no dev-server hooks at runtime. So the dialog now: - enumerates the four steps (Stop, Update, Restart, Reload); - pings /api/version every 5 s while open; - shows a "Server now serves vX.Y.Z" banner with a Reload button when the polled web version differs from the dialog's `current`; - shows an "offline" banner with a `npx @forgeplan/web start` hint when the ping fails (process killed but not yet restarted). Refs: PRD-013 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The dialog already detects the server-side version change. Reloading is the inevitable next step — adding a click between "we saw the new version" and "you see the new version" is friction without value (the user explicitly opened the update dialog, intent is clear). So when the polled /api/version differs from the dialog's `current`: - show a banner that the server now serves the new version + a fading "reloading…" hint; - arm a 1.5 s setTimeout that calls window.location.reload(); - offer a Cancel button to stop the timer (preserves any in-tab state the user wants to keep) and a "Reload now" button to skip the wait; - guard against the timer firing twice if the poller produces multiple hits before the page unloads (only the first detection arms it). Refs: PRD-013 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Reload alone won't pick up the new version: Node holds the old ES modules in memory after the rmSync — open inodes keep them alive, and new requests are served from the in-memory module cache. The process itself has to be restarted before browser reload becomes meaningful. Add a warn-styled line right under the step list to make the chain explicit (Stop → Update → Restart → Reload) and prevent the common "I just refreshed but nothing changed" support question. Refs: PRD-013 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per request: keep manual update only. The dialog now shows the current → latest header, the four-step manual recipe, and the copyable command. Everything else — the /api/version pinger, serverDown banner, detected-version banner, auto-reload setTimeout, Cancel/Reload-now buttons — is removed. Manual update is the only supported path. The user runs the command, restarts the server themselves, and reloads the tab on their own. Refs: PRD-013 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add Button, Code-with-copy, Dialog under template/src/shared/ui/ and a modalManager service under template/src/shared/services/modal/ so widgets can open dialogs without per-call mounting. ModalRoot is mounted once in +layout.svelte. Add /api/update-check endpoint that probes registry.npmjs.org for the latest @forgeplan/web (5-min server-process cache, 5-second timeout, GET only, never throws). VersionFooter polls it once at mount and every 30 minutes; when hasUpdate, an UpdateButton appears above the footer and opens UpdateDialog (current → latest + copyable manual command). Auto-update is intentionally out of scope: running `npx @forgeplan/web update` from the running server would rmSync the very files serving the request. Dialog explains this and offers the manual path only. Rule 22 amended to allow exactly one non-forgeplan endpoint hitting the literal URL https://registry.npmjs.org/@forgeplan/web/latest. Refs: PRD-013, RFC-012, EVID-017
Build pipeline now produces a second pre-built artifact alongside the legacy `dist/`: `dist-experimental/`, a single-file esbuild bundle of the SvelteKit server. esbuild inlines every reachable runtime dep (@sveltejs/kit, d3-*, dompurify, marked, …) into one ESM file, dropping the ~12M `node_modules/` from the artifact. Total: 14M → 1.5M (×9.3), 1696 → 62 files (×27). The new shape is gated behind `npx @forgeplan/web init --experimental`. Legacy `dist/` is left byte-identical and remains the default until the bundled shape graduates (≥2 minor versions without regressions). On graduation the flag is removed, legacy `dist/` is dropped, and `dist-experimental/` is renamed to `dist/`. TODO markers in `bin/forgeplan-web.mjs` and `scripts/build.mjs` flag the removal site. Build pipeline (`scripts/build.mjs`): - esbuild added to root devDependencies (^0.24.0) - new `bundleExperimentalDist()` runs after `copyToDist()`; reads the same template/build/index.js the legacy pipeline produced - shape asserts: no node_modules/, no server/, ≤3M (hard cap, fail-loud) - emits minimal package.json without `dependencies` and a build manifest with `experimental: true` Bin (`bin/forgeplan-web.mjs`): - --experimental switches SOURCE_DIST to dist-experimental/ - update inherits the persisted choice; --no-experimental migrates back - forgeplan-web.json gains `experimental: bool` - bin zero-deps invariant preserved (rule 23) CI: - smoke.yml + release.yml install root devDependencies via `npm ci` before running the build (esbuild needs to be on disk) - package-lock.json now committed at root for deterministic CI installs Smoke (verified locally on darwin-arm64): - legacy init: HTTP 200 on /api/version + / + correct envelope on /api/health - --experimental init: same envelopes, 1.5M, 62 files, no node_modules/ - experimental against real workspace (47 artifacts): /api/list, /graph, /health all match legacy shape Refs: PRD-014, RFC-013, EVID-018 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Build pipeline now produces a second pre-built artifact alongside the legacy `dist/`: `dist-experimental/`, a single-file esbuild bundle of the SvelteKit server. esbuild inlines every reachable runtime dep (@sveltejs/kit, d3-*, dompurify, marked, …) into one ESM file, dropping the ~12M `node_modules/` from the artifact. Total: 14M → 1.5M (×9.3), 1696 → 62 files (×27). The new shape is gated behind `npx @forgeplan/web init --experimental`. Legacy `dist/` is left byte-identical and remains the default until the bundled shape graduates (≥2 minor versions without regressions). On graduation the flag is removed, legacy `dist/` is dropped, and `dist-experimental/` is renamed to `dist/`. TODO markers in `bin/forgeplan-web.mjs` and `scripts/build.mjs` flag the removal site. Build pipeline (`scripts/build.mjs`): - esbuild added to root devDependencies (^0.24.0) - new `bundleExperimentalDist()` runs after `copyToDist()`; reads the same template/build/index.js the legacy pipeline produced - shape asserts: no node_modules/, no server/, ≤3M (hard cap, fail-loud) - emits minimal package.json without `dependencies` and a build manifest with `experimental: true` Bin (`bin/forgeplan-web.mjs`): - --experimental switches SOURCE_DIST to dist-experimental/ - update inherits the persisted choice; --no-experimental migrates back - forgeplan-web.json gains `experimental: bool` - bin zero-deps invariant preserved (rule 23) CI: - smoke.yml + release.yml install root devDependencies via `npm ci` before running the build (esbuild needs to be on disk) - package-lock.json now committed at root for deterministic CI installs Smoke (verified locally on darwin-arm64): - legacy init: HTTP 200 on /api/version + / + correct envelope on /api/health - --experimental init: same envelopes, 1.5M, 62 files, no node_modules/ - experimental against real workspace (47 artifacts): /api/list, /graph, /health all match legacy shape Refs: PRD-014, RFC-013, EVID-018
PRD-008/RFC-007 originally targeted forgeplan journal --json --until=ISO for time-travel reconstruction. F18-T1 verified that flag does not exist in CLI 0.28.0 and that forgeplan log --json is reindex-flat (history collapsed). Triggered RFC-007 Risk R-1 fallback: git is the only durable timeline since markdown is the source of truth (parent ADR-003). Implements RFC-007 Path D (git+cache hybrid): - Resolve at→sha via git rev-list -1 --before=<at> --first-parent HEAD -- .forgeplan/ - Memory LRU (32 entries, 60s TTL) → disk cache (.forgeplan-web/.snapshots/<sha>.json) - Cold path: git worktree add --detach to os.tmpdir() + forgeplan list/graph --json inside - Worktree removed best-effort after each reconstruction Updates RFC-007 algorithm + phases + options-considered + risks. PRD-008 SC-7/NFR-003/R-1 reworded to drop the journal-only assumption. runForgeplan() gains opts.cwd so the same helper can target a worktree. COMPARE mode endpoint returns 501 until F18-T6 lands diff projection. Refs: PRD-008 RFC-007 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the client-side foundations for the time-travel scrubber without any UI yet: - widgets/timeline/lib/snapshot-state.svelte.ts — runes-aware singleton ($state) holding mode/activeAt/t1/t2/collapsed/loading + loadSnapshotAt fetch wrapper that targets /api/snapshot. - widgets/timeline/lib/event-axis.ts — pure math for the scrubber: domain computation, timestamp ↔ pixel mapping, snap-to-nearest-event, prev/next event stepping. Single-event domain pads ±1h so the axis has somewhere to render. - widgets/timeline/lib/event-axis.test.ts — 21 unit tests covering empty domain, padding, clamping, invalid timestamps, snapping, and step direction edge cases. - widgets/timeline/index.ts — barrel. Collapsed state persists in localStorage. Falls back gracefully when localStorage is missing (SSR) or quota-exceeded. T4 will add the Timeline.svelte panel + /api/timeline-events derived from git log of .forgeplan/, then wire HomePage + DependencyGraph. Refs: PRD-008 RFC-007 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Brings the time-travel scrubber to life: - /api/timeline-events — read-only proxy over `git log --first-parent .forgeplan/`. Emits one event per commit with at/kind/artifactId/sha/ subject. kind classified heuristically (activate / supersede / evid-score / created-default). - widgets/timeline/ui/Timeline.svelte — collapsible panel below canvas-body. SVG axis with coloured tick marks per event kind, draggable scrubber with PointerEvent capture, ArrowLeft/Right step-by-event, Home/End jump, 200ms debounced fetch to /api/snapshot. role=slider for a11y. - widgets/timeline/index.ts — exports Timeline component. - pages/home/HomePage.svelte — renders <Timeline /> below the graph; derived `nodes`/`edges` switch between live pollers and snapshotStore. current when mode === 'single'. Status indicator surfaces "viewing snapshot at HH:MM" / loading / error / live·now. Cold path measured implicitly via /api/snapshot (T5 will profile formally). COMPARE mode endpoint still 501-stubbed pending T6. Refs: PRD-008 RFC-007 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a cream/beige light palette alongside the existing dark theme, plus an Auto/Light/Dark toggle in the header. Theme is keyed via data-theme on <html>; an inline pre-paint script in app.html sets it before SvelteKit hydrates so first paint is correct. All graph views (Force, Tree, Radial, Lanes, Matrix, Sankey, Sunburst) and shared chrome (Dialog, Button, HealthBar) now read from theme-aware CSS tokens instead of hardcoded white rgbas. SVG attribute usages switched to Svelte style: directives so var() resolves through inline CSS. Workaround: lightningcss tree-shook the new tokens from the build, so vite.config.ts now forces postcss + esbuild for CSS minify. Tracked via FIXME(prd-015-css-minify). Refs: PRD-015, RFC-014, EVID-019 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Smoke-tested /api/snapshot end-to-end against the dev server and discovered two real issues: 1. workspaceRoot() points at template/src/ in dev mode (vite SSR loads modules directly), so git pathspec '.forgeplan/' silently misses. Added gitRepoRoot() — caches the result of `git rev-parse --show-toplevel` once at first call. Both /api/timeline-events and /api/snapshot use it instead of workspaceRoot() for git invocations. 2. .forgeplan/lance/ is gitignored and never checked in (markdown is the source of truth per parent-repo ADR-003), so a freshly created worktree has no LanceDB index and `forgeplan list --json` fails with "Table 'artifacts' was not found". Added spawnForgeplanReindex(cwd) which calls `forgeplan reindex` inside the ephemeral worktree before list/graph. Bypasses runForgeplan's read-only allow-list — the write is scoped to /tmp, never the host workspace. Measured cold path 660ms (39 artifacts: worktree add + reindex + list + graph). Memory cache hits at 10-11ms, ≥60x speedup. Disk cache covers restart. NFR-001 target (<300ms cold) misses on first access; warm path well within budget. T5 was originally scoped to formalise the cache layer. Cache shipped in T2 already; this commit closes the gap by making the underlying reconstruction actually return data. Refs: PRD-008 RFC-007 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EVID-016 captures the end-to-end measurement against the running dev server: HTTP 200 from /api/snapshot for two distinct ISO timestamps producing different commit SHAs and artifact counts (39 vs 20), warm-path 10-11ms via memory cache, cold-path 660ms dominated by forgeplan reindex inside the ephemeral worktree. CL3 / measurement / supports — the probe runs the actual production code path through loopback, not isolated unit tests. forgeplan link EVID-016 PRD-008 / RFC-007 (informs); forgeplan score puts both at quality A/B with reliability 1.00/0.90; forgeplan activate flips PRD-008, RFC-007, and EVID-016 to active. CHANGELOG entry covers SINGLE mode delivery and explicit COMPARE-mode deferral to v0.2.1 (endpoint returns 501 with TODO). Refs: PRD-008 RFC-007 EVID-016 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the [Unreleased] section describing the F18 server endpoints (/api/snapshot, /api/timeline-events), the Timeline panel widget, canvas snapshot hydration, and gitRepoRoot() helper. Explicitly documents COMPARE mode deferral to v0.2.1. Refs: PRD-008 RFC-007 EVID-016 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a cream/beige light palette alongside the existing dark theme, plus an Auto/Light/Dark toggle in the header. Theme is keyed via data-theme on <html>; an inline pre-paint script in app.html sets it before SvelteKit hydrates so first paint is correct. All graph views (Force, Tree, Radial, Lanes, Matrix, Sankey, Sunburst) and shared chrome (Dialog, Button, HealthBar) now read from theme-aware CSS tokens instead of hardcoded white rgbas. SVG attribute usages switched to Svelte style: directives so var() resolves through inline CSS. Workaround: lightningcss tree-shook the new tokens from the build, so vite.config.ts now forces postcss + esbuild for CSS minify. Tracked via FIXME(prd-015-css-minify). Refs: PRD-015, RFC-014, EVID-019
# Conflicts: # CHANGELOG.md # template/src/pages/home/ui/HomePage.svelte # template/src/shared/server/index.ts
Replace single-canvas-with-view-toggle home page with a recursive
binary split-tree mosaic that hosts 1–4 DependencyGraph panes.
Each pane has its own view selector, reset, add and close controls
in a draggable header; layout (open panes + sizes in %) persists to
localStorage under forgeplan-web:layout:v1.
Drag semantics:
- Pane header onto another pane's center → swap views (sizes kept).
- Pane header onto another pane's edge → move (remove source +
insert at target's edge).
- Splitter pointer/keyboard drag → resize, clamped [10..90]%.
Layout model lives under template/src/widgets/mosaic/ (FSD widget):
- model/{types,tree}.ts pure tree ops with vitest coverage (20 tests).
- lib/{drag,persist}.ts module-level drag payload (workaround for the
spec-mandated empty getData during dragover) + 5 quadrant tests.
- ui/{MosaicCanvas,MosaicNodeView,PaneFrame,Splitter,DropOverlay}.svelte
recursive render via 3-track CSS Grid (a% 0px b% with overlay
splitter) — no JS layout math at render time.
A11y: splitters expose role=separator with arrow-key resize, pane
headers are toolbar with tabindex=-1, drag fallback writes text/plain
so the operation initialises in browsers that require non-empty data.
Refs: PRD-016, RFC-015, EVID-020
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace single-canvas-with-view-toggle home page with a recursive binary
split-tree mosaic that hosts 1–4 DependencyGraph panes. Each pane has
its own view selector, reset, add and close controls in a draggable
header; layout (open panes + sizes in %) persists to localStorage under
forgeplan-web:layout:v1.
Drag semantics:
- Pane header onto another pane's center → swap views (sizes kept).
- Pane header onto another pane's edge → move (remove source +
insert at target's edge).
- Splitter pointer/keyboard drag → resize, clamped [10..90]%.
Layout model lives under template/src/widgets/mosaic/ (FSD widget):
- model/{types,tree}.ts pure tree ops with vitest coverage (20 tests).
- lib/{drag,persist}.ts module-level drag payload (workaround for the
spec-mandated empty getData during dragover) + 5 quadrant tests.
- ui/{MosaicCanvas,MosaicNodeView,PaneFrame,Splitter,DropOverlay}.svelte
recursive render via 3-track CSS Grid (a% 0px b% with overlay splitter)
— no JS layout math at render time.
A11y: splitters expose role=separator with arrow-key resize, pane
headers are toolbar with tabindex=-1, drag fallback writes text/plain so
the operation initialises in browsers that require non-empty data.
Refs: PRD-016, RFC-015, EVID-020
Formatter normalised YAML list indentation in PRD-008/RFC-007/EVID-020 frontmatter (0-space → 2-space) after the merge resolution. PRD-008 Related Artifacts table EVID-016 → EVID-020 also picked up here. Refs: PRD-008 RFC-007 EVID-020
## Summary - F18 SINGLE mode: scrub canvas to any past workspace state via SVG scrubber + `/api/snapshot?at=ISO`. - Reconstruction = `git worktree add --detach` to `os.tmpdir()` + `forgeplan reindex` + `forgeplan list/graph --json`. Two-tier cache (memory LRU + disk). - New endpoint `/api/timeline-events` — read-only `git log` proxy emitting one event per `.forgeplan/`-touching commit. - `gitRepoRoot()` helper detects host git top via `git rev-parse --show-toplevel`. - COMPARE mode (Alt-drag, diff overlay) deferred to v0.2.1 — endpoint returns 501 with TODO marker. ## Why PRD-008 + RFC-007 originally proposed `forgeplan journal --json --until=ISO` replay. F18-T1 verified that flag does not exist in CLI 0.28.0 and that `forgeplan log --json` is reindex-flat (history collapsed). Triggered Risk R-1: pivot to git-based reconstruction since markdown is the source of truth (parent-repo ADR-003) and git is the only durable timeline. ## Test plan - [x] `npm run check` — 0 errors / 0 warnings across 450 files - [x] `npx vitest run` — 12 files / 102 tests pass (incl. 21 new `event-axis.test.ts`) - [x] End-to-end smoke against running dev server — see EVID-016: cold 660 ms, warm 10–11 ms, two distinct ISO timestamps produce distinct SHA + artifact counts (39 vs 20) - [x] `forgeplan score PRD-008` → A (0.87, R 1.00). `forgeplan score RFC-007` → B (0.67, R 0.90). All three artifacts (PRD-008, RFC-007, EVID-016) `active`. - [ ] CI matrix smoke (3 OS) — runs on PR ## Refs - PRD-008 — Time-travel slider for workspace history - RFC-007 — Time-travel snapshot reconstruction + scrubber UI - EVID-016 — F18 acceptance (CL3 / measurement / supports) 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Collaborator
|
include #66 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Bundles three feature streams from develop into v0.1.12:
/api/snapshot?at=ISO,/api/timeline-events,widgets/timeline/, canvas snapshot hydration. PRD-008 / RFC-007 / EVID-020 active.init --experimentalflag (PR feat(init): add --experimental flag for bundled dist (~9× smaller) #60) — opt-in to esbuild single-file bundle, ~9× smallerdist/. PRD-014 / RFC-013 / EVID-018 active. Closes the verification ask in Verify experimental bundle and remove the--experimentalflag (9x smaller bundle size) #59.getForgeplanVersion()+compareSemver. PRD-012 / RFC-011 / EVID-016 active.data-themeattribute. PRD-015 / RFC-014 / EVID-019 active.COMPARE mode for time-travel (Alt-drag two scrubbers + diff overlay) intentionally deferred — endpoint returns 501 with TODO marker.
Why
Five feature PRs accumulated on develop since v0.1.11 (May 6). Cutting them as a single minor-patch release rather than gating on the v0.2.0 milestone (full proactive-surfacer arc) keeps the cadence steady and ships the bundle-size win + multi-graph layout to users now.
Test plan
npm run check— 0 errors / 0 warnings across 486 filesnpx vitest run— 14 files / 127 tests passrelease.ymlworkflowRefs
PRDs activated since v0.1.11: PRD-008, PRD-012, PRD-013, PRD-014, PRD-015.
RFCs activated: RFC-007, RFC-011, RFC-012, RFC-013, RFC-014.
Evidence: EVID-016 / EVID-017 / EVID-018 / EVID-019 / EVID-020.
🤖 Generated with Claude Code