Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
e762cb5
feat(idef0): Pillar C Phase 1 — camera-bus seam + Tier-0 chat (RFC-034)
explosivebit Jul 6, 2026
f7726f5
feat(idef0): Pillar C daemon + Tier-1 — live onboarding agent (RFC-03…
explosivebit Jul 6, 2026
02229ee
docs(idef0): activate Pillar C — RFC-034 + ADR-010 + EVID-096 (live a…
explosivebit Jul 6, 2026
9fadc9c
fix(idef0): Tier-1 chat — buffer WS sends issued before the socket opens
explosivebit Jul 6, 2026
6093923
feat(idef0): full assistant chat — markdown, sessions, scroll, our style
explosivebit Jul 6, 2026
2ea9337
fix(idef0): chat is AI-only (drop Tier-0) + daemon streams token-by-t…
explosivebit Jul 6, 2026
a3212f4
feat(idef0): chat Phase-4 — cancel/Stop, live-continue sessions, prob…
explosivebit Jul 6, 2026
b68cf95
fix(idef0): revert chat daemon probe to WebSocket — fetch /health is …
explosivebit Jul 6, 2026
2538a0e
docs(playground): showcase ScrollArea primitive (rule 24)
explosivebit Jul 6, 2026
e42040e
fix(idef0): stop onboard-agent from leaking a Claude Code subprocess …
explosivebit Jul 7, 2026
286ced8
feat(idef0): chat panel v2 — floatable/dockable/resizable window + Ch…
explosivebit Jul 7, 2026
1cb6edb
feat(idef0): chat Info tab live data (token usage + instance discover…
explosivebit Jul 7, 2026
18bf9f8
feat(idef0): populate chat Info tab on open — carry instance data on …
explosivebit Jul 7, 2026
60ae14a
feat(idef0): magic "✨ Ask" launcher in onboard header (off the map)
explosivebit Jul 7, 2026
2be797f
feat(idef0): chat launcher is a ✨ sparkle in the chips toolbar (extra…
explosivebit Jul 7, 2026
67ee436
fix(idef0): zone detail card — dwell delay + fixed bottom-left corner
explosivebit Jul 7, 2026
f0ffeb7
fix(idef0): close 9 adversarially-confirmed Pillar C / RFC-035 web bugs
explosivebit Jul 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
---
depth: standard
id: ADR-010
kind: adr
links:
- target: PRD-038
relation: based_on
- target: ADR-003
relation: informs
status: active
title: Agent SDK + onboarding daemon in a separate optional npm package launched by a spawn-only bin/ subcommand
---

## Context

Pillar C of the composed-map onboarding program (PRD-038) adds a **live local
onboarding agent**: the user asks questions in the web chat and a real Claude
Code session answers, driving the map camera. The fixed design (FD-1..FD-7): use
the user's LOCAL Claude Code via the **Claude Agent SDK**
(`@anthropic-ai/claude-agent-sdk`), running in a **localhost daemon-bridge** the
user launches (the SvelteKit server structurally cannot spawn `claude` — rule
22 keeps it a read-only mirror).

The trap (PRD-038 **Q1**): **ADR-003 / rule 23** pin `bin/` to a named
allow-list of exactly `node:*` + `citty`. The Agent SDK is a heavy third-party
dependency with a large transitive tree. It cannot enter `bin/` without a
decision, because `bin/` is what `npx @forgeplan/web` runs **before the user has
installed anything** — the whole point of ADR-003 is that no third-party
resolution happens at `npx` time.

## Decision

**Selected**: Ship the Agent SDK + onboarding daemon as a **separate, optional
npm package** (working name `@forgeplan/web-agent`), launched by a **spawn-only**
`bin/` subcommand.

`bin/forgeplan-web.mjs` gains an `onboard-agent` subcommand that does exactly one
new thing: `child_process.spawn` the separate package's binary (resolved from the
user's environment / `npx @forgeplan/web-agent`) and stream its output. **`bin/`
imports nothing from the agent package** — no `import`, no `require`, only a
`spawn` of an external process. ADR-003's allow-list (`node:*` + `citty` +
relative siblings) is therefore **untouched**: the core `@forgeplan/web` stays
lean and `npx`-fast for the 99% of users who only view the map; the agent is
opt-in and its heavy dependency tree is resolved **only** when a user
deliberately runs the agent.

**Why Selected**: it is the only option that keeps ADR-003's `npx`-latency
guarantee intact while still letting Pillar C "use what already exists" (the
Agent SDK). The spawn-only boundary is the same trust seam rule 22 uses for the
`forgeplan` CLI — a process boundary, not an import.

## Alternatives Considered

| Option | Verdict | Why |
|--------|---------|-----|
| **A — separate optional npm package + spawn-only `bin/` subcommand** | **Chosen** | Core stays lean + `npx`-fast (ADR-003 intact); SDK resolved only on deliberate agent use; process boundary mirrors rule 22's `forgeplan`/`git` spawn seam. |
| B — bundled `dist-agent/` image (esbuild-inline, PRD-030/ADR-005 shape) | Rejected | The image discipline (PRD-030) is for **viewer variants** copied by `init`; it would bloat every install with the SDK + its transitive tree even for users who never run the agent, and an esbuild single-file bundle of the Agent SDK (which itself spawns `claude`) is fragile. |
| C — extend the ADR-003 allow-list to admit the SDK into `bin/` | Rejected | Reintroduces the exact `npx`-time third-party resolution ADR-003 removed — every `init`/`start`/`update` would pay to resolve the SDK before doing its job, for a feature most users never touch. Directly violates ADR-003's invariant I3. |

## Consequences

### Positive
- Core `@forgeplan/web` unchanged in weight + `npx` latency; ADR-003 / rule 23
hold verbatim (verified by the existing bin allow-list grep).
- The agent is strictly **opt-in**: no SDK, no `claude`, no API key for the
view-only user.
- Security is a natural consequence of the process boundary: the daemon is a
separate, user-launched, 127.0.0.1-bound process with a read-only agent
profile — the web never gains a code-execution surface.

### Negative (trade-offs)
- A **second package** to publish + version (`@forgeplan/web-agent`), plus a
documented spawn contract between the `onboard-agent` subcommand and that
package's binary.
- The user must install / `npx` the agent package on first use (mitigated by a
guided prompt from the `onboard-agent` subcommand when the package is absent).

### Risks
- **Version skew** between `@forgeplan/web` and `@forgeplan/web-agent` (mitigate:
the daemon advertises a protocol version in its WebSocket probe; the web
tolerates a missing/older daemon by staying in chat **Tier 0**).
- The spawn-only subcommand must **validate the agent package's presence** and
fail with an actionable install hint, never a raw ENOENT.

## Invariants

- `bin/` imports only `node:*`, `citty`, and relative `bin/` siblings — the
`onboard-agent` subcommand adds **only** a `child_process.spawn`, never an
`import`/`require` of the agent package (rule 23 grep must still pass).
- The daemon binds **127.0.0.1 only** and is launched explicitly by the user.
- The agent runs a **read-only** profile: Read/Glob/Grep + read-only forgeplan
MCP; no Write/Edit/Bash.
- The SvelteKit server (`/api/*`) is never involved in the agent path — the
browser talks to the daemon directly (rule 22 intact).

## Evidence Requirements

- A spawn smoke: `bin onboard-agent` spawns the agent package binary (or emits
the install hint when absent) — exit-code asserted.
- Rule-23 verification grep over `bin/` still reports OK (no new bare-specifier
imports).
- The agent package's SDK options object denies Write/Edit/Bash and binds
localhost only (asserted in the agent package's own tests).

## Related Artifacts

| Artifact | Type | Relation |
|----------|------|----------|
| PRD-038 | PRD | based_on (Pillar C, Q1) |
| ADR-003 | ADR | informs (the bin/ allow-list this preserves) |
| RFC (Pillar C daemon, pending) | RFC | based_on (the RFC that presumes this packaging) |

Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
---
depth: standard
id: EVID-096
kind: evidence
last_modified_at: 2026-07-06T15:34:34.875409+00:00
last_modified_by: claude-code/2.1.201
links:
- target: RFC-034
relation: informs
status: active
title: 'Pillar C live agent works end-to-end: daemon + local CC answered grounded + drove show_on_map camera; 153 tests, smoke PASS'
---

## Status

draft

## Summary

Prove-phase checkpoint for **RFC-034** (Pillar C, the live onboarding agent) and its packaging
**ADR-010**. The live agent works **end-to-end**: the localhost daemon boots, a **real local Claude
Code session** (via the Agent SDK) answered a project question **grounded in the actual repo**, and
the model **called the `show_on_map` tool** which relayed a camera frame to the client. Plus 153
web-side tests, `svelte-check` 0, and the daemon smoke — all green.

## Observations (measured, 2026-07-06)

### Live end-to-end turn (the headline — a real model turn, not a mock)

Spawned the daemon: `node agent/bin/agent.mjs --cwd <repo> --port 7461` → printed
`onboard-agent live on ws://127.0.0.1:7461`. A WebSocket client connected, received
`{type:"ready", protocolVersion:1, model:"forgeplan-web-agent (claude-agent-sdk)"}`, and sent
*"What is this project and what is it for? … then use show_on_map to point at the most important
zone."* The daemon's persistent `query()` session streamed a **grounded** answer (verbatim):

> "**@forgeplan/web** is a tiny zero-install npm CLI that scaffolds a pre-built SvelteKit app into a
> project's `.forgeplan-web/` folder, then serves a read-only, force-directed map of that project's
> Forgeplan artifacts… run `npx @forgeplan/web start` and *see* a project's decisions and structure
> as an interactive graph — no install, no write access to the workspace."

— then emitted `{type:"show_on_map", target:{kind:"zone", id:"z.surfaces"}}`, then continued
narrating the zone flow (z.surfaces → z.core → z.ui, z.decisions records why) and offered to walk
deeper. Frames observed: `ready` → `token`* (streamed) → `show_on_map` → `token`* → `done`. The
answer is factually correct and sourced from the real repo — the read-only agent read the project.

### Automated + smoke (measured)

- `npx vitest run src/widgets/map-chat src/widgets/composed-map` → **14 files / 153 tests PASS**
(Tier-0 tier0/chat-store/MapChat + Tier-1 agent-client/chat-store + camera-bus + tour + drill).
- `npx svelte-check` → **0 errors** (2 pre-existing a11y warnings on the map `<svg>`).
- `node agent/scripts/smoke.mjs` → **exit 0, ALL CHECKS PASS**: protocol round-trip; `buildOptions`
denies Write/Edit/Bash + allows `mcp__onboard__show_on_map`; message-queue generator shape; daemon
binds 127.0.0.1, `GET /health` responds, WS sends `{ready}`.
- Rule-23 allow-list grep over `bin/` → **OK** for all `.mjs` (the `onboard-agent` subcommand is
spawn-only; no import of `@forgeplan/web-agent`; root `package.json` untouched).

### Invariants confirmed

- **ADR-010**: the Agent SDK + daemon live in the SEPARATE `@forgeplan/web-agent` package (its own
deps `@anthropic-ai/claude-agent-sdk` ^0.3, `ws`, `zod`; own `node_modules`, gitignored). Core
`bin/` unchanged in weight; the subcommand only `child_process.spawn`s the package.
- **Rule 22**: the live path is browser↔daemon over `ws://127.0.0.1`; the SvelteKit `/api/*` server
is never involved.
- **Read-only**: `allowedTools` = Read/Glob/Grep + `show_on_map`; `disallowedTools` =
Write/Edit/Bash; the model cannot mutate the workspace.

## Known scope boundary

- The daemon's `--port` default is 7431; the live turn above used 7461 (explicit). Port discovery
(fixed-port probe vs a discovery file) is RFC-034 **OQ1**.
- `cancel` (abort mid-stream) and socket-closed-mid-tool-call are marked `TODO` (Phase-4 hardening),
not blockers.
- `show_on_map(node)` precision depends on marketplace **CM-02** (stable node ids); `zone`/`flow`
targeting (demonstrated) is exact.
- `@forgeplan/web-agent` is not yet published to npm — the subcommand's `npx` fallback is verified
against the registry 404 only; re-verify post-publish.

## Structured Fields

verdict: supports
congruence_level: 3
evidence_type: test

## Related Artifacts

- **RFC-034** (`informs`) — the daemon/protocol/camera/chat architecture this proves; activation
gated on this checkpoint (rule 11, R_eff > 0).
- **ADR-010** — the packaging decision (separate optional package + spawn-only subcommand) this
build realises and confirms.
- **RFC-033** — the tour camera the agent drives via the `camera-bus` seam (Phase 1).
- **PRD-038** — parent PRD (Pillar C, FD-1..FD-7).
- **`docs/MAP-PACK-FINDINGS-FOR-MARKETPLACE.md`** — CM-02 (stable node ids) for precise
`show_on_map(node)`.


Loading
Loading