## eneskirca/nodeterm — v0.3.17…v0.4.0

_474+ commits._

### Features
- **feat(control): read-only `issues` and `prs` verbs — the board's GitHub lane for agents** (0d26bec)
- **feat(control): orchestration state survives an app restart** (f8139a7)
- **feat(kanban): board dispatch — a card you move into the dispatch column starts its own agent run** (9262bdb)
- **feat(server): the canvas authority governs shared projects on a hosted core** (8969f15)
- **feat(core): canvas authority, the single writer of shared projects' content** (753b614)
- **feat(core): workspace store seams for a content authority** (2cc3f2e)
- **feat(updates): Figma-style Update button in the title bar** (a8ad076)
- **feat(kanban): boards sync live, item by item, from the one store funnel** (7520f70)
- **feat(kanban): item-level board ops on canvas:mut, with deterministic default columns** (f86c0de)
- **feat(control): --after-success waits for a station to REPORT success, not just end its turn** (c13a474)
- **feat(file-links): detect Unicode, bracketed, spaced and bare paths and file:// URIs** (59b4be7)
- **feat(file-links): hover shows the resolved path; Shift+Cmd/Ctrl+click opens with the OS app** (4275eff)
- **feat(agents): Claude subagent cards from its own SubagentStart/SubagentStop hooks** (c9cffe1)
- **feat(team): sync canvas EDGES — a peer's link is no longer deleted by your next save** (e9f1452)
- **feat(kanban): make Omni a scope of the kanban side, not a third view** (b35ab5d)
- **feat(control): a retried open does not open a second node (--request-id)** (1dc5b3d)

### Fixes
- **fix(control): issues/prs review round — sanitize ids, honour unapproved mappings** (7485b74)
- **perf(ssh): on-screen terminals attach first on a project switch** (d32d93b)
- **Merge pull request #1048 from eneskirca/fix/minimapdock-tests** (aba3900)
- **fix(canvas-order): release a remove the capped cast record forgets; a forgotten echo matches nothing** (f57a832)
- **fix(canvas-authority): keep a governed project's node order parent-first** (e7a735f)
- **fix(canvas-sync): cast every own store write into an off-screen governed project (N2, extended)** (32142b0)
- **fix(canvas-authority): send an un-diffable outside edit whole, one log per failure streak (N5, N7, N6, D9)** (8a6dea7)
- **fix(kanban): one unsafe-display set, column colour repair, tolerant label toggle, dead-card diff (D7, D12)** (b80b0fa)
- **fix(kanban-sync): the rendered project's live set is React Flow's, Omni board included (N4)** (1b08cae)
- **fix(server): no browser save lands un-overlaid while the authority stops (N3)** (0103e27)
- **fix(kanban-ops): three or more concurrent board adds converge in every interleaving (D5)** (4888a27)
- **fix(canvas-order): epoch-gated edge mirrors, FIFO gate release, bounded restart degrade (D4)** (f6f4275)
- **fix(canvas-sync): rebuild removes and edge ops from their known fields, and cap removes (D1)** (18e00e9)
- **fix(canvas-authority): publish the outside-edit diff untrusted (N1)** (a78d2d8)
- **fix(server-canvas-control): deliver only when the write and the claim both landed (D3)** (85ebeb9)
- **fix(canvas-sync): cast own writes into an off-screen governed project (N2)** (db14edc)
- **fix(canvas-authority): send a governed outside edit's persisted project on workspace:server-change (D2, R15)** (2fa3579)
- **Merge pull request #1052 from eneskirca/fix/after-stale-done** (83723b9)
- **fix(durable-state): review of #1054 — sender told at boot, byte budgets, no stale overwrite** (9212315)
- **Merge origin/main (#1053 interrupt hold, #1051 board dispatch) into fix/after-stale-done** (0172637)
- **Merge origin/fix/after-stale-done (#1052 rework)** (6826030)
- **Merge pull request #1053 from eneskirca/fix/esc-cancel-status** (9df1217)
- **fix(control): review of #1052 — only background subagents hold, and five hold/release edges** (bac9bef)
- **fix(agents): only count an interrupt marker whose turn was opened before it** (061e1f5)
- **fix(durable-state): stat and read the same file descriptor** (323dc42)
- **fix(board-dispatch): review round — bound consent, re-checked queue, no armed leftovers** (2acf6be)
- **fix(agents): end an interrupted Claude turn from the transcript marker** (14c19b4)
- **fix(control): a turn that ends with background tasks running does not release --after** (e20bb97)
- **fix(server): a canvas-control verb never writes back the copy it loaded before a launch** (673ab0f)
- **fix(control): plain --after no longer fires on a done from before new work was handed over** (4d79315)
- **perf(canvas): bound the unread glow, minimap beats and waiting-rope flow** (fe82993)
- **Merge pull request #1047 from eneskirca/fix/zoom-out-raster-scale** (ce621e2)
- **fix(canvas): an all-filtered node change batch no longer re-renders the canvas** (99b65c3)
- **fix(canvas): never promote the viewport during a camera move** (d5102a0)
- **fix(core): a content write keeps this machine's exec values in the index cache** (ccedca0)
- **fix(kanban): the Omni board never deletes a peer's card; a fresh node goes before its card** (0c65bc1)
- **Merge pull request #1045 from eneskirca/fix/relay-host-scope-tmp-leak** (0264eee)
- **Merge pull request #1037 from eneskirca/fix/relay-scope-boundary** (c8f1439)
- **Merge pull request #1044 from eneskirca/fix/test-tmp-leaks** (e9feb5b)
- **fix(kanban): tolerant board reads, order ops that converge, project-scoped order keys** (785ac6f)
- **Merge pull request #1040 from eneskirca/fix/relay-guest-no-ssh-dial** (1189305)
- **Merge pull request #1041 from eneskirca/fix/omni-kanban-review** (cd10aff)
- **Merge pull request #1043 from eneskirca/fix/canvas-sync-tmp-leak** (b8a0685)
- **Merge remote-tracking branch 'origin/main' into fix/relay-guest-no-ssh-dial** (d2f588f)
- **Merge pull request #1038 from eneskirca/fix/relay-pendinglaunch-local** (5266932)
- **Merge pull request #1039 from eneskirca/fix/fake-platform-tmp-leak** (56ffef7)
- **fix(control): a station's report ends when new work REACHES its pane, not when the send answers** (c245dbe)
- **Merge pull request #992 from ezwep/fix/986-crisp-low-dpi** (42ba1a8)
- **Merge pull request #1036 from eneskirca/fix/relay-revoke-unpin** (163b96b)
- **Merge pull request #1035 from eneskirca/fix/relay-pty-id-namespace** (432496a)
- **Merge pull request #1026 from eneskirca/fix/bulk-account-move** (218d4fb)
- **fix(relay): a relay tab never makes the guest dial SSH** (6012b98)
- **fix(relay): strip exec fields from project documents sent to non-editor peers** (077c747)
- **fix(security): make pendingLaunch a machine-local exec field** (2d3e54c)
- **fix(relay): make a project-scoped relay session an actual boundary** (b8804bb)
- **fix(relay): removing a device revokes its relay trust; split pin stores by role** (bc90bce)
- **Merge pull request #1028 from eneskirca/fix/ssh-agent-docs-freshness** (10adf67)
- **fix(relay): namespace relay pty output and gate it to sessions the tab opened** (0ebdb31)
- **Merge origin/main into fix/ssh-agent-docs-freshness** (08da81d)
- **Merge pull request #1033 from eneskirca/fix/request-id-followups-2** (5fcb974)
- **fix(team): a peer op in the switch window cannot mix two projects' nodes** (7230247)
- **fix(ssh): review fixes for the agent-tools freshness check** (00344af)
- **fix(control): announce the per-run id before posting; guard finishAnswer** (aa8c289)
- **fix(team): a reconnect keeps our causal position (reset no longer zeroes lastSeq)** (01dfced)
- **fix(team): a re-creation waits for our own pending remove of the same id** (c4485fd)
- **fix(control): name the request id on a timeout; finish late answers too** (3a120d5)
- **Merge pull request #1030 from eneskirca/fix/file-link-unverifiable** (635a768)
- **fix(agents): Claude subagent labels never shift to the next child** (b2084d5)
- **fix(team): an adopt keeps owed only what the adopted scene still holds** (bec0b3e)
- **fix(team): an adopt keeps a refused node and its held edges owed** (abc5898)
- **fix(team): edge sync port fixes — eager thunk, synchronous edge refs, one id one edge, held edges** (e618211)
- **fix(file-links): "could not check" is never "file not found"** (625136e)
- **fix(team): a delete beats a concurrent drag frame — causally, not by a tiebreak** (9a52d24)
- **fix(ssh): keep an SSH host's agent shims, skills and instruction blocks current** (7700f58)
- **fix(kanban): Omni lane reads the live canvas, reports its modal, respects the lane's session** (988f9bd)
- **fix(accounts): move an account's sessions all at once, and survive a project switch** (8cb8755)
- **Merge pull request #1024 from eneskirca/fix/file-link-live-cwd** (f85d476)
- **Merge pull request #1025 from eneskirca/fix/mirror-seed-test-boundary** (5933752)

### Backend
- **Merge pull request #1058 from eneskirca/feat/control-issues-prs-verbs** (f9e0539)
- **Merge pull request #1049 from eneskirca/test/pendinglaunch-trust-behaviour** (91216d7)
- **Merge origin/main into test/pendinglaunch-trust-behaviour** (bdbde42)
- **Merge pull request #1057 from eneskirca/perf/ssh-smooth** (5b3434b)
- **Merge pull request #1050 from eneskirca/perf/bounded-status-animations** (436f878)
- **Merge pull request #1056 from eneskirca/feat/shared-canvas-authority** (2284edd)
- **Merge origin/main into feat/shared-canvas-authority** (29d04e1)
- **Merge remote-tracking branch 'origin/main' into feat/shared-canvas-authority** (09caa99)
- **Merge pull request #1054 from eneskirca/feat/durable-orchestration-state** (eacab17)
- **Merge remote-tracking branch 'origin/main' into feat/durable-orchestration-state** (161987b)
- **Merge pull request #1051 from eneskirca/feat/board-dispatch** (ff2a56f)
- **Merge pull request #1046 from eneskirca/feat/titlebar-update-button** (d94eddd)
- **Merge remote-tracking branch 'origin/main' into HEAD** (ac06c72)
- **Merge pull request #1042 from eneskirca/feat/after-outcome** (6c937d2)
- **Merge remote-tracking branch 'origin/main' into HEAD** (c850816)
- **Merge remote-tracking branch 'origin/main' into feat/after-outcome** (9ca77ca)
- **Merge pull request #1034 from eneskirca/feat/after-outcome** (2e0ae7b)
- **Merge origin/main (#1029-#1033) into feat/after-outcome** (3b0324b)
- **Merge pull request #1031 from eneskirca/feat/file-link-tokens** (51afd28)
- **Merge pull request #1029 from eneskirca/feat/file-link-hover-open** (6caa62b)
- **Merge pull request #1032 from eneskirca/feat/claude-native-subagent-hooks** (306699e)
- **Merge remote-tracking branch 'origin/main' into feat/claude-native-subagent-hooks** (5d33527)
- **Merge pull request #1027 from eneskirca/feat/control-idempotent-spawn** (00ce200)
- **Merge pull request #1022 from eneskirca/feat/board-comment-to-agent** (9aeab4e)
- **Merge remote-tracking branch 'origin/main' into feat/board-comment-to-agent** (b628566)
- **Merge pull request #1017 from eneskirca/feat/orchestrator-failure-notice** (bc8cbdb)
- **Merge remote-tracking branch 'origin/main' into feat/board-comment-to-agent** (6c7ce48)
- **Merge origin/main (#1014, #1019) into feat/orchestrator-failure-notice** (0d04892)

### Tests
- **test(canvas-sync): behaviour-test Canvas's applyToStored (D12)** (26477e3)
- **test(hosted-e2e): no wall-clock flush claims, and a parsed readiness gate (D6, R14)** (08cbf45)
- **test(server): shared canvas edits persist on a headless hosted core** (cfb4f2b)
- **test(tmp-sandbox): allow Chromium's scoped_dir scratch dirs from the real-Electron test** (34f0a0a)
- **test(pendingLaunch): behavioural tests for the owner/relay trust paths** (0523776)
- **test: give jsdom suites jsdom's localStorage on Node 25+** (f67ddf7)
- **test(relay-host): the scoped-guest root is a testTmpDir, not a bare mkdtemp** (1713055)
- **test(owner-client): follow #1040's sanitizeRelayMutation in the relay-origin pin** (54ed00a)
- **test: canvas-sync.pending-launch leaves no temp dir behind** (7682dd9)
- **test: stop the suite leaving temp directories behind** (c34ebc8)
- **test(relay): pin that held pendingLaunch text never reaches non-editor peers** (6d73126)
- **test: convergence suite's userDataDir under the run root; docs** (d3cf2a2)
- **test: canvas-sync's userDataDir lives under the run root** (3ef9550)
- **test: fake-platform root teardown never throws, tears down last** (8acbf84)
- **test: fakePlatform's temp dirs no longer outlive the run** (e3e7ad3)
- **test: stop leaking temp directories into /tmp** (380d6c3)
- **test(team): pin the REMOVED_MAX eviction of the rule-4 tombstones** (a627739)
- **test(canvas): strip comments before matching instead of a nested-quantifier regex** (8927bd9)
- **test(mirror): pin the clock in the seeded-entry age test** (d5fb5c9)

### Docs
- **docs: the stored-write publish hook in CONTRIBUTING, load-time repairs in the CLAUDE.md limits list** (6659dd9)
- **docs(canvas-sync): residual (2) is almost any visit, and the seen-drop is not protection (D13, D10)** (4462734)
- **docs(workspace-store): the save overlay fails closed; a content write does not merge an unprocessed outside edit (D8)** (b2d29a5)
- **docs: task 11 fix round 1, the clamp changes no verdict, and wording fixes** (4e15635)
- **docs(canvas-sync): the seen clamp is hygiene, and name where the relay mirror lives** (78df8c1)
- **docs: shared canvas authority, edge and board sync** (556e951)

### Chore
- **chore(release): v0.4.0** (f9bbdf8)
- **style: move idle() off save()'s JSDoc, and the gate comment onto the gate tests** (f0aa368)
- **refactor(canvas): one reducer for node, edge and board ops** (990b0d3)

_Recap by [Repo Wrapped](https://repowrapped.com/gh/eneskirca/nodeterm?utm_source=github-action)._