chore(release): v0.2.0#128
Merged
Merged
Conversation
Decompose GitHub issue #109 (multi-instance management) into 6 sub-issues (#110-#115) and a full Forgeplan artifact set. All 15 artifacts pass forgeplan validate with 0 MUST errors; 14 cross-references wired. Refs: PRD-024, PRD-025, PRD-026, PRD-027, PRD-028, PRD-029, ADR-003, ADR-004, SPEC-003, RFC-020, RFC-021, RFC-022, RFC-023, RFC-024, RFC-025 Also deprecates EVID-007 + EVID-008 (100% duplicates of EVID-004 + EVID-005). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Refactor the 367-line bin/forgeplan-web.mjs into a 5-line entry +
bin/cli.mjs (root command) + bin/commands/{init,update,start}.mjs (one
file per subcommand) + bin/lib/{config,gitignore,forgeplan-binary}.mjs
(shared helpers). citty@0.2.2 provides defineCommand / runMain / typed
args / auto-help / subcommand routing.
Behavior preserved 1:1 — every flag (-y, --force, --quiet,
--no-gitignore, --experimental, --no-experimental) and every alias
(upgrade, serve, run) still works. New --scope user|project flag is
declared on init/update/start as a placeholder (TODO(109b)) for the
next wave to wire.
Rule 23 amended to permit citty as the single named exception
(verification snippet updated). npm ls --omit=dev shows only
citty@0.2.2 (no transitive deps in this version).
Smoke test (3-OS gate) passes. PASS line confirmed locally.
Refs: PRD-024, RFC-020, ADR-003
Closes: #110
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
109b — init now resolves scope from --scope flag, -y default (project), or interactive readline prompt (default user in TTY; --scope required in non-TTY). User scope writes to ~/.forgeplan-web/ and skips host .gitignore. Project scope behavior unchanged. 109c — start follows the resolution chain: explicit --scope wins, else project (cwd/.forgeplan-web/) → user (~/.forgeplan-web/) → prompt-init (or fail in non-TTY). update mirrors the same chain. New helpers: bin/lib/scope.mjs (resolveScope, userScopePath, projectScopePath, findScaffold) and bin/lib/prompt.mjs (readline-based TTY prompt — citty 0.2.2 doesn't expose consola; TODO(109b-prompt) revisits on citty bump). config.mjs gets readScope(). Rule 20 amended: user-scope writes are bounded to ~/.forgeplan-web/ and gitignore-silent. All write-boundary invariants preserved per scope. Smoke: PASS (project scope path; user-scope and resolution chain verified via manual scenarios documented in agent report). Refs: PRD-025, PRD-026, RFC-021, RFC-022 Closes: #111, #112 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Port bits-ui Combobox (v2.18.1) into template/src/shared/ui/combobox/ mirroring the Select primitive shape: 5 sub-components (Combobox, ComboboxTrigger, ComboboxContent, ComboboxItem, ComboboxInput) + barrel index.ts. Re-exported from shared/ui/index.ts by name. Variants: default, mono. Sizes: sm, md. Pure CSS via app.css tokens (--bg, --bg-1, --bg-2, --fg, --fg-1, --accent, --line, ...) — works across light/dark/orch themes with no caller intervention. A11y: role=combobox / aria-expanded / aria-controls on trigger, role=listbox on content, role=option / aria-selected on items, focus-visible accent ring matching Select pattern. Keyboard: arrow-up/down navigation, Enter to select, Escape to close. /playground showcases all 4 variant×size combinations under the existing primitive grid; theme toggle in playground header swaps visuals automatically. svelte-check: 1053 files / 0 errors / 0 warnings. Rule-24 grep: clean (no upper-layer reaches into primitive internals). Build green; dist-experimental at 2.47M (under 3M cap). Refs: PRD-028, RFC-024 Closes: #114 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
109d — register every running forgeplan-web instance in
~/.forgeplan-web/instances.json so the UI (Wave 6) can list and switch
between them. Single global registry regardless of scaffold scope.
bin/lib/registry.mjs (172 lines) — pure node:* helpers: registryPath,
readRegistry, writeRegistryAtomic (tmp+rename, symlink defence),
isAlive (process.kill(pid,0) + 60s heartbeat freshness, EPERM=alive on
Windows), sweepStale, register, deregister, heartbeat, findFreePort
(increment from PORT base, max +100, exclusive listen probe),
makeInstanceId. Rule 23 clean (citty + node:* + relative siblings only).
bin/commands/start.mjs — port allocator, register child PID,
30s heartbeat (unref'd), SIGINT/SIGTERM forwarding + child-exit
deregister chain. forgeplanCli probed once at start (null on failure).
template/src/shared/server/registry.ts — TypeScript readonly mirror with
2s in-memory cache + single-flight, schema validation per row,
liveness sweep on every refresh. No fs.write*, no spawn.
template/src/routes/api/instances/+server.ts — GET-only endpoint
returning { ok, data: { instances }, cmd: "registry:read" }.
Rule 22 amended to add /api/instances as the second non-forgeplan
endpoint (after /api/update-check), with strict no-mutation +
no-network constraints.
Verified: 2 instances on 17400+17401 register; SIGKILL on instance A
swept at next start; heartbeat ticks at 30.05s delta; deregister within
2s of SIGTERM. svelte-check 0/0/0. Smoke PASS.
Refs: PRD-027, RFC-023, SPEC-003, ADR-004
Closes: #113
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…115) 109f — wire the new Combobox primitive (Wave 4) into HealthBar to switch between running forgeplan-web instances using the registry endpoint (Wave 5). Closes the #109 chain. New entity slice template/src/entities/instance/: - model/types.ts — Instance, InstanceScope, InstancesPayload (copy of SPEC-003 v1; TODO(spec-003-keep-in-sync) marks the duplication so schema bumps update both sides) - api/store.ts — instancePoller via shared/api/createPoller, 5s cadence - index.ts — barrel HealthBar.svelte branching logic: - instances.length >= 2 → Combobox (mono, sm) listing all instances with projectName + host:port; current entry marked; selecting another window.location.replace - instances.length < 2 → preserve existing /{project} static span Self-detection via window.location.host matched against instance.id. SSR-safe (typeof window guard). Rule 24 clean — Combobox styled only via its own variant/size props; consumer styles are layout/wrapper only. svelte-check 0/0/0. Smoke PASS. Two-instance scenario manually verified (5174↔5175 switch); single-instance fallback verified post-deregister. Refs: PRD-029, RFC-025 Closes: #115 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wave 7 — close-out for the multi-instance migration. Evidence collected from each Build wave (smoke + svelte-check + manual scenarios) and linked back to its driving PRD. All R_eff > 0; all 8 artifacts moved draft → active. EvidencePacks created: - EVID-029 → PRD-024 + ADR-003 + RFC-020 (citty refactor; smoke PASS) - EVID-030 → PRD-025 + RFC-021 + ADR-004 (init scope; non-TTY guard) - EVID-031 → PRD-026 + RFC-022 (start resolution chain) - EVID-032 → PRD-027 + RFC-023 + SPEC-003 + ADR-004 (registry; 2-instance + sweep + heartbeat verified) - EVID-033 → PRD-028 + RFC-024 (Combobox; 4 variant×size; check 0/0) - EVID-034 → PRD-029 + RFC-025 (HealthBar switcher; 5174↔5175 + fallback) Each evidence body carries Structured Fields (verdict: supports, congruence_level: 3, evidence_type: test) so R_eff parses cleanly. Activation: PRD-024..029, ADR-003, ADR-004 — all draft → active. forgeplan health is clean. Closes the rule 11 gate (R_eff > 0 + active) for #109 PR merge. Refs: PRD-024, PRD-025, PRD-026, PRD-027, PRD-028, PRD-029, ADR-003, ADR-004, EVID-029..034 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes #109. Sub-issues #110, #111, #112, #113, #114, #115 all closed. ## Summary Refactor `@forgeplan/web` for multi-instance use: 1. **citty-based CLI** — `bin/forgeplan-web.mjs` shrinks 367→5 lines; subcommands live in `bin/commands/{init,update,start}.mjs`; helpers in `bin/lib/`. Single allow-listed dep (`citty@0.2.2`) per amended rule 23. 2. **`--scope user|project` flag** — init writes to `<cwd>/.forgeplan-web/` or `~/.forgeplan-web/`. `init -y` keeps defaulting to project (compat); interactive prompt picks user as the new default. Non-TTY without flag fails clearly. 3. **`start` resolution chain** — project → user → prompt-init. Explicit `--scope` short-circuits the chain. 4. **Global instance registry** at `~/.forgeplan-web/instances.json` — incremental ports, pid + heartbeat liveness, atomic tmp+rename writes, stale sweep on every start. New `/api/instances` readonly endpoint mirrors the registry to the UI. 5. **Combobox primitive** in `template/src/shared/ui/combobox/` (bits-ui 2.18.1; default + mono variants × sm/md sizes; showcased on /playground; rule-24 clean). 6. **HealthBar instance switcher** — when ≥2 instances are running, the project label becomes a Combobox; selecting another instance navigates the browser to `http://host:port`. Single-instance behavior unchanged. ## Why Original issue: install `forgeplan-web` once per user (not per project), run several instances simultaneously, switch between them from any instance's UI. Refined plan keeps backwards compat (`--scope project` is still the `init -y` default) so existing setups don't break. ## Forgeplan artifacts (all `active`, R_eff > 0) - ADR-003 — amend rule 23 to permit citty in `bin/` - ADR-004 — registry path + JSON format + atomic writes - PRD-024 + RFC-020 — citty integration - PRD-025 + RFC-021 — `--scope` flag + user-scope path - PRD-026 + RFC-022 — start resolution chain - PRD-027 + RFC-023 + SPEC-003 — instance registry - PRD-028 + RFC-024 — Combobox primitive - PRD-029 + RFC-025 — HealthBar switcher - EVID-029..034 — smoke + svelte-check + manual scenarios per PRD Rules touched (each via dedicated PRD/ADR): - `.claude/rules/20-init-host-isolation.md` — user-scope writes are gitignore-silent (PRD-025) - `.claude/rules/22-readonly-proxy.md` — `/api/instances` allow-list extension (PRD-027) - `.claude/rules/23-bin-zero-deps.md` — citty named exception (ADR-003) ## Test plan - [x] `npm run smoke` — PASS at every wave (citty refactor, scope wiring, registry, HealthBar) - [x] `npm run build` — svelte-check 1056 files / 0 errors / 0 warnings - [x] Manual: `init -y --scope user` writes only to `~/.forgeplan-web/`, host `.gitignore` untouched - [x] Manual: `init -y` (no flag) defaults to project (compat preserved) - [x] Manual: Two instances on 5174 + 5175 (or first free pair); both register; SIGKILL on one swept on next start; heartbeat ticks at ~30s; deregister within 2s of SIGTERM - [x] Manual: HealthBar shows Combobox when ≥2 instances; single-instance fallback verified - [x] Manual: `/api/instances` returns standard envelope `{ ok, data: { instances }, cmd: "registry:read" }` - [x] `forgeplan health` clean post-Wave-7 - [x] Rule 23 verifier exits 0 (only `citty` + `node:*` + relative siblings in `bin/`) - [x] Rule 24 grep clean (no upper-layer `:global()` reaches into Combobox internals) - [ ] CI matrix (ubuntu/macos/windows × Node 22) — runs on PR open ## Out of scope (deferred follow-ups) - Cross-host federation (talking to instances on other machines) - Auth / per-instance access control - Migration tool for existing project-scope `.forgeplan-web/` → user-scope - Full XDG compliance (`XDG_CONFIG_HOME` etc.) — RFC-021 keeps `~/.forgeplan-web/` for predictability - Smoke coverage for `--scope user` path + multi-instance scenario (current smoke covers single project-scope instance only) 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Add `minify: true` + `keepNames: true` to the esbuild config in
`bundleExperimentalDist()`. The unminified single-file ESM bundle was
~1.4M; minification drops `dist-experimental/index.js` from 1395K to 683K
(–60%) and the full `dist-experimental/` from 2.7M to 1.79M, well below
the existing 3M cap (PRD-014 SC-1).
Side effect: minification renames the SvelteKit `env('HOST', '0.0.0.0')`
helper to a short identifier (e.g. `Ue(...)`), breaking the literal regex
in `patchHostDefault()`. Loosen the regex to capture whatever identifier
the minifier picked and reuse it in the replacement, so both the legacy
`dist/` (unminified, `env=env`) and the new `dist-experimental/`
(minified, `env=Ue`) get patched correctly. Verified by smoke run: the
experimental bundle binds to 127.0.0.1, `/api/health`, `/api/list`, and
`GET /` all return 200.
`keepNames: true` preserves `Function.name` / `class.name` so any runtime
diagnostics that key off names (sirv, polka, kit error formatters) still
resolve.
Closes #117
Refs: PRD-014
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
) ## Summary - Enable `minify: true` + `keepNames: true` in the esbuild config that produces `dist-experimental/index.js`. - `dist-experimental/index.js`: **1395K → 683K (–60%)**. - `dist-experimental/` total: **2.7M → 1.79M**, comfortably under the 3M cap. - Loosen the `patchHostDefault()` regex so it survives identifier mangling — both legacy `dist/` (`env=env`) and the new minified `dist-experimental/` (`env=Ue`) get patched correctly. ## Why Issue #117 (`area:tarball`): the experimental bundle ships unminified, paying a ~700K size penalty in every `npx @forgeplan/web init --experimental` install. PRD-014 SC-1 already pins a ≤3M cap on `dist/`; minifying brings the experimental shape closer to a future graduation target with headroom for upgrades. Notes on choices: - `keepNames: true` — preserves `Function.name` / `class.name` so sirv, polka, and SvelteKit error formatters that key off names still resolve. - `legalComments: "none"` was already set, so no license-banner regression. - The 3M cap (`DIST_EXPERIMENTAL_MAX_BYTES`) is left as-is — 1.79M actual leaves 40% headroom for legitimate growth (Svelte/d3/adapter-node bumps). ## Test plan - [x] `npm run build` succeeds, both `dist/` and `dist-experimental/` produced. - [x] `[build] patched HOST default 0.0.0.0 → 127.0.0.1 in ./dist/index.js (env=env)` — legacy still patches. - [x] `[build] patched HOST default 0.0.0.0 → 127.0.0.1 in ./dist-experimental/index.js (env=Ue)` — minified bundle gets the loopback patch with the mangled identifier captured. - [x] `npm run smoke` passes (legacy `dist/` happy path). - [x] Experimental shim smoke (manual): init `--experimental`, start server, probe endpoints — `/api/health` ok, `/api/list` ok, `GET /` returns HTML, all 200. - [x] `dirSizeBytes(DIST_EXPERIMENTAL)` = 1.88M, well below `DIST_EXPERIMENTAL_MAX_BYTES = 3M` cap. Refs: PRD-014, #117 🤖 Generated with [Claude Code](https://claude.com/claude-code)
#121) Replaces the single --experimental boolean with a config-driven multi-image pipeline. Two images for v1: stable (default) and nightly, both shipping with empty feature lists — framework only. The legacy SvelteKit-with- node_modules/ shape is removed; the esbuild bundle (PRD-014 / RFC-013) is now the universal artifact form for every image. - config/images.json + config/features.json: source of truth for image → flags mapping and per-flag lifecycle metadata (addedIn / expiresIn / rollout). - scripts/build.mjs: bundles once via esbuild, copies to dist/ (stable) and dist-<name>/ (others), each with its own forgeplan-web-build.json carrying { image, features }. Build-time validator fails fast when any flag is past expiresIn or has lifetime > 3 minor versions. - bin/lib/images.mjs: image-name → directory mapping, allow-list-safe (node:* + relative siblings only — rule 23 unchanged). - bin/commands/init.mjs + update.mjs: --image <name> flag (default stable). Persisted in forgeplan-web.json. --experimental kept as deprecated alias for --image nightly with stderr warning; removed in 0.3.0. - scripts/smoke.mjs: extended to exercise both images end-to-end; asserts no node_modules/ in scaffold, image fields match, idempotent gitignore. - README + CHANGELOG + rules 21/23 + CLAUDE.md updated. Forgeplan: PRD-030 / RFC-026 / ADR-005 / EVID-035 (active, R_eff > 0). Refs: PRD-030, RFC-026, ADR-005 Closes: #121 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
) ## Summary - **Promote bundle to stable**: legacy SvelteKit + `node_modules/` shape is removed from the published tarball; the esbuild bundle (≈1.8 MB, capped at 3 MB) is now the universal artifact form for every image. - **Feature-flag + image registry**: `config/images.json` + `config/features.json` describe what images ship and what flags each one carries. v1 ships `stable` (default) and `nightly`, both with **zero** flags — framework only. Lifecycle policy: a flag's `expiresIn` ≤ `addedIn + 3 minor`; build fails fast when a flag is past expiry. - **`--image <name>` CLI** on `init` and `update`. Default `stable`. Sticky in `forgeplan-web.json`. `--experimental` kept as deprecated alias for `--image nightly` for one minor release; removed in 0.3.0. ## Why Two pressures merged: PRD-014 / RFC-013's `dist-experimental/` bundle has been opt-in long enough to graduate, and a single `--experimental` boolean can't scale to three or four parallel tracks. PRD-030 asks for a registry, not more booleans, with a forcing function so experimental flags can't accrete indefinitely. ## Forgeplan - **PRD-030** — feature-flag + image system; promote experimental bundle to stable default (active) - **RFC-026** — image manifest and build-pipeline (active) - **ADR-005** — image as build artifact, not runtime config (active) - **EVID-035** — smoke + lifecycle-validator evidence (active, `evidence_type: test`, CL3, supports) - All scored, R_eff > 0. ## Test plan - [x] `npm run build` — emits `dist/` (image=stable, 1.79M) and `dist-nightly/` (image=nightly, 1.79M), both ≤3 MB, both with per-image `forgeplan-web-build.json`. - [x] `npm run smoke` — exercises both images end-to-end (init + start + `/api/health` + `/api/list` + `GET /` returning 200), asserts no `node_modules/` in scaffold, image fields match, gitignore idempotent. - [x] `--experimental` deprecation alias prints stderr warning and writes `image: "nightly"` to both config files. - [x] Lifecycle validator regression — synthetic flag with `expiresIn ≤ currentVersion` exits non-zero with descriptive error; same for `lifetime > 3 minor`. - [ ] CI matrix (ubuntu/macos/windows × Node 20.19/22) — pending PR run. ## Breaking changes (npm tarball internals only) - `dist-experimental/` is renamed to `dist-nightly/` in `package.json#files`. Users running `npx @forgeplan/web init` see no difference; only consumers that pinned the directory name in scripts must switch. - Legacy build pipeline functions (`installRuntimeDeps`, `emitDistPackageJson`, `copyToDist`) removed from `scripts/build.mjs`. ## Out of scope - Per-image runtime gating logic (v1 ships empty flag lists). - Telemetry on which image users pick. - A third image (e.g. `lts`). Refs: PRD-030, RFC-026, ADR-005 Closes: #121 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Process-debt cleanup. Cluster A — 7 RFCs/SPEC whose parent PRDs already shipped active via #109/#118 — were left in draft, distorting `forgeplan health`. Flipped to active to match parent state: - RFC-020 (citty arch) ← PRD-024 active - RFC-021 (scope resolver) ← PRD-025 active - RFC-022 (start scaffold) ← multi-instance wave - RFC-023 (port allocator) ← PRD-027 active - RFC-024 (Combobox primitive) ← PRD-029 active - RFC-025 (HealthBar switcher) ← PRD-029 active - SPEC-003 (instances schema) ← PRD-027 + ADR-004 active PRD-001 deprecated: title/body mismatch (title 'show versions in footer', body 'bootstrap CLAUDE.md baseline') — both shipped out-of-band. Markdown was Lance-only; surfaced into git as part of deprecation. EVID-004/EVID-005 retain dependency on deprecated parent; they're frozen evidence and don't need rework. Health: 79 → 86 active, 16 → 8 draft, 2 → 3 deprecated. Refs: PRD-024, PRD-025, PRD-027, PRD-029, ADR-004 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
## Summary - Activate 7 RFCs/SPEC in cluster A whose parent PRDs already shipped active via #109/#118 (`draft → active`). - Deprecate stale PRD-001 (title/body mismatch — both interpretations shipped out-of-band). - Health: `79 → 86 active`, `16 → 8 draft`, `2 → 3 deprecated`. ## Why `forgeplan health` was misleading: 7 supporting RFC/SPEC artifacts plus 1 muddled PRD sat in `draft` with parents that were long `active` with `R_eff > 0`. This was process debt from the `forgeplan activate` step being skipped at the end of the multi-instance + citty waves. Pure artifact-lifecycle hygiene — no code change, no behaviour change. ### Cluster A (activated) | Artifact | Title | Parent (active) | |---|---|---| | RFC-020 | citty integration architecture — bin/ subcommand split | PRD-024 | | RFC-021 | Scope resolver + user-scope path conventions for init | PRD-025 | | RFC-022 | start scaffold-resolution algorithm + error UX | multi-instance wave | | RFC-023 | Port allocator, pid-liveness probe, atomic registry writes | PRD-027 | | RFC-024 | Combobox primitive — bits-ui wrapper, /playground showcase | PRD-029 | | RFC-025 | HealthBar instance switcher + /api/instances readonly endpoint | PRD-029 | | SPEC-003 | Instance registry JSON schema (`~/.forgeplan-web/instances.json`) | PRD-027 + ADR-004 | ### PRD-001 (deprecated) Title says "Show forgeplan + forgeplan-web versions in UI footer" — long shipped (`__FORGEPLAN_WEB_VERSION__` + `/api/health` integration). Body says "Bootstrap CLAUDE.md baseline + add methodological guides" — also shipped (Red lines, routing table, hook table, `guides/INDEX.md`/`CLAUDE-MD-GUIDE.ru.md`/`GIT-FLOW-GUIDE.ru.md` all present). The artifact's markdown was Lance-only (never in git history); deprecation surfaced it as a new file at `prds/PRD-001-…md` with `status: deprecated`. EVID-004/EVID-005 still link to it — they're frozen evidence, no rework needed. ## Test plan - [x] `forgeplan health` runs clean — `verdict: healthy`, no new orphans. - [x] Front-matter `status:` correct on all 8 files (`active` × 7, `deprecated` × 1). - [x] No code touched (verify via `git diff --stat origin/develop...HEAD`). - [ ] Reviewer: confirm no parent PRD lost evidence pointer (relations are append-only in markdown, but worth a glance at the diff). - [ ] CI: smoke matrix green (no functional change but rule-21/22/23 verifications still run). ## Reversibility Fully reversible: edit `status:` line back to `draft` in front-matter, run `forgeplan scan-import` to refresh Lance. No git surgery needed. ## Drift risks - **Lock contention** — `forgeplan activate` ran in parallel hit the workspace lock 4× during this PR's prep. The CLI is not concurrency-safe for one workspace. Future cluster operations should serialise; not blocking for this PR. - **PRD-001 dependents** — EVID-004/005 still claim to "support" PRD-001. Since PRD-001 is now `deprecated`, those evidence packs are formally orphaned-by-status. No automatic cleanup; leaving them as historical record. Refs: PRD-024, PRD-025, PRD-027, PRD-029, ADR-004
Tactical follow-up to #121 (PR #122) — agent context files and the Russian README still described the legacy `dist/` + `dist-experimental/` shape. Bring them in line with what `develop` actually ships: - README.ru.md: new "Образы (images)" section mirroring README.md; package summary in "Что получаешь" notes the per-image bundle. - docs/agents/domain.md: replace "legacy dist/" + "opt-in dist-experimental/" entries with image / feature-flag definitions; document `--experimental` as deprecated alias. - docs/agents/paths.md: top-level table swaps `dist-experimental/` for `dist-nightly/` + `config/`; tarball inventory now reads from `config/images.json`. No code changes — `bin/`, `scripts/`, `template/` untouched, smoke unchanged. Tactical depth per rule 11 (doc copy-edit, no public behaviour change), so no new Forgeplan artifact required; references the existing PRD-030 / RFC-026 / ADR-005 chain. Refs: PRD-030, RFC-026, ADR-005, #121 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…up) (#124) ## Summary Tactical follow-up to #121 (PR #122). Three doc files still described the legacy `dist/` + `dist-experimental/` shape — bring them in line with what `develop` actually ships post-PR-#122. - `README.ru.md` — new "Образы (images): `stable` (по умолчанию) и `nightly`" section mirroring `README.md`; updated "Что получаешь" line to mention the per-image bundle. - `docs/agents/domain.md` — replace **legacy `dist/`** + **opt-in `dist-experimental/`** entries with **Image** + **Feature flag** definitions, and document `--experimental` as a deprecated alias. - `docs/agents/paths.md` — top-level table swaps `dist-experimental/` for `dist-nightly/` + `config/`; tarball inventory now references `config/images.json` for the canonical image list. ## Why `docs/agents/*.md` are auto-loaded by the `fpl-skills` skill set. Stale terms there mean every future agent invocation starts with an inaccurate mental model of the package shape — exactly the kind of drift rule 11 (Forgeplan as single source of truth) is meant to prevent. PRD-030 already activated; this PR closes the doc gap without re-opening the artifact discipline. ## Scope No code changes — `bin/`, `scripts/`, `template/` untouched. Smoke status from PR #122 still applies. Tactical depth per rule 11 (docs without behaviour change), so no new Forgeplan artifact required. ## Test plan - [x] `grep -rn 'dist-experimental\|--experimental' docs/` shows only the `--experimental` *deprecated alias* mention in `domain.md`. - [x] `git diff --stat` is bounded to the three doc files. - [x] No code paths touched → smoke unaffected. - [ ] CI: docs-only PRs still trigger the smoke matrix; expected green since no source changed. Refs: PRD-030, RFC-026, ADR-005, #121 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Pass precompress: false to adapter() so the build no longer emits .br/.gz siblings under client/. Each shipped image (dist/, dist-nightly/) drops from 2.0M → 1.5M — a ~520K saving per image, ~1.04 MB total. Trade-off (accepted): sirv serves assets uncompressed when no precompressed sibling exists (verified via Accept-Encoding probe); no Content-Encoding header is set. For the canonical npx start on 127.0.0.1 deployment this is negligible. Closes #120. Refs: PRD-031, EVID-036. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sweep stale numbers to match post-PRD-030 / post-PR-125 reality: - "~1.8 MB single-file bundle" → "~1.4 MB" (matches `[build] dist/ ready (1.40M)` after #125 drops precompressed siblings). - Dogfood card row reframed: · drop "~200 LOC" (entry shim is now 5 lines after ADR-003 split, real work spans ~1.3K across `bin/{cli,commands,lib}/*`) · drop "0 runtime deps" (ADR-003 amended to a named-allowlist — `citty` is the one allowed runtime dep, with 0 transitives) · drop "~6 MB tarball with bundled node_modules" (PRD-030 graduated the legacy SvelteKit-with-`node_modules/` shape; current local pack is ~870 kB / ~3 MB unpacked) · lead with what users actually care about (per-image size + tarball weight) instead of LOC bragging - "(~11 artifacts)" in `npm run dev` comment → "100+" (avoids drift; current `forgeplan health` reports 99 artifacts on develop). Mirrors apply to both README.md and README.ru.md. Refs: PRD-030, PR #125, ADR-003. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
) ## Summary - Flip `adapter()` → `adapter({ precompress: false })` in `template/svelte.config.js`. - Drops 84 precompressed siblings (`*.br` + `*.gz`) from both shipped images. - Each `dist*/` artifact: **2.0M → 1.5M** (–25%, ~520K saving per image, ~1.04 MB total). - Closes #120. ## Why Per issue #120, the precompressed siblings cost ~520K per image but buy negligible runtime CPU savings for the canonical local-first deployment (`npx @forgeplan/web start` on `127.0.0.1`, single user, no CDN). After PR #117 minified the server bundle, precompressed client assets were the largest fixed cost left in the experimental tarball. Decision (PRD-031 + EVID-036): chose **Option A** from #120 (disable at adapter level) over B (build.mjs strip — strictly more code for same runtime behaviour) and C (keep `.br`, drop `.gz` — half the win for ~zero additional safety). ## Trade-off (R-1, documented in PRD-031) Sirv serves assets uncompressed when no precompressed sibling exists. Verified via direct `Accept-Encoding: br, gzip` probe against a live `npx ... start` server in a scratch dir: ``` HTTP/1.1 200 OK Content-Length: 3047 ``` (Same response with and without `Accept-Encoding`. No `Content-Encoding` header in either case.) For loopback `127.0.0.1` deployments the cost is negligible. Mitigation paths if a real complaint surfaces: revert this flag, or wire on-the-fly compression in `template/src/hooks.server.ts`. ## Test plan - [x] `npm run build` — both images emit (`dist/ ready (1.40M)`, `dist-nightly/ ready (1.40M)`). - [x] `find dist dist-nightly -name '*.br' -o -name '*.gz' | wc -l` → 0 (was 84). - [x] `du -sh dist dist-nightly` → 1.5M each (was 2.0M each, well under PRD-014 SC-1 3M cap). - [x] `npm run smoke` → `ALL IMAGES PASS`. - [x] Manual scratch-dir probe: `init --image nightly` → `start` → `GET /` returns HTTP 200; `GET /_app/immutable/entry/app.*.js` returns HTTP 200 with correct Content-Length. - [ ] CI matrix (ubuntu/macos/windows) — pending. Refs: PRD-031, EVID-036, PRD-014, PRD-030, #117.
## Summary Sweep stale numeric claims in both READMEs to match the current post-PRD-030 / post-PR #125 reality. ## What changed (verbatim numbers) | Claim | Before | After | Source of truth | |---|---|---|---| | Per-image bundle size | ~1.8 MB | **~1.4 MB** | \`scripts/build.mjs\` post-build report (\`[build] dist/ ready (1.40M)\`) after PR #125 | | Dogfood — bin LOC | ~200 LOC | _removed_ — ADR-003 split bin/ across \`cli.mjs\` + \`commands/\` + \`lib/\`; entry shim is 5 lines, real total ~1.3K across 11 files | \`wc -l bin/**/*.mjs\` | | Dogfood — runtime deps in \`bin/\` | 0 | **1** (\`citty\`, 0 transitive) | ADR-003 (amended named-allowlist), \`package.json#dependencies\` | | Dogfood — tarball | ~6 MB with bundled \`node_modules\` | **~870 kB packed / ~3 MB unpacked** | \`npm pack --dry-run\` (post-PRD-030 single-file bundle, no \`node_modules/\` in tarball) | | Dogfood — CI matrix | \`3 OS in CI\` | **\`3 OS × Node 22\` green smoke matrix** | \`.github/workflows/*\` | | \`npm run dev\` artifact count comment | (~11 artifacts) | **(100+ artifacts)** | \`forgeplan health\` — 99 on develop, anti-drift framing | ## Why The previous numbers were accurate at the time they were written but drifted as PRD-030 graduated the bundle approach, ADR-003 amended the zero-dep rule to allow \`citty\`, and PR #125 dropped the precompressed siblings. Anyone reading the README today would have a wrong mental model of size, deps, and dogfood scale. ## Test plan - [x] \`grep -nE "1\\.8 MB |6 МБ|~200|~11" README.md README.ru.md\` returns no hits. - [x] Both READMEs render in GitHub markdown preview (table cells, no broken refs). - [x] No content-equivalent claims out of sync between EN and RU. - [x] Numbers match current local measurements (\`du -sh dist*\`, \`npm pack --dry-run\`, \`forgeplan health\`). Refs: PRD-030, PR #125, ADR-003.
Closes #116. Adds an array-backed "open tabs" store (`tabsStore`) and a centralised `useOpen(event, id)` hook so every artifact-opening surface (graph node, rail row, in-panel link, NodeRef chip, breadcrumb) honours the same modifier policy: Shift+click adds a new tab without dropping the active, plain click replaces the active tab. A `TabBar` widget renders above `ArtifactPanel` with per-tab close buttons; closing the active tab falls back to the first remaining; closing the last hides the panel. The store is intentionally unpersisted — `beforeunload` clears it so reload starts clean. Graph view highlights every artifact in the tab strip via two layers: the active tab gets the existing orange selection-ring + edge highlights (unchanged behaviour); inactive opened tabs and any 1-hop neighbour stay fully opaque (no BFS dimming) and edges incident to any opened node lose the dim treatment. Achieved by extending `nodeClass` to accept a `visibleIds` set and `edgeClass` to accept `openedIds`; each of the seven graph views computes `visibleIds = adjacentToSet(openedIds, edges)` once per render via `$derived`. Wiring touches every entry point so `MouseEvent`/`KeyboardEvent` reaches `useOpen` unchanged: `g.node` click + Enter/Space in all seven views, `InsightsRail` row buttons, `ArtifactPanel.onNavigate` for in-body NodeRef chips, `NodeRef.onSelect` itself. Verified with `npm run check` (0 errors) and a browser smoke walk via chrome MCP — all PRD-032 acceptance criteria (AC-1..AC-5) pass with no console errors. EvidencePack EVID-037 records the run with structured fields (verdict supports / CL3 / evidence_type test) → R_eff = 1.0. Refs: PRD-032, EVID-037 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bump `.opened` box stroke-width from default 1px to 2.2px and label font-weight to 600 across Force/Tree/Radial/Lanes views; apply equivalent emphasis on Matrix row/col labels, Sankey bar stroke, and Sunburst arc in the remaining views (where `.opened` was newly added). Active tab keeps the orange selection-ring as the only orange cue; opened-but-not- active now reads as "deliberately surfaced" against unopened background without competing with active highlight. Refs: PRD-032 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Reworks the highlight gradient to pure opacity, per user feedback: keep the orange selection-ring (and orange `edge-active` stroke) as the only orange cue, reserved for the active tab + its incident edges; everything else differentiates by opacity alone. - `nodeClass(id, focusId, distances, openedIds, visibleIds)` now buckets in priority: opened (= openedIds) → `node-active` (opacity 1); 1-hop adjacent (= visibleIds) → `node-near`; otherwise the existing hover-distance fallback. - Drops the `.opened` stroke-width / font-weight overrides added in the previous commit so the only visual cue between "opened" and "background" is opacity. - Tightens focus-soft opacity gradient so opened-vs-adjacent is distinguishable: `node-near` 0.7 (was 0.92), `node-mid` 0.5 (0.75), `node-far` 0.38 (0.64), `node-outside` 0.28 (0.56), `edge-dim` 0.32 (0.62) — and the base `.edge-dim` opacity drops to 0.18 from 0.44 so dimmed edges fade further. - `.edge-active` no longer bumps stroke-width to 2 — only the colour changes to var(--accent), so opened-incident and active-incident edges read at the same visual weight, with orange the only diff. Refs: PRD-032 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…atrix In Sankey/Sunburst the active selection bumps `.bar`/`.arc` fill-opacity from the base 0.78 to 1 plus an accent stroke and glow, while opened (non-active) tabs got nothing → opened bars looked dimmer than active even though both were "opened". Match active's fill-opacity on `.opened` so the only visual diff is the orange stroke/glow that marks the active. Matrix gets a parallel cue: the row/col label underline now also paints on `.opened` (in `var(--fg)`), reserving accent-coloured underlining for `.selected`. Refs: PRD-032 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sankey's `.link` base stroke-opacity is 0.36 (intentional ambient mute); `.edge-active` overrides to 0.92. Opened-incident edges previously had no class → fell back to 0.36, so edges flowing into opened-but-not-active nodes (ADR-001/2/3 in the user's screenshot) read at less than half the visibility of edges incident to the active node. `edgeClass` now returns a dedicated `edge-on` class for opened-incident edges (instead of empty string). SankeyView styles `.link.edge-on` at the same 0.92 stroke-opacity as `edge-active` — matching brightness, no accent colour. Force/Tree/Radial/Lanes already render edges at default full opacity so no CSS change there; the new class is a no-op there unless a future view wants to differentiate. Refs: PRD-032 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Force/Tree/Radial/Lanes selection-ring + `.node.selected .box` filters were using `drop-shadow(0 0 8px currentColor)`. In light theme `currentColor` resolves to the inherited foreground (near-black) instead of the accent, producing a dark gray halo around the active node next to the orange ring. Pin the drop-shadow colour to `var(--accent)` so the glow is always primary across themes. Refs: PRD-032 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
## Summary - New `tabsStore` (`entities/artifact-tabs`) + `useOpen(event, id)` hook centralise the Shift+click-vs-plain-click policy across every artifact-opening surface. - New `TabBar` widget (`widgets/artifact-tabs`) renders the open tabs above `ArtifactPanel` with per-tab close, fall-back-to-first when closing the active tab, and `beforeunload` clear (no persistence). - Graph multi-highlight: active tab keeps its orange selection-ring + edge accent (unchanged); every opened tab and its 1-hop neighbours stay fully opaque, with their incident edges no longer dimmed. Implemented by extending `nodeClass` (now takes `visibleIds`) and `edgeClass` (now takes `openedIds`); applied uniformly to all seven graph views (Force, Tree, Radial, Lanes, Matrix, Sankey, Sunburst). Closes #116. ## Why Reviewers and maintainers walking a chain of linked artifacts (PRD → RFC → ADR → evidence) lose scroll position and re-fetch on every plain click. Tabs make the comparison stateless and the linked context visible in the graph at all times. ## Test plan - [x] `npm run check` — 0 errors, 0 warnings (svelte-kit sync + svelte-check, 1064 files). - [x] `npm run build` — `dist/` and `dist-nightly/` produced (~1.41M each, both stable + nightly images). - [x] Browser smoke (chrome MCP, dev server at 127.0.0.1:5174): - AC-1 Shift+click adds tab → strip grows, active stays, both nodes highlighted. - AC-2 Plain click replaces active → ADR-003* → ADR-004*; non-active tabs untouched. - AC-3 Close active falls back to first remaining; close inactive just drops it. - AC-4 Close last → panel hidden, all node highlights cleared. - AC-5 Reload → strip empty, panel hidden. - All entry points honour Shift: graph node click, keyboard Shift+Enter, rail row, in-panel NodeRef chip. - 4 tabs in Force view → 1 selection-ring (active only), 2 orange edges (active-incident), ~5 plain edges (other-opened-incident, no dim), 10 visible nodes (opened + 1-hop), 89 dimmed. - 0 console errors throughout the entire walk. ## Forgeplan PRD-032 active, EVID-037 active (`verdict supports / congruence_level 3 / evidence_type test`) → `R_eff = 1.0` for PRD-032. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
First minor release on top of 0.1.13. Highlights: - Multi-instance registry + /api/instances + HealthBar switcher (PRD-027 / RFC-023 / SPEC-003 / ADR-004). - `init --scope user|project` with workspace-agnostic user scope (PRD-025 / RFC-021 / ADR-004). - `bin/` adopts citty (^0.2.2) and splits into cli/commands/lib (ADR-003). - Feature-flag + image system; bundle approach is now the universal artifact form for every dist*/ image. `--experimental` becomes a deprecated alias for `--image nightly` (removed in 0.3.0). - Multi-tab artifact viewing, Combobox primitive in shared/ui, adapter-node precompress disabled (smaller tarball). Refs: PRD-025, PRD-027, PRD-030 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3 tasks
fedorovvvv
added a commit
that referenced
this pull request
May 9, 2026
## Summary Back-merges the v0.2.0 version bump commit (`2415393`) from `main` (PR #128) into `develop`. Step 10 of the release procedure (CLAUDE.md → Release procedure): > Back-merge to develop so the version bump and any release-only fixes don't get lost. The original `release/v0.2.0` branch was auto-deleted by GitHub on merge into `main`, so this PR reuses a `chore/back-merge-v0.2.0` branch with the same merge state. ## Why `release/v0.2.0` was merged into `main` via PR #128 with `--merge` (commit `ea21d01`). The version bump commit `2415393` is now only on `main`; without this back-merge, the next feature work on `develop` would start from `0.1.13` again. ## Test plan - [ ] Diff shows only `package.json` x2, lockfiles x2, and `CHANGELOG.md` (`+47 / -0` from the `[0.2.0]` entry). - [ ] After merge, `develop` HEAD reports `version: 0.2.0` in both `package.json` and `template/package.json`. - [ ] CI smoke matrix green on `develop` post-merge. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
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
First minor release on top of 0.1.13.
/api/instances+ HealthBar switcher (PRD-027 / RFC-023 / SPEC-003 / ADR-004)init --scope user|projectwith workspace-agnostic user scope (PRD-025 / RFC-021 / ADR-004)bin/adopts citty (^0.2.2) and splits intocli/,commands/,lib/(ADR-003)--experimentalis a deprecated alias for--image nightly(removed in 0.3.0) (PRD-030 / RFC-026 / ADR-005)Comboboxprimitive inshared/ui/,adapter-nodeprecompress disabledWhy
19 commits on
developsince 0.1.13, multiplefeatentries → MINOR bump per Conventional Commits + SemVer (guides/GIT-FLOW-GUIDE.ru.md§6).Refs: PRD-025, PRD-027, PRD-030Test plan
npm run buildsucceeds locally;dist/+dist-nightly/regenerated, both ≤ 3 MBnpm run smokeexercises bothstableandnightlyimagesnode bin/forgeplan-web.mjs init -yagainst a scratch dir copies the new imagenode bin/forgeplan-web.mjs --versionprints0.2.0Release procedure
After merge (per CLAUDE.md):
git checkout main && git pullgit tag -a v0.2.0 -m "release v0.2.0" && git push origin v0.2.0v0.2.0, targetmain→ Publish (firesrelease.yml→npm publish --provenance).git checkout develop && git merge --no-ff release/v0.2.0 && git push origin develop.🤖 Generated with Claude Code