Skip to content

feat(agent): navigate tool + working folder (off-scene authoring) - #262

Merged
winlp4ever merged 2 commits into
mainfrom
feat/agent-navigate-working-folder
Aug 29, 2026
Merged

feat(agent): navigate tool + working folder (off-scene authoring)#262
winlp4ever merged 2 commits into
mainfrom
feat/agent-navigate-working-folder

Conversation

@winlp4ever

Copy link
Copy Markdown
Contributor

What

S8 of the board-authoring plan: gives the agent a working folder — a mutable cursor, like a shell's cwd — decoupled from the user's on-screen layer. The agent can navigate into a folder and author there without moving the user's view, using the S7 headless sync intake.

Design

  • ctx.rootId becomes the mutable working folder (mutated mid-turn by navigate; ctx is a stable object across tool calls, so later calls see it). Added ctx.sceneRootId = the layer the visible store projects (the user's view, fixed for the turn).
  • mutatorFor(ctx) routes per call: working folder == scene layer → StoreMutator (renders + syncs, unchanged); ≠ → HeadlessMutator (off-scene).
  • HeadlessMutator writes into a non-visible layer by reusing StoreMutator verbatim against a throwaway off-scene createCanvasStore seeded (from the whole-board oplog) with the target layer's content. Its local change batches are forwarded to the sync-correct intake: persistence.record + getBoardSyncRef().submitLocalBatch(batch, { scene: false }). Nothing renders; the store computes node.update.prev correctly, so no op is hand-built. clientId + id scheme match the scene store so batches/ids stay board-valid.
  • navigate(target) tool (folder_id | "root" | "up"): resolves the destination, switches the working folder + write routing, and returns the folder's current contents (its ls). Listed in the agent prompt.
  • link_notes resolves both endpoints in the working folder (the off-scene layer when headless), not just the visible store.
  • Off-scene creates are excluded from the post-turn scene arrange/recenter (they aren't in the visible store).

Why reuse StoreMutator via an off-scene store rather than hand-build ops: node.add/edge.add are trivial, but node.update needs a correct prev slice that the store computes automatically — wrapping the store gets rewrite/patch parity and correct prev for free.

Scope

In scope: navigate + headless create/link/rewrite/patch into the working folder. Out of scope (→ S9): create_folder + per-call parent_id overrides. Deferred: mid-turn re-scoping of the search index (navigate's return is the read mechanism); an on-finish "worked in Folder X" receipt card.

Test plan

  • HeadlessMutator tests: a create lands in the target layer off-scene (not in the visible store), is recorded to the whole-board oplog with the right parentId, and enters the sync intake scene:false; sessions accumulate; createLink joins two off-scene notes; a fresh session seeds from the oplog so it can rewrite a note already in that folder.
  • navigate tests: entering a folder sets the working folder + routes writes off-scene; "root" restores visible writes; "up" walks to the parent; a non-folder / unknown target errors without changing the working folder.
  • Full agent suite (554 tests) + npm run check-all pass.

Give the agent a mutable working folder — a cursor like a shell's cwd,
decoupled from the user's on-screen layer — so it can author into a subfolder
without moving the user's view.

- ctx.rootId becomes the mutable working folder; add ctx.sceneRootId (the
  visible layer, fixed). mutatorFor routes by rootId vs sceneRootId.
- HeadlessMutator: writes into an off-scene layer by reusing StoreMutator
  against a throwaway store seeded with the target layer's content, forwarding
  its change batches to the S7 sync intake (record + submitLocalBatch scene:false).
  Nothing renders; the store computes node.update prev correctly.
- navigate(target) tool: folder_id | 'root' | 'up' — sets the working folder,
  returns the folder's contents (its ls), and switches write routing. Listed in
  the agent prompt.
- link_notes resolves endpoints in the working folder; off-scene creates are
  excluded from the post-turn scene arrange/recenter.
Code review found the write path was working-folder aware but get_note,
edit_note, write_note's rewrite check, and arrange_notes still read the visible
ctx.store — so after navigating off-scene the agent couldn't read/edit/rewrite
notes it just authored, and arrange_notes rearranged the USER's on-screen layer
(breaking navigate's 'don't move the view' promise).

- Add workingLayerStore(ctx): the off-scene layer store when navigated away,
  else the visible store. Route get_note / edit_note / write_note existence /
  arrange_notes through it.
- HeadlessMutator.ensure() flushes persistence before re-seeding, so re-entering
  a folder in the same turn reflects this turn's own writes (fixes overlap +
  broken links on re-entry). Expose layerStore().
- Tests: off-scene read/edit/rewrite/arrange act on the working folder and never
  move the visible layer; re-entry sees prior writes.
@winlp4ever

Copy link
Copy Markdown
Contributor Author

Review response (code-review high)

All 5 findings were valid and shared one root cause — the WRITE path was working-folder aware, but get_note / edit_note / write_note's rewrite check / arrange_notes still read the visible ctx.store. Fixed in 50480e6:

Added regression tests: off-scene read/edit/rewrite/arrange act on the working folder and never move the visible layer; re-entry sees prior writes. Prompt line broadened to say every note tool operates inside the working folder. check-all + full suites green.

@winlp4ever
winlp4ever merged commit cc4a31a into main Aug 29, 2026
5 checks passed
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.

1 participant