Skip to content

release: v0.3.0 — IDEF0 composed-map program (render, drill-down, onboarding, 3D-iso) - #172

Merged
explosivebit merged 155 commits into
mainfrom
release/v0.3.0
Jul 9, 2026
Merged

explosivebit merged 155 commits into
mainfrom
release/v0.3.0

Conversation

@explosivebit

Copy link
Copy Markdown
Contributor

Summary

Release v0.3.0 (MINOR) — cuts the entire IDEF0 composed-map program from
develop to main. 152 commits since v0.2.4, no breaking changes.

Headline features landed since v0.2.4:

Version bump

  • package.json + template/package.json: 0.2.40.3.0.

Release checklist

  • Version bumped in both package manifests
  • CI matrix (ubuntu / macos / windows) green on this PR
  • Merge to main (merge commit)
  • Annotated tag v0.3.0 on main + push
  • GitHub Release published (fires release.yml → npm with provenance)
  • Back-merge release/v0.3.0develop

Test plan

Full CI matrix on this PR must pass. No new code beyond the version bump; all
feature work was already CI-verified on develop (each of #167/#169/#170/#171
merged green).

🤖 Generated with Claude Code

explosivebit and others added 30 commits May 8, 2026 22:17
…rrors

Adopts forgeplan ≥ 0.28 slug-canonical identity in the Web viewer and
replaces the generic 502 from /api/snapshot with a discriminated error
envelope. One PR, one touch on shared/server/snapshot.ts (RFC-015 G-4).

Changes:
- ArtifactSummary / ArtifactDetail / ArtifactSnapshot extended with five
  optional identity fields (slug, predicted_number, assigned_number,
  id_canonical, id_display). Legacy artefacts without slug coexist via
  optional-field fallback.
- New entities/artifact/lib/identity.ts — single displayId(a) helper
  used by every UI surface that renders an artefact identifier (7
  graph view modes + InsightsRail + ArtifactPanel header + markdown
  export). Preserves the "?" marker for drafts verbatim from the CLI.
- New entities/artifact/lib/identifier-guard.ts — three-shape route
  guard (display id / draft with marker / slug). /api/get/[id] now
  accepts slug input and PRD-74? without 400.
- shared/server/snapshot.ts — reconstructFromWorktree returns a
  discriminated union with six error codes (host_config_missing,
  worktree_add_failed, reindex_failed, list_parse_failed,
  graph_parse_failed, commit_unreachable). Pre-flight git cat-file -e
  detects pruned SHAs explicitly. Two FIXME markers from PRD-008
  (worktree-add, reindex-failure) replaced with explicit error returns.
- New sanitizeStderr — strips host paths under /Users/, /home/,
  /private/var/, redacts env-style assignments, truncates at word
  boundary <= 1024 chars (RFC-015 D-5 + I-4).
- /api/snapshot failure envelope adds error_code + stderr_excerpt;
  legacy `error: string` preserved for rollback path. Successful
  envelope unchanged (NFR-006). widgets/timeline/lib/snapshot-state
  forwards the new fields to the store.

Why:
- Pre-merge artifacts with slug-only identity returned 400 from
  /api/get/[id], leaking through silent type erasure to all 7 graph
  views. Drafts and activated artifacts looked identical (no `?`
  marker). PRD-016 §Problem.
- Real-world incident on @gertsai/shared: host gitignored
  .forgeplan/config.yaml legitimately, reindex aborted with
  "os error 2", /api/snapshot returned a generic 502, ~60min spent
  narrowing the cause. The two FIXME markers in snapshot.ts
  (:268-270, :282-284) had documented this for months. Now the API
  surfaces host_config_missing with the literal "os error 2" excerpt
  preserved (PRD-016 AC-6) and points at guides/FORGEPLAN-GITIGNORE.md.

Tests:
- npm run check — 1052 files, 0 errors / 0 warnings (NFR-001 / AC-5).
- npx vitest run — 18 files, 179 tests passed.
- 33 new tests: 4 displayId, 18 identifier-guard, 11 sanitizeStderr +
  isHostConfigMissingError.

Refs: PRD-016, RFC-015, EVID-021
## Summary

Back-merge of v0.2.4 from `main` into `develop` — propagates the `0.2.4`
version bump so subsequent feature branches start at the published SHA.

Branch `release/v0.2.4` was auto-deleted after merging PR #149; this
branch was created off `main` to perform the same back-merge per
[`CLAUDE.md` Release procedure §10](../blob/develop/CLAUDE.md).

## Why

Standard final step of the release flow.

## Notes

- `v0.2.4` tag pushed on `main` ✅
- GitHub Release v0.2.4 published ✅
- `release.yml` ran but `npm publish` returned **404 PUT
@forgeplan/web** — likely `NPM_TOKEN` repo secret needs rotation. Tag is
intact; can rerun the workflow after the secret is refreshed, or publish
manually. NOT a blocker for this PR.

Refs: PRD-033, RFC-027, EVID-039
The /api/snapshot failure branch serialised only {ok, at, sha, error},
silently dropping the error_code + stderr_excerpt that getSnapshot()
already produces (RFC-015 D-4). The client store and Timeline never saw
them, so the structured-error UX (incl. the host_config_missing
remediation hint) was dead end-to-end. Forward both fields and render
them in Timeline (code badge + collapsible sanitized stderr). Add an
endpoint regression test that asserts the failure payload carries them.

Also reconcile the identity-field comments with the forgeplan 0.33
contract audit: the CLI never emits the identity triple — list/get --json
expose only a nullable top-level slug; id_display/id_canonical are
MCP-DTO-only (>= 0.31), predicted_number/assigned_number are frontmatter
only. The display-path stays as forward-compatible scaffolding (degrades
to raw id); the prior ">= 0.28 returns the triple" comments were false.

Refs: RFC-015, PRD-016
Referenced by the host_config_missing snapshot error now surfaced in the
Timeline (RFC-015 D-4). Documents the canonical .forgeplan/.gitignore
contract — why config.yaml / notes/ / state/ must be tracked and
session.yaml must not — and the one-commit migration from a drifted state.

Refs: RFC-015

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…apshot errors (#151)

## Summary

- Adapts the `@forgeplan/web` viewer to forgeplan's **slug-canonical
identity** (PROB-060): the `/api/get/[id]` guard now accepts slug /
lowercase / draft-marker ids (no more false 400s); 5 optional identity
fields are threaded through the artifact + snapshot types; node labels
route through `displayId()`.
- Adds **structured `/api/snapshot` failures** (RFC-015 D-4):
`SnapshotErrorCode` + sanitized `stderr_excerpt`, with a
`host_config_missing` detector that points users at the new
`guides/FORGEPLAN-GITIGNORE.md`.
- **Fixes a dead wire** caught by a forgeplan-0.33 contract audit: the
endpoint dropped `error_code`/`stderr_excerpt` (serialised only
`{ok,at,sha,error}`) although `getSnapshot()` produced them. Now
forwarded + rendered in the Timeline, guarded by a regression test.
- **Reconciles misleading comments.** The forgeplan **CLI** never emits
the identity triple — `list`/`get --json` expose only a nullable
top-level `slug`; `id_display`/`id_canonical` are MCP-DTO-only (>=
0.31), `predicted_number`/`assigned_number` are frontmatter-only.

## Why

- forgeplan 0.31+ introduced slug-canonical identity so parallel
branches stop colliding on sequential numbers. The viewer must not 400
on slug/draft ids and should surface actionable snapshot errors instead
of a generic 502.
- **Honest caveat for review:** against the CLI transport this app uses
(rule 22), the identity *display* path (`displayId`, 7 graph views) is
currently **dormant** — `id_display` never arrives, so labels degrade to
the raw id (byte-identical to pre-PROB-060). It is kept as
forward-compatible scaffolding (revert+re-add would cost more than the
harmless sleep). The genuinely live new behaviour is the route-guard
widening + the structured-error surfacing. Full audit verdict + delta in
EVID-040.

## Test plan

- `npm run check` -> 0 errors / 0 warnings (1082 files).
- `npm test` -> 192/192 (21 files), incl. new
`src/routes/api/snapshot/endpoint.test.ts` regression (asserts the
failure payload carries `error_code`/`stderr_excerpt`; fails if the fix
is reverted).
- Independent code-review: PASS, 0 findings; rule 22 (GET-only read-only
proxy) + rule 24 (no primitive overrides) verified.
- Evidence: EVID-040 (CL3, verdict=supports, evidence_type=test);
RFC-015 r_eff=1.

Refs: PRD-016, RFC-015, EVID-040

🤖 Generated with [Claude Code](https://claude.com/claude-code)
The PROB-060 fix evidence (EVID-040, code-review PASS of commit 0101560)
and its informs-links to RFC-015 / PRD-016 were created in the workspace
during the fix but never committed — PR #151 carried only template/ +
the guide. Land the markdown so the artifact graph matches reality
(markdown is source of truth; EVID-040 active, RFC-015 r_eff=1).

Refs: RFC-015, PRD-016, EVID-040

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… links (#152)

## Summary

Lands the PROB-060 fix evidence that PR #151 omitted. `EVID-040`
(independent code-review PASS of commit `0101560`, RFC-015 D-4 wire fix)
and its `informs` links to `RFC-015` / `PRD-016` were created in the
`.forgeplan/` workspace during the fix but never committed — #151
carried only `template/` source + the guide.

## Why

Markdown is the source of truth for the artifact graph (parent ADR-003).
Every other EvidencePack (EVID-001..021) is tracked; EVID-040 must be
too, or the graph on `develop` is incomplete and `RFC-015`'s evidence
chain is missing its review. EVID-040 is active with structured fields
(`verdict: supports`, `congruence_level: 3`, `evidence_type: test`);
locally it gives RFC-015 `r_eff=1`.

## Test plan

- Docs/artifact-only change (3 markdown files under `.forgeplan/`); no
source touched.
- `forgeplan validate EVID-040` → PASS; structured fields present.

Refs: RFC-015, PRD-016, EVID-040

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Both features are already implemented on develop — the artifacts just
lagged in draft. Verified by a per-feature implementation audit against
the develop codebase:

- version-footer (PRD-012 / RFC-011 / EVID-016): 5/5 FRs in code —
  widgets/version-footer/ui/VersionFooter.svelte (web version via
  __FORGEPLAN_WEB_VERSION__) + routes/api/version/+server.ts +
  getForgeplanVersion() in shared/server/forgeplan.ts. R_eff=1.00.
- template-hardening (RFC-003 / EVID-006): runes migration (0 legacy
  export let / createEventDispatcher / slot / svelte/store across 84
  .svelte files) + read-only proxy enforcement (READ_ONLY_SUBCOMMANDS
  allow-list in shared/server/forgeplan.ts). R_eff=1.00.

Closes the stuck-draft EVID-006/EVID-016 anomalies (both ~1300h in draft).

Refs: PRD-012, RFC-011, RFC-003, EVID-016, EVID-006

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The "Update available" button reappeared on every poll/reload with no way
to dismiss it — the last open FR of PRD-013 (shared-ui). The UpdateDialog
now offers "Dismiss for this session", which resolves the modal promise
with a 'dismiss' sentinel; VersionFooter records the dismissed `latest`
version in sessionStorage and hides the button while it matches. A newer
release re-surfaces it (stored version no longer matches); dialog content
is never lost. Logic extracted to lib/session-dismiss.ts (pure, SSR-safe)
with unit tests (shouldShowUpdate gate + persistence round-trip + throw/SSR
fallbacks). svelte-check 0/0 (1084 files), vitest 198/198.

With FR-011 done, shared-ui is feature-complete (9/9 MUST + 3/3 SHOULD) —
activate its artifacts (EVID-017 → PRD-013 → RFC-012, R_eff=1.00). Clears
the last stuck-draft EVID anomaly (EVID-017, ~1300h).

Refs: PRD-013, RFC-012, EVID-017

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…g artifacts (#153)

## Summary

Activates 5 forgeplan artifacts whose features are **already implemented
on develop** but whose paperwork lagged in `draft`. Verified by a
per-feature implementation audit against the develop codebase (no code
change in this PR — `.forgeplan/` markdown only).

| Feature | Artifacts | Evidence on develop |
|---|---|---|
| version-footer | PRD-012, RFC-011, EVID-016 |
`widgets/version-footer/ui/VersionFooter.svelte` +
`routes/api/version/+server.ts` + `getForgeplanVersion()`; 5/5 FRs;
R_eff=1.00 |
| template-hardening | RFC-003, EVID-006 | runes migration (0 legacy
patterns / 84 .svelte) + `READ_ONLY_SUBCOMMANDS` allow-list (rule 22);
R_eff=1.00 |

## Why

Both EVID-006 and EVID-016 were stuck in draft ~1300h (flagged by
`forgeplan anomalies`). The work shipped long ago under other PRs; this
just reconciles the artifact graph with reality and clears the
stuck-draft noise.

## Test plan

- `forgeplan validate` PRD-012 / RFC-011 / RFC-003 → 0 errors (warnings
only).
- `forgeplan score` → R_eff 1.00 on all three after activating the
evidence.
- Implementation audit cited concrete file paths satisfying every MUST
FR.

Refs: PRD-012, RFC-011, RFC-003, EVID-016, EVID-006

🤖 Generated with [Claude Code](https://claude.com/claude-code)
…shared-ui (#154)

## Summary

Implements the last open requirement of **PRD-013 (shared-ui)** —
**FR-011**: the "Update available" affordance can be dismissed for the
session without losing the dialog content. With it done, shared-ui is
feature-complete, so this PR also activates its artifacts.

**Code (FR-011):**
- `UpdateDialog.svelte` — new "Dismiss for this session" action →
`modalManager.close(modalId, 'dismiss')`.
- `VersionFooter.svelte` — awaits the dialog result; on `'dismiss'`
records the dismissed `latest` in `sessionStorage` and hides the button
while it matches. A newer release re-surfaces it.
- `lib/session-dismiss.ts` (new, pure + SSR-safe) —
`readDismissedVersion` / `writeDismissedVersion` / `shouldShowUpdate`
gate.
- `lib/session-dismiss.test.ts` (new) — gate logic + persistence
round-trip + throw/SSR fallbacks.

**Paperwork:** EVID-017 → PRD-013 → RFC-012 activated (R_eff=1.00).
Clears the last stuck-draft EVID anomaly (EVID-017, ~1300h in draft).

## Why

Per-feature audit found shared-ui at 9/9 MUST + 2/3 SHOULD, missing only
FR-011 (a SHOULD). Rather than mark it won't-fix, we built it — a
non-dismissible update nag is a real UX wart, and the fix is small.

## Test plan

- `npm run check` → 0 errors / 0 warnings (1084 files).
- `npm test` → 198/198 (22 files), incl. 6 new `session-dismiss` tests.
- `forgeplan validate` PRD-013 / RFC-012 → 0 errors; R_eff 1.00 after
EVID-017 active.

Refs: PRD-013, RFC-012, EVID-017

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Toggle-gated risk overlay on the dependency graph: nodes whose R_eff is
degraded (< 0.6) glow with a drop-shadow halo whose radius scales by a
composite riskScore (RFC-008: (1-R_eff) × decay_factor over a 90-day
window). Plus a "Risk anatomy" section in ArtifactPanel (composite score,
decay timer, informing-evidence list with the weakest EVID marked).

Implementation notes / deviations (all documented, justified):
- rule 22: risk is computed CLIENT-SIDE from already-fetched /api/score +
  /api/graph — zero /api files changed, no /api/decay added.
- rule 24: the on/off control uses the shared Toggle primitive, which grows
  a `dataAction` prop (no :global override).
- Glow applies to the 4 box-views (Force/Tree/Radial/Lanes) only. Matrix
  (.cell grid) / Sankey (.bar) / Sunburst (.arc) have no per-node concept,
  so SC-9 "no glow on Sankey/Sunburst" holds unconditionally; the toolbar
  toggle disables when every visible pane is sankey/sunburst.
- FR-003 uses filter: drop-shadow (clips to shape in SVG) instead of the
  PRD's box-shadow wording (which does not clip in SVG — RFC's rejected C).
- FR-007 (Should) is degraded: per-EVID congruence_level/evidence_type live
  only in EVID body markdown, not in any allow-listed JSON, so they render
  as "—"; the SC-6 DOM contract (.weakest on lowest-R_eff EVID) is met via
  /api/score. Widening the allow-list was deliberately avoided.

svelte-check 0/0 (1086 files); vitest 236/236 (+11 risk-score cases).

Refs: PRD-009, RFC-008
EVID-041 records the design→build→verify result (svelte-check 0/0,
vitest 236/236, rule 22/24 PASS, all Must FRs met; FR-007 degraded by
rule-22 — documented). Linked informs→PRD-009/RFC-008, R_eff=1.00.

Refs: PRD-009, RFC-008, EVID-041
…#155)

## Summary

Implements **PRD-009 / RFC-008 — risk overlay**. A toggle-gated
drop-shadow halo on dependency-graph nodes whose R_eff is degraded (`<
0.6`); halo radius scales by a composite `riskScore` (RFC-008: `(1 −
R_eff) × decay_factor` over a 90-day window). Plus a "Risk anatomy"
section in ArtifactPanel (composite score, decay timer,
informing-evidence list with the weakest EVID flagged).

Built design→build→verify via workflow; orchestrator re-verified on the
tree.

## Verification

- `npm run check` → **0 errors / 0 warnings** (1086 files).
- `npm test` → **236/236** (23 files; +11 pure `risk-score` cases).
- **rule 22** ✓ — 0 `/api` files changed; risk computed client-side from
already-fetched `/api/score` + `/api/graph` (no `/api/decay`, no
allow-list widening).
- **rule 24** ✓ — on/off uses the shared `Toggle` primitive (grew a
`dataAction` prop; no `:global()` override).
- Evidence: **EVID-041** (CL3, verdict=supports),
`informs`→PRD-009/RFC-008, R_eff=1.00.

## Decisions made autonomously (flag if you disagree — revertible)

1. **FR-007 (Should) degraded** — per-EVID
`congruence_level`/`evidence_type` aren't in any allow-listed JSON (only
in EVID body markdown). The SC-6 DOM contract (`.weakest` on the
lowest-R_eff EVID) is met via `/api/score`; CL/type render as "—".
Allow-list widening deliberately avoided. Proper fix is upstream (expose
CL/type in `get`/`score --json`).
2. **Matrix / Sankey / Sunburst excluded from glow** — no per-node
concept there; glow applies to the 4 box-views
(Force/Tree/Radial/Lanes). SC-9 ("no glow on Sankey/Sunburst") holds
unconditionally; toolbar toggle disables when every visible pane is
sankey/sunburst.
3. **`filter: drop-shadow` not `box-shadow`** — box-shadow doesn't clip
to shape inside SVG (RFC's rejected option C).
4. **Graph-level glow radius reflects R_eff only** (not decay) —
`valid_until` isn't on the bulk list, only on `get`; the full composite
(R_eff × decay) is shown in the ArtifactPanel risk-anatomy section.

## Safety

Branch → revertible PR into **develop** (test integration). Nothing
shipped to prod (main/npm untouched). Toggle defaults **off**, persisted
per-user via settings.

Refs: PRD-009, RFC-008, EVID-041

🤖 Generated with [Claude Code](https://claude.com/claude-code)
New widgets/stats-pulse: a 6th InsightsRail "Stats" tab with a health
score (0..100, deterministic median-based formula), R_eff histogram,
weekly velocity, status-transitions, and a decay signal — plus per-chart
plain-language status badges + tooltips. All computed CLIENT-SIDE from
already-polled allow-listed endpoints (/api/list, /api/score, /api/health,
/api/log, /api/stale); charts are widget-local SVG on CSS tokens.

Deliberately DROPPED two spec items that violate hard constraints (the
implementation does NOT include them):
- GET /api/pulse (RFC-009) — not an allow-listed read-only subcommand
  (rule 22). Stats are aggregated client-side instead.
- server-written .forgeplan-web/health-history.json (RFC-009) — violates
  init host-isolation (rule 20). The 30-day trend (FR-011) is instead
  reconstructed client-side by replaying the /api/log event stream.
- FR-003 decay calendar ships as a coarse at-risk/stale proxy from
  /api/health: valid_until is only on /api/get/[id], not any allow-listed
  aggregate; a true 12-month heat-map needs opt-in per-id fan-out (TODO).

svelte-check 0/0 (1103 files); vitest 299/299 (+63 stats-pulse cases).
PRD-010/RFC-009 bodies reconciled separately to match this as-built shape.

Refs: PRD-010, RFC-009
… reconcile spec

EVID-042 records the verified build (svelte-check 0/0, vitest 299/299,
rule 22/24 PASS, all Must FRs). PRD-010 + RFC-009 gain an "As-Built
Reconciliation" section marking the constraint-violating surfaces
(GET /api/pulse, server-written health-history.json) as superseded —
they are not in the code; stats compute client-side. R_eff=1.00.

Refs: PRD-010, RFC-009, EVID-042
…#156)

## Summary

Implements **PRD-010 / RFC-009 — workspace pulse**. A 6th InsightsRail
"Stats" tab: deterministic health score (0–100, median-based), R_eff
histogram, weekly velocity, status-transitions, decay signal — each with
a plain-language status badge + tooltip. New FSD widget
`widgets/stats-pulse`; charts are widget-local SVG on CSS tokens; pure
compute in `lib/` with co-located tests.

Built design→build→verify via workflow; verifier re-ran independently.

## Verification
- `npm run check` → **0 / 0** (1103 files).
- `npm test` → **299/299** (28 files; +63 stats-pulse cases).
- **rule 22** ✓ — no `/api/pulse`; stats from allow-listed `/api/list`,
`/api/score`, `/api/health`, `/api/log`, `/api/stale`.
- **rule 24** ✓ — charts widget-local SVG (tokens only); 6th tab reuses
Tabs/TabsList; no `:global()` into primitives.
- Evidence **EVID-042** (CL3, supports), R_eff=1.00.

## ⚠️ Spec reconciliation (important)

PRD-010/RFC-009 originally **mandated two surfaces that violate hard
constraints** — both deliberately **NOT** built, and now marked
superseded via an "As-Built Reconciliation" section in each artifact:

1. **`GET /api/pulse` — dropped** (rule 22: not an allow-listed
read-only subcommand). Stats compute **client-side**.
2. **server-written `.forgeplan-web/health-history.json` — dropped**
(rule 20: init host-isolation). FR-011's 30-day trend is **reconstructed
client-side** from the `/api/log` event stream.
3. **FR-003 decay calendar — degraded** to a coarse at-risk/stale proxy
from `/api/health` (`valid_until` is only on `/api/get/[id]`, not any
allow-listed aggregate). True 12-month heat-map = opt-in per-id fan-out
(`TODO(fr-003-calendar)`).

Re-introducing #1/#2 to satisfy the original spec would break the red
lines — the reconciliation is the correct call.

## Safety
Branch → revertible PR into **develop**. Nothing shipped to prod. New
tab is additive; defaults unaffected.

Refs: PRD-010, RFC-009, EVID-042

🤖 Generated with [Claude Code](https://claude.com/claude-code)
New widgets/hints: a rule-DSL + ranking dispatcher that surfaces proactive
hints from workspace state (stale spikes, low-R_eff artifacts, imminent
valid_until, new blind spots, orphans, velocity drops). Rules are pure,
fixture-tested, with single-file tunable thresholds (FR-005). Hints render
via existing shared/ui primitives (no new primitive, no :global override).

All inputs come from already-polled allow-listed endpoints (/api/health,
/api/stale, /api/blindspots, /api/blocked, /api/score, /api/list) — NO
/api/anomalies, no allow-list widening (rule 22). PRD-011/RFC-010 did not
mandate a forbidden surface, so no spec reconciliation was needed.

svelte-check 0/0 (1116 files); vitest 330/330 (+31 hint-rule/compute cases).

Refs: PRD-011, RFC-010
EVID-043 records the verified build (svelte-check 0/0, vitest 330/330,
rule 22/24 PASS, rule-DSL is a real engine). Hints compute client-side
from allow-listed endpoints — no /api/anomalies, no spec reconciliation
needed. R_eff=1.00. (Workflow verify step failed on a structured-output
schema retry-cap; orchestrator verified directly — recorded in EVID-043.)

Refs: PRD-011, RFC-010, EVID-043
## Summary

Implements **PRD-011 / RFC-010 — proactive hints engine**. A rule-DSL +
ranking dispatcher (`widgets/hints`) that surfaces actionable hints from
workspace state: stale spikes, low-R_eff artifacts, imminent
`valid_until`, new blind spots, orphans, velocity drops. Rules are pure
+ fixture-tested with single-file tunable thresholds (FR-005); hints
render via existing shared/ui primitives.

Built via workflow (design→build); the workflow's verify step hit an
infrastructure failure (structured-output schema retry-cap), so the
**orchestrator verified directly** on the tree.

## Verification
- `npm run check` → **0 / 0** (1116 files).
- `npm test` → **330/330** (30 files; +31 hint-rule/compute cases).
- **rule 22** ✓ — 0 `/api` files changed; no `/api/anomalies`; hints
from allow-listed `/api/health`, `/api/stale`, `/api/blindspots`,
`/api/blocked`, `/api/score`, `/api/list`.
- **rule 24** ✓ — hints use existing primitives; no `:global()` into
internals.
- Evidence **EVID-043** (CL3, supports), R_eff=1.00.
- No spec reconciliation needed (PRD-011/RFC-010 didn't mandate a
forbidden surface).

## Safety
Branch → revertible PR into **develop**. Nothing shipped to prod.
Additive widget.

Refs: PRD-011, RFC-010, EVID-043

🤖 Generated with [Claude Code](https://claude.com/claude-code)
The conformance audit found rule-22's text lagged shipped, artifact-backed
reality: /api/snapshot + /api/timeline-events spawn `git` (read-only history
reconstruction; snapshot also runs `forgeplan reindex` inside an ephemeral
throwaway worktree, never the host index), and /api/instance-status exports
an OPTIONS preflight + CORS for the cross-origin instance switcher. All are
read-only and were merged under PRD-008/RFC-007, PRD-016/RFC-015, and #134;
this amendment documents them so the next reviewer doesn't read them as
violations. No code change — rule text only.

Refs: RFC-007, RFC-015
…RS (#158)

## Summary

Reconciles **rule-22** with already-shipped, artifact-backed `/api/*`
endpoints that its text didn't describe (flagged by the v0.2.x
conformance audit). **No code change — rule text only.**

Two new sections + a verification carve-out:
1. **git-reconstruction endpoints** (`/api/snapshot`,
`/api/timeline-events`) — they spawn **`git`** (not `forgeplan`)
read-only for history; `/api/snapshot` runs `forgeplan reindex` **inside
an ephemeral throwaway worktree** (documented exception to "forbidden
reindex" — never the host index). All argv-based, validated SHA/ISO
inputs, scoped to `.forgeplan/`, timed out.
2. **OPTIONS/CORS carve-out** (`/api/instance-status`) — uses only
allow-listed `health`+`claims`, but exports a side-effect-free `OPTIONS`
preflight + CORS `*` for the cross-origin instance switcher.

## Why

The audit confirmed these endpoints are read-only and safe, but rule-22
didn't mention the "spawn git read-only" category or the OPTIONS
preflight — so the next reviewer would read them as violations. These
endpoints landed under PRD-008/RFC-007 (time-travel), PRD-016/RFC-015
(snapshot identity), and #134 (instance switcher); this is documentation
reconciliation, not a new decision.

## Safety
Branch → revertible PR into **develop**. Docs-only, no code touched.

Refs: RFC-007, RFC-015
…col)

Closes the "висяки" gap surfaced when the conformance audit left 3 orphaned
claims (read-only reviewers crashed before release). rule-12 now mandates,
orchestrator-side:
- step 0: `forgeplan_claims` BEFORE dispatch — the next agent sees what is
  already taken and by whom (no double-assignment).
- a "Claim hygiene — no висяки" section: sweep orphaned claims after every
  sprint/workflow, force-release on crash/timeout (not wait for TTL), sweep
  read-only reviewers' self-claims too; /smith + /autorun consult claims
  before recommending the next step.

ADR-002 (the governing decision, which already specified this protocol +
the `release --force` mitigation) was still draft — now activated with
EVID-044 (audit: rule-12 exists+indexed+hardened, protocol exercised +
orphans swept this session). R_eff 0.80. Clears a stuck draft.

Refs: ADR-002, EVID-044
…col) (#159)

## Summary

Hardens the sub-agent **claim discipline** so orphaned claims ("висяки")
stop happening, and activates **ADR-002** (the governing decision) which
was stuck in `draft`.

**rule-12 additions (orchestrator MUST):**
- **Step 0 — `forgeplan_claims` before dispatch:** the next agent checks
what is already claimed and by whom → no double-assignment ("следующий
смотрит, что взято и кто над этим работает").
- **"Claim hygiene — no висяки" section:** sweep orphaned claims after
every sprint/workflow; force-release on crash/timeout (don't wait for
TTL); sweep read-only reviewers' self-claims too; `/smith` + `/autorun`
consult `forgeplan_claims` before recommending next work.

**ADR-002 activated** (`draft → active`, R_eff 0.80) via **EVID-044**
(audit: rule-12 exists + indexed + hardened; protocol exercised this
session — the conformance audit's reviewers claimed RFC-008/009/010 then
crashed; orphans swept via `release --force`).

## Why

The conformance audit left 3 orphaned claims on RFC-008/009/010
(read-only `architect-reviewer`s crashed on a schema retry-cap before
releasing). ADR-002 already anticipated this ("claim висит до expiry →
mitigation `release --force`") but was never activated, and rule-12
lacked an explicit pre-check + post-run sweep. This change makes the
discipline enforceable and in-force.

## Safety
Branch → revertible PR into **develop**. Rules/artifact docs only — no
code touched.

Refs: ADR-002, EVID-044
Consolidates the durable learnings from the 0.33 work into one doc:
CLI-vs-MCP contract (identity triple is MCP/frontmatter-only), R_eff
semantics (decision-property; evidence packs read r_eff 0), the
"cheap + self-correcting" process model, a stats-dashboard reading
guide, and an index of the upstream issues filed (forgeplan #397/#394/
#393/#348/#374; marketplace #165/#166/#167) + local follow-ups.

Refs: forgeplan#397, marketplace#165, marketplace#166, marketplace#167
## Summary
One durable doc (`docs/forgeplan-insights-and-upstream.md`) capturing
the useful learnings from the forgeplan-0.33 work:
- **Insights:** CLI-vs-MCP contract (identity triple is
MCP/frontmatter-only, dormant in the viewer); R_eff semantics
(decision-property; EVIDs read r_eff 0); the "make mistakes cheap +
self-correcting" process model.
- **Stats dashboard reading guide** (the 4 stats-pulse panels —
what/how/why/when).
- **Upstream issues index:** forgeplan #397 (identity in JSON), #394
(dup-id), #393 (r_eff staleness), #348, #374; marketplace #165 (reviewer
StructuredOutput), #166 (read-only self-claim leak), #167 (smith
claims+digest).
- **Local follow-ups:** risk-anatomy=1.00-on-EVID quirk, FR-007
degraded, PRD-017.

Docs-only. Refs: forgeplan#397, marketplace#165/#166/#167.
One-line addition: the AGENT-AUTHORING-GUIDE addendum issue
(marketplace#168) now appears in the upstream-issues table of
`docs/forgeplan-insights-and-upstream.md`. Docs-only.
explosivebit and others added 27 commits July 7, 2026 16:53
…/health probe

RFC-035 follow-up. The Info tab's "Other projects" row only appeared after
the first message, because otherInstances rode only on the WebSocket `ready`
frame (opened lazily on first send). Now the cheap `fetch /health` liveness
probe — which already polls every 15s — carries the same data, so the row
(and model) populate the moment the chat opens, with no WebSocket and no
subprocess.

- agent.mjs: GET /health JSON gains `capabilities` + `otherInstances`
  (readOtherLiveInstances(self), try/catch → [] on any registry error),
  mirroring the ready frame. CORS header + shape otherwise unchanged.
- agent-client.ts: ProbeResult carries optional capabilities/otherInstances;
  probeDaemon parses them from the /health JSON (malformed → []).
- chat-store.ts: checkDaemon sets otherInstances from the probe result on
  tier1 — same state the ready frame feeds, so the 15s probe keeps it live
  even with no open connection. Tokens deliberately stay "—" until a real
  WS usage frame (correct — no usage before a turn).

Deliberately does NOT touch the WS connection lifecycle
(ensureConnection/connectAgent/send) — that path carried the earlier
"stuck offline" bugs; the low-risk probe extension avoids it.

Live-verified with two daemons: Info shows "sees 1 other · Work:7432" on
chat open with zero messages sent. smoke 0, vitest 83/83, svelte-check 0.
Note: a simultaneous-startup registry write race can briefly drop a row
(self-heals via the 30s heartbeat) — pre-existing registry contention
(RFC-035 I5), not introduced here.

Refs: RFC-035
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The chat launcher floated over the map (bottom-right), which was in the way,
and looked like an ordinary secondary button. Two changes:

- shared/ui Button gains a `magic` variant (rule 24 — the look lives in the
  primitive): an animated iridescent rainbow gradient (hue-sliding shimmer),
  a soft pulsing glow, and two twinkling ✦ sparkle accents (pure CSS pseudo-
  elements, no dep). White label + text-shadow carry contrast in both themes;
  `prefers-reduced-motion` freezes to a static gradient. Showcased on
  /playground + documented in shared/ui/README.md. (Also fixed a self-inflicted
  compile break: a CSS comment containing `*/` closed the block early.)
- The launcher moves OUT of the map overlay into the /onboard header, next to
  "Exit to standard view →", as a `variant="magic"` "✨ Ask" / "Close chat"
  button. ComposedMapView gains `showChatLauncher` (default true) + a bindable
  `chatOpen`, so the onboard host drives the chat from its header while the
  dashboard host keeps its own (now also magic) launcher — neither breaks.
- Removed the `.map-chat-pos` absolute wrapper: its stacking context was
  trapping the RFC-035 FloatingWindow's position:fixed root inside a local
  paint order instead of letting it escape to the viewport.

Live-verified on /onboard: magic Ask sits in the header off the map, opens the
chat, toggles to Close chat. Full suite 58 files / 771 tests, svelte-check 0.

Refs: RFC-035
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…boost gradient)

Refines the launcher per user direction (supersedes the filled "✨ Ask" header
button from 60ae14a):

- New shared/ui `MagicStar` primitive: the ✨ sparkles motif (one main
  four-pointed sparkle + two small accent sparkles) drawn as a gradient-stroked
  CONTOUR (fill:none, stroke=animated linear-gradient) in extraboost.ai's
  signature palette (#5B8DEF → #9D7BEA → #FB7185 → #FBBF24 → #34D399, looping).
  The colors visibly cycle — a 3s SMIL gradient rotation + a 4s CSS hue-rotate
  sweep — and freeze to a static gradient under prefers-reduced-motion. Unique
  gradient id per instance; showcased on /playground.
- The launcher moves from the bottom-right over-the-map corner into the TOP
  chips toolbar, LEFT of the "All" chip: FlowChips gains a `leading` snippet
  (guarded so it still shows on zero-flow maps) and ComposedMapView passes the
  compact `<Button variant="ghost" size="icon"><MagicStar/></Button>` there.
- Retired the just-added filled `magic` Button variant (superseded by
  MagicStar) and removed the onboard-header launcher + the now-unused
  showChatLauncher / bindable chatOpen plumbing — the launcher is common to
  both hosts again, mounted identically.

Live-verified on /onboard: the ✨ sits left of "All" in the chips row with a
visibly cycling gradient and opens the chat. vitest 211 pass, svelte-check 0.

Refs: RFC-035
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The "What's inside" zone-detail panel popped up and re-rendered on every
pointer move across a zone (handleCanvasPointerMove set detailZoneId
immediately), and sat top-right where it now collides with the chips toolbar
+ the ✨ launcher.

- Dwell: detailZoneId is now set behind a 350ms timer (ZONE_DWELL_MS) that
  only fires if the cursor is still resting on the same zone — a quick pass no
  longer flashes the card. The hover ring (hoveredZoneId) stays immediate. The
  timer is cleared on zone change, on closeZoneDetail, on descend/level change,
  and on teardown. Sticky behavior kept: once shown it stays until a different
  zone is dwelt on or × dismisses it.
- Moved ZoneDetailCard from top-right (top:52 right:16) to bottom-left
  (bottom:16 left:16), clear of the chips row and the bottom-center tour card;
  the "What's inside" list still scrolls.

Live-verified: fast pass shows nothing; resting ~350ms on a zone shows the
card in the bottom-left corner. vitest composed-map 101 pass, svelte-check 0.

Refs: RFC-035
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
An adversarial 3-dimension web review (verified each with a concrete trigger)
found 9 real bugs the green suite + a manual click-through missed. All fixed +
regression-tested.

HIGH:
- Cross-turn frame race: cancel+immediate-resend on the one persistent WS could
  land a cancelled turn's token/usage/done on the next answer. Fix: per-turn
  `turnId` threaded client↔daemon on user_message + every server frame; the
  client drops frames whose turnId != current; cancel advances the turn.
- Close-chat-mid-stream left `pending` stuck + an orphaned "thinking…" bubble
  (newChat became a no-op). Fix: stopAgentProbe now calls fallBackToTier0.
- Escape closed the chat AND navigated the map (one press, two actions). Fix:
  FloatingWindow.handleKeydown stopPropagation on Escape.
- FloatingWindow docked width seeded/left unclamped → panel off-screen on a
  narrow first-visit or live-shrink. Fix: clamp dockedWidth on resize + init.
- handleError appended the error then fell to tier0 in the same tick → the
  error text never rendered (blank CTA). Fix: keep the transcript visible
  whenever messages exist; CTA only for the empty case.

MEDIUM:
- Error frame had no fatal/non-fatal discriminant → any per-turn error forced
  full tier0 fallback. Fix: `fatal:boolean` on the error frame; tier0 only when
  fatal.
- maxWidth() floored at minWidth below a 352px viewport → window spilled
  off-screen. Fix: cap geometry against raw viewport dimensions.
- hoveredZoneId not reset on descend/ascend/climbTo → stale hover-ring flash on
  re-entry (deterministic zone ids). Fix: clear it alongside detailZoneId.
- checkDaemon probe writes unguarded → a fast close→reopen let an older probe
  overwrite a newer result. Fix: monotonic probe generation guard.

Also fixed a type-cast bug in the error-frame parse surfaced by the fatal
change (runtime type check, no unsafe cast). PROTOCOL_VERSION bumped;
turnId/fatal are additive (missing → tolerated, backward-compatible).

smoke exit 0, vitest 60 files / 782 tests, svelte-check 0 errors.

Refs: RFC-035
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…amera chat) (#169)

## Summary

**The live onboarding agent (PRD-038 Pillar C).** Talk to your project
in the web chat and a **real local Claude Code session** answers —
grounded in the actual repo — while the **map moves as it explains**.
Two-tier + graceful: Tier 0 answers offline from `map.json`; Tier 1
upgrades to the live agent when the daemon is running.

This PR consolidates the Pillar C **shape + build + evidence**
(supersedes the shape-only #168).

## Proven end-to-end (EVID-096)

Spawned the daemon, connected a WS client, asked *"what is this project
and what is it for?"* — the local CC answered verbatim: *"@forgeplan/web
is a tiny zero-install npm CLI that scaffolds a pre-built SvelteKit app
into `.forgeplan-web/`, serves a read-only force-directed map of
Forgeplan artifacts… `npx @forgeplan/web start`…"* — then **called
`show_on_map` → `{zone: z.surfaces}`** and narrated the zone flow. Real
model turn, grounded, camera-driven.

## What's in it

- **RFC-034 + ADR-010** (active) — the daemon/protocol/camera/chat
architecture + the packaging decision (separate optional package +
spawn-only subcommand).
- **Phase 1** — `camera-bus` seam (the one primitive a chat uses to move
the RFC-033 tour camera) + Tier-0 chat (`map-chat`: client-grounded,
model-free, offline).
- **`agent/`** — NEW separate package `@forgeplan/web-agent` (ADR-010:
own deps `@anthropic-ai/claude-agent-sdk` + `ws` + `zod`, never in
core): a 127.0.0.1 WebSocket daemon booting a persistent Agent SDK
`query()` session in a **read-only** profile (Read/Glob/Grep +
`show_on_map`; deny Write/Edit/Bash), with an in-process
`createSdkMcpServer` `show_on_map` tool that relays camera frames.
- **`bin/commands/onboard-agent.mjs`** — spawn-only subcommand (rule 23:
`spawn`s the package, never imports it).
- **Tier-1 web wiring** — `agent-client.ts` (read-only WS client) +
`chat-store` Tier-1 (stream → assistant bubble; `show_on_map` →
camera-bus; degrades to Tier 0 when the daemon is down).

## Invariants

- **Rule 22**: the live path is browser↔daemon over `ws://127.0.0.1`;
`/api/*` is never involved.
- **Rule 23 / ADR-010**: core `bin/` stays `node:*`+citty+siblings
(spawn-only); the SDK lives only in the `agent/` package; root
`package.json` untouched.
- **Read-only**: the agent cannot mutate the workspace.

## Test plan

- `npx vitest run src/widgets/map-chat src/widgets/composed-map` →
**153/153**.
- `npx svelte-check` → **0 errors**.
- `node agent/scripts/smoke.mjs` → exit 0 (protocol + read-only profile
+ bind + `/health` + `{ready}`).
- Rule-23 allow-list grep over `bin/` → OK.
- **Live end-to-end turn** (above) — daemon + local CC + `show_on_map`
(EVID-096, CL3).

## To try it
`npx @forgeplan/web onboard-agent` in your project → the web chat
detects it (`● live`) → ask away.

Refs: PRD-038, RFC-034, ADR-010, EVID-096, RFC-033

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Throwaway /iso-spike route de-risking a true-3D IDEF0 exploded-pyramid map
view. Threlte v8 + three; R1 non-centered nesting (child plane explodes
under the parent zone-box, not centered), dashed MeshLine frustum
connectors (attenuate=false), ICOM boundary arrows via real classifyIcom
(ADR-007). Spike-scoped (// TODO(spike)); pre-RFC.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…stages

Checkpoint of the workflow build: IsoScene monolith split into ui/ (12
components), lib/ (4), model/ (1). Matte material, dynamic re-layering +
depthWindow accordion, node & layer hover-cards. 4 known svelte-check
implicit-any errors to fix next. Spike-scoped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…epthWindow

Thin translucent paper sheets (PLATE_THICKNESS/opacity down), hover
highlights individual element not whole sheet, leader-line cards gated
behind showInfoCards=false (minimap), windowPlanes root-anchored
(first N levels expand downward), dashed-connector depthTest/renderOrder
visibility fix, A11yProxy 3-kind union guard. svelte-check 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…reverse collapse

Clicking a zone explodes its sub-layer chain downward (R1 nesting),
dashed frustum connectors now render corner-to-corner (depthTest:false
+ renderOrder), breadcrumb + ascend, depthWindow caps depth. Fixed
each_key_duplicate in IsoA11yProxy (same node id across two expanded
planes). svelte-check 0, live-verified explode + connectors + 0 console.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…s shows

At depthWindow=2, descending into a deeper plane pushed the new level
past the display window, hiding it as a sliver until you manually bumped
the control to 3. Now focusZone grows depthWindow to min(3, chainLen+1)
after each descend, so the layer you click into is always expanded.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Dim/desaturate by distance-from-deepest (drilled-into plane stays full,
ancestors recede), wider PLANE_GAP, softer depth desaturation, ancestor
node boxes darken with depth. Partial declutter — spatial overlap from R1
nesting remains (needs a layout/camera decision, not just dimming).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Only the drilled-into (deepest) plane renders node boxes; ancestor
planes collapse to faint zone-frame outlines (context, not clutter).
At rest (root) the root still shows full detail; drilling fades context
to frames and details the focus. Clears the dense-node overlap.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Move IsoScene + ui/ + lib/ + model/ into a widgets/iso-map/ slice
(internal structure preserved, relative imports intact). /iso-spike
route now imports from the widget (route->widget, FSD-legal). Prep for
mounting the 3D iso in the Map view's minimap corner. svelte-check 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
IsoMinimap (container-sized host reusing the shared mapPoller) + a lazy
IsoMapCorner wrapper; DependencyGraph shows it in the bottom-right corner
only on the Map view, keeping the 2D Minimap on the other 6 views. Main
2D ComposedMapView untouched. three/Threlte dynamic-imported (client
code-split). svelte-check 0, dev live-verified (Map view: 2D map + 3D
corner, 0 console errors).

KNOWN SHIP-BLOCKER: dist build = 6.0M vs 3M cap (three in SSR bundle +
unused draco/basis loaders from @threlte/extras). Bundle surgery next.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Kill unused @threlte/extras draco/basis loaders (~1.5M) via a vite alias
stub (vite/stubs/three-loaders.ts); ssr=false + browser-guarded dynamic
import for /iso-spike and IsoMapCorner so three is fully OUT of the SSR
server bundle (0 three markers in dist/index.js). Remaining over-cap is
three+threlte itself (808K, lazy client chunk). Still 0.29M over the 3M
cap — cap decision pending.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The 3D Map-view corner (three.js + Threlte, ~808K lazy client chunk)
pushes dist to ~3.4M. Cap raised to 3.5M per user decision; three is
loaded only when the Map view opens (deferred download, no cold-start
cost). TODO(iso-adr): amend PRD-030 NFR-005 via ADR before ship.

npm run build now passes: images built stable + nightly, dist 3.4M.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… only surface

The 3D iso view now lives entirely in widgets/iso-map, consumed by the
Map-view corner (IsoMapCorner). The /iso-spike dev route was a public
route that would have shipped in the app AND was the last SSR path
pulling three into the server bundle. Removing it: no user-facing
throwaway route, three fully client-lazy. Map corner live-verified,
npm run build passes (dist 3.4M), svelte-check 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…control toggle

New widgets/composed-map/model/shared-drill-bus.svelte.ts — a plain-data
module singleton holding the shared focus chain (NO three/@threlte import,
so the 2D/SSR path stays three-free). ComposedMapView (2D) and iso-view-
state (3D) both read/write it: descend/ascend in EITHER view drives the
other. 3D reflects + highlights the 2D's current focus and always shows
the current depth expanded. IsoControls (1/2/3+ascend) gains a hide/show
toggle. Live-verified both directions (3D zone-click -> 2D descends to
Decision Trail; 2D 'All' -> 3D resets to root), 0 console errors,
svelte-check 0, vite build green, three still lazy/code-split.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fix the build.mjs cap comment: the amended cap is PRD-030 NFR-001 / SC-4
/ rule 21 (not NFR-005 — that's the flag-lifecycle policy), and the
deliberate 3->3.5 MiB bump is now recorded in ADR-011. Resolves the
TODO(iso-adr) placeholder.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ry, doc/RFC drift

- HIGH: sync-drop race — 3D now records a pending focus-chain target when
  an update lands mid-animation and re-applies it on animation settle, so
  the 3D always converges to the 2D (RFC-036 NFR-004 / INV-E no-drift).
- MED: IsoMapCorner wraps the 3D mount in <svelte:boundary> so a runtime
  WebGL/init failure shows an honest fallback (FR-007), not a broken corner.
- docs: dropped the stale 'throwaway spike' comment, deduped the
  TODO(iso-promote) copies, removed the phantom TODO(iso-draco-basis),
  aligned shared-drill-bus public API names to the RFC-036 contract.
svelte-check 0, vitest 782/782, npm run build passes (dist 3.4M both images).
TODO: shared-drill-bus unit tests still owed (EVID-100 finding #2).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…D-100 #2)

16 new tests: shared-drill-bus reducer idempotence + bidirectional 3D<->2D
sync contract + mid-animation pending-retry convergence (the sync-drop-race
fix under test). vitest 782 -> 798, svelte-check 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Record + activate the Forgeplan artifact chain for the 3D isometric
layered-overview minimap shipped into the Map-view corner:

- PRD-039  requirement (child of PRD-036 composed-map)
- RFC-036  lazy iso-map widget, 2D-synced via three-free shared-drill-bus
- ADR-011  ship three.js+Threlte lazy chunk; raise dist cap 3->3.5 MiB
           (amends PRD-030 NFR-001 / SC-4 / rule 21)
- EVID-099..107  dist-cap build, code review + re-review, security
           rule-22 governance, render-proof, AC-3/AC-1 interaction proof,
           and two guardian gates (final: PASS, both prior gaps closed).

Guardian re-gate (EVID-107) PASS: R_eff>0 on all three, both EVID-105
gaps closed and verified in source, blast radius viewer-only + reversible.

Refs: PRD-039

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rner (#170)

## Summary

Replaces the 2D minimap in the **Map view's** bottom-right corner with a
lazy-loaded **3D isometric layered-overview minimap** (exploded-pyramid
IDEF0
stack). The other 8 graph views keep their unchanged 2D minimap; the
main 2D
composed-map is untouched.

- **3D corner widget** (`widgets/iso-map/`, SOLID/FSD-decomposed):
Threlte v8 +
three, OrthographicCamera + OrbitControls, stacked isometric zone
planes,
node boxes, dashed frustum, depth control (1/2/3 + ascend) + show/hide
toggle.
- **Bidirectional 3D↔2D drill sync** via a new **three-free**
`shared-drill-bus`
singleton (plain-data focus chain): clicking a zone in 3D descends the
2D map
and vice-versa; 3D always reflects the 2D focus. The 2D path never
imports three.
- **Lazy load**: three/@threlte load only on Map-view mount (dynamic
import,
browser-guarded, `<svelte:boundary>` fallback) — zero WebGL on the other
views.
- **Bundle surgery**: stubbed unused draco/basis loaders (−1.5 MB),
pushed three
  out of SSR → dist 6.0 MB → **3.29 MiB** (< 3.5 MiB cap).
- Dropped the throwaway `/iso-spike` dev route — the corner minimap is
the only surface.

## Why

The composed-map's flat 2D minimap couldn't convey the layered depth
structure
of the IDEF0 decomposition. The 3D exploded-pyramid overview shows the
whole
altitude stack at a glance and stays in lock-step with the 2D drill
state.

Driving Forgeplan chain (all **active**, guardian **PASS**):
- **PRD-039** — requirement (child of PRD-036 composed-map)
- **RFC-036** — lazy iso-map widget + shared-drill-bus design
- **ADR-011** — ship three.js+Threlte lazy chunk; raise dist cap 3→3.5
MiB
  (amends PRD-030 NFR-001 / SC-4 / rule 21)
- Evidence EVID-099..107 (dist-cap, code-review + re-review, rule-22
security
governance, render-proof, AC-3/AC-1 interaction proof, 2 guardian
gates).

## Test plan

- `svelte-check` — **0 errors** (8 pre-existing a11y warnings).
- `vitest` — **798/798** pass (incl. 16 new shared-drill-bus +
iso-view-state
sync tests: idempotence, bidirectional sync, mid-animation convergence).
- `npm run build` — **PASS**, dist **3.29 MiB < 3.5 MiB** cap, both
images;
  `three` isolated to a lazy client chunk, 0 markers in the SSR bundle.
- **Live Playwright interaction proof** (EVID-106): depth 1/2/3 state
change,
ascend descend→climb→re-disable, show/hide toggle round-trip, and every
non-Map view confirmed to keep its 2D minimap with `three` never
mounted.
- **rule-22 security audit** (EVID-104): 0 new server/network surface —
the one
new client GET consumes the already-allow-listed read-only
`/api/map/layers/<zone>`.

Refs: PRD-039

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Bring the .forgeplan markdown in sync with the index and clear the
working-tree clutter left after the Pillar-B/C + iso arcs.

- Fix markdown<->index desync (RED LINE #4): PRD-038 + RFC-035 were
  activated in the Lance index during the Pillar C (#169) close-out but
  their status never landed in the committed markdown, so a scan-import
  would silently revert them to draft. Restore status: active + the
  session's score/link updates on the Pillar-B/C evidence chain.
- Commit EVID-098 (RFC-035 chat-panel-v2 verification) — it was never
  captured by any prior commit.
- Add the three reference docs (CLAUDE-PLUGINS, MAP-PACK v0.2.0 findings,
  proactive hints rules).
- gitignore transient forgeplan/local paths (anomalies-journal.jsonl,
  map/.work/, .local/).
- Remove stray scratch (pnpm-lock.yaml — project is npm; plus dev-run
  yml/md droppings).

No source/behaviour change; markdown is now the durable source of truth
(ADR-003) that matches the derived index.

Refs: PRD-038

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ee (#171)

## Summary

Pre-release housekeeping on `develop` — no source or runtime behaviour
change.

- **Fix markdown↔index desync (RED LINE #4).** `PRD-038` + `RFC-035`
were
activated in the Lance index during the Pillar C (#169) close-out, but
their
  `status: active` never landed in the committed markdown. A `forgeplan
scan-import` on develop would have silently reverted them to `draft` and
"unactivated" the Pillar C chain. Restored `status: active` (+ the
session's
  score/link updates on the Pillar-B/C evidence chain). Verified: after
  `scan-import`, both stay `active`.
- **Commit `EVID-098`** (RFC-035 chat-panel-v2 verification) — it
existed only
  in the working tree, captured by no prior commit.
- **Add three reference docs**: `docs/CLAUDE-PLUGINS.md`,
  `docs/MAP-PACK-v0.2.0-FINDINGS.md`, `docs/hints-rules.md`.
- **gitignore** transient forgeplan/local paths:
`.forgeplan/anomalies-journal.jsonl`, `.forgeplan/map/.work/`,
`.local/`.
- **Remove stray scratch**: `pnpm-lock.yaml` (project is npm —
`package-lock.json`
  is the lockfile) + dev-run `*.yml` / `*.md` droppings.

## Why

Bring `develop` to a clean, durable state before cutting the `v0.3.0`
release
branch. The markdown is the source of truth (ADR-003); it must match the
derived
index so a fresh clone + `scan-import` reproduces the activated Pillar C
chain.

## Test plan

- `forgeplan scan-import` → `PRD-038` + `RFC-035` remain **active**
(desync cured).
- No `template/` source touched → existing CI (svelte-check + vitest +
build
  matrix) unaffected; this is a docs/metadata-only change.
- `git diff --cached` verified: no scratch, no gitignored paths, no
screenshots staged.

Refs: PRD-038

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Release v0.3.0 (MINOR) — the IDEF0 composed-map program:
composed-map T4 render + recursive drill-down, onboarding tour (Pillar B),
live local-agent guide (Pillar C), and the 3D isometric Map-corner minimap.
152 commits since v0.2.4; no breaking changes.
@explosivebit
explosivebit merged commit 78f4fd5 into main Jul 9, 2026
3 checks passed
@explosivebit
explosivebit deleted the release/v0.3.0 branch July 9, 2026 12:11
@fedorovvvv

Copy link
Copy Markdown
Collaborator

Great!!!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants