# Oh My Pi Workbook

State\, memory\, tangent subagents\, permissions\, and extension design

A complete practical guide to OMP sessions\, memory\, tangent subagents\, tool permissions\, and extensions\, with fictional labs\, intact examples\, and explicit evidence boundaries\.

## Contents

### Start here

A complete practical guide to OMP sessions\, memory\, tangent subagents\, tool permissions\, and extensions\, with fictional labs\, intact examples\, and explicit evidence boundaries\.

- [Start here](<https://present-sketch-tp94.here.now/chapters/unified-start-here>)

### Sessions\, resets\, and reviewable history

Choose the intended conversation\, change only the intended session boundary\, and inspect the full representation before considering disclosure\.

- [Sessions\, resets\, and reviewable history](<https://present-sketch-tp94.here.now/chapters/unified-continuity>)
- [Orientation](<https://present-sketch-tp94.here.now/chapters/continuity-orientation>)
- [Start here](<https://present-sketch-tp94.here.now/chapters/continuity-start-here>)
- [The continuity ledger](<https://present-sketch-tp94.here.now/chapters/continuity-the-continuity-ledger>)
- [Return Desk lab setup](<https://present-sketch-tp94.here.now/chapters/continuity-return-desk-lab-setup>)
- [Return Desk resume selection](<https://present-sketch-tp94.here.now/chapters/continuity-return-desk-resume-selection>)
- [Return Desk continuation](<https://present-sketch-tp94.here.now/chapters/continuity-return-desk-continuation>)
- [Return Desk missing\-directory decisions](<https://present-sketch-tp94.here.now/chapters/continuity-return-desk-missing-directory-decisions>)
- [Decision Desk forking](<https://present-sketch-tp94.here.now/chapters/continuity-decision-desk-forking>)
- [Decision Desk resetting deliberately](<https://present-sketch-tp94.here.now/chapters/continuity-decision-desk-resetting-deliberately>)
- [Decision Desk refusals and failures](<https://present-sketch-tp94.here.now/chapters/continuity-decision-desk-refusals-and-failures>)
- [Review Packet choosing a snapshot](<https://present-sketch-tp94.here.now/chapters/continuity-review-packet-choosing-a-snapshot>)
- [Review Packet local HTML export](<https://present-sketch-tp94.here.now/chapters/continuity-review-packet-local-html-export>)
- [Review Packet reading the whole packet](<https://present-sketch-tp94.here.now/chapters/continuity-review-packet-reading-the-whole-packet>)
- [Sharing is a separate disclosure decision](<https://present-sketch-tp94.here.now/chapters/continuity-sharing-is-a-separate-disclosure-decision>)
- [Operation decision matrix](<https://present-sketch-tp94.here.now/chapters/continuity-operation-decision-matrix>)
- [Recovery and safe disclosure checklists](<https://present-sketch-tp94.here.now/chapters/continuity-recovery-and-safe-disclosure-checklists>)
- [Evidence and limitations](<https://present-sketch-tp94.here.now/chapters/continuity-evidence-and-limitations>)
- [Sessions\, resets\, and reviewable history\: next steps](<https://present-sketch-tp94.here.now/chapters/unified-continuity-next>)

### Memory and reusable knowledge

Distinguish current context from retained evidence and reusable procedures\, then inspect scope and outcomes instead of trusting memory acknowledgements\.

- [Memory and reusable knowledge](<https://present-sketch-tp94.here.now/chapters/unified-memory>)
- [Orientation](<https://present-sketch-tp94.here.now/chapters/memory-orientation>)
- [Start with a read\, not a setting change](<https://present-sketch-tp94.here.now/chapters/memory-start-with-a-read-not-a-setting-change>)
- [Four places knowledge can live](<https://present-sketch-tp94.here.now/chapters/memory-four-places-knowledge-can-live>)
- [The configuration this workbook teaches](<https://present-sketch-tp94.here.now/chapters/memory-the-configuration-this-workbook-teaches>)
- [Five fictional lab stories](<https://present-sketch-tp94.here.now/chapters/memory-five-fictional-lab-stories>)
- [The automatic lifecycle](<https://present-sketch-tp94.here.now/chapters/memory-the-automatic-lifecycle>)
- [The command desk](<https://present-sketch-tp94.here.now/chapters/memory-the-command-desk>)
- [Privacy and costs](<https://present-sketch-tp94.here.now/chapters/memory-privacy-and-costs>)
- [Troubleshooting](<https://present-sketch-tp94.here.now/chapters/memory-troubleshooting>)
- [Your checkpoint](<https://present-sketch-tp94.here.now/chapters/memory-your-checkpoint>)
- [Browser agent interface](<https://present-sketch-tp94.here.now/chapters/memory-browser-agent-interface>)
- [Sources\, method and limitations](<https://present-sketch-tp94.here.now/chapters/memory-sources-method-and-limitations>)
- [Memory and reusable knowledge\: next steps](<https://present-sketch-tp94.here.now/chapters/unified-memory-next>)

### Tangent work and live control

Find the exact tangent\, address its current conversation\, and separate view changes\, turn interruption\, job cancellation\, revival\, and terminal intent\.

- [Tangent work and live control](<https://present-sketch-tp94.here.now/chapters/unified-tan>)
- [Orientation](<https://present-sketch-tp94.here.now/chapters/tan-orientation>)
- [A tan is already running](<https://present-sketch-tp94.here.now/chapters/tan-a-tan-is-already-running>)
- [1\. Understand the fork](<https://present-sketch-tp94.here.now/chapters/tan-1-understand-the-fork>)
- [2\. Start from Main](<https://present-sketch-tp94.here.now/chapters/tan-2-start-from-main>)
- [3\. Find and focus the right tan](<https://present-sketch-tp94.here.now/chapters/tan-3-find-and-focus-the-right-tan>)
- [4\. Steer and queue work](<https://present-sketch-tp94.here.now/chapters/tan-4-steer-and-queue-work>)
- [5\. Leave and switch safely](<https://present-sketch-tp94.here.now/chapters/tan-5-leave-and-switch-safely>)
- [6\. Observe jobs and read results](<https://present-sketch-tp94.here.now/chapters/tan-6-observe-jobs-and-read-results>)
- [7\. Continue a finished tan](<https://present-sketch-tp94.here.now/chapters/tan-7-continue-a-finished-tan>)
- [8\. Interrupt\, cancel\, or kill](<https://present-sketch-tp94.here.now/chapters/tan-8-interrupt-cancel-or-kill>)
- [9\. Coordinate several tans](<https://present-sketch-tp94.here.now/chapters/tan-9-coordinate-several-tans>)
- [10\. Protect context and recover](<https://present-sketch-tp94.here.now/chapters/tan-10-protect-context-and-recover>)
- [11\. Operate through Main](<https://present-sketch-tp94.here.now/chapters/tan-11-operate-through-main>)
- [12\. State and control reference](<https://present-sketch-tp94.here.now/chapters/tan-12-state-and-control-reference>)
- [Evidence and method](<https://present-sketch-tp94.here.now/chapters/tan-evidence-and-method>)
- [Tangent work and live control\: next steps](<https://present-sketch-tp94.here.now/chapters/unified-tan-next>)

### Tool permissions and approvals

Explain why a particular tool call is allowed\, prompted\, or denied by following its operation\, tier\, policy identity\, host capabilities\, and actual effect boundary\.

- [Tool permissions and approvals](<https://present-sketch-tp94.here.now/chapters/unified-permissions>)
- [Orientation](<https://present-sketch-tp94.here.now/chapters/permissions-orientation>)
- [Start with the operation](<https://present-sketch-tp94.here.now/chapters/permissions-start-with-the-operation>)
- [Read\, write\, and exec are tiers](<https://present-sketch-tp94.here.now/chapters/permissions-read-write-and-exec-are-tiers>)
- [Approval Desk\: modes and policies](<https://present-sketch-tp94.here.now/chapters/permissions-approval-desk-modes-and-policies>)
- [Approval Desk\: ordered shell rules](<https://present-sketch-tp94.here.now/chapters/permissions-approval-desk-ordered-shell-rules>)
- [Dispatch Desk\: device and path gates](<https://present-sketch-tp94.here.now/chapters/permissions-dispatch-desk-device-and-path-gates>)
- [Boundary Desk\: one call at a time](<https://present-sketch-tp94.here.now/chapters/permissions-boundary-desk-one-call-at-a-time>)
- [Boundary Desk\: when the host can ask](<https://present-sketch-tp94.here.now/chapters/permissions-boundary-desk-when-the-host-can-ask>)
- [Configuration and launch precedence](<https://present-sketch-tp94.here.now/chapters/permissions-configuration-and-launch-precedence>)
- [Subagents and inherited policies](<https://present-sketch-tp94.here.now/chapters/permissions-subagents-and-inherited-policies>)
- [Approval is not a sandbox](<https://present-sketch-tp94.here.now/chapters/permissions-approval-is-not-a-sandbox>)
- [Recovery without widening permission](<https://present-sketch-tp94.here.now/chapters/permissions-recovery-without-widening-permission>)
- [Evidence and limitations](<https://present-sketch-tp94.here.now/chapters/permissions-evidence-and-limitations>)
- [Tool permissions and approvals\: next steps](<https://present-sketch-tp94.here.now/chapters/unified-permissions-next>)

### Extensions inside those boundaries

Build explicit human and machine capabilities whose permissions\, state lifetimes\, delivery\, loading\, and verification match the host that actually runs them\.

- [Extensions inside those boundaries](<https://present-sketch-tp94.here.now/chapters/unified-extensions>)
- [Orientation](<https://present-sketch-tp94.here.now/chapters/extensions-orientation>)
- [Choose what to build](<https://present-sketch-tp94.here.now/chapters/extensions-choose-what-to-build>)
- [Prepare a reversible lab](<https://present-sketch-tp94.here.now/chapters/extensions-prepare-a-reversible-lab>)
- [Seed Desk\: welcome and inventory](<https://present-sketch-tp94.here.now/chapters/extensions-seed-desk-welcome-and-inventory>)
- [Seed Desk\: reservations on the active branch](<https://present-sketch-tp94.here.now/chapters/extensions-seed-desk-reservations-on-the-active-branch>)
- [Review Desk\: edit and decide locally](<https://present-sketch-tp94.here.now/chapters/extensions-review-desk-edit-and-decide-locally>)
- [Review Desk\: a native panel and portable dialogs](<https://present-sketch-tp94.here.now/chapters/extensions-review-desk-a-native-panel-and-portable-dialogs>)
- [Editors\, themes and composer shapes](<https://present-sketch-tp94.here.now/chapters/extensions-editors-themes-and-composer-shapes>)
- [Package Lab\: one file to an embedded host](<https://present-sketch-tp94.here.now/chapters/extensions-package-lab-one-file-to-an-embedded-host>)
- [Discovery\, installation and reload](<https://present-sketch-tp94.here.now/chapters/extensions-discovery-installation-and-reload>)
- [Tools\, interception and native delegation](<https://present-sketch-tp94.here.now/chapters/extensions-tools-interception-and-native-delegation>)
- [Session navigation and event\-driven behavior](<https://present-sketch-tp94.here.now/chapters/extensions-session-navigation-and-event-driven-behavior>)
- [Background work and owner\-addressed delivery](<https://present-sketch-tp94.here.now/chapters/extensions-background-work-and-owner-addressed-delivery>)
- [Models\, providers\, credentials and memory](<https://present-sketch-tp94.here.now/chapters/extensions-models-providers-credentials-and-memory>)
- [Resources\, event buses\, MCP and Gemini manifests](<https://present-sketch-tp94.here.now/chapters/extensions-resources-event-buses-mcp-and-gemini-manifests>)
- [Permission\-denied file fallbacks](<https://present-sketch-tp94.here.now/chapters/extensions-permission-denied-file-fallbacks>)
- [Public feature reference](<https://present-sketch-tp94.here.now/chapters/extensions-public-feature-reference>)
- [All 46 extension events](<https://present-sketch-tp94.here.now/chapters/extensions-all-46-extension-events>)
- [Debugging and verification](<https://present-sketch-tp94.here.now/chapters/extensions-debugging-and-verification>)
- [Build and distribution checklist](<https://present-sketch-tp94.here.now/chapters/extensions-build-and-distribution-checklist>)
- [Evidence and method](<https://present-sketch-tp94.here.now/chapters/extensions-evidence-and-method>)
- [Extensions inside those boundaries\: next steps](<https://present-sketch-tp94.here.now/chapters/unified-extensions-next>)

### Connections and next steps

Connect the boundaries\, resolve shared terminology\, and choose a safe next step\.

- [An empty context is not an empty history or memory store](<https://present-sketch-tp94.here.now/chapters/connection-which-state-survives>)
- [A conversation fork is not a worktree or a cloned runtime](<https://present-sketch-tp94.here.now/chapters/connection-forks-and-shared-workspaces>)
- [Changing focus\, stopping work\, and cancelling disclosure](<https://present-sketch-tp94.here.now/chapters/connection-stop-the-owned-operation>)
- [Permission belongs to an operation\, a revision\, and a lifetime](<https://present-sketch-tp94.here.now/chapters/connection-authority-and-lifetimes>)
- [A result\, a receipt\, and permission to share are different facts](<https://present-sketch-tp94.here.now/chapters/connection-results-delivery-and-disclosure>)
- [Match each claim to the layer that was checked](<https://present-sketch-tp94.here.now/chapters/connection-evidence-and-reading-controls>)
- [Shared glossary](<https://present-sketch-tp94.here.now/chapters/unified-glossary>)
- [Next steps](<https://present-sketch-tp94.here.now/chapters/unified-next-steps>)

## Reading paths

### Return to a known conversation without restoring old files

Use this route when a history is missing\, the newest conversation is the wrong one\, or a project directory has moved\; establish identity and scope before adding work\.

- [Start here](<https://present-sketch-tp94.here.now/chapters/continuity-start-here>)
- [The continuity ledger](<https://present-sketch-tp94.here.now/chapters/continuity-the-continuity-ledger>)
- [Return Desk lab setup](<https://present-sketch-tp94.here.now/chapters/continuity-return-desk-lab-setup>)
- [Return Desk resume selection](<https://present-sketch-tp94.here.now/chapters/continuity-return-desk-resume-selection>)
- [Return Desk continuation](<https://present-sketch-tp94.here.now/chapters/continuity-return-desk-continuation>)
- [Return Desk missing\-directory decisions](<https://present-sketch-tp94.here.now/chapters/continuity-return-desk-missing-directory-decisions>)
- [Decision Desk refusals and failures](<https://present-sketch-tp94.here.now/chapters/continuity-decision-desk-refusals-and-failures>)
- [Four places knowledge can live](<https://present-sketch-tp94.here.now/chapters/memory-four-places-knowledge-can-live>)
- [Troubleshooting](<https://present-sketch-tp94.here.now/chapters/memory-troubleshooting>)
- [Recovery and safe disclosure checklists](<https://present-sketch-tp94.here.now/chapters/continuity-recovery-and-safe-disclosure-checklists>)

### Understand why an old fact returns after a reset

Separate retained history\, cached injection\, scoped memory\, and reusable skills\; work through the fictional inspection stories without disclosing real memory\.

- [The continuity ledger](<https://present-sketch-tp94.here.now/chapters/continuity-the-continuity-ledger>)
- [Four places knowledge can live](<https://present-sketch-tp94.here.now/chapters/memory-four-places-knowledge-can-live>)
- [The configuration this workbook teaches](<https://present-sketch-tp94.here.now/chapters/memory-the-configuration-this-workbook-teaches>)
- [Five fictional lab stories](<https://present-sketch-tp94.here.now/chapters/memory-five-fictional-lab-stories>)
- [The automatic lifecycle](<https://present-sketch-tp94.here.now/chapters/memory-the-automatic-lifecycle>)
- [The command desk](<https://present-sketch-tp94.here.now/chapters/memory-the-command-desk>)
- [Decision Desk resetting deliberately](<https://present-sketch-tp94.here.now/chapters/continuity-decision-desk-resetting-deliberately>)
- [Privacy and costs](<https://present-sketch-tp94.here.now/chapters/memory-privacy-and-costs>)
- [Troubleshooting](<https://present-sketch-tp94.here.now/chapters/memory-troubleshooting>)
- [Sources\, method and limitations](<https://present-sketch-tp94.here.now/chapters/memory-sources-method-and-limitations>)

### Find\, redirect\, and leave an already\-running tangent

Start with the current recipient\, then distinguish steering\, follow\-up\, observation\, revival\, cancellation\, and explicit kill\; do not substitute a Main session reset for a focus change\.

- [A tan is already running](<https://present-sketch-tp94.here.now/chapters/tan-a-tan-is-already-running>)
- [1\. Understand the fork](<https://present-sketch-tp94.here.now/chapters/tan-1-understand-the-fork>)
- [3\. Find and focus the right tan](<https://present-sketch-tp94.here.now/chapters/tan-3-find-and-focus-the-right-tan>)
- [4\. Steer and queue work](<https://present-sketch-tp94.here.now/chapters/tan-4-steer-and-queue-work>)
- [5\. Leave and switch safely](<https://present-sketch-tp94.here.now/chapters/tan-5-leave-and-switch-safely>)
- [6\. Observe jobs and read results](<https://present-sketch-tp94.here.now/chapters/tan-6-observe-jobs-and-read-results>)
- [7\. Continue a finished tan](<https://present-sketch-tp94.here.now/chapters/tan-7-continue-a-finished-tan>)
- [8\. Interrupt\, cancel\, or kill](<https://present-sketch-tp94.here.now/chapters/tan-8-interrupt-cancel-or-kill>)
- [10\. Protect context and recover](<https://present-sketch-tp94.here.now/chapters/tan-10-protect-context-and-recover>)
- [11\. Operate through Main](<https://present-sketch-tp94.here.now/chapters/tan-11-operate-through-main>)
- [12\. State and control reference](<https://present-sketch-tp94.here.now/chapters/tan-12-state-and-control-reference>)
- [Decision Desk resetting deliberately](<https://present-sketch-tp94.here.now/chapters/continuity-decision-desk-resetting-deliberately>)

### Give an agent a capability without giving it a blanket grant

Follow the smallest\-surface decision through shared human\/tool behavior\, branch\-aware state\, one\-action authority\, portable UI\, and realistic verification\.

- [Choose what to build](<https://present-sketch-tp94.here.now/chapters/extensions-choose-what-to-build>)
- [Prepare a reversible lab](<https://present-sketch-tp94.here.now/chapters/extensions-prepare-a-reversible-lab>)
- [Seed Desk\: welcome and inventory](<https://present-sketch-tp94.here.now/chapters/extensions-seed-desk-welcome-and-inventory>)
- [Decision Desk forking](<https://present-sketch-tp94.here.now/chapters/continuity-decision-desk-forking>)
- [Seed Desk\: reservations on the active branch](<https://present-sketch-tp94.here.now/chapters/extensions-seed-desk-reservations-on-the-active-branch>)
- [Review Desk\: edit and decide locally](<https://present-sketch-tp94.here.now/chapters/extensions-review-desk-edit-and-decide-locally>)
- [Review Desk\: a native panel and portable dialogs](<https://present-sketch-tp94.here.now/chapters/extensions-review-desk-a-native-panel-and-portable-dialogs>)
- [Tools\, interception and native delegation](<https://present-sketch-tp94.here.now/chapters/extensions-tools-interception-and-native-delegation>)
- [Discovery\, installation and reload](<https://present-sketch-tp94.here.now/chapters/extensions-discovery-installation-and-reload>)
- [Debugging and verification](<https://present-sketch-tp94.here.now/chapters/extensions-debugging-and-verification>)
- [Build and distribution checklist](<https://present-sketch-tp94.here.now/chapters/extensions-build-and-distribution-checklist>)

### Keep delayed results with the conversation that requested them

Use this route when a result arrives twice\, disappears from later job snapshots\, or reaches the wrong branch\; distinguish work lifetime\, owner identity\, committed body\, and wake\-up\.

- [The continuity ledger](<https://present-sketch-tp94.here.now/chapters/continuity-the-continuity-ledger>)
- [Decision Desk forking](<https://present-sketch-tp94.here.now/chapters/continuity-decision-desk-forking>)
- [Decision Desk resetting deliberately](<https://present-sketch-tp94.here.now/chapters/continuity-decision-desk-resetting-deliberately>)
- [6\. Observe jobs and read results](<https://present-sketch-tp94.here.now/chapters/tan-6-observe-jobs-and-read-results>)
- [7\. Continue a finished tan](<https://present-sketch-tp94.here.now/chapters/tan-7-continue-a-finished-tan>)
- [Prepare a reversible lab](<https://present-sketch-tp94.here.now/chapters/extensions-prepare-a-reversible-lab>)
- [Package Lab\: one file to an embedded host](<https://present-sketch-tp94.here.now/chapters/extensions-package-lab-one-file-to-an-embedded-host>)
- [Session navigation and event\-driven behavior](<https://present-sketch-tp94.here.now/chapters/extensions-session-navigation-and-event-driven-behavior>)
- [Background work and owner\-addressed delivery](<https://present-sketch-tp94.here.now/chapters/extensions-background-work-and-owner-addressed-delivery>)
- [Debugging and verification](<https://present-sketch-tp94.here.now/chapters/extensions-debugging-and-verification>)

### Inspect a complete packet without a live share

Choose the correct snapshot\, inspect older and nested fictional history\, and separate local acceptance\, external script loading\, redaction\, encryption\, and disclosure authorization\.

- [Start here](<https://present-sketch-tp94.here.now/chapters/continuity-start-here>)
- [Review Packet choosing a snapshot](<https://present-sketch-tp94.here.now/chapters/continuity-review-packet-choosing-a-snapshot>)
- [Review Packet local HTML export](<https://present-sketch-tp94.here.now/chapters/continuity-review-packet-local-html-export>)
- [Review Packet reading the whole packet](<https://present-sketch-tp94.here.now/chapters/continuity-review-packet-reading-the-whole-packet>)
- [Sharing is a separate disclosure decision](<https://present-sketch-tp94.here.now/chapters/continuity-sharing-is-a-separate-disclosure-decision>)
- [Privacy and costs](<https://present-sketch-tp94.here.now/chapters/memory-privacy-and-costs>)
- [10\. Protect context and recover](<https://present-sketch-tp94.here.now/chapters/tan-10-protect-context-and-recover>)
- [Review Desk\: edit and decide locally](<https://present-sketch-tp94.here.now/chapters/extensions-review-desk-edit-and-decide-locally>)
- [Recovery and safe disclosure checklists](<https://present-sketch-tp94.here.now/chapters/continuity-recovery-and-safe-disclosure-checklists>)
- [Evidence and limitations](<https://present-sketch-tp94.here.now/chapters/continuity-evidence-and-limitations>)

### Explain and repair a blocked or unexpectedly unprompted tool call

Identify the exact operation and current scope\, trace the effective policy through modes\, rules\, dispatch\, launch inputs\, and child construction\, then choose a bounded recovery without making yolo the default fix\.

- [Start with the operation](<https://present-sketch-tp94.here.now/chapters/permissions-start-with-the-operation>)
- [Read\, write\, and exec are tiers](<https://present-sketch-tp94.here.now/chapters/permissions-read-write-and-exec-are-tiers>)
- [Approval Desk\: modes and policies](<https://present-sketch-tp94.here.now/chapters/permissions-approval-desk-modes-and-policies>)
- [Approval Desk\: ordered shell rules](<https://present-sketch-tp94.here.now/chapters/permissions-approval-desk-ordered-shell-rules>)
- [Dispatch Desk\: device and path gates](<https://present-sketch-tp94.here.now/chapters/permissions-dispatch-desk-device-and-path-gates>)
- [Boundary Desk\: one call at a time](<https://present-sketch-tp94.here.now/chapters/permissions-boundary-desk-one-call-at-a-time>)
- [Boundary Desk\: when the host can ask](<https://present-sketch-tp94.here.now/chapters/permissions-boundary-desk-when-the-host-can-ask>)
- [Configuration and launch precedence](<https://present-sketch-tp94.here.now/chapters/permissions-configuration-and-launch-precedence>)
- [Subagents and inherited policies](<https://present-sketch-tp94.here.now/chapters/permissions-subagents-and-inherited-policies>)
- [Approval is not a sandbox](<https://present-sketch-tp94.here.now/chapters/permissions-approval-is-not-a-sandbox>)
- [Recovery without widening permission](<https://present-sketch-tp94.here.now/chapters/permissions-recovery-without-widening-permission>)
- [Evidence and limitations](<https://present-sketch-tp94.here.now/chapters/permissions-evidence-and-limitations>)

## Start here

OMP work continues through more than a transcript\.&#32;A conversation can be reopened while its workspace has changed\.&#32;A remembered preference can return after live context was cleared\.&#32;A tangent can finish its first job yet remain available for another conversation\.&#32;A tool can be enabled but denied for a particular call\,&#32;or run without asking under a policy the visible mode alone does not explain\.&#32;An extension can keep a selection across a session switch without having saved that selection anywhere\.

These are not contradictions\.&#32;They are different state and authority boundaries\.

This workbook teaches one operating habit\:&#32;**name the target\,&#32;identify the state and authority involved\,&#32;and check the result at the layer where the effect should occur\.**&#32;That habit connects returning to yesterday’s work\,&#32;retaining useful knowledge\,&#32;coordinating another agent\,&#32;understanding a tool decision\,&#32;building a capability\,&#32;and preparing material for review\.

### Read as one book

The five parts form a progression\:

| Part | Central question | What it prepares you to understand |
| --- | --- | --- |
| [Sessions\,&#32;resets\,&#32;and reviewable history](<https://present-sketch-tp94.here.now/chapters/unified-continuity>) | Which conversation and which representation of it am I using\? | Persistent identity\,&#32;live context\,&#32;provider\-facing state\,&#32;workspace scope\,&#32;and exported copies\. |
| [Memory and reusable knowledge](<https://present-sketch-tp94.here.now/chapters/unified-memory>) | What can return beyond the current conversation\,&#32;and why\? | Memory scope\,&#32;retrieval\,&#32;correction\,&#32;managed skills\,&#32;automation\,&#32;privacy\,&#32;and costs\. |
| [Tangent work and live control](<https://present-sketch-tp94.here.now/chapters/unified-tan>) | Which worker am I addressing\,&#32;and what exactly am I stopping or continuing\? | Focus\,&#32;agent identity\,&#32;background jobs\,&#32;shared files\,&#32;result delivery\,&#32;and revival\. |
| [Tool permissions and approvals](<https://present-sketch-tp94.here.now/chapters/unified-permissions>) | Why is this exact operation allowed\,&#32;prompted\,&#32;or denied\? | Tiers\,&#32;policy precedence\,&#32;ordered shell rules\,&#32;device identity\,&#32;one\-call decisions\,&#32;UI capability\,&#32;launch inputs\,&#32;and host authority\. |
| [Extensions inside those boundaries](<https://present-sketch-tp94.here.now/chapters/unified-extensions>) | How can a capability expose useful actions without hiding its lifetime or authority\? | Human and machine interfaces\,&#32;persistence\,&#32;permission\,&#32;UI modes\,&#32;delivery\,&#32;loading\,&#32;and verification\. |

All&#32;**65 original catalogued source reading sections remain in full**\,&#32;alongside the&#32;**complete new permissions part**\.&#32;The original code\,&#32;exercises\,&#32;failure cases\,&#32;references\,&#32;and evidence chapters remain part of this book\.&#32;The retained Orientation chapters establish each source’s scope\;&#32;the part introductions explain how that material fits together\.&#32;Repeated warnings are not silently discarded\:&#32;a warning about memory deletion\,&#32;a warning about conversation reset\,&#32;and a warning about widening tool permission protect different boundaries\.

Read front to back for the complete progression\,&#32;or choose a reading path for a concrete problem\.&#32;The connection chapters bring the parts together without replacing their detailed contracts\.&#32;The&#32;[shared glossary](<https://present-sketch-tp94.here.now/chapters/unified-glossary>)&#32;explains terms that recur with different meanings\.

### Prerequisites and practice modes

You can read the entire book without an OMP installation\,&#32;account\,&#32;credentials\,&#32;or provider access\.

| Activity | Prerequisites | Boundary |
| --- | --- | --- |
| Read chapters and the complete Markdown edition | A browser or Markdown reader | Reading does not operate OMP\. |
| Use the&#32;[Memory lab](<https://present-sketch-tp94.here.now/labs/memory>) | A JavaScript\-capable browser for its controls | The stories and selectable illustration are fictional\;&#32;no memory backend is connected\. |
| Inspect downloaded continuity fixtures | A shell\;&#32;`jq`&#32;for the supplied JSON queries\;&#32;Bun for the Return Desk helper\;&#32;`shasum`&#32;for the optional comparisons | Read or prepare only disposable fictional material\. |
| Rehearse native continuity commands | A compatible installed OMP and an interactive terminal\,&#32;using the complete generated lab environment | Stop at model or authentication setup\.&#32;No provider prompt is part of that route\. |
| Work through permissions cases | Read the fictional JSON and recorded outcomes | No OMP launch\,&#32;policy change\,&#32;shell execution\,&#32;or live approval is required\. |
| Work through extension examples | Basic TypeScript\,&#32;Bun\,&#32;a compatible custom OMP host\,&#32;and the matching dependencies named by the example | A matching version string alone does not prove API compatibility\. |

The&#32;[combined example archive](<https://present-sketch-tp94.here.now/examples.zip>)&#32;preserves the supplied directory structure and contains the Extension and Continuity examples\,&#32;plus the Permissions case fixtures\.&#32;Permissions practice uses&#32;[Approval Desk](<https://present-sketch-tp94.here.now/examples/approval-desk/cases.json>)\,&#32;[Dispatch Desk](<https://present-sketch-tp94.here.now/examples/dispatch-desk/cases.json>)\,&#32;and&#32;[Boundary Desk](<https://present-sketch-tp94.here.now/examples/boundary-desk/cases.json>)\.&#32;These three files are inert cases\,&#32;not a live simulator or configuration to install\.&#32;Memory’s practice material is in its chapters and interactive lab\;&#32;Tan’s Lantern Library fixtures are printed in its chapters\.&#32;There is no invented Memory or Tan download package\.

A disposable directory is useful data separation\,&#32;not an operating\-system sandbox\.&#32;Likewise\,&#32;`--no-extensions`\,&#32;an in\-memory session\,&#32;or a clean profile does not establish that every startup activity is offline\.&#32;A read tier is a declaration\,&#32;not a universal promise of read\-only behavior or zero network activity\.&#32;The source chapters explain the narrower controls they actually use\.

### Keep the input surface attached to the instruction

The original captions identify where an example belongs\.&#32;Preserve that distinction when reading or copying it\:

- **Terminal shell\:**&#32;executable launches\,&#32;configuration inspection\,&#32;and fixture inspection\,&#32;only under the stated prerequisites and owned scope\.
- **Main’s OMP composer\:**&#32;native slash commands and ordinary requests to Main\.
- **Focused Tan chat\:**&#32;plain\-language messages to the verified agent\,&#32;with steering and follow\-up behavior determined by its current state\.
- **Model tool interface\:**&#32;schema\-shaped arguments for a tool that actually exists in that session\.&#32;A JSON argument object is not a terminal command\.
- **Extension or SDK code\:**&#32;source to inspect or load through its documented host\,&#32;not text to paste into a conversation\.
- **Permissions case data\:**&#32;fictional declarations\,&#32;arguments\,&#32;responses\,&#32;and shell\-shaped strings to classify\.&#32;Do not execute those strings\,&#32;submit those fictional calls\,&#32;or install the cases as settings\.
- **Workbook browser controls\:**&#32;navigation\,&#32;disclosures\,&#32;authored illustration or milestone selection\,&#32;copying\,&#32;and self\-reported lesson ticks only\.

Copying a snippet does not execute it\.&#32;A slash command is not automatically a model\-callable tool\.&#32;A method documented for a command context is not made safe inside a tool by a type cast\.&#32;Selecting an approval milestone in the browser does not approve any real OMP call\.

### Fiction is the default learning environment

Cedar\,&#32;Lantern Library\,&#32;Lantern Board\,&#32;Cedar Library\,&#32;Harbor Notes\,&#32;Seed Desk\,&#32;Review Desk\,&#32;Field Notes\,&#32;and the permissions part’s Cedar Seed Library are teaching fixtures\,&#32;not a shared real project\.&#32;Similar names do not establish shared files\,&#32;sessions\,&#32;or evidence\.&#32;In particular\,&#32;Tan’s Lantern Library and Continuity’s Lantern Board are separate stories\,&#32;and the recorded Decision Desk checks used a separate Cedar Library fixture\.&#32;The permissions recording tools are not the Seed Desk reservation extension\,&#32;despite the seed\-library theme\.

Use the supplied fiction to predict outcomes\,&#32;inspect complete records\,&#32;and understand refusals\.&#32;Source chapters retain optional real\-use prompts and service\-integration examples\,&#32;but their inclusion is not a request to execute every snippet\.&#32;Completing this unified reading route requires&#32;**no real memory disclosure\,&#32;authentication experiment\,&#32;provider inference exercise\,&#32;live upload\,&#32;privileged\-broker exercise\,&#32;or destructive session operation**\.&#32;Do not use&#32;`/drop`\,&#32;`/memory clear`\,&#32;or&#32;`/memory reset`&#32;as workbook cleanup\.&#32;Sharing remains a decision exercise\,&#32;not an upload exercise\.&#32;A permissions refusal is not a request to change personal settings or switch to yolo\.

Some source checkpoints describe actual operations a reader might later perform\.&#32;Leave those unchecked if you only read or simulated them\.&#32;Understanding an operation and having verified it in a real session are different accomplishments\.

### Read evidence at its stated strength

The book distinguishes four kinds of statements\:

- **Recorded observation\:**&#32;a supplied report describes a completed check under named conditions\.
- **Source\-backed behavior\:**&#32;the source workbook traces a contract to the implementation or types it received\.
- **Exercise or expected checkpoint\:**&#32;a result to test or reason about\,&#32;not a newly observed result\.
- **Editorial connection\:**&#32;an explanation or operating recommendation derived from several chapters\,&#32;not a new API guarantee\.

The sources describe custom snapshots\.&#32;Reports that name&#32;`omp/18.0.7`&#32;do not establish parity with every release carrying that version\.&#32;Memory’s Mnemopi settings are a recorded configuration snapshot\,&#32;not a statement about your machine\.&#32;The permissions source and new evidence are dated 30 August 2026\;&#32;earlier reports remain historical\.

Permissions retains two runs of existing focused tests—96 passes across four files\,&#32;then 105 across two—plus a separate Main\-executed private report of 46 fictional scenarios and an isolated configuration CLI observation\.&#32;The 201 passing existing tests are not one newly rerun aggregate of the earlier books\.&#32;Combining the manuscripts does not rerun their tests\,&#32;inspect a reader’s databases or policies\,&#32;or turn SDK\,&#32;controller\,&#32;RPC helper\,&#32;recording\-adapter\,&#32;or terminal\-emulator evidence into physical TUI proof\.

### Reading with a browser agent

The unified reading interface is&#32;**`window.ompWorkbook`\,&#32;version 1**\,&#32;with tutorial\-only scope\.&#32;Consult its live&#32;`discover()`&#32;and&#32;`inspect()`&#32;results and&#32;[the browser contract](<https://present-sketch-tp94.here.now/browser-interface.json>)&#32;for exact controls and availability\;&#32;do not construct control IDs from labels\.&#32;It cannot observe or control a real OMP process or resolve that process’s permission policy\.

Unified lesson ticks use&#32;`omp-workbook-checklist-v1`&#32;in browser storage\.&#32;They record the reader’s report\,&#32;not a test result\.&#32;The separate Memory lab retains&#32;`window.memoryTutorial`&#32;and its own checklist storage on that lab page only\.&#32;Its API is not an alias for the unified reading interface\.

A navigation receipt reports a navigation request\,&#32;not a successfully loaded destination\.&#32;Inspect the destination document after navigation\;&#32;a wait on the old document cannot follow it\.&#32;For the complete distinction between tutorial observations and OMP evidence\,&#32;read&#32;[Match each claim to the layer that was checked](<https://present-sketch-tp94.here.now/chapters/connection-evidence-and-reading-controls>)\.

## Sessions\,&#32;resets\,&#32;and reviewable history

Begin with the question that precedes every useful action\:&#32;**where am I\,&#32;and what state have I actually recovered\?**

A saved conversation can explain yesterday’s decision without restoring yesterday’s files\.&#32;A reset can empty model context while retaining earlier journal entries\.&#32;A local export can include history that a compact transcript view does not show\.&#32;These distinctions make continuity the foundation for the rest of the book\.

### Follow the three desks

**Return Desk**&#32;begins with a deliberately small disagreement\:&#32;yesterday’s Lantern Board conversation says “Ready for pickup\,” while today’s workspace says “Packed and waiting\.” The correct result is not to make those observations match\.&#32;It is to identify the intended journal and understand that the current file remains current\.&#32;Read the ledger before using the helper\,&#32;and use the complete generated environment if you rehearse native resume or continue\.

**Decision Desk**&#32;separates a conversational fork from workspace isolation\,&#32;then gives fresh\,&#32;clear\,&#32;and new different jobs\.&#32;The important identity is the persistent header identity exposed by&#32;`SessionManager.getSessionId()`\.&#32;The source’s&#32;`AgentSession.sessionId`&#32;is provider\-facing and can change without a new journal being created\.

**Review Packet**&#32;follows material out of the active conversation into dumps\,&#32;sidecars\,&#32;HTML\,&#32;and possible sharing routes\.&#32;The Harbor Notes parent\,&#32;Scout\,&#32;and Checklist journals make older and nested content inspectable without exposing a real session\.&#32;This is different from the later Extension part’s Review Desk\,&#32;which records local review decisions and has no publishing operation\.

### Use the lab without borrowing personal history

The public starting points are&#32;[Return Desk’s fixture inventory](<https://present-sketch-tp94.here.now/examples/return-desk/exercise.json>)\,&#32;[its lab helper](<https://present-sketch-tp94.here.now/examples/return-desk/lab.ts>)\,&#32;[Decision Desk’s recipe](<https://present-sketch-tp94.here.now/examples/decision-desk/commands.txt>)\,&#32;and&#32;[Review Packet’s inspection recipe](<https://present-sketch-tp94.here.now/examples/review-packet/inspection-recipe.json>)\.

The helper creates owned fictional material\;&#32;it does not launch OMP\.&#32;Native resume and continue practice uses the same actual terminal tab and generated environment\.&#32;If startup asks for model or authentication setup\,&#32;stop rather than importing credentials or substituting a personal journal\.&#32;Fixture inspection remains useful even when native interaction is blocked\.

The missing\-directory and failure chapters are decision exercises\.&#32;They are not instructions to delete a directory\,&#32;manufacture a live provider failure\,&#32;or force a transition past a guard\.

### Keep the evidence attached

The&#32;[continuity ledger](<https://present-sketch-tp94.here.now/chapters/continuity-the-continuity-ledger>)&#32;is the recurring reference\.&#32;The&#32;[operation decision matrix](<https://present-sketch-tp94.here.now/chapters/continuity-operation-decision-matrix>)&#32;is a lookup aid after the worked chapters\,&#32;not a replacement for their prerequisites and failure phases\.

The recorded checks include manager\,&#32;SDK\,&#32;controller\,&#32;parser\,&#32;and read\-only RPC startup paths\,&#32;plus a separate headless\-browser export check\.&#32;They do not establish physical terminal behavior\,&#32;universal rollback\,&#32;real provider repair\,&#32;or live upload cancellation\.&#32;The final evidence chapter preserves those limits in full\.

## Orientation

Resume the conversation you meant to resume\.&#32;Explore an alternative without losing the original conversation\.&#32;Reset the state you intended to reset\.&#32;Prepare a review packet without treating a hidden panel\,&#32;an encrypted link\,&#32;or a cancellation message as proof of privacy\.

This is an independent educational workbook for the supplied current custom implementation of Oh My Pi\.&#32;It is not official product marketing or a compatibility promise for every OMP release\.

## Start here

Oh My Pi\,&#32;usually shortened to&#32;**OMP**\,&#32;is an agentic coding command\-line application\.&#32;You give it a task\;&#32;its agent can work through model responses and tools that inspect or change a project\.

A session records the conversation and associated session information\.&#32;**It is not a backup of the workspace\.**&#32;Reopening a conversation that discussed an earlier file does not restore that file\.&#32;Forking a conversation does not create a separate Git worktree\.&#32;Clearing the conversation does not erase retained history or previously generated exports\.

This workbook follows three fictional stories\:

| Story | Fictional reader | Practical outcome |
| --- | --- | --- |
| Return Desk | Maya maintains Lantern Board across several sittings\. | Find the intended history\,&#32;reopen it deliberately\,&#32;and distinguish resuming from relocating a session\. |
| Decision Desk | Eli wants to explore another direction for Lantern Board\. | Fork the conversation\,&#32;then choose correctly between fresh\,&#32;clear\,&#32;and new\. |
| Review Packet | Noor reviews the invented Harbor Notes project\. | Inspect a local packet\,&#32;including older and nested history\,&#32;before making any disclosure decision\. |

The recorded Decision Desk tests use a separate fictional Cedar Library fixture\.&#32;Those tests support the operation boundaries\;&#32;they are not a recording of Maya or Eli using the public Lantern Board recipe\.

### What you need

You need basic prompting\,&#32;terminal use\,&#32;and filesystem paths—not extension or RPC programming\.

For the public exercises\:

- A downloaded\,&#32;disposable copy of the&#32;[complete examples bundle](<https://present-sketch-tp94.here.now/downloads/continuity-examples.zip>)\.
- A terminal shell\.
- `jq`&#32;for the JSON inspection and receipt commands\.
- Bun for the supplied Return Desk lab helper\.
- `shasum`&#32;for the optional byte\-comparison checks\.
- An already installed&#32;`omp`&#32;executable and an interactive terminal for native session exercises\.

The receipt\-capture commands reproduce the supplied macOS\-style&#32;`mktemp -t`&#32;recipes\.&#32;Their exact shell behavior is not presented as a Windows or cross\-platform installation recipe\.

Run commands described as “from&#32;`examples/`” in the extracted directory containing&#32;`return-desk/`\,&#32;`decision-desk/`\,&#32;and&#32;`review-packet/`\.

**No installation\,&#32;login\,&#32;provider prompt\,&#32;live sharing\,&#32;or destructive session exercise is required\.**&#32;If a clean lab profile stops at model or authentication setup\,&#32;stop there\.&#32;Do not supply credentials to force the exercise onward\.

### Three kinds of evidence

Throughout the book\:

- **Source\-backed expectation**&#32;means the supplied implementation defines that behavior\.
- **Recorded check**&#32;means a completed supplied report observed it under its stated test conditions\.
- **Your observation**&#32;means something you actually inspected in your own disposable lab\.

These are not interchangeable\.&#32;Printing a command is not executing it\.&#32;A component test is not a physical terminal test\.&#32;A local fake provider handle is not a real provider incident\.

Some downloadable files retain preparation\-time verification labels\.&#32;The completed reports summarized in&#32;[Evidence and limitations](<https://present-sketch-tp94.here.now/chapters/continuity-evidence-and-limitations#continuity-evidence-and-limitations>)&#32;supersede those older labels for the named checks—not for every possible environment or reader action\.

Begin with&#32;[the continuity ledger](<https://present-sketch-tp94.here.now/chapters/continuity-the-continuity-ledger#continuity-the-continuity-ledger>)\,&#32;then follow the three desks\.&#32;Keep the&#32;[operation decision matrix](<https://present-sketch-tp94.here.now/chapters/continuity-operation-decision-matrix#continuity-operation-decision-matrix>)&#32;and&#32;[recovery and disclosure checklists](<https://present-sketch-tp94.here.now/chapters/continuity-recovery-and-safe-disclosure-checklists#continuity-recovery-and-safe-disclosure-checklists>)&#32;nearby\.

## The continuity ledger

Before choosing an operation\,&#32;name the state you want to change\.

“Reset my session” is too vague\.&#32;It might mean stopping a response\,&#32;dropping conversation context\,&#32;starting a different identity\,&#32;discarding provider\-facing handles\,&#32;or deleting local history\.&#32;Those are different requests\.

### The seven boundaries

| Layer | What it means | What it is not |
| --- | --- | --- |
| **Live and model context** | The messages currently held by the agent\,&#32;plus the transformations used to prepare model context\. | Every entry ever retained in the journal\,&#32;or an exact captured HTTP request\. |
| **Durable journal** | The saved JSONL conversation record\,&#32;including messages and metadata\.&#32;It can retain older and alternative paths\. | A directory snapshot or a complete record of every unfinished streaming token\. |
| **Persistent session identity** | The identity in the session header\,&#32;exposed by&#32;`SessionManager.getSessionId()`\. | Necessarily the same value as a provider\-facing session ID\. |
| **Provider\-facing state** | Request identity\,&#32;local transport\/session handles\,&#32;and possibly a separate prompt\-cache key\. | A guarantee about remote retention\,&#32;remote deletion\,&#32;or cache behavior\. |
| **Cwd and project scope** | The active working directory\,&#32;recorded header cwd\,&#32;and any additional workspace roots\.&#32;These can differ during fallback or failure\. | A copy of the project at the time the conversation began\. |
| **Workspace files** | The files that exist now in the project\. | Files restored merely because an old conversation was reopened\. |
| **Artifacts and outputs** | Session\-adjacent artifacts and nested journals\,&#32;generated HTML\,&#32;temporary dump sidecars\,&#32;and copied text\. | One automatically synchronized\,&#32;automatically deleted privacy boundary\. |

The rendered transcript is another&#32;**view**&#32;of these layers\.&#32;It may collapse older context or hide some content\.&#32;A shorter display does not establish a shorter journal\.

A journal’s entries have their own IDs and parent relationships\.&#32;The active leaf selects a conversation path for context rebuilding\.&#32;You do not need to learn the entire tree implementation here\;&#32;you do need to remember that&#32;**all stored entries**&#32;and&#32;**the active conversation path**&#32;are not the same thing\.

### Milestone\:&#32;Identify yesterday before opening anything

**Need\.**&#32;Maya wants yesterday’s Lantern Board discussion\.

**Obstacle\.**&#32;Yesterday’s conversation says “Ready for pickup\,” but today’s workspace file says “Packed and waiting\.” Both statements can be present without OMP having lost anything\.

Read the public fixtures first\:

- [Yesterday’s journal](<https://present-sketch-tp94.here.now/examples/return-desk/yesterday.jsonl>)
- [Today’s workspace text](<https://present-sketch-tp94.here.now/examples/return-desk/today.txt>)
- [Return Desk exercise inventory](<https://present-sketch-tp94.here.now/examples/return-desk/exercise.json>)

**Terminal shell — read\-only inspection\,&#32;from extracted&#32;`examples/`\:**

~~~sh
jq -s 'map(select(.type == "session") | {id, cwd, title})' return-desk/yesterday.jsonl
jq -s 'map(select(.type == "message" and .message.role == "user") | .message.content)' return-desk/yesterday.jsonl
cat return-desk/today.txt
~~~

**Structured example output — derived from the public header\,&#32;not a new terminal capture\:**

~~~json
[
  {
    "id": "11111111-1111-4111-8111-111111111111",
    "cwd": "/fictional/lantern-board",
    "title": "Lantern Board: yesterday's labels"
  }
]
~~~

The fixture’s user message is\:

> For our fictional Lantern Board\,&#32;use the label Ready for pickup\.&#32;We will revisit it tomorrow\.

The workspace file identifies its current label as\:

> Current label\:&#32;Packed and waiting

**What changes\?**&#32;These inspection commands change neither the fixture nor an OMP session\.

**What stays separate\?**&#32;The full session identity\,&#32;the header’s recorded cwd\,&#32;the conversation text\,&#32;and today’s file contents are four different observations\.&#32;The short entry ID of a message is not the persistent session identity\.

**Recorded check\.**&#32;`identity-journal-cwd-workspace`&#32;reopened a real&#32;`SessionManager`&#32;journal and retained its identity\,&#32;location\,&#32;entries\,&#32;and cwd while the fictional workspace bytes remained unchanged\.

**Failure and recovery\.**&#32;If&#32;`jq`&#32;cannot read or parse the fixture\,&#32;fix the working directory or restore a clean downloaded copy\.&#32;Do not substitute a personal journal\.&#32;The&#32;`/fictional/lantern-board`&#32;path and&#32;`fictional-offline-model`&#32;label are fixture data\,&#32;not a project or provider to configure\.

**Self\-check\.**&#32;After resuming yesterday\,&#32;which label should&#32;`today.txt`&#32;contain\?

**Answer\:**&#32;“Packed and waiting\.” Resuming history does not reverse the file change represented by the fixture\.

### A path is not proof of persistence

The current manager can allocate a session ID and transcript path before materializing the file\.&#32;New\-session persistence is lazy until assistant output or another explicit persistence reason makes the file necessary\.

That means\:

- An assigned filename does not prove that a resumable file exists\.
- An empty new conversation can still have model or thinking metadata in memory\.
- A session created with&#32;`--no-session`&#32;normally has no journal file\,&#32;but a dump can still create a temporary sidecar\.
- Completed entries and in\-flight streaming text have different durability boundaries\.

The supplied&#32;`SessionManager`&#32;comments describe completed\-entry writes reaching the operating system without&#32;`fsync`\:&#32;software\-crash durability is not a power\-loss backup guarantee\.

**Source anchors\:**&#32;`packages/coding-agent/src/session/session-manager.ts`&#32;—&#32;`SessionManager`\,&#32;`getSessionId`\,&#32;`getSessionFile`\,&#32;`isSessionOnDisk`\,&#32;`buildSessionContext`\;&#32;`packages/coding-agent/src/session/session-context.ts`&#32;—&#32;`buildSessionContext`\;&#32;`packages/coding-agent/src/session/session-entries.ts`&#32;—&#32;`SessionHeader`\,&#32;`ResetBoundaryEntry`\.

## Return Desk lab setup

The raw fixture is suitable for reading\.&#32;Native resume practice needs something more\:&#32;an existing project directory and an owned session collection\,&#32;without mixing the exercise into ordinary history\.

The supplied&#32;[lab helper](<https://present-sketch-tp94.here.now/examples/return-desk/lab.ts>)&#32;provides that preparation\.&#32;It does not launch OMP or import its SDK\.

### Milestone\:&#32;Copy fiction into an owned lab

**Need\.**&#32;Maya wants to practice the real command surfaces\.

**Obstacle\.**&#32;Placing a fixture in a normal session store would mix exercise data with personal history\.&#32;Opening the unre\-based fictional path would also confuse recorded cwd with a real project\.

First discover the helper’s supported operations\,&#32;then create a lab\.

**Terminal shell — fixture preparation\,&#32;from extracted&#32;`examples/`\:**

~~~sh
bun return-desk/lab.ts '{"version":1,"op":"discover"}'
RECEIPT=$(mktemp -t continuity-return-receipt)
bun return-desk/lab.ts '{"version":1,"op":"act","action":"create"}' | tee "$RECEIPT"
~~~

Stop if the receipt reports&#32;`ok: false`\.&#32;Read it before continuing\.

Each successful create makes a new temporary root containing\:

- Dedicated home\,&#32;agent\/config\,&#32;temporary\,&#32;and XDG directories\.
- A project containing the fictional&#32;`today.txt`\.
- Yesterday’s copied journal\,&#32;with its cwd rebased to that project\.
- A newer unrelated fictional note\.
- A lab marker and receipt\.
- Complete native commands with the correct paths\.

Keep the following variables in this same terminal\:

**Terminal shell — receipt values and executable selection\:**

~~~sh
LAB=$(jq -r '.root' "$RECEIPT")
SOURCE=$(jq -r '.yesterday' "$RECEIPT")
SESSIONS=$(jq -r '.sessions' "$RECEIPT")
PROJECT=$(jq -r '.cwd' "$RECEIPT")
OMP_BIN=$(command -v omp)
~~~

`OMP_BIN`&#32;must identify the intended installed executable\.&#32;Stop if it is empty\.&#32;If your shell reports an alias or function rather than an absolute executable path\,&#32;resolve that before using the generated commands\.

You can re\-inspect the existing lab through the helper’s supplied version\-1 inspection operation\:

**Terminal shell — read\-only lab inspection\;&#32;this&#32;`jq`&#32;composition supplies the documented inspection request\:**

~~~sh
bun return-desk/lab.ts "$(jq -c '{version:1,op:"inspect",root:.root}' "$RECEIPT")"
~~~

Inspection validates the lab marker and canonical root\.&#32;It does not inspect a running OMP process\.

### Understand the generated environment

The native commands begin with&#32;`env -i`\,&#32;retain&#32;`PATH`\,&#32;and supply dedicated home\/config\/session paths\.&#32;They also include explicit&#32;`--cwd`&#32;and&#32;`--session-dir`&#32;values and flags disabling extensions\,&#32;skills\,&#32;rules\,&#32;LSP\,&#32;tools\,&#32;and title generation for the recipe\.

The lab settings disable memory\,&#32;autolearn\,&#32;advisors\,&#32;skills\,&#32;secrets\,&#32;marketplace auto\-update\,&#32;and automatic resume\.

**Use the whole printed command\.&#32;Do not shorten it to a bare&#32;`omp --resume …`\.**

This is data separation\,&#32;not an operating\-system sandbox\.&#32;In particular\:

- Native OMP startup may discover providers or refresh metadata without a model prompt\.
- The helper’s synthetic&#32;`TMUX_PANE`&#32;is only a terminal\-identity fallback\.
- OMP prefers stdin’s actual TTY identity when available\.
- Resume and continue practice must use the&#32;**same actual terminal tab**&#32;and generated environment\.

**What changes\?**&#32;The helper creates new fictional files under one owned temporary root\.

**What does not change\?**&#32;It does not open or rewrite ordinary session history\,&#32;launch OMP\,&#32;or submit a model request\.

**Recorded checks\.**&#32;The public helper’s discover\,&#32;create\,&#32;and inspect operations completed successfully inside the isolated proof\.&#32;A separate source\-CLI check used the generated lab for resume\,&#32;continue\,&#32;and fork with read\-only RPC inspection\.

That CLI check added private disabled\-provider policy and a guarded transport\.&#32;It was not physical TUI interaction and does not make ordinary native startup network\-free\.

**Failure and recovery\.**&#32;If native startup requires setup or authentication\,&#32;stop\.&#32;Do not log in\,&#32;paste a key\,&#32;copy an auth database\,&#32;or send a prompt\.&#32;You can still inspect the fictional files and study the recorded results\;&#32;record native interaction as blocked\,&#32;not passed\.

**Self\-check\.**&#32;Does a successful create receipt prove that OMP resumed yesterday\?

**Answer\:**&#32;No\.&#32;It proves fixture preparation succeeded\.&#32;Native execution and identity inspection are separate observations\.

## Return Desk resume selection

Maya remembers the topic\,&#32;but the newest file is not necessarily the conversation she wants\.

The safest approach is to name the intended identity or known file\,&#32;then inspect the resulting session before asking it to do more work\.

### Milestone\:&#32;Open the known copied journal

**Need\.**&#32;Return to yesterday’s labels\,&#32;not today’s unrelated packaging note\.

**Obstacle\.**&#32;A recent\-session list answers a recency question\.&#32;Maya has an identity question\.

Print the exact command from the receipt\:

**Terminal shell — print the generated native command\:**

~~~sh
jq -r '.commands.resumeExplicitPath' "$RECEIPT"
~~~

Read\,&#32;copy\,&#32;and run the complete printed command in the same terminal\.&#32;Printing it alone is not the exercise\.

If startup succeeds\,&#32;remain idle and inspect the session\.

**Human OMP slash commands — inside the isolated lab\,&#32;one at a time\:**

~~~text
/session info
/dirs
~~~

The supplied TUI session\-info implementation displays a file and an ID\.&#32;`/dirs`&#32;lists the manager’s working directory and additional roots\.

Check that\:

1. The displayed session file is the receipt’s&#32;`yesterday`&#32;file\.
2. The conversation contains the two fictional Lantern Board messages\.
3. The working directory is the copied project\,&#32;not&#32;`/fictional/lantern-board`\.
4. The workspace remains the current workspace\.

Use the saved header’s&#32;`type: "session"`&#32;record as the durable identity reference\.&#32;Modern journals may have a title slot before that record\,&#32;so selecting by type is safer than assuming the first physical line is always the header\.

Do not assume that every field named&#32;`sessionId`&#32;in every surface means persistent journal identity\.&#32;In the supplied implementation\,&#32;`AgentSession.sessionId`&#32;is provider\-facing\;&#32;the distinction becomes important after&#32;`/fresh`\.

**What changes\?**&#32;A native process opens the selected saved conversation and rebuilds runtime context\.

**What does not change\?**&#32;It is not implicitly forked\,&#32;and workspace files are not restored\.

**Expected observation\.**&#32;The copied journal has persistent identity&#32;`11111111-1111-4111-8111-111111111111`\.

**Recorded check\.**&#32;The separate source\-CLI resume check returned that identity\,&#32;the intended journal\,&#32;two messages\,&#32;the copied project cwd\,&#32;and unchanged workspace bytes\.&#32;It exited successfully after read\-only inspection\.

**Failure and recovery\.**&#32;Stop on setup\,&#32;unexpected identity\,&#32;unexpected cwd\,&#32;or a persistence error\.&#32;Do not send a prompt to ask the model whether the switch worked\.&#32;A model’s answer would be weaker evidence than the session file and scope\.

**Self\-check\.**&#32;Why inspect both identity and cwd\?

**Answer\:**&#32;The right conversation in the wrong project scope is still unsafe to continue\.

### Milestone\:&#32;Inspect and cancel the in\-session picker

**Need\.**&#32;Maya wants to look for another history without committing to a switch\.

**Obstacle\.**&#32;Opening a selector and selecting a session are different actions\.

**Human OMP slash command — inside the idle fictional session\:**

~~~text
/resume
~~~

The native picker begins in&#32;**current\-folder**&#32;scope\.

- **Tab**&#32;toggles between current\-folder and all\-projects scope\.
- **Enter**&#32;selects the highlighted native session\.
- **Escape**&#32;cancels the picker and returns to the current session\.

All\-projects data is loaded lazily and cached for that picker\.&#32;Reopen the picker if you need a refreshed listing after histories change elsewhere\.

“Current folder” is a session\-listing scope\,&#32;not a filesystem permission boundary\.&#32;With a custom session directory\,&#32;the list is drawn from that directory\.&#32;“All projects” searches the native global store\;&#32;it is not a disk\-wide search of arbitrary custom folders\,&#32;backups\,&#32;or every nested artifact\.

The lab’s explicit session directory is separate from ordinary managed global buckets\.&#32;An empty all\-projects view in this lab does not mean the current\-folder fixtures disappeared\.

For this milestone\,&#32;cancel with Escape\.&#32;Then inspect session information and directories again\.

**What changes\?**&#32;The selector’s scope and presentation can change\.

**What does not change on cancellation\?**&#32;No selected target replaces the active conversation\.

**Recorded check\.**&#32;Actual&#32;`SessionSelectorComponent`&#32;key handling and an inert controller host confirmed cancellation\,&#32;current\-folder startup\,&#32;Tab use of preloaded global data\,&#32;and delivery of the selected record on Enter\.&#32;Physical terminal rendering was not tested\.

**Failure and recovery\.**&#32;If global loading fails\,&#32;remain in the current session and retry the picker later\.&#32;Do not interpret an empty search result as deletion\.&#32;Titles\,&#32;previews\,&#32;and status labels are navigation aids\,&#32;not a complete journal audit\.

**Self\-check\.**&#32;Does pressing Tab authorize switching to another project\?

**Answer\:**&#32;No\.&#32;It changes the search scope\.&#32;Selection is a separate action\.

### Milestone\:&#32;Resolve a known identity deliberately

The slash command can resolve a native session identity or filename prefix directly\.

**Human OMP slash command — inside the idle fictional session\:**

~~~text
/resume 11111111-1111-4111-8111-111111111111
~~~

The current slash implementation searches locally first\,&#32;then explicitly allows global fallback—even when the active manager has a custom session directory\.

It does&#32;**not**&#32;have the terminal command’s direct filesystem\-path branch\.&#32;Do not treat a slash argument containing a JSONL path as equivalent to terminal&#32;`--resume`\.

The resolver accepts case\-insensitive prefixes of\:

- The session ID\.
- The filename stem\.
- The ID portion after the timestamp separator in the filename\.

It takes the&#32;**first matching record**\,&#32;not a guaranteed unique match\.&#32;Prefer a full known identity over a short ambiguous prefix\.

**What changes\?**&#32;A successful lookup switches or reloads the matched conversation\.

**What does not change\?**&#32;It does not create a fork or restore workspace files\.&#32;A same\-file reload can still rebuild runtime context\.

**Recorded check\.**&#32;`local-global-prefix-and-unknown`&#32;confirmed local\-first resolution\,&#32;case\-insensitive identity matching\,&#32;filename matching\,&#32;interactive global fallback\,&#32;and startup\-directory confinement\.

**Failure and recovery\.**&#32;An unknown slash identity reports not found\.&#32;Do not silently pick a different conversation\.&#32;Verify the target against the supplied header or reopen the picker\.

**Self\-check\.**&#32;Is a very short prefix safer because it is easier to type\?

**Answer\:**&#32;No\.&#32;Convenience does not establish uniqueness\.

### Terminal resume is a different surface

The following table is a&#32;**non\-runnable syntax reference**\,&#32;not a record of execution\:

| Surface | Syntax | Meaning |
| --- | --- | --- |
| Human\,&#32;inside OMP | `/resume` | Open the in\-session picker\. |
| Human\,&#32;inside OMP | `/resume <id-prefix>` | Resolve a native identity\/filename prefix\,&#32;local first with global fallback\. |
| Terminal | `omp --resume` | Open the startup picker\. |
| Terminal | `omp --resume <id-or-path>` | Resolve an identity or directly open a path\-like value\. |
| Terminal | `omp --session-dir <directory> --resume <id-prefix>` | Confine startup identifier lookup to that directory\. |

The startup restriction applies to&#32;**identifier lookup**\.&#32;It is not a general prohibition on direct paths or the picker’s all\-projects scope\.&#32;The environment’s&#32;`PI_CODING_AGENT_SESSION_DIR`&#32;can also supply an explicit startup directory\.

A path\-like terminal value contains&#32;`/`&#32;or&#32;`\`\,&#32;or ends in&#32;`.jsonl`\.&#32;It goes directly to&#32;`SessionManager.open`\.

**Important path trap\:**&#32;an unknown identifier and a missing explicit file are not equivalent\.&#32;The supplied open\/set\-file implementation can treat an empty or missing explicit path as a fresh session and materialize a header there\.&#32;Do not use a typoed path as a guaranteed “not found” test\.

### Milestone\:&#32;Cancel startup and test an unknown identity

Exit the fictional session normally\:

**Human OMP slash command — inside the lab\:**

~~~text
/exit
~~~

Print the startup picker command\,&#32;then run the whole printed command\.

**Terminal shell — print the generated startup picker command\:**

~~~sh
jq -r '.commands.resumePicker' "$RECEIPT"
~~~

Cancel that picker with Escape\.

Unlike in\-session cancellation\,&#32;this cancels startup rather than returning to an already active conversation\.&#32;The source\-backed exit text is&#32;`No session selected`\.&#32;If both the current\-folder and global listings are empty\,&#32;startup instead reports&#32;`No sessions found`\.

An empty current\-folder list does not automatically switch the picker to all projects\.&#32;Startup may preload the global list\,&#32;but the scope switch remains deliberate\.

Next\,&#32;print and run the generated unknown\-identity command\:

**Terminal shell — print the generated unknown\-target command\:**

~~~sh
jq -r '.commands.unknownIdentity' "$RECEIPT"
~~~

**Expected observation\.**&#32;The native target&#32;`not-a-fictional-session`&#32;fails identifier resolution rather than silently selecting another history\.

**What changes\?**&#32;A startup attempt begins and then exits or fails\.&#32;There is no successful conversation selection in either exercise\.

**What does not follow\?**&#32;Do not infer that every piece of startup bookkeeping\,&#32;including breadcrumbs\,&#32;must remain untouched\.&#32;Re\-establish the intended conversation before the continuation exercise\.

**Recorded boundary\.**&#32;Picker component cancellation and manager\-level unknown\-ID behavior were checked\.&#32;Physical startup picker cancellation and its terminal exit text remain source\-backed rather than physically observed\.

**Self\-check\.**&#32;Which cancellation keeps a running conversation available\:&#32;in\-session&#32;`/resume`\,&#32;or terminal&#32;`--resume`\?

**Answer\:**&#32;In\-session picker cancellation\.

**Source anchors\:**&#32;`packages/coding-agent/src/slash-commands/builtin-lifecycle.ts`&#32;—&#32;`resume`&#32;and&#32;`dirs`&#32;entries\;&#32;`packages/coding-agent/src/session/session-listing.ts`&#32;—&#32;`sessionMatchesResumeArg`\,&#32;`resolveResumableSession`\;&#32;`packages/coding-agent/src/main.ts`&#32;—&#32;`createSessionManager`\,&#32;`runRootCommand`\;&#32;`packages/coding-agent/src/modes/components/session-selector.ts`&#32;—&#32;`SessionSelectorComponent`\;&#32;`packages/coding-agent/src/modes/controllers/selector-controller.ts`&#32;—&#32;`showSessionSelector`\,&#32;`handleResumeSession`\.

## Return Desk continuation

“Continue” is convenient when you mean this terminal’s applicable previous conversation\.&#32;It is a poor substitute for an exact identity when several histories are plausible\.

There is&#32;**no interactive&#32;`/continue`&#32;slash command**&#32;in this workflow\.

### Milestone\:&#32;Continue from a deliberately established breadcrumb

**Need\.**&#32;Maya wants to leave yesterday’s conversation and return to it from the same terminal\.

**Obstacle\.**&#32;The session collection also contains a newer unrelated note\,&#32;and earlier startup experiments may have changed bookkeeping\.

Re\-establish yesterday first\:

**Terminal shell — print the known resume command again\:**

~~~sh
jq -r '.commands.resumeExplicitPath' "$RECEIPT"
~~~

Run the complete printed command\.&#32;If it reaches the idle fictional session\,&#32;inspect it and exit normally without prompting\.

Then print and run the continuation command in the&#32;**same actual terminal tab**\:

**Terminal shell — print the generated continuation command\:**

~~~sh
jq -r '.commands.continue' "$RECEIPT"
~~~

After startup\,&#32;inspect again\:

**Human OMP slash commands — inside the resumed lab\,&#32;one at a time\:**

~~~text
/session info
/dirs
~~~

**Expected observation\.**&#32;Yesterday’s intended identity and fictional conversation return\.

**What changes\?**&#32;A new process adopts the selected conversation\.&#32;If continuation finds no usable history—or must honor a fresh empty boundary—it can create a new empty identity instead\.

**What does not change\?**&#32;Continuation does not restore yesterday’s workspace files\.

**Recorded checks\.**

- A controlled manager check made an older breadcrumb beat a newer file\.
- Missing breadcrumbs used most\-recent\-modified history\.
- Stale breadcrumbs pointing at missing files fell back\.
- Fresh\,&#32;unmaterialized boundaries did not resurrect earlier history\.
- Empty history created a new identity\.
- The separate source\-CLI continuation check returned yesterday’s identity and two messages with unchanged workspace bytes\.

The public helper initially gives the unrelated note a newer modification time\.&#32;Native startup and shutdown can add lifecycle records\,&#32;however\,&#32;so the files’ relative modification times may change during practice\.&#32;Seeing yesterday return verifies your selected result\;&#32;it does not by itself reproduce the controlled “older breadcrumb beats newer file” test\.

**Failure and recovery\.**&#32;If another conversation returns\,&#32;do not send work to it\.&#32;Use the exact known resume target\.&#32;If a new empty conversation appears\,&#32;check breadcrumb availability\,&#32;the terminal\,&#32;the generated environment\,&#32;and the session directory before assuming history was deleted\.

**Self\-check\.**&#32;What should you use when you know the exact conversation you need\?

**Answer\:**&#32;An exact known&#32;`--resume`&#32;identity or verified path\,&#32;not a guess based on&#32;`--continue`\.

### How continuation chooses

The current&#32;`SessionManager.continueRecent`&#32;behavior can be understood as this decision sequence\:

1. **Read the terminal\-scoped breadcrumb\.**&#32;A missing or corrupt breadcrumb is not a usable selection\.&#32;A stale breadcrumb whose file is gone is normally ignored\.
2. **Honor a fresh missing\-file boundary\.**&#32;A deliberately new session whose lazy journal never materialized must not cause old history to reappear\.&#32;Continuation starts empty instead\.
3. **Recover an interactive root if necessary\.**&#32;A breadcrumb pointing into nested session artifacts is walked upward toward the top\-level interactive journal\,&#32;with an eight\-level cap\.
4. **Use an applicable same\-cwd breadcrumb\.**&#32;This can win over a more recently modified journal\.
5. **Handle a different recorded project\.**&#32;An existing different project is not simply adopted by continuation\.&#32;Ordinarily OMP falls back to history in the current scope\.&#32;A vanished source project can trigger the relocation case in the next chapter\.
6. **Use recent history when appropriate\.**&#32;“Recent” here is based on modification time of usable session files—not title\,&#32;filename date\,&#32;or your intended topic\.
7. **Create a new session if none is found\.**

The nested\-artifact recovery rule is source\-backed\;&#32;it was not executed in the supplied Return Desk scenarios\.

### Terminal identity matters

`getTerminalId()`&#32;prefers stdin’s real TTY device path\.&#32;Terminal and multiplexer environment variables are fallbacks\.

Consequently\,&#32;the helper’s synthetic pane value does not override a real TTY\.&#32;Using another terminal tab can change which breadcrumb is applicable even when you use the same session directory\.

Breadcrumbs are stored separately from the journal\.&#32;They are navigation bookkeeping\,&#32;not an additional workspace snapshot\.

### Two startup details worth recognizing

- `autoResume`&#32;defaults to false\.&#32;When enabled and no explicit session choice or session directory takes precedence\,&#32;startup uses the continuation behavior and restores prior model\/thinking state when history is found\.
- A compatibility normalization recognizes a full UUID in the supported&#32;`--continue <UUID>`&#32;layout\,&#32;including the sole\-positional\-message case\,&#32;and turns it into resume\.&#32;Prefer explicit&#32;`--resume <id>`&#32;rather than relying on that compatibility rule\.

At the manager\-creation boundary\,&#32;`--no-session`&#32;wins over string resume and continue processing\.&#32;That does&#32;**not**&#32;prove that the separately routed bare startup picker or a later interactive resume can never open a file\.&#32;Do not combine flags and treat the combination as a privacy policy\.

**Source anchors\:**&#32;`packages/coding-agent/src/session/session-manager.ts`&#32;—&#32;`continueRecent`\,&#32;`resolveBreadcrumbToInteractiveRoot`\;&#32;`packages/coding-agent/src/session/session-paths.ts`&#32;—&#32;`writeTerminalBreadcrumb`\,&#32;`readTerminalBreadcrumbEntry`\;&#32;`packages/tui/src/ttyid.ts`&#32;—&#32;`getTerminalId`\;&#32;`packages/coding-agent/src/main.ts`&#32;—&#32;`normalizeContinueSessionArgs`\,&#32;`createSessionManager`\.

## Return Desk missing\-directory decisions

A saved conversation can outlive the project directory recorded in its header\.

That is not merely another search failure\.&#32;It raises a new question\:&#32;**where should this conversation be rooted now\?**

### Milestone\:&#32;Choose relocation or fallback consciously

**Need\.**&#32;Maya finds the right journal\,&#32;but its recorded project directory is gone\.

**Obstacle\.**&#32;Different entry paths do not ask the same question or perform the same relocation\.

This is a decision exercise\.&#32;Do not delete a directory to manufacture it\.

**Exact action\:**&#32;identify which entry path you are using\,&#32;then apply the matching row below\.&#32;For an ID\-based relocation prompt\,&#32;choose Yes only when the launch directory is the intended replacement project\.

| Entry path and condition | Current behavior | What to inspect afterward |
| --- | --- | --- |
| Startup&#32;`--resume <id>`\;&#32;recorded cwd still exists | Open the matched journal\.&#32;Startup adopts the resumed project scope and reloads cwd\-scoped settings\/caches\. | Identity\,&#32;journal file\,&#32;active directories\,&#32;applicable project settings\. |
| Startup&#32;`--resume <id>`\;&#32;recorded cwd is gone\;&#32;Yes or default answer | Re\-root that same session into the launch directory\.&#32;The journal and its artifact namespace are relocated\,&#32;not duplicated as a fork\. | Same persistent ID\;&#32;new journal location\;&#32;updated header cwd\. |
| Same ID lookup\;&#32;No | Cancel startup rather than fall through to a new conversation\. | No successful resume\;&#32;original journal remains available\. |
| Same ID lookup\;&#32;consent unavailable without a TTY | Fail with an instruction to run interactively\. | No assumed relocation\. |
| Startup&#32;`--resume <path.jsonl>`\;&#32;recorded cwd is gone | Direct opening bypasses the ID relocation prompt\.&#32;Active cwd falls back to the launch project without relocating the journal\. | Same journal location\;&#32;active cwd may differ from the retained recorded header cwd\. |
| Runtime session\-file switch\;&#32;recorded cwd is gone | Keep the current active project instead of changing into a nonexistent directory\. | Target identity\,&#32;unchanged active cwd\,&#32;original journal location\. |
| Startup picker selects a session with missing cwd | Its direct\-open path does not provide the ID\-lookup relocation consent flow\. | Launch\-cwd fallback and journal location\,&#32;rather than assumed relocation\. |
| `--continue`\;&#32;breadcrumb’s source project is gone and destination lacks its own history | Can automatically re\-root the same session into the destination\. | Same identity\,&#32;relocated journal\,&#32;destination cwd\. |
| `--continue`\;&#32;destination has its own applicable history | Prefer destination history rather than automatically adopting the vanished project’s session\. | Which identity actually won\. |

For the ID prompt\,&#32;an empty answer means acceptance in the supplied source\.&#32;Declining produces the source\-backed startup message&#32;`Resume cancelled: session was not moved.`

### What re\-rooting does not repair

Re\-rooting concerns the journal\/artifact namespace and the session’s cwd\.&#32;It does not\:

- Restore a missing repository\.
- Move or reconstruct the old workspace files\.
- Make old tool results accurate for the new directory\.
- Prove that the destination’s instructions are compatible with the old task\.
- Rewrite every historical path inside conversation content\.

A successful cross\-project resume can also change which project settings and instructions apply\.&#32;That is why cwd belongs in the ledger rather than in a footnote\.

**Recorded checks\.**&#32;Injected acceptance moved the same identity\;&#32;decline returned no manager\;&#32;unavailable consent raised the interactive\-use instruction\.&#32;Direct\-path opening retained the journal location and used launch\-cwd fallback\.&#32;Controller checks also confirmed missing\-cwd fallback and settings\-save failure before switching\.

Those answers were injected into the existing prompt seam\,&#32;not typed into a physical readline prompt\.

**Failure and recovery\.**&#32;If relocation or cwd re\-scoping fails\,&#32;stop before further work\.&#32;Inspect identity\,&#32;current journal location\,&#32;and active directories\.&#32;The supplied code includes relocation and rollback handling\,&#32;but the evidence does not establish an all\-filesystem\,&#32;all\-failure atomicity guarantee\.

**Self\-check\.**&#32;Maya opened a direct JSONL path\,&#32;and the active cwd is now the launch directory\.&#32;Has the journal necessarily moved\?

**Answer\:**&#32;No\.&#32;Direct\-path fallback and ID\-based re\-rooting are different operations\.

**Source anchors\:**&#32;`packages/coding-agent/src/main.ts`&#32;—&#32;`promptMoveSession`\,&#32;`moveMissingCwdSessionIfNeeded`\,&#32;`switchToResumedProject`\;&#32;`packages/coding-agent/src/session/session-manager.ts`&#32;—&#32;`open`\,&#32;`setSessionFile`\,&#32;`moveTo`\,&#32;`continueRecent`\;&#32;`packages/coding-agent/src/config/settings.ts`&#32;—&#32;`reloadForCwd`\.

## Decision Desk forking

Eli wants to explore another direction while keeping the original conversation available\.

A fork is useful for that\.&#32;It separates conversation identity and future journal ownership\.&#32;It does not isolate workspace changes\.

### Milestone\:&#32;Fork the current conversation

**Need\.**&#32;Keep the original Lantern Board discussion while creating another conversational starting point\.

**Obstacle\.**&#32;The word “fork” can suggest a Git branch\,&#32;a file snapshot\,&#32;or a previous\-message picker\.&#32;The supplied&#32;`/fork`&#32;does none of those things\.

Use the complete&#32;[Decision Desk recipe](<https://present-sketch-tp94.here.now/examples/decision-desk/commands.txt>)&#32;and its&#32;[state ledger](<https://present-sketch-tp94.here.now/examples/decision-desk/state-ledger.json>)\.

Create a&#32;**new**&#32;disposable lab for this story\.&#32;Preserve the previous receipt separately if you still need it\.

**Terminal shell — Decision Desk preparation\,&#32;from extracted&#32;`examples/`\:**

~~~sh
bun return-desk/lab.ts '{"version":1,"op":"discover"}'
RECEIPT=$(mktemp -t continuity-decision-receipt)
bun return-desk/lab.ts '{"version":1,"op":"act","action":"create"}' | tee "$RECEIPT"
~~~

Stop on&#32;`ok: false`\.&#32;Then retain its paths and the intended executable\:

**Terminal shell — read the successful Decision Desk receipt\:**

~~~sh
LAB=$(jq -r '.root' "$RECEIPT")
SOURCE=$(jq -r '.yesterday' "$RECEIPT")
SESSIONS=$(jq -r '.sessions' "$RECEIPT")
PROJECT=$(jq -r '.cwd' "$RECEIPT")
OMP_BIN=$(command -v omp)
~~~

The same prerequisites and stop rules from&#32;[Return Desk lab setup](<https://present-sketch-tp94.here.now/chapters/continuity-return-desk-lab-setup#continuity-return-desk-lab-setup>)&#32;apply\.

Record the parent and workspace before opening OMP\:

**Terminal shell — read\-only baseline\:**

~~~sh
shasum -a 256 "$SOURCE"
jq -s 'map(select(.type == "session") | {id,parentSession,cwd,title})' "$SOURCE"
cat "$PROJECT/today.txt"
jq -r '.commands.resumeExplicitPath' "$RECEIPT"
~~~

Run the complete printed resume command\.&#32;Stop if it requires model\/auth setup\.&#32;Do not submit a prompt\.

Inside the idle fictional conversation\:

**Human OMP slash command — create the conversational fork\:**

~~~text
/fork
~~~

Then inspect the child\:

**Human OMP slash commands — inside the fork\,&#32;one at a time\:**

~~~text
/session info
/dirs
~~~

Keep the child active for the reset sequence in the next chapter\.

### The fork ledger

A successful interactive fork has these source\-backed effects\:

| Field | Expected result |
| --- | --- |
| Persistent identity | A new journal ID\. |
| Transcript path | A new JSONL path\. |
| Parent metadata | `parentSession`&#32;records the previous persistent ID\. |
| Title and cwd | Retained from the current session\. |
| Live conversation | Retained\. |
| Ordinary steering\/follow\-up queues | Retained by this fork path\. |
| Journal entries | All existing non\-header entries are carried into the new journal—not only the currently visible messages\. |
| Workspace files | Still the same files in the same workspace\. |
| Session artifacts | Copied recursively where available\,&#32;best\-effort\. |

For this public source\,&#32;the child’s parent ID should be\:

`11111111-1111-4111-8111-111111111111`

The current registry description mentions a fork “from a previous message\.” The implementation route is more specific\:&#32;`handleForkCommand`&#32;calls&#32;`AgentSession.fork()`&#32;immediately\.&#32;**This is a current\-state fork\,&#32;not a previous\-message picker\.**

A copied journal can retain older branches and pre\-clear entries that are not in current model context\.&#32;Forking is therefore not a way to reduce the disclosure content of a later export\.

### Artifacts are not a verified backup

`copySessionArtifacts`&#32;derives the artifact directory by removing&#32;`.jsonl`&#32;from the transcript filename\.

- Missing sources are ignored\.
- Other copy errors are logged rather than thrown from that helper\.
- The fork can therefore exist without every expected artifact having copied\.

That copy does not cover arbitrary workspace files\,&#32;remote copies\,&#32;previously generated exports elsewhere\,&#32;or a guaranteed complete backup of every dependency\.

**Recorded checks\.**&#32;The real SDK\/registry\/controller fork check created a new identity with parent metadata\,&#32;retained messages and ordinary queues\,&#32;preserved parent bytes around the fork call\,&#32;and copied one ordinary artifact\.

The same test then wrote a fictional file in the shared workspace and confirmed that the parent manager saw the changed file at the same cwd\.&#32;The write was a test action\,&#32;not something&#32;`/fork`&#32;performed\.

A separate real&#32;`copySessionArtifacts`&#32;check encountered a regular file blocking the destination directory\.&#32;It returned without throwing and did not copy the artifact\.&#32;That supports the best\-effort warning\;&#32;it is not broad filesystem coverage\.

**Failure and recovery\.**&#32;Interactive&#32;`/fork`&#32;refuses while streaming\.&#32;An in\-memory session cannot fork through this path\.&#32;A before\-switch hook can veto it\.&#32;After any refusal or error\,&#32;inspect the actual file and identity before proceeding\.

**Self\-check\.**&#32;If Eli later changes a workspace file while working in the fork\,&#32;will resuming the parent undo that file change\?

**Answer\:**&#32;No\.&#32;The conversation identities differ\;&#32;the workspace is shared\.

### Interactive and startup forks differ

This is a source\-backed comparison\,&#32;not another executed scenario\:

| Boundary | Interactive&#32;`/fork` | Startup&#32;`--fork` |
| --- | --- | --- |
| Source | Current session | Explicit path or resolved native identity |
| Cwd | Existing active cwd | Launch cwd supplied to startup |
| Conversation | Retains current live messages | Rebuilds from copied journal history |
| Parent\-process queues | Ordinary queues remain on the active fork path | No parent\-process steering\/follow\-up queues are cloned |
| Journal | New identity\;&#32;original retained | New identity\;&#32;source retained |
| Artifacts | Best\-effort copy | Best\-effort copy by default |
| Workspace isolation | None | None |

Full forks may inherit a provider prompt\-cache identity\.&#32;That is separate from journal identity and from provider request identity\.&#32;Changing the startup model\,&#32;thinking\,&#32;prompt\,&#32;or tool shape can suppress automatic inheritance\;&#32;explicit provider\/cache overrides are separate controls\.

You do not need to tune those controls for this workbook\.&#32;The important conclusion is simpler\:&#32;**a new journal is not proof of a cold provider cache\,&#32;and a fork is not&#32;`/fresh`\.**

**Source anchors\:**&#32;`packages/coding-agent/src/slash-commands/builtin-session.ts`&#32;—&#32;`fork`&#32;entry\;&#32;`packages/coding-agent/src/modes/controllers/command-controller.ts`&#32;—&#32;`handleForkCommand`\;&#32;`packages/coding-agent/src/session/agent-session.ts`&#32;—&#32;`fork`\;&#32;`packages/coding-agent/src/session/session-manager.ts`&#32;—&#32;`fork`\,&#32;`forkFrom`\,&#32;`copySessionArtifacts`\;&#32;`packages/coding-agent/src/main.ts`&#32;—&#32;`createSessionManager`\,&#32;`buildSessionOptions`\.

## Decision Desk resetting deliberately

Eli now has a conversational fork\.&#32;Before changing it\,&#32;write down the intended boundary\:

- Keep the conversation\,&#32;refresh local provider\-facing state\:&#32;**fresh**\.
- Drop the live\/model conversation but keep this journal identity\:&#32;**clear**\.
- Start another conversation identity\:&#32;**new**\.

These operations are not escalating levels of secure deletion\.

### A before\-and\-after reset ledger

The table describes successful native operations\.&#32;“Identity” means persistent journal identity\.

| State | `/fresh` | `/clear` | `/new` |
| --- | --- | --- | --- |
| Live conversation | Retained | Cleared | Cleared |
| Existing journal history | Unchanged by the operation | Retained\;&#32;reset boundary appended | Retained in the previous journal |
| Persistent ID and path | Retained | Retained | New ID and allocated path |
| Title | Retained | Retained | Ordinary new session starts without the prior title |
| Model and settings | Retained | Retained | Not a factory reset\;&#32;current model\/settings remain applicable |
| Provider\-facing state | Local handles closed\;&#32;fresh transient ID | Local handles closed\;&#32;fresh transient ID | Local handles closed\;&#32;normal identity selection resynchronized |
| Ordinary queues | Retained | Cleared | Cleared |
| Cwd and workspace | Unchanged | Unchanged | Current project remains |
| Prior artifacts and exports | Retained | Retained | Prior artifacts retained\,&#32;not copied as a fork |
| Active plan reference | Retained | Path retained and reference re\-armed | Ordinary new\-session reference resets to its default |

Normally the provider\-facing ID after&#32;`/new`&#32;follows the new journal ID\.&#32;An explicit provider\-session override is a separate input\;&#32;do not turn that normal case into a universal identity rule\.

### Milestone\:&#32;Refresh the provider boundary without losing the conversation

**Need\.**&#32;Eli wants to discard local provider transport\/session state while retaining the conversation\.

**Obstacle\.**&#32;“Fresh session” sounds like an empty conversation\,&#32;but this command is deliberately narrower\.

In the idle fictional fork\:

**Human OMP slash command\:**

~~~text
/fresh
~~~

**Expected observation\.**&#32;The visible conversation remains\.&#32;The status reports how many local provider states were pruned\.&#32;Zero can be normal\.

The implementation closes cached provider\-state entries\,&#32;clears the local map\,&#32;mints a fresh provider\-facing ID\,&#32;rekeys associated session\-memory identity\,&#32;and invalidates append\-only context for rebuilding\.

It does not append a reset boundary\,&#32;change the journal header ID\,&#32;or clear ordinary steering\/follow\-up queues\.&#32;The next model\-context build uses the retained conversation—not every old entry that might remain elsewhere in the journal\.

**Recorded check\.**&#32;One inert provider handle received&#32;`close()`\.&#32;The provider\-facing ID changed\;&#32;persistent journal ID\,&#32;file\,&#32;bytes\,&#32;messages\,&#32;and ordinary queues remained unchanged\.

That is local state\-transition evidence\.&#32;It does not prove that a real provider outage\,&#32;authentication failure\,&#32;remote conversation problem\,&#32;or retention concern was fixed\.

**Failure and recovery\.**&#32;If a response is still streaming\,&#32;wait or abort through OMP’s normal control\,&#32;then retry\.&#32;A pruning count is not a remote deletion receipt\.&#32;Even local close errors can be logged while the handle map is cleared\.

**Self\-check\.**&#32;Does a changed&#32;`AgentSession.sessionId`&#32;after&#32;`/fresh`&#32;prove that a new durable conversation was created\?

**Answer\:**&#32;No\.&#32;That getter is provider\-facing\.&#32;Compare the persistent header ID and file\.

### Milestone\:&#32;Clear live context while keeping the journal

**Need\.**&#32;Eli wants the next conversation to stop depending on the current turns\,&#32;but wants to continue this persistent session\.

**Obstacle\.**&#32;A blank live transcript could be mistaken for erased history\.

In the idle fictional fork\:

**Human OMP slash command\:**

~~~text
/clear
~~~

**Expected observation\.**&#32;The live transcript clears and OMP reports a context reset while the session continues\.

The source\-backed reset drops\:

- Live messages\.
- Steering\,&#32;follow\-up\,&#32;and pending next\-turn messages\.
- Pending tool calls and error state\.
- Deferred session\-scoped tool decisions and internal per\-turn state\.
- This agent’s scheduled continuation work and owned asynchronous jobs\.

It also rotates provider\-facing state and refreshes applicable base\/project context\.&#32;Clearing conversation messages does not disable project instructions or erase a separate memory backend\.

The persistent ID\,&#32;path\,&#32;title\,&#32;cwd\,&#32;model\,&#32;settings\,&#32;and active plan path remain\.&#32;A&#32;`reset_boundary`&#32;is appended to the journal\.

Subsequent model\-context and collapsed\-live rebuilds honor that boundary\.&#32;Full\-history paths still retain earlier entries\.&#32;This distinction survives reopening\;&#32;it is not merely a temporary screen clear\.

**Recorded check\.**&#32;The real reset retained identity\,&#32;path\,&#32;title\,&#32;model\,&#32;and cwd\;&#32;cleared messages\,&#32;ordinary queues\,&#32;and a synthetic pending tool marker\;&#32;and appended one reset boundary\.&#32;Reopening rebuilt an empty context after the boundary while full\-history reconstruction retained the earlier fictional messages\.

The test also appended one fictional post\-clear message and observed only that message in rebuilt model context\.&#32;It did not send a next provider request\.

**Failure and recovery\.**&#32;`/clear`&#32;refuses while streaming or while foreground bash\/Python work is running\.&#32;Its controller first aborts active compaction and waits for it to stop\.&#32;The warning text does not enumerate every predicate\.

If reset throws after beginning\,&#32;do not assume nothing changed\.&#32;Inspect the journal boundary and active state before retrying\;&#32;this reset is not presented as an all\-stage transaction\.

**Self\-check\.**&#32;Can a later full HTML export still include the conversation from before&#32;`/clear`\?

**Answer\:**&#32;Yes\.&#32;Clear is a context boundary\,&#32;not an export scrubber\.

### Milestone\:&#32;Start a different identity without deleting the old one

**Need\.**&#32;Eli wants another conversation rather than continuing the fork’s identity\.

**Obstacle\.**&#32;An empty new journal may not yet exist as a physical file\.

**Human OMP slash command\:**

~~~text
/new
~~~

**Expected observation\.**&#32;OMP starts an empty conversation under a new persistent identity and allocated path\.

Ordinary&#32;`/new`\:

- Preserves the prior journal and workspace\.
- Does not create a fork parent link\.
- Clears messages and queues\.
- Aborts current work after the veto point rather than using the same streaming refusal as&#32;`/fresh`\.
- Refreshes base\/project prompt context\.
- Does not reset the whole installation’s settings\.

A new session can contain initial metadata without having a materialized transcript file\.&#32;Therefore\,&#32;an absent third JSONL file is not evidence that the identity failed to change\.

**Recorded check\.**&#32;The real new\-session transition allocated another identity and path\,&#32;emptied messages and ordinary queues\,&#32;and retained the previous journal and workspace\.&#32;Its immediate identity was measured directly in the SDK ledger\,&#32;not inferred from file count\.

**Failure and recovery\.**&#32;A before\-switch hook can veto&#32;`/new`\.&#32;Transient UI may already have been cleared by the controller\,&#32;so a changed status area is not proof of a new identity\.&#32;Other failures need inspection\;&#32;there is no universal rollback promise for this path\.

**Self\-check\.**&#32;Does ordinary&#32;`/new`&#32;delete the fork you just left\?

**Answer\:**&#32;No\.

### Inspect the saved outcome

Exit without sending a model prompt\.

**Human OMP slash command\:**

~~~text
/exit
~~~

Then inspect only the lab’s files\.

**Terminal shell — read\-only comparison\,&#32;in the same shell holding the receipt variables\:**

~~~sh
jq -s 'map(select(.type == "session") | {id,parentSession,cwd,title})' "$SESSIONS"/*.jsonl
shasum -a 256 "$SOURCE"
jq -s 'map(select(.type == "reset_boundary") | {type,id,parentId})' "$SESSIONS"/*.jsonl
jq -s 'map(select(.type == "message") | .message.content)' "$SESSIONS"/*.jsonl
cat "$PROJECT/today.txt"
~~~

Look for retained parent history\,&#32;a child header linked to the original identity\,&#32;a reset boundary in the cleared child\,&#32;and retained fictional messages\.

These queries aggregate saved journals\.&#32;**They do not measure a now\-closed process’s live context\.**&#32;Correlate the child header with its recorded parent and the child file path you noted\.&#32;Inspect that individual file when you need per\-file evidence\.

The original hash is useful evidence\,&#32;but do not demand universal byte equality across native startup and shutdown\.&#32;Lifecycle records can be added outside the narrower fork call\.&#32;The recorded parent\-byte assertion was scoped to that call\.

### Milestone\:&#32;Fork from the original at startup

**Need\.**&#32;Eli wants another identity from a saved conversation without first opening it interactively\.

**Obstacle\.**&#32;Startup has a launch cwd and no parent process’s ordinary message queues to clone\.

Print the supplied startup fork command\:

**Terminal shell — print the generated native startup fork command\:**

~~~sh
jq -r '.commands.forkExplicitPath' "$RECEIPT"
~~~

Run the entire printed command\.&#32;Stop on setup\;&#32;do not prompt\.&#32;If startup succeeds\,&#32;inspect session information and directories\,&#32;then exit normally\.

This recipe supplies the copied project as launch cwd\.&#32;It therefore does not visually demonstrate two different project directories\.&#32;The separate parser\/manager check did use a different launch cwd and confirmed the rule\.

**Expected observation\.**&#32;A new child identity retains source history and records the original persistent ID as parent\.&#32;The source remains available\.&#32;Session artifacts are copied best\-effort by default\;&#32;workspace files are not copied\.

**Recorded checks\.**&#32;The parser\/manager test confirmed launch\-cwd behavior and rejection of&#32;`--fork`&#32;with&#32;`--no-session`\.&#32;The separate source\-CLI startup fork returned a new identity\,&#32;two retained fictional messages\,&#32;and unchanged workspace bytes\.

Source\-ID fork lookup is implemented but was not the exercised startup\-fork target form\;&#32;the recorded startup examples used explicit paths\.

**Failure and recovery\.**&#32;Unknown source IDs fail\.&#32;`--fork`&#32;with&#32;`--no-session`&#32;is invalid\.&#32;After a write or artifact error\,&#32;inspect the child journal and artifact availability before calling the result a complete copy\.

**Self\-check\.**&#32;Does startup fork inherit queued follow\-up messages from an old OMP process\?

**Answer\:**&#32;No\.&#32;It rebuilds from saved history\,&#32;not another process’s queues\.

**Source anchors\:**&#32;`packages/coding-agent/src/session/agent-session.ts`&#32;—&#32;`freshSession`\,&#32;`resetSessionContext`\,&#32;`newSession`\,&#32;`sessionId`\;&#32;`packages/agent/src/agent.ts`&#32;—&#32;`reset`\;&#32;`packages/coding-agent/src/modes/controllers/command-controller.ts`&#32;—&#32;`handleFreshCommand`\,&#32;`handleResetContextCommand`\,&#32;`handleClearCommand`\,&#32;`#runNewSessionFlow`\;&#32;`packages/coding-agent/src/session/session-context.ts`&#32;—&#32;`buildSessionContext`\.

## Decision Desk refusals and failures

The successful path tells you what an operation is for\.&#32;Refusals and partial failures tell you when not to trust a convenient status message\.

This chapter uses failure cards\.&#32;Do not manufacture a live stream\,&#32;run a foreground job\,&#32;install a cancelling extension\,&#32;or trigger a provider failure for the exercise\.

### Milestone\:&#32;Classify the boundary before retrying

**Need\.**&#32;Eli sees a warning\,&#32;a blank\-looking UI region\,&#32;or an error after a transition\.

**Obstacle\.**&#32;“It did not finish normally” does not have one universal meaning\.

**Exact action\:**&#32;stop submitting work\,&#32;identify the operation and failure phase\,&#32;then inspect session information\,&#32;directories\,&#32;retained messages\,&#32;and relevant journal files\.

| Situation | What the current implementation does | Safe response |
| --- | --- | --- |
| `/fresh`&#32;while streaming | Refuses the refresh\. | Wait or abort normally\,&#32;then retry if refreshing provider state is still the goal\. |
| Interactive&#32;`/fork`&#32;while streaming | Controller refuses the fork\. | Do not assume it is queued to run later\. |
| `/clear`&#32;during streaming or foreground bash\/Python work | Reset method refuses\. | Let work settle or abort it normally\;&#32;inspect any file effects separately\. |
| `/clear`&#32;during compaction | Controller aborts and waits for compaction before attempting reset\. | A later refusal does not mean no maintenance action happened\. |
| `/new` | Offers a veto point\,&#32;then aborts current work and transitions\. | Do not treat it as a streaming\-guard equivalent\. |
| Fork in an in\-memory session | Returns failure\;&#32;controller reports “not persisted or cancelled\.” | Use the persistent fictional lab\,&#32;not a guessed save flag\. |
| Mode\-specific transition restriction | Some modes can block new\/fork transitions\. | Respect the restriction\;&#32;do not bypass it to make the workbook pass\. |

A refusal at the guarded method’s entry can preserve conversation state while the surrounding controller has already cleared editor text\,&#32;stopped a status indicator\,&#32;or cancelled maintenance\.

**Recorded check\.**&#32;Synthetic streaming and foreground predicates exercised the actual guards without running a real stream or foreground executable\.&#32;The tested conversation ledger remained unchanged\.

**Self\-check\.**&#32;Does “wait or abort” mean the refused command will automatically execute after the response ends\?

**Answer\:**&#32;No\.&#32;Inspect the settled state and deliberately retry the intended command\.

### Hooks can veto new\,&#32;fork\,&#32;and resume

An installed extension can return a cancellation result from&#32;`session_before_switch`&#32;for the reasons&#32;`new`\,&#32;`fork`\,&#32;or&#32;`resume`\.

You do not need to author a hook to understand the consequence\:

- Before\-event cancellation is a legitimate refusal\.
- A transient UI clear is not an identity measurement\.
- Ordinary queues that survived a veto still belong to the unchanged session\.
- A generic fork failure message does not distinguish every cause\.

**Recorded check\.**&#32;A real SDK extension vetoed fork\,&#32;new\,&#32;and resume\.&#32;Identity\,&#32;messages\,&#32;journal bytes\,&#32;and ordinary steering\/follow\-up queues were preserved in those checks\.

The native public lab disables extension discovery\.&#32;It is not intended to reproduce that hook scenario interactively\.

### The resume success\-status caveat

The supplied&#32;`SelectorController.handleResumeSession()`&#32;awaits&#32;`session.switchSession()`&#32;but does not check its boolean result before repainting and reporting success\.

Two separate checks establish the relevant boundaries\:

1. A controller test supplied a&#32;`false`&#32;switch result and observed a&#32;`Resumed session`&#32;status despite unchanged identity\.
2. The Decision Desk SDK test used a real hook cancellation and verified that underlying session state stayed unchanged\.

Those are complementary checks\,&#32;not a claim that a physical TUI with a real cancelling hook was exercised end to end\.

**Practical rule\:**&#32;after a questionable resume\,&#32;trust the actual session file\,&#32;identity\,&#32;and directories—not the success wording alone\.

### A failed switch has phases

The resume controller first flushes pending settings\.&#32;In the recorded preflight failure\,&#32;switching never began\.

Inside&#32;`AgentSession.switchSession`\,&#32;the operation then includes a veto point\,&#32;abort\/flush preparation\,&#32;a captured local\-state snapshot\,&#32;target loading\,&#32;context replacement\,&#32;restoration of available model\/thinking\/service\-tier information\,&#32;and reconciliation\.

The guarded target\-load block has rollback handling for many captured local fields\.

**Recorded check\.**&#32;Failure injected after actual target loading restored prior identity\,&#32;messages\,&#32;provider\-facing identity\,&#32;and ordinary queues\,&#32;then rethrew\.

Do not extend that result into any of these claims\:

- Every failure before the captured snapshot is rolled back\.
- Closed physical provider handles are recreated\.
- External effects or completed file edits are undone\.
- `/new`\,&#32;`/fork`\,&#32;and&#32;`/clear`&#32;share the same transaction boundary\.
- A mode or prompt\-refresh error always means the session switch was rolled back\.

The supplied switch path catches some post\-switch reconciliation and prompt\-refresh errors and logs them without undoing an otherwise committed switch\.&#32;The interactive cwd adapter is also a separate layer\.

A resumed interrupted conversation can receive a synthetic abort record\.&#32;Resume is not a promise that opening and closing every journal is byte\-for\-byte read\-only\.

### Warning only\:&#32;drop is not an erasure exercise

**Do not run&#32;`/drop`&#32;in this workbook\.**

The inspected command path requests best\-effort deletion of the previous session file and its artifact directory\,&#32;then starts a new session\.&#32;It has no confirmation dialog in that path\.

`AgentSession.newSession({ drop: true })`&#32;catches and logs a deletion failure and can still proceed\.&#32;The UI can consequently show&#32;`Session dropped`&#32;after incomplete deletion\.

That status does not establish secure erasure\.&#32;Forks\,&#32;exports\,&#32;backups\,&#32;temporary sidecars\,&#32;clipboard history\,&#32;workspace files\,&#32;and provider\-held copies may remain\.&#32;A session without a file is refused by the drop controller\.

Drop was not executed in the supplied proof\.&#32;Its warning is source inspection\,&#32;not a deletion result\.

**Self\-check\.**&#32;Which is stronger evidence\:&#32;`Session dropped`\,&#32;or an appropriately scoped retention\/deletion audit\?

**Answer\:**&#32;The audit\.&#32;The status label is not an erasure receipt\.

**Source anchors\:**&#32;`packages/coding-agent/src/session/agent-session.ts`&#32;—&#32;`newSession`\,&#32;`fork`\,&#32;`switchSession`\,&#32;`resetSessionContext`\;&#32;`packages/coding-agent/src/modes/controllers/selector-controller.ts`&#32;—&#32;`handleResumeSession`\;&#32;`packages/coding-agent/src/modes/controllers/command-controller.ts`&#32;—&#32;`handleDropCommand`\,&#32;`#runNewSessionFlow`\;&#32;`packages/coding-agent/src/slash-commands/builtin-lifecycle.ts`&#32;— lifecycle command routes\.

## Review Packet choosing a snapshot

Noor needs to understand the fictional Harbor Notes review\,&#32;including an earlier draft and the nested checklist review\.

The first decision is not “text or HTML\?” It is&#32;**which state should the packet represent\?**

### Milestone\:&#32;Choose the layer before creating an artifact

**Need\.**&#32;Produce a reviewable account of the work\.

**Obstacle\.**&#32;A live\-context dump can omit earlier journal history while including current system and tool context\.&#32;A saved\-file export can include earlier history without reconstructing a running agent’s current prompt\.

Read the&#32;[Review Packet inspection recipe](<https://present-sketch-tp94.here.now/examples/review-packet/inspection-recipe.json>)\.&#32;Keep its interactive examples as reference\;&#32;the runnable public packet exercise uses the fictional saved journals\.

| Operation | Snapshot source | Output and important limit |
| --- | --- | --- |
| TUI&#32;`/dump` | Current live messages\,&#32;current system prompt\,&#32;configuration\,&#32;and tool inventory | Clipboard text\,&#32;plus an attempted temporary JSON sidecar\.&#32;Not an all\-journal export\. |
| Dump sidecar | Current messages after the session’s LLM conversion boundary\,&#32;plus current model\/configuration\,&#32;raw prompt\,&#32;and wire tool schemas | Persistent temporary JSON file\.&#32;Not an exact captured HTTP request\. |
| Slash&#32;`/export` | Session\-manager header\,&#32;all entries\,&#32;leaf\,&#32;current system prompt\,&#32;and tool names\/descriptions | Local HTML\;&#32;adjacent nested transcripts included by default\.&#32;TUI requests an OS open afterward\. |
| Terminal&#32;`--export` | Saved journal and adjacent nested transcripts | Local HTML and printed path\.&#32;No running&#32;`AgentSession`&#32;prompt\/tool inventory is reconstructed\. |

For valid saved inputs and distinct output paths\,&#32;dump and export do not append a “dumped” or “exported” journal entry\.&#32;Output creation is still a filesystem effect\.

**What changes\?**&#32;Choosing the operation determines which data is copied into another artifact\.

**What does not change\?**&#32;Neither operation is a conversation reset or a disclosure scrub\.

**Recorded check\.**&#32;The Harbor Notes dump contained current context but not the pre\-clear message\.&#32;HTML embedded pre\-clear history\.&#32;Live\-state HTML included explicitly fictional current system\/tool metadata\;&#32;file\-based HTML did not supply those top\-level live fields\.

**Failure and recovery\.**&#32;If the chosen snapshot does not contain the required evidence\,&#32;choose the correct layer\.&#32;Do not solve a missing\-history problem by claiming the live dump is complete\,&#32;and do not solve a privacy problem by hiding a viewer panel\.

**Self\-check\.**&#32;Noor needs the earlier blue\-cover discussion\.&#32;Is a post\-clear live dump sufficient\?

**Answer\:**&#32;Not necessarily\.&#32;The saved journal\/full HTML path retains that earlier entry\.

### What dump can contain

`AgentSession.formatSessionAsText()`&#32;passes current messages to&#32;`formatSessionDumpText`\.

Depending on what those messages contain\,&#32;the text can include\:

- System prompt blocks\.
- Model and thinking configuration\.
- Tool descriptions and parameter information\.
- User\,&#32;developer\,&#32;and assistant messages\.
- Stored thinking blocks and tool\-call arguments\.
- Tool results\.
- Bash and Python execution records\.
- Custom\/hook messages\.
- File\-mention contents\.
- Branch and compaction summaries\.

The formatter omits bash\/Python execution records marked&#32;`excludeFromContext`\.&#32;That omission is specific to this formatting path\.&#32;It is not a universal journal or export exclusion policy\.

When tool descriptors are already in the system prompt\,&#32;the formatter can avoid duplicating the separate tool inventory\.&#32;That does not make the information absent from the dump\.

Do not reproduce real system instructions\,&#32;private dumps\,&#32;credentials\,&#32;or hidden reasoning in a public workbook or issue packet\.&#32;This workbook uses fictional examples precisely so those risks can be taught without publishing them\.

### Milestone\:&#32;Account for the sidecar separately

**Need\.**&#32;Noor wants to know whether the dump produced another file that also needs review\.

**Obstacle\.**&#32;A clipboard\-oriented command can leave persistent temporary data behind\.

The following is a&#32;**non\-runnable reference**&#32;to the human command\.&#32;Do not run it against personal history as a workbook exercise\.

~~~text
/dump
~~~

The sidecar filename follows the source’s&#32;`omp-llm-request-`&#32;naming pattern under the operating system’s temporary directory\.

Its fields include\:

| Field | Review concern |
| --- | --- |
| `model` | Model metadata is more than a short display label\. |
| `thinkingLevel` | Configuration context\. |
| `serviceTier` | Per\-family request configuration\. |
| `systemPrompt` | Raw current prompt blocks can contain sensitive context\. |
| `tools` | Descriptions and wire schemas can disclose implementation details\. |
| `messages` | Converted current messages can contain sensitive content\. |

The sidecar is built locally at the conversion boundary\.&#32;It is not proof that a provider request occurred\,&#32;and it is not necessarily identical to the final provider\-specific payload after all later transformations\.

In the TUI\,&#32;the command attempts the sidecar first\,&#32;appends its path and a raw\-context warning to the text\,&#32;then copies that text to the clipboard\.

The shared plain\-output builtin returns the text instead of invoking the TUI clipboard path\.

**Recorded check\.**&#32;Sidecars were written from fictional live and in\-memory sessions\.&#32;Injected conversion failure left the plain transcript available\.&#32;No clipboard operation occurred in those tests\.

**Failure and recovery\.**

- Sidecar failure does not suppress the plain transcript\.
- The TUI source reports sidecar unavailability\;&#32;the plain\-output handler silently omits the failed sidecar path\.
- If clipboard copying fails after sidecar creation\,&#32;a sidecar may still exist\.
- A temporary filename does not mean automatic deletion after the command\.

Also\,&#32;zero live messages do not guarantee the text&#32;`No messages to dump yet.`&#32;The current formatter always produces a Configuration section\.&#32;The sidecar method explicitly returns no path for zero messages\,&#32;but the formatted text can still be nonempty\.

**Self\-check\.**&#32;Does&#32;`--no-session`&#32;mean&#32;`/dump`&#32;cannot leave anything on disk\?

**Answer\:**&#32;No\.&#32;In\-memory conversation formatting can still write a temporary sidecar\.

**Source anchors\:**&#32;`packages/coding-agent/src/session/agent-session.ts`&#32;—&#32;`formatSessionAsText`\,&#32;`dumpLlmRequestToTmpDir`\,&#32;`exportToHtml`\;&#32;`packages/coding-agent/src/session/session-dump-format.ts`&#32;—&#32;`formatSessionDumpText`\;&#32;`packages/coding-agent/src/slash-commands/builtin-collaboration.ts`&#32;—&#32;`dump`\,&#32;`export`\;&#32;`packages/coding-agent/src/modes/controllers/command-controller.ts`&#32;—&#32;`handleDumpCommand`\.

## Review Packet local HTML export

The runnable packet exercise uses only the downloaded Harbor Notes journals\.&#32;It does not require a running model session\.

### Milestone\:&#32;Export the fictional saved history

**Need\.**&#32;Noor wants a local review packet containing the saved history\.

**Obstacle\.**&#32;Exporting from a running session and exporting from a file have different metadata boundaries\.

Prerequisites\:&#32;an already installed OMP compatible with this journal format\/export implementation\,&#32;a disposable extracted examples directory\,&#32;and&#32;`jq`\/`shasum`&#32;for the inspections below\.&#32;No login or provider request is part of this exercise\.

Verify the known input before using it\:

**Terminal shell — read\-only input check\,&#32;from extracted&#32;`examples/`\:**

~~~sh
jq -s 'map(select(.type == "session") | {id,cwd,title})' review-packet/harbor-review.jsonl
shasum -a 256 review-packet/harbor-review.jsonl review-packet/harbor-review/Scout.jsonl review-packet/harbor-review/Scout/Checklist.jsonl
~~~

Use a fresh copy of the examples so the output filename does not replace a useful earlier packet\.

**Terminal shell — supplied native local export recipe\:**

~~~sh
omp --export review-packet/harbor-review.jsonl review-packet/harbor-review.html
~~~

**Expected observation\.**&#32;OMP prints&#32;`Exported to: review-packet/harbor-review.html`&#32;and writes that local file\.

The source handles terminal export before normal session startup\.&#32;It opens a manager with breadcrumb suppression\,&#32;reads the valid journal\,&#32;gathers adjacent nested transcripts\,&#32;generates HTML\,&#32;and exits\.&#32;It does not construct a running&#32;`AgentSession`\.

The file export does not reconstruct a current live system prompt or tool inventory\.&#32;Saved entries themselves can still contain system\,&#32;tool\,&#32;file\,&#32;or other sensitive data\;&#32;absence of top\-level live fields is not a privacy guarantee\.

**What changes\?**&#32;A local HTML output is created\.

**What does not change in the checked valid\-input case\?**&#32;The journal remains unchanged\;&#32;no active conversation is switched\.

Repeat the byte inspection\:

**Terminal shell — read\-only post\-export comparison\:**

~~~sh
shasum -a 256 review-packet/harbor-review.jsonl review-packet/harbor-review/Scout.jsonl review-packet/harbor-review/Scout/Checklist.jsonl
~~~

**Recorded check\.**&#32;Valid fictional exports retained journal bytes and embedded the expected older and nested history\.&#32;A real source\-CLI export completed successfully\,&#32;including a filename containing spaces\.

**Failure and recovery\.**&#32;If writing fails\,&#32;inspect the target path and output before retrying\.&#32;Do not assume an automatic retry\,&#32;a transactional output\,&#32;or a particular missing\-directory creation policy\.&#32;Never choose a journal or useful workspace file as the output path\:&#32;the implementation writes to the requested output path\.

**Self\-check\.**&#32;Does creating HTML also publish it through&#32;`/share`\?

**Answer\:**&#32;No\.&#32;Local file generation and sharing are separate operations\.

### Milestone\:&#32;Keep shell quoting separate from slash parsing

**Need\.**&#32;Noor wants a filename containing a space\.

**Obstacle\.**&#32;Terminal shell quoting and human slash\-command parsing are not the same parser\.

The supplied terminal recipe preserves the spaced filename as one argument\:

**Terminal shell — supplied quoted\-output recipe\,&#32;from extracted&#32;`examples/`\:**

~~~sh
omp --export review-packet/harbor-review.jsonl 'review-packet/harbor review.html'
~~~

**Recorded check\.**&#32;The actual source CLI exported successfully to a spaced output filename\.&#32;This is not a claim that interactive slash quoting works\.

The following table is a&#32;**non\-runnable human slash\-command reference**\:

| Reference | Current behavior |
| --- | --- |
| `/export packet.html` | Write local HTML with current live prompt\/tool descriptions and all manager entries\;&#32;TUI requests OS opening\. |
| `/export --themes packet.html` | Use the selected TUI dark\/light themes rather than the default web palettes\. |
| `/export packet.html --themes` | The parser also accepts this ordering\. |
| `/export "packet review.html"` | Rejected as multiple whitespace\-delimited path tokens\.&#32;Quotes do not preserve the space\. |
| `/export one.html two.html` | Rejected with usage\. |
| `/export --copy` | Warns to use&#32;`/dump`\;&#32;no HTML export\. |
| `/export copy`&#32;or&#32;`/export clipboard` | Same copy\-alias warning\. |

`parseExportArgs`&#32;recognizes&#32;`--themes`\,&#32;removes that token\,&#32;and accepts at most one remaining whitespace\-delimited path\.&#32;It does not implement shell expansion\,&#32;quote interpretation\,&#32;or general option validation\.&#32;An unrecognized single token can become a literal filename\.

Use a no\-space filename for interactive export\.&#32;Do not assume&#32;`~`&#32;or shell variables will be expanded there\.

Terminal&#32;`--export`&#32;instead consumes its input as a flag value and uses the first positional message as output path\.&#32;It does not use&#32;`parseExportArgs`&#32;or expose the slash command’s&#32;`--themes`&#32;syntax\.&#32;Keep the terminal recipe to its documented input and optional output\.

**What changes\?**&#32;The terminal recipe creates a second local HTML output\.

**What does not change\?**&#32;Quoting changes argument grouping\,&#32;not snapshot scope or disclosure policy\.

**Failure and recovery\.**&#32;For an interactive path error\,&#32;choose a no\-space path\.&#32;Do not switch to&#32;`/dump`&#32;merely because a warning mentions it unless you also want the dump’s broader current\-state and sidecar behavior\.

**Self\-check\.**&#32;Why does terminal quoting work while slash quoting fails\?

**Answer\:**&#32;The shell passes one argv element\;&#32;the slash parser splits the command text on whitespace\.

### Persistence and missing\-input limits

An in\-memory manager without a session file is rejected by HTML export with\:

`Cannot export in-memory session to HTML`

A successful dump is not proof that HTML export is available\.

For existing saved inputs\,&#32;the recorded export path did not mutate the journal\.&#32;Do not generalize that into “this command can never create or alter an input file under any path condition\.”

`exportFromFile`&#32;translates an&#32;`ENOENT`&#32;that reaches it into&#32;`File not found: …`\.&#32;But its delegated&#32;`SessionManager.open`&#32;path also has fresh\-session behavior for empty\/missing explicit files\.&#32;The supplied reports did not exercise a missing\-input terminal export\.

**Validate the input first\.**&#32;Do not use export as a file\-existence test\,&#32;and do not promise that every missing\-input case is a harmless not\-found failure\.

**Source anchors\:**&#32;`packages/coding-agent/src/export/html/args.ts`&#32;—&#32;`parseExportArgs`\;&#32;`packages/coding-agent/src/export/html/index.ts`&#32;—&#32;`exportSessionToHtml`\,&#32;`exportFromFile`\,&#32;`buildSessionData`\;&#32;`packages/coding-agent/src/main.ts`&#32;—&#32;`runRootCommand`&#32;export fast\-path\;&#32;`packages/coding-agent/src/cli/flag-tables.ts`&#32;—&#32;`--export`\;&#32;`packages/coding-agent/src/modes/controllers/command-controller.ts`&#32;—&#32;`handleExportCommand`\.

## Review Packet reading the whole packet

A local HTML file can contain embedded conversation data without being a self\-contained offline viewer\.

That distinction was not merely inferred\.&#32;The completed browser check found that blocking the export’s CDN scripts left visible controls but&#32;**no rendered transcript**\.

### Milestone\:&#32;Review offline without assuming the viewer works offline

**Need\.**&#32;Noor wants to inspect the packet without allowing external requests\.

**Obstacle\.**&#32;The transcript is embedded\,&#32;but two viewer dependencies are external\.

The supplied template references\:

- `https://cdnjs.cloudflare.com/ajax/libs/marked/15.0.4/marked.min.js`
- `https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js`

The template also inlines CSS\,&#32;viewer JavaScript\,&#32;and the generated tool renderer bundle\.&#32;Those embedded assets do not remove the two external dependencies\.

**Exact action\:**&#32;if your browser environment can block external requests\,&#32;keep them blocked when opening the fictional HTML\.&#32;If you cannot establish that boundary\,&#32;use the raw\-file route below instead of calling the preview offline\.

**Recorded browser observation\.**&#32;With both CDN scripts blocked\,&#32;controls such as Auto\,&#32;Light\,&#32;Dark\,&#32;Default\,&#32;No\-tools\,&#32;User\,&#32;Labeled\,&#32;and All remained visible\.&#32;The transcript did not render\.

Do not try to find messages or open Scout in that failed\-render state\.

Use the actual fictional journals as the offline fallback\:

**Terminal shell — read\-only offline fallback\,&#32;from extracted&#32;`examples/`\:**

~~~sh
cat review-packet/harbor-review.jsonl
cat review-packet/harbor-review/Scout.jsonl
cat review-packet/harbor-review/Scout/Checklist.jsonl
~~~

Find these fixture markers\:

| File | Marker and meaning |
| --- | --- |
| Parent journal | `PRE-CLEAR-HISTORY`\:&#32;the earlier fictional draft used a blue cover\. |
| Parent journal | `CURRENT-CONTEXT`\:&#32;the current review\-packet request\. |
| Scout journal | `SCOUT-TRANSCRIPT`\:&#32;the fictional headings inspection\. |
| Checklist journal | `CHECKLIST-TRANSCRIPT`\:&#32;title\,&#32;summary\,&#32;and owner\. |

The parent contains a saved&#32;`reset_boundary`&#32;between the earlier and current messages\.

**What changes\?**&#32;Only your inspection surface\.

**What does not change\?**&#32;Blocking scripts does not remove embedded data\,&#32;and reading raw journals does not mutate them\.

**Failure and recovery\.**&#32;A blank viewer is an availability problem\,&#32;not proof of an empty or sanitized packet\.&#32;Stay with text inspection if external code loading is not permitted\.

**Self\-check\.**&#32;Does “the data is embedded” imply “the viewer works without external scripts”\?

**Answer\:**&#32;No\.

### Inspect the actual embedded payload when needed

For this fictional output\,&#32;you can inspect the embedded JSON without executing the HTML\.

The following optional command uses the exact session\-data tag emitted by the supplied template\.&#32;Publication review executed this filter against a fictional file export\,&#32;confirming the parent\,&#32;Scout and Checklist data and the absence of top\-level live prompt\/tool fields\.

**Terminal shell — optional read\-only payload inspection using&#32;`jq`\,&#32;from extracted&#32;`examples/`\:**

~~~sh
jq -eRs '
  capture("<script id=\"session-data\" type=\"application/json\">(?<payload>[^<]+)</script>")
  | .payload
  | @base64d
  | fromjson
' review-packet/harbor-review.html
~~~

This reads a file\,&#32;decodes its base64 JSON\,&#32;and prints it\.&#32;It does not run the embedded scripts or contact the CDN\.

Inspect the header\,&#32;all entries\,&#32;leaf\,&#32;and nested sessions—not just a matching phrase\.&#32;In this file\-based recipe\,&#32;top\-level live&#32;`systemPrompt`&#32;and&#32;`tools`&#32;fields should not have been supplied\.

The command depends on the current template’s tag spelling\.&#32;If it fails or produces no usable payload\,&#32;treat that as an inspection failure\,&#32;not as proof that no data exists\.&#32;A different implementation may require a different approved inspection method\.

Base64 is an encoding\,&#32;not encryption\.&#32;Someone who receives the HTML can recover its embedded data even if the visible viewer does not work\.

### Milestone\:&#32;Follow the nested fictional review

**Need\.**&#32;Noor wants to inspect the checklist work behind the parent’s task result\.

**Obstacle\.**&#32;The parent’s short result is not the complete nested transcript\.

The supplied files use the exporter’s adjacent\-directory convention\:

**Non\-runnable reference — supplied fictional disk layout\:**

~~~text
review-packet/harbor-review.jsonl
review-packet/harbor-review/Scout.jsonl
review-packet/harbor-review/Scout/Checklist.jsonl
~~~

Public copies\:

- [Harbor Notes parent](<https://present-sketch-tp94.here.now/examples/review-packet/harbor-review.jsonl>)
- [Scout transcript](<https://present-sketch-tp94.here.now/examples/review-packet/harbor-review/Scout.jsonl>)
- [Checklist transcript](<https://present-sketch-tp94.here.now/examples/review-packet/harbor-review/Scout/Checklist.jsonl>)

`collectSubSessions`&#32;finds these recursively and embeds keys&#32;`Scout`&#32;and&#32;`Scout/Checklist`\.

For an optional rendered preview\,&#32;make a separate\,&#32;explicit decision to allow the two public CDN scripts&#32;**for this fictional packet only**\.&#32;If you decline\,&#32;the raw and decoded JSON routes already provide the review material\.

Only after permitting those requests and reloading the fictional HTML\:

1. Find the parent’s current\-context and pre\-clear\-history content\.
2. Open Scout’s agent link\.
3. Open Checklist from within Scout\.
4. Confirm the nested checklist text\.
5. Press Escape to return from Checklist to Scout\.

Do not use copy\-link controls in this exercise\.

**Recorded browser check\.**&#32;Headless Chromium opened Scout and Checklist through keyboard interaction\,&#32;displayed the checklist transcript\,&#32;and returned to Scout on Escape\.&#32;Pre\-clear parent history was visible\.

That browser check used a privately generated fictional live\-state export\,&#32;with explicitly fictional prompt\/tool metadata\.&#32;The runnable file\-export recipe uses the same saved history and nesting convention but does not add that live metadata\.&#32;Neither result is a claim about a reader’s browser or a real private session\.

**What changes\?**&#32;Viewer navigation changes the local displayed transcript\.

**What does not change\?**&#32;It does not switch a running OMP session or rewrite the saved journal\.

**Failure and recovery\.**&#32;If a nested link is unavailable\,&#32;inspect the raw nested file and the actual payload\.&#32;Missing directories\,&#32;`.bak`&#32;filenames\,&#32;and empty\/corrupt journals can be omitted\.&#32;Other access errors can fail collection\.&#32;Absence from a packet is not proof that no nested work ever existed\.

**Self\-check\.**&#32;Is the parent’s “Fictional review complete” result enough to establish what Checklist saw\?

**Answer\:**&#32;No\.&#32;Inspect the nested transcript itself\.

### The visible leaf is not the disclosure boundary

HTML embeds all manager entries and the leaf identifier\.&#32;It can therefore contain more than the currently displayed path\.

Before disclosure\,&#32;account for\:

- Alternative and earlier branches\.
- Pre\-clear history\.
- Tool inputs and outputs\.
- Nested session content\.
- Header cwd and additional roots\.
- Parent references and other metadata\.
- Fields that the viewer does not render\.

The export helper removes&#32;`previousSessionFiles`&#32;from exported headers\.&#32;It does not remove every path or identifying field\.

Likewise\,&#32;a No\-tools filter\,&#32;collapsed thinking\,&#32;or hidden custom\-message display does not delete embedded content\.&#32;Even an All filter is a viewer control\,&#32;not a complete field\-by\-field privacy audit\.

`includeSubSessions: false`&#32;exists as a programmatic&#32;`ExportOptions`&#32;setting\.&#32;It is not a supplied native slash flag\.&#32;Do not invent an omit\-nested command option\.

**Source anchors\:**&#32;`packages/coding-agent/src/export/html/index.ts`&#32;—&#32;`buildSessionData`\,&#32;`sessionHeaderForExport`\,&#32;`collectSubSessions`\,&#32;`generateHtml`\;&#32;`packages/coding-agent/src/export/html/template.html`\;&#32;`packages/coding-agent/src/export/html/template.js`&#32;—&#32;`bootSession`\,&#32;sub\-session overlay functions\;&#32;`packages/collab-web/src/tool-render/tools/task.tsx`&#32;—&#32;`taskRenderer`\.

## Sharing is a separate disclosure decision

Noor has reviewed a local packet\.&#32;That does not automatically authorize uploading it\.

This chapter is a decision and evidence exercise\.&#32;**Do not execute&#32;`/share`&#32;as part of the workbook\.**&#32;No live upload is needed to learn the boundaries\.

### Milestone\:&#32;Identify the route before authorizing disclosure

**Need\.**&#32;Noor may eventually need a link for an authorized reviewer\.

**Obstacle\.**&#32;The same human command can use a default encrypted snapshot or a custom executable handler with a different data contract\.

**Exact action\:**&#32;review the effective sharing settings and whether the active agent directory contains a custom share script\.&#32;Then identify what data that route would receive\.&#32;Do not invoke sharing to discover its policy\.

The supplied settings schema places sharing controls under Interaction&#32;\/&#32;Collab\:

| Setting | Current default or source | Meaning |
| --- | --- | --- |
| `share.store` | `blob` | Upload the encrypted snapshot to the share server\. |
| `share.serverUrl` | `DEFAULT_SHARE_URL` | Upload\/viewer base\.&#32;The accompanying operation document names&#32;`https://my.omp.sh/s`\. |
| `share.redactSecrets` | `true` | Pass the session’s secret obfuscator to default sharing when available\. |
| `secrets.enabled` | `false` | SDK creation only builds the normal secret obfuscator when enabled\. |

The defining body of the imported&#32;`DEFAULT_SHARE_URL`&#32;constant is not included in the source bundle\.&#32;Treat the effective installed&#32;`share.serverUrl`&#32;as authoritative rather than assuming a documented endpoint\.

Global settings\,&#32;project settings\,&#32;explicit overlays\,&#32;and runtime overrides can affect effective values\.&#32;A value in one config file is not necessarily the current effective value\,&#32;especially after a project\-scope change\.

**Current default correction\:**&#32;GitHub gist is not the default or preferred route in the supplied schema\.&#32;`share.store`&#32;defaults to&#32;`blob`\.&#32;There is no supplied&#32;`--gist`&#32;flag\.

### Default sharing and custom sharing are different contracts

| Route | Data contract |
| --- | --- |
| Default encrypted sharing | Builds a JSON snapshot directly from the manager and optional current agent state\;&#32;optionally applies the typed secret\-redaction pass\;&#32;compresses and encrypts it\. |
| TUI custom handler | Receives the path to an ordinary temporary HTML export\.&#32;It is not handed the default encrypted\/redacted snapshot\. |
| Shared non\-TUI builtin | Calls default&#32;`shareSession`&#32;directly\;&#32;it does not load the custom TUI share script\. |

Default&#32;`buildShareSnapshot`&#32;calls&#32;`buildSessionData`\.&#32;**It does not collect adjacent subagent JSONL files\.**&#32;The supplied interception check confirmed that omission\.

Ordinary HTML export\,&#32;including the TUI custom\-handler input\,&#32;does collect adjacent nested transcripts by default\.

This does not mean default sharing contains no subagent\-derived information\.&#32;Parent journal entries can already contain task inputs\,&#32;results\,&#32;and quoted output\.

Default sharing can work for an in\-memory session because it builds the snapshot from the manager’s entries without requiring a session file\.&#32;A custom TUI handler still depends on HTML export\,&#32;so a manager without a session file can fail before the handler runs\.&#32;There is no automatic fallback from that custom path\.

**What changes\?**&#32;An approved share would create a separate disclosure artifact or handler effect\,&#32;not a new conversation identity\.

**What does not change\?**&#32;The default snapshot does not append sharing entries to the source journal\.&#32;Encryption and redaction operate on the outgoing representation\,&#32;not as an erasure operation on the original\.

**Recorded check\.**&#32;Intercepted default sharing encrypted a fictional snapshot\,&#32;removed a configured synthetic secret\,&#32;retained an unmatched fictional string\,&#32;left source entries unchanged\,&#32;and did not collect adjacent nested sessions\.

**Self\-check\.**&#32;Can Noor assume that a custom handler receives the same sanitized data as default sharing\?

**Answer\:**&#32;No\.&#32;It receives ordinary HTML under a separate contract\.

### Redaction is conditional and field\-specific

`share.redactSecrets: true`&#32;is not a universal privacy guarantee\.

The current default\-share path skips its typed redaction pass when there is no obfuscator or the obfuscator reports no configured\/recognized secret handling\.&#32;With&#32;`secrets.enabled`&#32;at its schema default of false\,&#32;the normal SDK path does not create that obfuscator\.

When active\,&#32;the typed pass rewrites selected text\-bearing fields\,&#32;including relevant\:

- Header title and cwd\.
- Current system prompt and tool descriptions\.
- Message text\,&#32;stored thinking text\,&#32;tool\-call arguments\,&#32;and error text\.
- Tool\-result content and execution output\.
- File\-mention paths and contents\.
- Summaries\,&#32;labels\,&#32;and title\-change text\.

It drops specified opaque provider replay material and untyped payloads\,&#32;such as certain&#32;`details`\,&#32;`data`\,&#32;output schemas\,&#32;and compaction preserve data\,&#32;rather than attempting a universal recursive scrub\.

Important limits remain\:

- Unknown or unconfigured strings can survive\.
- Image bytes remain intact before the later size\-trimming pass\.
- The typed header walk does not rewrite every possible path\-bearing field\.&#32;For example\,&#32;`additionalDirectories`&#32;is not rewritten by&#32;`redactShareHeader`\.
- IDs\,&#32;parent references\,&#32;and pseudonymous account\-related metadata can still be identifying or linkable\.
- Dropping a metadata field can also remove context a reviewer would otherwise need\.

The recorded check confirmed configured\-secret removal\,&#32;opaque tool\-result\-details omission\,&#32;small\-image preservation\,&#32;and complete skipping of redaction with an absent or empty obfuscator\.

Local HTML export does not run this share\-redaction pass\.&#32;A transcript may already contain some obfuscated content\,&#32;but that is not the same as auditing the output artifact\.

### Encryption protects a different boundary

Default sharing\:

1. Builds the snapshot\.
2. Applies the configured redaction pass when available\.
3. Gzips the JSON\.
4. Seals it using a fresh AES\-256\-GCM key and a 12\-byte IV\.
5. Uploads the sealed blob\.
6. Returns a viewer URL containing the key in its fragment after&#32;`#`\.

The recorded intercepted POST URL did not contain the fragment key\.&#32;Ordinary HTTP requests do not carry URL fragments automatically\.

But&#32;**possession of the complete link grants decryption access**\.&#32;A recipient can forward it\.&#32;Messages\,&#32;screenshots\,&#32;browser history\,&#32;and other places where the complete link is stored can become access\-bearing copies\.

Do not turn “the key is in the fragment” into “no client\-side code or recipient can disclose it\.” The viewer is part of the trust boundary\.

Encryption is not redaction\,&#32;anonymity\,&#32;access approval\,&#32;or secure deletion\.

### Size trimming is loss\,&#32;not privacy review

The production sealed\-byte budgets are\:

- Share server\:&#32;**1\,000\,000 bytes**\.
- Gist route\:&#32;**5\,000\,000 bytes**\,&#32;before base64 expansion\.

When a snapshot is too large\,&#32;the implementation progressively\:

1. Replaces large inline image payloads and large data URLs\.
2. Caps long strings at lengths of 32\,768\,&#32;8\,192\,&#32;2\,048\,&#32;then 512\.
3. Removes oldest entries by repeatedly halving the retained entry list while more than four entries remain\.
4. Throws if the result still cannot fit\.

The string caps are implementation string\-length caps\,&#32;distinct from the final sealed\-byte budget\.

The result reports truncation\,&#32;and the command can display a note that large content was trimmed\.&#32;Trimming can remove evidence or qualifiers\.&#32;It is not a secret detector and does not make surviving content safe\.

The recorded trimming tests used a smaller explicit 4\,000\-byte budget\,&#32;confirmed image\/text trimming and unchanged originals\,&#32;and checked an impossible\-budget error\.&#32;They did not upload a production\-size packet\.

### Gist fallback is not custom\-handler fallback

Optional&#32;`share.store: gist`&#32;tries the gist route when&#32;`gh`&#32;is installed and authenticated\.&#32;It stores the sealed blob base64\-encoded as&#32;`session.ompshare.txt`&#32;in a secret gist\.

If&#32;`gh`&#32;is unusable or gist creation fails\,&#32;default sharing can fall back to the share server\.

That fallback matters for disclosure policy\:&#32;approving one destination is not automatically approving the fallback destination\.

**Recorded check\.**&#32;A deliberately unauthenticated fake&#32;`gh`&#32;received only&#32;`auth status`\.&#32;No gist was created\.&#32;The code fell back to one intercepted server POST\.

By contrast\,&#32;a TUI custom handler is discovered in the active&#32;`getAgentDir()`&#32;in this order\:

1. `share.ts`
2. `share.js`
3. `share.mjs`

The first existing candidate must default\-export the expected function\.&#32;The ordinary default location is under&#32;`~/.omp/agent`\,&#32;but profile\/agent\-directory resolution can change that location\.

If loading fails\,&#32;the command errors and returns\.&#32;If execution throws\,&#32;it errors and returns\.&#32;**Neither failure falls back to default sharing\.**

A custom handler is executable code\,&#32;not a declarative upload destination\.&#32;Have its policy reviewed by its maintainer rather than assuming the default encryption or redaction settings constrain it\.

Its result can be\:

- A URL string\.
- An object with optional&#32;`url`&#32;and\/or&#32;`message`\.
- `undefined`\,&#32;which produces a generic shared status\.

A generic status is not independent proof that a handler uploaded anything\.

### Milestone\:&#32;Interpret cancellation without assuming revocation

**Need\.**&#32;Noor wants to stop a sharing action\.

**Obstacle\.**&#32;Restoring the editor and cancelling transport work are different events\.

This is a recorded\-boundary exercise\,&#32;not an instruction to start an upload and press Escape\.

The TUI loader has an abort signal\,&#32;but&#32;`handleShareCommand`&#32;does not pass that signal into default&#32;`shareSession`&#32;or the custom callback\.

The recorded default sequence was\:

1. An intercepted POST began and was held pending\.
2. The real loader handled Escape\.
3. The UI reported&#32;`Share cancelled`\.
4. No transport abort signal had been supplied\.
5. The held operation completed afterward\.
6. The late URL was not displayed or opened\.

The recorded custom sequence likewise completed a callback effect after Escape\.&#32;Its temporary HTML remained while the handler was pending and was removed after settlement\.

**What changes on Escape\?**&#32;The editor is restored\,&#32;the loader is cancelled\,&#32;and later result display\/opening is suppressed\.

**What does not follow\?**&#32;A started upload or custom effect is not necessarily stopped\,&#32;undone\,&#32;or revoked\.

Temporary custom HTML is removed in&#32;`finally`&#32;after settlement\,&#32;with cleanup errors ignored\.&#32;A crash or failed cleanup can leave it behind\.&#32;“Temporary” is not an erasure guarantee\.

**Failure and recovery\.**&#32;Treat a cancelled or ambiguous share as possibly completed until the destination is checked through an approved process\.&#32;Do not repeat the share merely to recover a missing URL\.&#32;Do not use&#32;`/clear`\,&#32;`/new`\,&#32;or&#32;`/drop`&#32;as upload revocation\.

A server error also does not establish that nothing reached the remote system\.&#32;The supplied HTTP\-error check observed one intercepted POST and error propagation\,&#32;not a universal remote non\-delivery guarantee\.

No generic revoke\/delete\-link workflow is established by this evidence\.

**Self\-check\.**&#32;Noor sees&#32;`Share cancelled`\.&#32;Is that enough to tell an owner that nothing was uploaded\?

**Answer\:**&#32;No\.

**Source anchors\:**&#32;`packages/coding-agent/src/export/share.ts`&#32;—&#32;`buildShareSnapshot`\,&#32;`redactShareHeader`\,&#32;`redactShareEntry`\,&#32;`redactShareMessage`\,&#32;`shareSession`\,&#32;`sealToFit`\,&#32;`tryCreateGist`\,&#32;`uploadToServer`\;&#32;`packages/coding-agent/src/export/custom-share.ts`&#32;—&#32;`getCustomSharePath`\,&#32;`loadCustomShare`\;&#32;`packages/coding-agent/src/config/settings-schema.ts`&#32;— sharing and secrets settings\;&#32;`packages/coding-agent/src/sdk.ts`&#32;— obfuscator construction\;&#32;`packages/coding-agent/src/modes/controllers/command-controller.ts`&#32;—&#32;`handleShareCommand`\;&#32;`packages/tui/src/components/cancellable-loader.ts`&#32;—&#32;`handleInput`\.

## Operation decision matrix

Use this matrix to choose an operation by intent\.

“Identity” means persistent journal identity\.&#32;None of these conversation\-management operations is a workspace rollback\.&#32;Work already performed by tools remains a separate filesystem concern\.

| Your need | Operation | Main effect | Boundary to remember |
| --- | --- | --- | --- |
| Find another conversation while OMP is open | `/resume` | Picker\,&#32;then runtime switch if selected | Escape keeps the current session\;&#32;inspect identity after questionable status\. |
| Reopen a specific native conversation | Terminal&#32;`--resume <id-or-path>` | Opens the selected history | IDs and direct paths have different failure and missing\-cwd behavior\. |
| Choose at startup | Terminal&#32;`--resume` | Startup picker | Cancellation exits startup\;&#32;empty folder does not auto\-select all projects\. |
| Return to this terminal’s applicable prior history | Terminal&#32;`--continue` | Breadcrumb\,&#32;recent fallback\,&#32;or new empty session | Not always newest\;&#32;fresh boundaries and relocation rules matter\.&#32;No&#32;`/continue`\. |
| Explore an alternative from the active conversation | `/fork` | New identity\,&#32;parent metadata\,&#32;retained conversation and journal entries | Same workspace\;&#32;ordinary queues retained\;&#32;artifact copy best\-effort\. |
| Create an alternative from saved history at launch | Terminal&#32;`--fork <id-or-path>` | New identity rooted at launch cwd | No parent\-process queues\;&#32;incompatible with&#32;`--no-session`\. |
| Refresh local provider\-facing state\,&#32;keep conversation | `/fresh` | Local handles cleared\;&#32;provider\-facing ID rotates | Journal and ordinary queues remain\;&#32;no real\-provider repair guarantee\. |
| Drop live\/model context\,&#32;keep this persistent session | `/clear` | Messages\/queues cleared\;&#32;reset boundary appended | Old history\,&#32;artifacts\,&#32;exports\,&#32;and workspace remain\. |
| Start a different empty conversation identity | `/new` | New identity\/path and reset conversation | Prior journal retained\;&#32;new file may be lazy\;&#32;not a settings factory reset\. |
| Understand local deletion behavior | `/drop`\,&#32;warning only | Best\-effort previous\-journal\/artifact deletion\,&#32;then new session | Do not exercise here\;&#32;no secure\-erasure guarantee or confirmation in this path\. |
| Inspect current live context as text | `/dump` | Clipboard\/plain text plus best\-effort sidecar | Can disclose raw current prompt\/tool\/context data and leave a temporary file\. |
| Export current session\-manager history with current metadata | Slash&#32;`/export` | Local HTML\;&#32;TUI requests OS opening | No\-space slash path\;&#32;nested history included\;&#32;no share\-redaction pass\. |
| Export a valid saved journal without a running agent | Terminal&#32;`--export` | File\-based HTML and printed output path | No reconstructed live prompt\/tool inventory\;&#32;validate input and output paths\. |
| Authorize external disclosure | `/share`\,&#32;reference only | Default encrypted snapshot or custom\-handler effect | Route\,&#32;redaction\,&#32;truncation\,&#32;complete\-link possession\,&#32;and late completion all matter\. |

For export’s valid\-input and missing\-path distinctions\,&#32;return to&#32;[local HTML export](<https://present-sketch-tp94.here.now/chapters/continuity-review-packet-local-html-export#continuity-review-packet-local-html-export>)\.&#32;For upload decisions\,&#32;use&#32;[Sharing is a separate disclosure decision](<https://present-sketch-tp94.here.now/chapters/continuity-sharing-is-a-separate-disclosure-decision#continuity-sharing-is-a-separate-disclosure-decision>)\,&#32;not a reset command\.

## Recovery and safe disclosure checklists

These are reader checklists\,&#32;not automatic session tests\.&#32;Checking a box records your own work\;&#32;it does not mean a website inspected OMP\,&#32;your files\,&#32;or a provider\.

### Recovery checklist

When selection or transition behavior is uncertain\:

- \[&#32;\]&#32;**Stop adding work\.**&#32;Do not prompt the possibly wrong conversation\.
- \[&#32;\]&#32;**Identify the operation\.**&#32;Resume\,&#32;continue\,&#32;fork\,&#32;fresh\,&#32;clear\,&#32;and new have different expected effects\.
- \[&#32;\]&#32;**Check the active session file\.**&#32;Do not rely on title or a success toast\.
- \[&#32;\]&#32;**Check persistent identity separately from provider identity\.**&#32;Use the saved session header for durable identity\.
- \[&#32;\]&#32;**Check directories\.**&#32;Inspect the active working directory and additional roots\;&#32;distinguish them from an old header cwd\.
- \[&#32;\]&#32;**Account for queues and jobs\.**&#32;Fresh\/fork can retain ordinary queues\;&#32;clear\/new discard them\.&#32;Cancellation does not reverse completed file effects\.
- \[&#32;\]&#32;**Inspect the relevant journal\.**&#32;A reset boundary explains why old entries remain while current context is empty\.
- \[&#32;\]&#32;**Treat failure phase as evidence\.**&#32;A settings preflight failure\,&#32;hook veto\,&#32;guarded rollback\,&#32;and post\-commit reconciliation error are different outcomes\.
- \[&#32;\]&#32;**Use an explicit known target for recovery\.**&#32;Do not repeatedly try continue and hope recency chooses correctly\.
- \[&#32;\]&#32;**Preserve useful evidence privately\.**&#32;Do not clear or drop merely to make the screen look simpler\.

If a persistence error leaves live messages ahead of disk\,&#32;do not assume restart will recover those messages\.&#32;If you need a diagnostic copy\,&#32;treat any dump and sidecar as sensitive and review them locally\.&#32;Do not publish a raw dump as a shortcut\.

If cwd re\-scoping fails\,&#32;do not let tools operate while scope is uncertain\.&#32;The parent terminal shell’s directory alone does not prove the OMP process and manager agree\.

### Safe disclosure checklist

Before forwarding any real packet outside its current trusted boundary\:

- \[&#32;\]&#32;**Name the recipient and purpose\.**&#32;Include only what that review requires\.
- \[&#32;\]&#32;**Identify the snapshot source\.**&#32;Live dump\,&#32;file export\,&#32;live export\,&#32;default share\,&#32;or custom HTML are not interchangeable\.
- \[&#32;\]&#32;**Inspect older history\.**&#32;Include pre\-clear entries and alternative branches in the review scope\.
- \[&#32;\]&#32;**Inspect all nested content actually embedded\.**&#32;Do not stop at the parent’s task summary\.
- \[&#32;\]&#32;**Inspect header and metadata paths\.**&#32;Cwd\,&#32;additional roots\,&#32;titles\,&#32;parent references\,&#32;and other identifiers can disclose project or user information\.
- \[&#32;\]&#32;**Inspect system\/tool data without republishing it blindly\.**&#32;Current prompts\,&#32;tool descriptions\,&#32;schemas\,&#32;file mentions\,&#32;and opaque metadata require deliberate handling\.
- \[&#32;\]&#32;**Inspect images and linked content\.**&#32;Text redaction does not inspect every pixel or authorize loading every external resource\.
- \[&#32;\]&#32;**Separate visibility from removal\.**&#32;Filters\,&#32;collapsed sections\,&#32;and blank viewers do not scrub embedded bytes\.
- \[&#32;\]&#32;**Inventory sidecars and clipboard copies\.**&#32;They may outlive the command and the session\.
- \[&#32;\]&#32;**Check effective sharing configuration and custom\-handler presence\.**&#32;Review fallback destinations as well as the preferred destination\.
- \[&#32;\]&#32;**Treat redaction as assistance\,&#32;not approval\.**&#32;Unknown strings can survive\;&#32;no obfuscator can mean no typed redaction pass\.
- \[&#32;\]&#32;**Account for truncation\.**&#32;A smaller packet may omit crucial evidence without removing sensitive surviving content\.
- \[&#32;\]&#32;**Protect complete share links\.**&#32;The fragment key is access\-bearing\.
- \[&#32;\]&#32;**Do not interpret cancellation as revocation\.**&#32;A started operation may finish after the UI returns\.
- \[&#32;\]&#32;**Hold the packet if inspection is incomplete\.**&#32;An authorized\,&#32;manually written summary may be safer than forwarding an unreviewed full artifact\.

No live sharing is part of completing this workbook\.

### Finish the three desks

You have reached the intended outcome when you can explain these decisions without relying on a status label\:

| Situation | Sound conclusion |
| --- | --- |
| Maya knows yesterday’s full identity\. | Resume that known target and check file plus project scope\. |
| Continue returns an empty conversation\. | Investigate breadcrumb\/fresh\-boundary\/history availability\;&#32;do not assume deletion\. |
| Eli wants a second conversational direction\. | Fork\,&#32;while recognizing that workspace files remain shared\. |
| Eli wants to retain the conversation but reset local provider state\. | Fresh\;&#32;do not claim a remote incident was repaired\. |
| Eli wants an empty live conversation under the same persistent ID\. | Clear\;&#32;retained journal history still needs export review\. |
| Noor needs the earlier Harbor Notes discussion\. | Inspect saved\/full history\,&#32;not only post\-clear live messages\. |
| Noor’s blocked\-CDN HTML shows only controls\. | Use raw or decoded data inspection\;&#32;the viewer is not self\-contained offline\. |
| A sharing loader says cancelled\. | Treat upload\/handler completion as unresolved\,&#32;not revoked\. |

### Clean up only owned exercise material

`LAB`&#32;identifies the newly created temporary lab\.&#32;`RECEIPT`&#32;identifies a separate temporary JSON receipt\.

When finished\,&#32;remove only the exact exercise paths you own and no longer need\.&#32;The generated Review Packet HTML belongs to your disposable downloaded examples copy\.

No automated cleanup command is supplied that could be mistaken for a real\-session deletion instruction\.&#32;Do not use&#32;`/drop`&#32;as workbook cleanup\,&#32;and do not describe ordinary file removal as secure erasure\.

## Evidence and limitations

This edition is grounded in supplied implementation bodies\,&#32;public fictional fixtures\,&#32;completed isolated reports\,&#32;and a completed browser report\.&#32;No new commands or live checks were run to author this manuscript\.

The evidence supports specific behavior in a particular source snapshot\.&#32;It does not establish compatibility with every installed release\.

### Supplied implementation inspection

The main source responsibilities are\:

| Concern | Supplied paths and symbols |
| --- | --- |
| Persistent identity\,&#32;journal operations\,&#32;lazy files\,&#32;artifacts\,&#32;continuation | `packages/coding-agent/src/session/session-manager.ts`&#32;—&#32;`SessionManager.open`\,&#32;`fork`\,&#32;`forkFrom`\,&#32;`newSession`\,&#32;`moveTo`\,&#32;`continueRecent`\,&#32;`copySessionArtifacts` |
| Identifier resolution and listing | `packages/coding-agent/src/session/session-listing.ts`&#32;—&#32;`resolveResumableSession`\,&#32;`sessionMatchesResumeArg`\,&#32;`findMostRecentSession` |
| Breadcrumbs and terminal identity | `packages/coding-agent/src/session/session-paths.ts`\;&#32;`packages/tui/src/ttyid.ts`&#32;—&#32;`getTerminalId` |
| Startup routing | `packages/coding-agent/src/main.ts`&#32;—&#32;`createSessionManager`\,&#32;`runRootCommand`\,&#32;missing\-cwd and project\-switch helpers\;&#32;`packages/coding-agent/src/cli/args.ts`\;&#32;`packages/coding-agent/src/cli/flag-tables.ts` |
| Runtime reset and transition behavior | `packages/coding-agent/src/session/agent-session.ts`&#32;—&#32;`freshSession`\,&#32;`resetSessionContext`\,&#32;`newSession`\,&#32;`fork`\,&#32;`switchSession` |
| Context versus retained history | `packages/coding-agent/src/session/session-context.ts`&#32;—&#32;`buildSessionContext`\;&#32;`packages/coding-agent/src/session/session-entries.ts`&#32;—&#32;`ResetBoundaryEntry` |
| Human command routing and UI caveats | `packages/coding-agent/src/slash-commands/builtin-lifecycle.ts`\,&#32;`builtin-session.ts`\,&#32;`builtin-collaboration.ts`\;&#32;`packages/coding-agent/src/modes/controllers/command-controller.ts`\,&#32;`selector-controller.ts` |
| Dumps and exports | `packages/coding-agent/src/session/session-dump-format.ts`\;&#32;`packages/coding-agent/src/export/html/args.ts`\,&#32;`index.ts`\,&#32;`template.html`\,&#32;`template.js` |
| Sharing\,&#32;conditional redaction\,&#32;handlers\,&#32;cancellation | `packages/coding-agent/src/export/share.ts`\,&#32;`custom-share.ts`\;&#32;`packages/coding-agent/src/config/settings-schema.ts`\;&#32;`packages/coding-agent/src/secrets/obfuscator.ts`\;&#32;`packages/tui/src/components/cancellable-loader.ts` |
| Startup\/provider boundaries | `packages/coding-agent/src/sdk.ts`\;&#32;`packages/coding-agent/src/config/model-registry.ts`\,&#32;`model-provider-discovery.ts` |

Implementation bodies and recorded outcomes take precedence over stale descriptions\.&#32;In particular\,&#32;this workbook does not adopt the older implications that\:

- `/fork`&#32;opens a previous\-message picker\.
- GitHub gist is the default share route\.
- Embedded HTML data guarantees an offline viewer\.
- A dump necessarily contains all retained journal history\.
- Every explicit missing path is a guaranteed not\-found failure\.
- Every “session ID” is the persistent journal ID\.

### Completed recorded checks

The supplied coverage summary reports&#32;**38 passing checks**&#32;across three suites\:

| Suite | Recorded coverage | Important qualification |
| --- | --- | --- |
| Return Desk — 11 checks | Identity\/history\/cwd retention\,&#32;local\/global lookup\,&#32;unknown IDs\,&#32;no\-session manager precedence\,&#32;breadcrumb priority and fallback\,&#32;missing\-cwd decisions\,&#32;selector behavior\,&#32;public helper\,&#32;and source\-CLI resume\/continue\/fork | Most checks use manager\/component\/controller seams\.&#32;Native startup used RPC read\-only inspection and private disabled\-provider policy\,&#32;not a physical TUI\. |
| Decision Desk — 11 checks | Real SDK\/registry\/controller fork\,&#32;fresh\,&#32;clear\,&#32;new\,&#32;queue retention\/removal\,&#32;synthetic guards\,&#32;in\-memory fork refusal\,&#32;real extension veto\,&#32;injected switch rollback\,&#32;artifact success\/failure\,&#32;command inventory | Provider handles were inert\;&#32;busy predicates were synthetic\;&#32;artifact coverage was narrow\.&#32;Drop was not executed\. |
| Review Packet — 16 checks | Live versus file snapshots\,&#32;nested history\,&#32;path parsing\,&#32;source\-CLI export\,&#32;sidecars and failure\,&#32;in\-memory limits\,&#32;intercepted encrypted sharing\,&#32;conditional redaction\,&#32;trimming\,&#32;gist fallback\,&#32;server error\,&#32;custom handlers\,&#32;and late completion after Escape | No real upload or clipboard operation\.&#32;Share transport was intercepted\,&#32;and&#32;`gh`&#32;was a deliberately unauthenticated fake\. |

All three final launch reports recorded successful exit without timeout\.

The native continuity check ran the real source CLI with only state\/message inspection and stdin EOF shutdown\.&#32;Resume and continue returned the intended fictional identity\;&#32;fork returned another identity\;&#32;each retained two fictional messages and unchanged workspace bytes\.&#32;Its network\-attempt records were empty under the private restrictions\.

The supplied coverage summary also reports a passing public&#32;`bun check`&#32;and no remaining blockers in the preceding review\.&#32;That summary is not a new verification of this manuscript\,&#32;a website build\,&#32;or an installed distribution\.

Publication review also independently checked the complete manuscript with no remaining blockers and executed its added receipt\-capture\,&#32;JSON inspection\,&#32;checksum and embedded\-payload decoder commands on owned fictional copies\.&#32;The reset\-inspection queries were syntax\-checked against initial copied journals\;&#32;the actual reset behavior is established by the separate SDK checks above\,&#32;not by those queries\.

### Completed browser evidence

The separate headless Chromium report reviewed a generated fictional Harbor Notes packet\,&#32;including its decoded parent\,&#32;Scout\,&#32;Checklist\,&#32;and explicitly fictional current prompt\/tool metadata\.

It recorded\:

- **Blocked CDN scripts\:**&#32;visible controls\,&#32;no rendered transcript\.
- **Allowed CDN scripts\:**&#32;visible pre\-clear history\,&#32;keyboard navigation into Scout and Checklist\,&#32;and Escape returning to Scout\.

This establishes the tested viewer behavior\.&#32;It does not establish a self\-contained offline viewer\,&#32;a physical desktop\-open result\,&#32;or clipboard behavior\.

The generated proof exports remain private artifacts\;&#32;this workbook links only the supplied public fixtures and recipes\.

### What remains unverified

Material limitations remain explicit\:

- **Physical TUI behavior\:**&#32;full installed interactive startup\,&#32;terminal rendering\,&#32;picker exit presentation\,&#32;and platform\-specific input behavior were not physically tested\.
- **Real providers\:**&#32;no inference\,&#32;authentication recovery\,&#32;transport repair\,&#32;remote deletion\,&#32;or provider\-cache outcome was established\.
- **Concurrency\:**&#32;real streaming cancellation\,&#32;hidden\-turn scheduling\,&#32;async\-job races\,&#32;and compaction timing were not exercised by the synthetic guard checks\.
- **Universal rollback\:**&#32;the switch\-failure injection covered the guarded target\-load block\,&#32;not every preflight\,&#32;post\-commit\,&#32;external\,&#32;or filesystem effect\.
- **Filesystem breadth\:**&#32;one successful artifact file and one blocked destination do not establish symlink\,&#32;huge\-tree\,&#32;cross\-device\,&#32;permission\,&#32;or power\-loss guarantees\.
- **Missing export inputs\:**&#32;the reports checked valid input journals\,&#32;not every empty\/missing\-path behavior delegated through the loader\.
- **Clipboard and OS opening\:**&#32;controller open requests were intercepted\;&#32;TUI dump clipboard delivery and real desktop opening were not performed\.
- **Live sharing\:**&#32;no actual share server or GitHub publication\,&#32;viewer decryption service\,&#32;revocation\,&#32;expiry\,&#32;or retention policy was verified\.
- **Redaction completeness\:**&#32;configured synthetic cases do not prove that arbitrary real secrets\,&#32;personal information\,&#32;image content\,&#32;or extension metadata will be removed\.
- **Implementation identity\:**&#32;the supplied inventory fingerprints source files\,&#32;but no exact release\/commit mapping establishes what a reader’s installed OMP contains\.&#32;Session format version 3 is not itself a product\-release promise\.
- **Delegated code\:**&#32;some loader\/storage\/provider helpers and the generated tool\-view bundle were not included as implementation bodies\.&#32;Named repository tests were not supplied as executed test logs\.
- **Reader state\:**&#32;no workbook page\,&#32;checklist\,&#32;or fixture inspection establishes what is active in a reader’s actual OMP process\.

When installed behavior differs\,&#32;preserve that difference as an observation and stop the affected exercise\.&#32;Do not turn an expectation into a passed check\.

The durable habit is the same across all three stories\:&#32;**choose conversations by identity\,&#32;choose resets by state boundary\,&#32;and choose disclosure by inspected content—not by the appearance of the screen\.**

## Sessions\,&#32;resets\,&#32;and reviewable history\:&#32;next steps

You can now ask for a precise continuity operation instead of an unspecified reset\.&#32;You can also explain why a cleared live transcript can coexist with retained history and an export containing earlier material\.

That still leaves another question\:&#32;**what knowledge can return without reopening that journal\?**&#32;A separate memory backend\,&#32;a managed skill\,&#32;or applicable project guidance is not erased merely because conversation messages were cleared\.

Continue to&#32;[Memory and reusable knowledge](<https://present-sketch-tp94.here.now/chapters/unified-memory>)\.&#32;Keep the ledger beside the next part’s four\-place model\:&#32;one describes session and output boundaries\;&#32;the other describes where useful knowledge can live\.&#32;Their relationship is developed in&#32;[An empty context is not an empty history or memory store](<https://present-sketch-tp94.here.now/chapters/connection-which-state-survives>)\.

## Memory and reusable knowledge

Continuity preserves access to a conversation\.&#32;Memory supports a different need\:&#32;retrieving useful evidence when that conversation is no longer the active one\.

A stored preference\,&#32;a recalled excerpt\,&#32;and a managed skill are not three names for the same object\.&#32;Nor does storing something guarantee that the next relevant query will retrieve it\.&#32;This part teaches a checkable loop\:&#32;identify the evidence\,&#32;understand its scope\,&#32;inspect complete content before correction\,&#32;and verify the relevant outcome\.

### Read the configuration as a snapshot

The source teaches a sanitized Mnemopi configuration dated 28 August 2026\,&#32;with a configuration record at 00\:01 UTC the following day\.&#32;Its tables distinguish persisted global overrides from resolved defaults\.&#32;They do not establish the health of a database\,&#32;embedding worker\,&#32;provider\,&#32;or currently running session\.

Mnemopi is the selected backend in that snapshot\.&#32;The native&#32;`local`&#32;backend and Hindsight are alternatives\,&#32;not extra active layers that should be added to the explanation\.&#32;The recorded&#32;`per-project`&#32;scope derives from resolved cwd\,&#32;not Git root\.&#32;A session relocation or a launch from a subdirectory can therefore require a fresh scope investigation\;&#32;retaining a conversation identity does not establish an unchanged memory bank\.

Read&#32;[the complete configuration](<https://present-sketch-tp94.here.now/chapters/memory-the-configuration-this-workbook-teaches>)&#32;before treating a threshold\,&#32;budget\,&#32;or automation switch as applicable elsewhere\.

### Use the complete fictional lab

The&#32;[local Memory lab](<https://present-sketch-tp94.here.now/labs/memory>)&#32;retains all five Cedar stories\:&#32;deliberate preference retention\,&#32;decision recall\,&#32;reflection\,&#32;exact\-row correction\,&#32;and turning a verified technique into a lesson or skill\.&#32;Its selectable three\-stage illustration\,&#32;copy controls\,&#32;disclosures\,&#32;and lesson ticks remain interactive\.

Selecting the durable\-fact stage does not store a fact\.&#32;Selecting later recall does not search a database\.&#32;The lab’s&#32;`window.memoryTutorial`&#32;interface exists on that page only and operates tutorial state\.&#32;Use the&#32;[Memory browser\-interface chapter](<https://present-sketch-tp94.here.now/chapters/memory-browser-agent-interface>)&#32;for that interface\,&#32;not the unified site’s control contract\.

The original lab’s default\-location examples for managed skills are not universal active\-profile paths\.&#32;The manuscript explains their location under the active agent configuration directory\.&#32;Keep the default path illustration separate from your actual resolved configuration\.

### Three boundaries to carry forward

First\,&#32;`/memory view`&#32;shows injected instructions and cached recalled text\,&#32;not a database inventory or a new search\.&#32;Second\,&#32;a retain acknowledgement is not sufficient proof of a successful write\,&#32;and a clipped preview is not enough content for a replacement edit\.&#32;Third\,&#32;local SQLite and local embeddings do not establish offline processing\:&#32;the recorded online memory\-model setting permits additional model use\,&#32;and recalled content enters the main model’s context\.

Do not confuse&#32;`/clear`&#32;with&#32;`/memory clear`&#32;or&#32;`/memory reset`\.&#32;The former is a live\-context reset that retains journal history\;&#32;the latter commands request broad scoped memory\-file deletion in the recorded controller and are warning\-only here\.&#32;Neither is a universal erasure mechanism\.

Finally\,&#32;when this source says to start a fresh session after startup\-related configuration changes\,&#32;do not silently substitute the&#32;`/fresh`&#32;command\.&#32;Continuity established that&#32;`/fresh`&#32;refreshes local provider\-facing state\;&#32;it is not a general backend or tool\-reload recipe\.

## Orientation

Keep what matters\.&#32;Recall it later\.

[Interactive workbook](<https://present-sketch-tp94.here.now/labs/memory>)&#32;·&#32;[About](<https://present-sketch-tp94.here.now/about>)&#32;·&#32;[Contact and feedback](<https://present-sketch-tp94.here.now/contact>)&#32;·&#32;[Privacy](<https://present-sketch-tp94.here.now/privacy>)&#32;·&#32;[Agent index](<https://present-sketch-tp94.here.now/llms.txt>)&#32;·&#32;[Sitemap](<https://present-sketch-tp94.here.now/sitemap.xml>)&#32;·&#32;[Crawler policy](<https://present-sketch-tp94.here.now/robots.txt>)

This independent tutorial is a companion to an OMP session\,&#32;not a connection to it\.&#32;It teaches a sanitized configuration snapshot dated&#32;**28 August 2026**\,&#32;not the live state of your installation\.&#32;Every story and the Cedar project are fictional\.&#32;No private memories\,&#32;transcripts\,&#32;credentials or authentication data are included\.&#32;This is not official OMP support or a company website\.

## Start with a read\,&#32;not a setting change

Your current conversation is OMP’s working context\.&#32;Durable memory is information it can retrieve in a later session\.&#32;Saying something once puts it in the conversation\;&#32;it does not guarantee a useful memory will return\.

Open OMP in your usual project directory and ask the agent\:

~~~text
Use recall to find my preferences for this project. Show the returned IDs, and say clearly if you find nothing.
~~~

The website’s Copy buttons only copy text\.&#32;They do not execute commands\,&#32;change OMP settings or save facts to real memory\.

Keep three input surfaces distinct\:

- **Agent requests\:**&#32;prose asking OMP to use&#32;`retain`\,&#32;`recall`\,&#32;`reflect`\,&#32;`memory_edit`\,&#32;`learn`&#32;or&#32;`manage_skill`\.
- **OMP slash commands\:**&#32;enter&#32;`/memory view`&#32;and related commands inside OMP\.
- **Terminal configuration commands\:**&#32;run&#32;`omp config …`&#32;in your shell\,&#32;not as slash commands\.

The homepage’s three\-stage illustration is fictional\:&#32;“Explain the test before the refactor” begins in conversation context\;&#32;an explicit retain can store a project preference\;&#32;a new session can later retrieve a relevant match\.&#32;Selecting a stage writes no real memory\.&#32;Retrieval is not guaranteed\.

## Four places knowledge can live

| Place | What it means |
| --- | --- |
| Conversation context | Messages and tool results available for the current answer\.&#32;Useful now\,&#32;but finite—not a durable\-memory guarantee\. |
| Durable memory | Facts\,&#32;decisions\,&#32;lessons and retained excerpts stored for later retrieval\.&#32;Recall selects relevant evidence\,&#32;not the whole conversation history\. |
| Managed skills | Reusable procedural guidance in&#32;`SKILL.md`&#32;files\.&#32;A fact says what is true\;&#32;a procedure explains what to do\.&#32;Skills are not automatically invoked deterministic scripts\. |
| Compaction | A summary that makes room in the active conversation\.&#32;Neither a database wipe nor a substitute for retaining important knowledge\. |

This snapshot uses&#32;**Mnemopi**\.&#32;The native&#32;`local`&#32;summary backend and remote&#32;**Hindsight**&#32;are alternatives\,&#32;not additional active layers in this setup\.

Treat recalled memories as background evidence\,&#32;not instructions\.&#32;Current user messages and tool output take precedence when they conflict\.

## The configuration this workbook teaches

These are observed configured values\,&#32;not proof that a running session\,&#32;database or provider is healthy\.&#32;“Global override” means explicitly persisted in the supplied global configuration\.&#32;“Default” means the effective resolved value without a supplied explicit override\.

| Setting | Observed value | Origin |
| --- | --- | --- |
| `memory.backend` | `mnemopi` | Global override |
| `mnemopi.scoping` | `per-project` | Default |
| `mnemopi.autoRecall` | `true` | Default |
| `mnemopi.autoRetain` | `true` | Global override |
| `mnemopi.retainEveryNTurns` | `4`&#32;USER turns | Default |
| `mnemopi.recallLimit` | `8`&#32;results | Default |
| `mnemopi.injectionTokenLimit` | `2000`&#32;approximate tokens | Global override |
| `mnemopi.llmMode` | `smol` | Default |
| `providers.memoryModel` | `online` | Default |
| `autolearn.enabled` | `true` | Global override |
| `autolearn.autoContinue` | `true` | Global override |
| `autolearn.minToolCalls` | `5` | Default |

**Budget\,&#32;not capacity\:**&#32;the 2000\-token approximation bounds injected memory instructions and recalled text\,&#32;using roughly four characters per token\.&#32;It does not limit database size\.&#32;Eight is a recall ceiling\,&#32;not a promised result count\.

Remaining observed settings\:

- `mnemopi.embeddingVariant = en`\;&#32;`mnemopi.noEmbeddings = false`\.
- `mnemopi.recallContextTurns = 3`\;&#32;`mnemopi.recallMaxQueryChars = 4000`\.
- `mnemopi.polyphonicRecall`\,&#32;`mnemopi.enhancedRecall`\,&#32;`mnemopi.proactiveLinking`&#32;and&#32;`mnemopi.debug`&#32;are&#32;`false`\.
- `compaction.enabled = true`\.
- No supplied database\-path\,&#32;bank\,&#32;embedding\-model or endpoint overrides\.&#32;No Mnemopi environment variables were observed in the supplied parent environment\;&#32;other sessions can differ\.

## Five fictional lab stories

Adapt these prompts to non\-sensitive facts in your own work\.&#32;They are requests to the agent\,&#32;not slash commands\.&#32;The stories illustrate checks to perform\,&#32;not observed runtime results\.

### 1\.&#32;Save a preference deliberately

**Fictional situation\:**&#32;Cedar’s maintainer keeps asking for a small testable change before a wider refactor\.

Ask OMP\:

~~~text
Use retain to remember this project preference: explain the smallest testable change before proposing a wider refactor. Then use recall to find it and show its exact ID.
~~~

`retain`&#32;requests a save immediately\;&#32;it does not wait for the four\-turn batch\.&#32;A preference saved here is project\-scoped\,&#32;not automatically universal\.

**Check\:**&#32;recall returns the intended preference and an ID\.&#32;Try again in a fresh session from the same directory\.

**Pitfall\:**&#32;the retain acknowledgement counts requested items\.&#32;It alone is not proof of a successful write\.

### 2\.&#32;Resume the reason\,&#32;not just the task

**Fictional situation\:**&#32;a new session starts after Cedar’s queue\-design discussion\.&#32;You remember the decision\,&#32;but not its constraints\.

Ask OMP\:

~~~text
Use recall to find Cedar's durable job-queue decision, rejected alternatives and migration constraints. Show the returned IDs. Separate saved evidence from assumptions before proposing next steps.
~~~

The first prompt can trigger automatic recall\.&#32;Ask for on\-demand&#32;`recall`&#32;later when the topic changes\.

**Check\:**&#32;look for the decision’s rationale\,&#32;source and date—not merely a confident summary\.

**Pitfall\:**&#32;“No relevant memories found” does not mean the entire store is empty\.&#32;Try specific project and decision terms\.

### 3\.&#32;Connect related lessons

**Fictional situation\:**&#32;Cedar has several retry and timeout decisions\.&#32;You want the pattern\,&#32;including disagreements\.

Ask OMP\:

~~~text
Use reflect to examine Cedar's retry and timeout decisions. Compare their tradeoffs; distinguish agreement, conflict and missing evidence. Use recall for IDs when checking a source.
~~~

Here\,&#32;`reflect`&#32;retrieves and formats scoped memories for the agent to synthesize\.&#32;It is not a separate guaranteed reasoning service or an exhaustive database audit\.

**Check\:**&#32;the answer distinguishes retrieved evidence from the agent’s interpretation\.

**Pitfall\:**&#32;a fluent synthesis can still omit relevant memories\.&#32;The recall limit still applies\.

### 4\.&#32;Correct the row\,&#32;not the preview

**Fictional situation\:**&#32;Cedar’s agreed retry limit changed from three to five\.&#32;An old memory still says three\.

Begin with inspection\,&#32;without editing\:

~~~text
Recall Cedar's retry-limit decision and show exact IDs. Read each candidate's memory:// address in full, including its bank and store. Do not edit yet. Identify the row that says three retries.
~~~

Ask OMP to read&#32;`memory://`&#32;followed by an&#32;**exact returned ID**\.&#32;That is OMP’s internal resource address\,&#32;not an HTTP endpoint on this website\.&#32;It reveals full content and metadata\;&#32;a recall preview can be clipped\.

After selecting the working\-store row\,&#32;ask\:

~~~text
Use memory_edit update on the exact working-store ID we just selected. Replace only the three-retry rule with five retries, preserving the rest of its full content. Read back that same ID, then recall the topic again.
~~~

Store rules in this snapshot\:

- `update`&#32;and&#32;`forget`&#32;operate on working rows\.&#32;To remove an unwanted row\,&#32;ask OMP to forget the exact selected ID\.
- `invalidate`&#32;supports working or episodic rows\.&#32;Ask OMP to invalidate the selected ID\,&#32;optionally linking a verified replacement ID\.
- Extracted&#32;`fact`&#32;projections are read\-only\:&#32;expect&#32;`not_editable`\,&#32;not an edit\.

**Check\:**&#32;inspect the operation’s returned status\,&#32;bank and store\.&#32;Verify the full row and related recall results afterward\.

**Pitfall\:**&#32;an update replaces content wholesale\.&#32;Never reconstruct it from a clipped preview\.&#32;Episodic update\/forget can report&#32;`not_found`\;&#32;stale copies may remain elsewhere\.&#32;Forgetting one eligible row is not universal erasure\.

### 5\.&#32;Turn a verified fix into a technique

**Fictional situation\:**&#32;a duplicate\-delivery test failed before Cedar’s fix and passed afterward\.&#32;Now there is a lesson worth keeping\.

Ask OMP\:

~~~text
We verified Cedar's duplicate-delivery fix with a failing test before the fix and a passing rerun afterward. Use learn to capture the cause, fix, limits and verification. If the steps generalize, also create a managed skill named webhook-replay-check with prerequisites, steps and failure checks. Exclude credentials.
~~~

`learn`&#32;stores a lesson and can also write a skill\.&#32;`manage_skill`&#32;creates\,&#32;updates or deletes managed skills separately\.&#32;Generated files live in the&#32;`managed-skills`&#32;directory under OMP’s agent configuration directory\,&#32;separate from authored&#32;`skills`\;&#32;authored names take precedence\.

**Check\:**&#32;recall the lesson and inspect&#32;`managed-skills/webhook-replay-check/SKILL.md`&#32;under that configuration directory\.&#32;If discovery lags\,&#32;start a fresh session\.

**Pitfall\:**&#32;verify your own technique first\.&#32;Skill creation can fail after the lesson is saved\;&#32;check both outcomes\.&#32;The example’s claimed before\/after test evidence is fictional\,&#32;not evidence for your project\.

## The automatic lifecycle

1. **First turn\:**&#32;automatic recall uses the first non\-empty prompt and recent context\.&#32;It is&#32;**not a new search every turn**\;&#32;later prompt rebuilds can reuse the cached snippet\.&#32;Request recall when needed\.
2. **Four USER turns\:**&#32;at agent\-end\,&#32;automatic retention checks for at least four new user turns since the retention cursor\.&#32;It batches the unretained suffix—not four assistant replies or tool calls\.
3. **Before compaction\:**&#32;a fresh recall supplies additional context to the compaction summary\.&#32;This is distinct from the first\-turn recall gate\.
4. **On shutdown\:**&#32;best\-effort disposal retains the remaining transcript and drains pending extraction\,&#32;skipping fresh extraction and full consolidation\.&#32;Shutdown deadlines can interrupt completion\.

### A separate loop\:&#32;auto\-learn

With both autolearn switches on\,&#32;an eligible completed top\-level turn with&#32;**at least five tool calls**&#32;can trigger an extra private capture turn\.&#32;Counts do not accumulate across prompts\.&#32;Aborted turns\,&#32;plan mode and goal\-mode turns are skipped\.&#32;This adds model use\;&#32;it is not&#32;`autoRetain`&#32;and does not run after every prompt\.

## The command desk

Enter these slash commands inside OMP\.&#32;Nothing on this website executes them\.

| Command | Meaning and limits |
| --- | --- |
| `/memory view` | Shows the injected payload\:&#32;instructions plus cached recalled text\,&#32;not the whole database or a fresh search\. |
| `/memory stats` | Shows scoped bank counts\,&#32;working\/episodic memory\,&#32;triples and database locations\.&#32;Use it to understand scope\. |
| `/memory diagnose` | Inspects scoped database diagnostics and integrity findings\.&#32;Does not establish provider authentication or embedding availability\. |
| `/memory enqueue` | **Mutates memory\.**&#32;Alias\:&#32;`/memory rebuild`\.&#32;Retains the remainder\,&#32;flushes extraction and requests full\,&#32;age\-gated cross\-session consolidation—not an index\-only rebuild\.&#32;An “enqueued” banner is not proof of success\;&#32;check diagnostics and recall afterward\. |

### Destructive warning\:&#32;clear is a wipe\,&#32;not a refresh

`/memory clear`&#32;and&#32;`/memory reset`&#32;delete all currently scoped database files and sidecars\,&#32;including rescued legacy banks and shared\/base databases when present—even beyond ordinary per\-project recall\.&#32;**The controller described in this snapshot has no confirmation\.**&#32;Inspect scope and backups first\.&#32;Removal can fail\;&#32;a success banner does not prove erasure\.

`/memory mm`&#32;is Hindsight\-only\,&#32;not a Mnemopi command workflow\.

### Optional configuration changes—not applied by this tutorial

Use your shell\,&#32;not OMP’s prompt\.&#32;Read a value first\:

~~~sh
omp config get mnemopi.autoRetain --json
~~~

Choose individual changes deliberately\;&#32;this is not a script to run wholesale\.

| Goal | Shell command | Caveat |
| --- | --- | --- |
| Pause periodic retention only | `omp config set mnemopi.autoRetain false` | Not a complete privacy switch\;&#32;other save paths remain\. |
| Skip extra capture turns | `omp config set autolearn.autoContinue false` | Standing auto\-learn guidance remains\. |
| Disable the active memory backend | `omp config set memory.backend off` | Does not delete existing data\. |
| Disable auto\-learn separately | `omp config set autolearn.enabled false` | Also disables its generated\-skill tools on fresh startup\. |

Read back changed keys with&#32;`omp config get`&#32;and&#32;`--json`\.&#32;Start a fresh OMP session after changes affecting startup tools or the backend\.

## Privacy and costs

### In a real OMP session

Mnemopi uses local SQLite and local embedding execution\.&#32;Initial embedding\-model downloads may use the network\.&#32;With&#32;`providers.memoryModel = online`\,&#32;retained user excerpts can reach the tiny\/smol role for fact extraction\,&#32;and consolidation can use that online model too\.

The snapshot’s configured global smol role is&#32;`google-antigravity/gemini-3.7-flash:medium`\,&#32;with no TINY override\.&#32;Actual runtime model resolution and authentication were not exercised\.&#32;Recalled memories also enter the main model’s context\.&#32;Budget for extraction\,&#32;consolidation and extra capture turns\;&#32;do not assume zero egress or guaranteed encryption\.

**Automation switches are separate\.**&#32;Turning&#32;`autoRetain`&#32;off gates periodic batches—not explicit saves\,&#32;`learn`\,&#32;enqueue or shutdown retention\.&#32;Setting&#32;`memory.backend`&#32;to&#32;`off`&#32;disables the active backend\,&#32;but does not delete data or stop normal main\-model conversation\/session persistence\.&#32;Disable autolearn separately for generated skills\.

**Forgetting is not universal erasure\.**&#32;An eligible row can be deleted without erasing transcripts\,&#32;backups\,&#32;provider copies\,&#32;skills or every derivative\.&#32;Keep secrets out of memory\;&#32;verify corrections instead of assuming all copies disappeared\.

### On this website

The interactive homepage stores only self\-reported checklist flags in the browser’s localStorage key&#32;`omp-memory-workbook:checklist:v1`\.&#32;Reset lesson ticks removes that key\;&#32;it does not touch OMP data\.&#32;Storage can fail\,&#32;and saved status is the last local observation\,&#32;not a multi\-tab guarantee\.&#32;Copy controls attempt to write a displayed prompt or command to the clipboard\;&#32;manual selection is the fallback\.&#32;They do not read your clipboard or execute the copied text\.

Fonts are self\-hosted\.&#32;The tutorial script makes no network requests\,&#32;but loading public pages and fonts requires delivery through here\.now and the read\-only Cloudflare delivery layer\.&#32;That layer does not add a tutorial datastore\.&#32;This workbook does not establish what access logs the hosting providers keep or who can access them\.&#32;No account\,&#32;authentication\,&#32;payment or local OMP connection is offered\.&#32;See the&#32;[privacy explanation](<https://present-sketch-tp94.here.now/privacy>)&#32;for the distinction between site behavior and real memory processing\.

## Troubleshooting

### Different session\,&#32;different results\?

Check the directory and bank first\.&#32;`per-project`&#32;derives its bank from&#32;**resolved cwd\,&#32;not git root**\.&#32;Subdirectories and moved projects can differ\.&#32;Same\-cwd legacy rescue may add recall banks\.&#32;Compare scoped stats and diagnostics\.

### Rows exist\,&#32;but the payload lacks them\?

`/memory view`&#32;shows current injection\,&#32;not stored coverage\.&#32;Ask for recall using project names\,&#32;decision terms\,&#32;constraints or dates\.&#32;An empty search differs from a backend\-unavailable error\.

### A recent conversation was not retained\?

Count new USER turns against the four\-turn threshold\.&#32;For an important fact\,&#32;request explicit retain and verify retrieval\.&#32;Do not depend on shutdown finishing\.

### Settings say “on\,” but tools or recall fail\?

Persisted settings can differ from running state\.&#32;Start a fresh session\.&#32;Check&#32;`/memory diagnose`\,&#32;then embedding download\/worker availability and memory\-model availability\.&#32;Model resolution can fall back without an LLM\;&#32;database integrity alone proves neither capability\.

### An old answer keeps coming back\?

Inspect exact IDs\,&#32;banks and stores\.&#32;Check duplicates\,&#32;rescued banks\,&#32;read\-only fact projections and cached injection\.&#32;State the current correction explicitly\;&#32;current user\/tool evidence wins\.&#32;Re\-read and recall after editing rather than deleting vaguely matching rows\.

## Your checkpoint

Mark these only after your own checks\.&#32;Homepage ticks are self\-reported lesson progress\,&#32;not OMP verification or real memory state\.

- \[&#32;\]&#32;I can distinguish context\,&#32;memory and skills\.
- \[&#32;\]&#32;I saved a non\-sensitive fact and recalled its ID in a new session\.
- \[&#32;\]&#32;I inspected full content before choosing an edit\.
- \[&#32;\]&#32;I checked my bank scope and understand the automation switches\.

The homepage is readable without JavaScript\.&#32;Without it\,&#32;select prompt text manually\;&#32;the illustration remains readable and lesson ticks cannot be saved\.

## Browser agent interface

### When to use—and when not to

Use the interface to read authored chapters\,&#32;navigate the workbook\,&#32;select the fictional illustration\,&#32;open disclosures\,&#32;inspect tutorial state or operate an explicitly requested local lesson checklist\.&#32;Do not use it to search\,&#32;save\,&#32;correct or erase actual OMP memories\,&#32;run terminal commands\,&#32;authenticate to OMP or establish database health\.&#32;No REST endpoint or MCP service is provided by this site\.

Open the&#32;[canonical homepage](<https://present-sketch-tp94.here.now/labs/memory>)&#32;in a JavaScript\-capable browser and evaluate in the&#32;**page’s main JavaScript world**\.&#32;The API is&#32;`window.memoryTutorial`\,&#32;version 1\.&#32;A fetch\-only document reader\,&#32;an isolated extension world or a support page does not expose this object\.&#32;If it is absent\,&#32;use the authored Markdown for reading\;&#32;do not invent a network API\.

Call&#32;`discover()`&#32;for the live schemas\,&#32;permissions\,&#32;exact control IDs\,&#32;enabled states and gaps\.&#32;These schemas\,&#32;not guesses from visible labels\,&#32;define the currently loaded page’s interface\.

| Operation | Input | Result |
| --- | --- | --- |
| `discover()` | None | Scope\,&#32;permissions\,&#32;schemas\,&#32;controls and gaps\. |
| `inspect()` | None | Authoritative page revision\,&#32;content revision\,&#32;current chapter\,&#32;illustration\,&#32;checklist\/storage\,&#32;clipboard\,&#32;disclosures and stable controls\. |
| `query({text, limit})` | Text of at most 240 characters\;&#32;optional integer limit 1–10\,&#32;default 5\. | Searches authored chapter text\,&#32;including closed disclosures\,&#32;never memories\.&#32;Status is&#32;`matched`\,&#32;`empty`&#32;or&#32;`empty-query`\;&#32;unavailable content is a structured failure\. |
| `act({id, action, expectedRevision, …})` | Exact registered control ID\,&#32;supported action and a fresh revision\. | `performed`&#32;or&#32;`unchanged`\,&#32;`changed`\,&#32;current revision and optional clipboard outcome\. |
| `wait({afterRevision, timeoutMs, match})` | Existing revision from this document\;&#32;optional integer 0–5000 milliseconds\,&#32;default 1500\;&#32;optional&#32;`match`&#32;with&#32;`chapterId`&#32;and\/or&#32;`demoStep`&#32;\(1\,&#32;2 or 3\)\. | `matched`&#32;only after a newer revision satisfying every supplied condition\,&#32;otherwise&#32;`timed-out`\;&#32;includes current revision and state\. |
| `diagnose()` | None | Tutorial runtime\,&#32;storage\,&#32;clipboard capabilities and control integrity—not OMP health\. |

`query()`&#32;matches all whitespace\-separated search terms case\-insensitively within a chapter\.&#32;Results have a chapter ID\,&#32;title\,&#32;anchor\,&#32;navigation control ID and excerpt\.&#32;A chapter excerpt is not the full source text\.

### Read\,&#32;act\,&#32;observe

Run this JavaScript in the homepage’s main world\.&#32;It selects only a fictional stage\:

~~~js
const t = window.memoryTutorial;
const capabilities = t.discover();
const chapters = t.query({ text: "compaction", limit: 3 });
const before = t.inspect();
const result = await t.act({
  id: "demo-recall",
  action: "click",
  expectedRevision: before.revision
});
const observed = result.ok && result.changed
  ? await t.wait({
      afterRevision: before.revision,
      timeoutMs: 1000,
      match: { demoStep: 3 }
    })
  : t.inspect();
const diagnostics = t.diagnose();
~~~

An already selected stage or another unchanged action does not guarantee a newer revision\.&#32;Do not interpret a wait timeout as a successful transition\,&#32;or a successful stage selection as a successful real memory operation\.

### Action shapes and revision rules

- Use&#32;`action: "click"`&#32;for a discovered click control\,&#32;such as a navigation\,&#32;illustration\,&#32;copy or reset control\.
- Use&#32;`action: "setChecked"`&#32;with a boolean&#32;`checked`&#32;only for a checkbox control\.&#32;This changes self\-reported local progress\,&#32;not verified OMP state\.&#32;Do not tick it without the reader’s actual completion or explicit request\.
- Use&#32;`action: "setOpen"`&#32;with a boolean&#32;`open`&#32;only for a disclosure’s&#32;**summary control ID**\,&#32;such as&#32;`settings-extra-toggle`\,&#32;not its containing details ID\.
- Supply&#32;`checked`&#32;only for&#32;`setChecked`\,&#32;and&#32;`open`&#32;only for&#32;`setOpen`\.&#32;Extra or unsupported fields are rejected\.
- Inspect immediately before every action\.&#32;Pass that response’s opaque&#32;`revision`&#32;as&#32;`expectedRevision`\;&#32;do not use&#32;`contentRevision`&#32;or manufacture a token\.
- Revisions expire on reload\.&#32;User actions\,&#32;scrolling and asynchronous clipboard results can change them\.&#32;On&#32;`STALE_REVISION`\,&#32;inspect again and reassess the intended action\.
- Honor&#32;`enabled`&#32;and&#32;`unavailableReason`\.&#32;Disabled\,&#32;hidden\,&#32;inert or closed\-disclosure targets are not actionable\.&#32;Open the appropriate discovered disclosure first\,&#32;then inspect again\.&#32;Never bypass a disabled control by editing the DOM\.
- Actions use the same native click\/change paths as human controls\.&#32;A structured success is not permission to claim unobserved effects\.

For example\,&#32;open the configuration disclosure without changing a setting\:

~~~js
const t = window.memoryTutorial;
const before = t.inspect();
await t.act({
  id: "config-options-toggle",
  action: "setOpen",
  open: true,
  expectedRevision: before.revision
});
~~~

### Failures and boundaries

Failures use&#32;`{ok:false, error:{code,message}}`\,&#32;with revision\/scope metadata and optional details\.&#32;The discovery schema lists&#32;`INVALID_ARGUMENT`\,&#32;`STALE_REVISION`\,&#32;`STALE_DOCUMENT`\,&#32;`INVALID_REVISION`\,&#32;`UNKNOWN_TARGET`\,&#32;`AMBIGUOUS_TARGET`\,&#32;`DISABLED_TARGET`\,&#32;`UNSUPPORTED_ACTION`\,&#32;`ACTION_FAILED`\,&#32;`UNAVAILABLE`&#32;and&#32;`BUSY`\.

Clipboard access depends on browser policy and user activation\.&#32;Inspect the returned outcome\;&#32;copying may require manual selection\.&#32;A timed\-out attempt is unconfirmed\,&#32;not proof that a later browser write is impossible\.&#32;Saved checklist state reports the last local observation\,&#32;without multi\-tab locking\.

The API requires no login\,&#32;API key\,&#32;authentication or payment\.&#32;It operates the tutorial only\:&#32;no OMP\,&#32;filesystem\,&#32;credentials\,&#32;real memories\,&#32;command execution or network requests from its script\.&#32;`diagnose()`&#32;cannot assess your OMP installation\.

## Sources\,&#32;method and limitations

The original workbook was AI\-authored by Ultima from supplied sanitized configuration and OMP\-facing source\.&#32;Its factual review is source\-based\,&#32;not a live memory audit or a claim of human certification\.&#32;This Markdown edition derives from the authored homepage and its browser API implementation\.&#32;The configuration was observed with&#32;`omp config list --json`\;&#32;the supplied record is 29 August 2026 at 00\:01 UTC\,&#32;while the workbook snapshot is dated 28 August\.&#32;Memory contents and database health were not inspected\.

This describes a custom local build\,&#32;not guaranteed upstream version parity\.&#32;Core extraction\/consolidation internals were not exhaustively supplied\.&#32;Examples describe expected checks rather than observed outcomes\.&#32;The&#32;[upstream Oh My Pi project](<https://github.com/can1357/oh-my-pi>)&#32;is the place to consult public software documentation\;&#32;it is not the operator or official support channel for this independent workbook\.

Implementation references below are source\-relative paths under&#32;`packages/coding-agent/src/`\,&#32;not private filesystem links\:

| Source | Relevant symbols |
| --- | --- |
| `mnemopi/config.ts` | `loadMnemopiConfig`\,&#32;`computeMnemopiBankScope`\,&#32;`projectBank`\,&#32;`extendRecallWithLegacyBanks`\,&#32;`truncateApproxTokens` |
| `mnemopi/state.ts` | `MnemopiSessionState`\:&#32;`beforeAgentStartPrompt`\,&#32;`maybeRetainOnAgentEnd`\,&#32;`recallForCompaction`\,&#32;`consolidate`\,&#32;`dispose`\,&#32;`getScopedMemory`\,&#32;`editScopedMemory`\;&#32;`getMnemopiScopedDbPaths` |
| `mnemopi/backend.ts` | `mnemopiBackend`\,&#32;`resolveMnemopiProviderOptions`\,&#32;`resolveMemoryCompletionInput` |
| `tools/memory-recall.ts`\,&#32;`tools/memory-retain.ts`\,&#32;`tools/memory-reflect.ts`\,&#32;`tools/memory-edit.ts` | `MemoryRecallTool`\,&#32;`MemoryRetainTool`\,&#32;`MemoryReflectTool`\,&#32;`MemoryEditTool` |
| `internal-urls/memory-protocol.ts` | `MemoryProtocolHandler.resolve`\,&#32;`renderMnemopiMemory` |
| `autolearn/controller.ts`\,&#32;`autolearn/managed-skills.ts` | `AutoLearnController`\,&#32;`buildAutoLearnInstructions`\,&#32;`writeManagedSkill`\,&#32;`getManagedSkillsDir` |
| `tools/learn.ts`\,&#32;`tools/manage-skill.ts` | `LearnTool`\,&#32;`ManageSkillTool` |
| `memory-backend/types.ts`\,&#32;`memory-backend/off-backend.ts` | `MemoryBackend`\,&#32;`offBackend` |
| `modes/controllers/command-controller.ts` | `handleMemoryCommand` |

Reading fonts are served with the site under the SIL Open Font License\.&#32;No telemetry or account connection is included in the tutorial script\.

[Return to the interactive workbook](<https://present-sketch-tp94.here.now/labs/memory>)&#32;·&#32;[About](<https://present-sketch-tp94.here.now/about>)&#32;·&#32;[Contact and feedback](<https://present-sketch-tp94.here.now/contact>)&#32;·&#32;[Privacy](<https://present-sketch-tp94.here.now/privacy>)&#32;·&#32;[Agent index](<https://present-sketch-tp94.here.now/llms.txt>)&#32;·&#32;[Sitemap](<https://present-sketch-tp94.here.now/sitemap.xml>)&#32;·&#32;[Crawler policy](<https://present-sketch-tp94.here.now/robots.txt>)

## Memory and reusable knowledge\:&#32;next steps

Useful memory is evidence with scope and provenance\,&#32;not standing permission and not an infallible account of the project\.&#32;A corrected row can coexist with an older journal\,&#32;a skill\,&#32;a cached injection\,&#32;or a previously exported copy\.&#32;Verify the representation that matters instead of treating one successful operation as a claim about all copies\.

The next part adds another active conversation\.&#32;A tangent can inherit memory\-derived content already present in the conversation\,&#32;as well as access to shared files\.&#32;Its narrow assignment does not make that inheritance a privacy boundary\.

Continue to&#32;[Tangent work and live control](<https://present-sketch-tp94.here.now/chapters/unified-tan>)\,&#32;carrying two questions\:&#32;what evidence did the worker receive\,&#32;and what authority does its actual runtime have\?

## Tangent work and live control

A&#32;**tan**&#32;is an OMP tangent subagent\,&#32;not TanStack\.&#32;It is a contextual\,&#32;tool\-capable conversation launched from Main’s persisted history\.&#32;It can work alongside Main\,&#32;but its conversation fork does not create a separate checkout or worktree\.

This part starts where control mistakes are most costly\:&#32;a tan is already running\,&#32;and you need to know what the next input will affect\.

### Keep three state tracks visible

The&#32;**view**&#32;may be Main\,&#32;a focused Tan chat\,&#32;or an overlay\.&#32;The&#32;**agent**&#32;may be running\,&#32;idle\,&#32;parked\,&#32;aborted\,&#32;or absent\.&#32;The&#32;**initial background job**&#32;has its own running or settled state and may later disappear from the job list\.

Those tracks can change independently\.&#32;A completed initial job normally parks the tan and returns focus to Main\.&#32;A revived tan can subsequently answer another prompt and become idle without restarting that old job\.&#32;A draft is not permanently addressed to the agent for which you began typing it\.

Use the full Tan agent ID for conversation identity and a separately discovered job ID for the initial managed run\.&#32;A supervised process has yet another identity\:&#32;its process name\.&#32;Display labels\,&#32;row positions\,&#32;and shortened task previews are not substitutes\.

### Read the practice assignment as a bounded contract

Lantern Library’s catalog\,&#32;label guide\,&#32;and visitor FAQ are fictional textual fixtures included in the source chapters\.&#32;You can work through every control decision without launching another provider request\.&#32;There is no separate downloadable Tan example package\.

The assignments deliberately name read inputs\,&#32;write ownership\,&#32;excluded work\,&#32;and evidence expected in the report\.&#32;That makes concurrent work reviewable\,&#32;but a “do not edit” instruction is not filesystem isolation\.&#32;When two workers share a directory\,&#32;even a read\-only report can become stale as another worker changes its inputs\.

The initial subagent setup also deserves attention\:&#32;the recorded helper defaults unattended approval to&#32;`yolo`\,&#32;while explicit per\-tool policies remain inherited\.&#32;Main’s live extension instances and interactive facilities are not simply cloned into the child\.&#32;Do not assume an extension safeguard follows a tan merely because it was active in Main\.

### Operate the actual surface

Read&#32;[Find and focus the right tan](<https://present-sketch-tp94.here.now/chapters/tan-3-find-and-focus-the-right-tan>)&#32;with your effective bindings in mind\.&#32;The chapters preserve important implementation\-specific cautions about Escape\,&#32;double\-left\,&#32;the Hub\,&#32;queues\,&#32;images\,&#32;retry\,&#32;and Main\-owned command paths\.

The recorded proof used real session and controller components with a mock provider and a recording UI adapter\.&#32;It supports the named lifecycle observations\,&#32;not physical key delivery\,&#32;real task quality\,&#32;cache savings\,&#32;or complete external\-process cancellation\.&#32;Keep&#32;[Evidence and method](<https://present-sketch-tp94.here.now/chapters/tan-evidence-and-method>)&#32;attached to those claims\.

## Orientation

*An Ultima\-authored guide to starting a tangent\,&#32;finding it again\,&#32;changing its direction\,&#32;continuing it\,&#32;and stopping the right thing\.*

This workbook is for operating Oh My Pi through its terminal interface and through Main\.&#32;It emphasizes the moment&#32;**after a tan has started**\,&#32;when knowing the current target matters more than remembering a command\.

**Scope\:**&#32;this describes a&#32;**custom local OMP source snapshot from 29 August 2026**\.&#32;The installed CLI reported&#32;`omp/18.0.7`\;&#32;that version number does&#32;**not**&#32;establish upstream\-release parity or prove that an installed binary matches these sources\.

All&#32;**Lantern Library**&#32;examples below are fictional teaching fixtures—not observed user conversations\,&#32;files\,&#32;or work\.&#32;Expected observations are checks to make in your own session\,&#32;not claims that the examples have been executed there\.

## A tan is already running

**What do you want to do\?**

Before typing\,&#32;distinguish three things\:

- **Main chat\:**&#32;ordinary prompts address Main\.
- **Agent Hub\:**&#32;selecting a row does not yet focus its chat\.&#32;Inspect the&#32;**full agent ID**\,&#32;not merely the display name&#32;`tan`\.
- **Focused tan chat\:**&#32;after successful activation\,&#32;look for the notice beginning&#32;**`Viewing agent …`**&#32;with the intended ID\.&#32;The transcript and status line are retargeted to that session\.&#32;The dimmed editor outline is only a secondary cue—not a unique identity\.

After any automatic return to Main\,&#32;**recheck the target before submitting an existing draft**\.

| I want to… | Action in this implementation | Important distinction |
| --- | --- | --- |
| Find the tan | Open the runtime&#32;**Agent Hub**&#32;with&#32;**Alt\+A**&#32;or&#32;**Ctrl\+S**\;&#32;inspect its exact ID\.&#32;From Main\,&#32;use&#32;`/jobs`&#32;for background\-job status\. | A Tan agent ID and its background job ID are different\. |
| Watch or talk to it | In the Hub\,&#32;select the exact Tan row and press&#32;**Enter**\. | A parked tan may need revival before focus succeeds\. |
| Correct its current work | In the verified tan chat\,&#32;type a plain\-language correction and press&#32;**Enter**&#32;while it is streaming\. | This queues steering\;&#32;it is not a guaranteed immediate hard interruption\. |
| Give it the next task | In the verified tan chat\,&#32;submit with&#32;**Ctrl\+Q**&#32;or&#32;**Ctrl\+Enter**&#32;while it is streaming\. | A follow\-up waits until the current work would otherwise yield\. |
| Return to Main without cancelling it | In ordinary focused chat\,&#32;**Escape clears a nonempty text draft**\;&#32;with an empty draft\,&#32;**Escape returns to Main**\.&#32;Inspect the result after each press\. | Do not blindly press Escape twice\:&#32;the second press might already be acting on Main\. |
| Return using Left | With an empty focused editor\,&#32;deliberately press&#32;**Left\,&#32;Left**\. | It returns&#32;**directly to Main**\,&#32;despite the current banner saying “parent\.” |
| Continue a finished tan | Find its&#32;**parked**&#32;row\,&#32;press&#32;**Enter**\,&#32;then send a new\,&#32;scoped prompt\. | This continues the agent conversation\;&#32;it does not restart its old background job\. |
| Interrupt a turn to advance a queued correction | With the tan&#32;**streaming**\,&#32;a message&#32;**already queued**\,&#32;and&#32;**no text or images in the editor**\,&#32;press&#32;**Enter**\. | This requests a turn abort\.&#32;Queues\,&#32;job cancellation\,&#32;and agent killing remain separate\. |
| Cancel its background job | Ask Main to discover the exact current job identity\,&#32;then cancel that&#32;**job ID**&#32;through the Hub tool\. | `/jobs`&#32;itself is a status command\,&#32;not a cancellation command\. |
| Explicitly kill the tan | In the runtime Hub\,&#32;inspect the exact Tan row\,&#32;then press&#32;**x**\. | The explicit\-kill path requests a durable tombstone and retains the transcript\.&#32;It does not undo edits\. |

The ordinary focused\-Escape behavior above assumes&#32;**no higher\-priority panel or speech playback and no global loop\-mode handling taking precedence**\.&#32;See&#32;[Leave and switch safely](<https://present-sketch-tp94.here.now/chapters/tan-5-leave-and-switch-safely#tan-5-leave-and-switch-safely>)\.

> **Two common wrong turns**
>
> - `/tan`&#32;launches work\.&#32;It has no management subcommands in this snapshot\.&#32;A word such as&#32;`stop`&#32;after&#32;`/tan`&#32;is work text—not an instruction to stop an existing tan\.
> - The runtime&#32;**Agent Hub**&#32;opened by Alt\+A\/Ctrl\+S is not the&#32;`/agents`&#32;dashboard for configuring agent definitions\.

---

## 1\.&#32;Understand the fork

### One conversation branches\;&#32;one workspace remains shared

A tan is a&#32;**separate\,&#32;contextual agent conversation**&#32;created from Main’s persisted conversation history\.&#32;It receives a new work request and can run concurrently with Main\.

It is tool\-capable—not merely a second text answer\.&#32;Depending on the tools available when it is constructed\,&#32;it can read files\,&#32;edit them\,&#32;execute work\,&#32;and use other enabled capabilities\.

The useful mental picture is\:

> **Two conversations\,&#32;potentially two active workers\,&#32;one shared working directory\.**

The conversation fork does&#32;**not**&#32;create an isolated checkout\,&#32;Git branch\,&#32;or worktree\.&#32;There is no automatic tan merge operation\.&#32;If a tan edits a file in the shared directory\,&#32;that edit is already in the directory Main uses\.

### What a tan is not

| Nearby concept | How it differs |
| --- | --- |
| A quick side question | `/btw`&#32;uses an ephemeral side\-request path\.&#32;Its tool calls are discarded rather than executed\.&#32;A tan has a real session and can perform tool work\. |
| An ordinary task subagent | Ordinary task execution starts from a delegated assignment and an agent definition\,&#32;with its own model\,&#32;lifecycle\,&#32;isolation\,&#32;and result conventions\.&#32;Do not assume those conventions all apply to&#32;`/tan`\. |
| Another terminal session | Opening another terminal does not automatically fork Main’s conversation\.&#32;Tan registry identities and background\-job rows are process\-local controls\,&#32;not universal handles across terminal processes\. |
| A supervised process | A server\,&#32;watcher\,&#32;debugger\,&#32;or REPL managed through the Hub’s process operations is addressed by a project\-scoped&#32;**process name**\.&#32;It is not a conversational Tan agent\. |
| A safe sandbox | Shared directory access and tool permissions still matter\.&#32;“Do not edit” is a task instruction\,&#32;not filesystem isolation\. |
| A permanent worker | Completion can dispose the live session and leave a parked record\.&#32;Revival depends on retained files and available runtime support\. |

### The fork has a task boundary

The initial tan is told that the earlier conversation belongs to its parent and that its own responsibility is the new tangent\.

The controller also clears the&#32;**clone’s inherited todo list**\,&#32;including a persisted empty todo edit\.&#32;It does&#32;**not**&#32;clear Main’s todo list\.

This reduces a common failure\:&#32;a fork sees Main’s unfinished checklist and tries to finish Main’s work instead of its own assignment\.

**Operating recommendation\:**&#32;make the boundary concrete anyway\.&#32;State\:

1. The desired outcome\.
2. The files it may read\.
3. The files it may change—or that it must not change any\.
4. Work owned by Main or another tan\.
5. What evidence its report must contain\.

---

## 2\.&#32;Start from Main

### The exact syntax

**OMP slash command — enter in Main’s composer\,&#32;not a shell\:**

~~~text
/tan <work>
~~~

Replace&#32;`<work>`&#32;with the task\.&#32;The command takes the remaining\,&#32;trimmed text as the work item\.&#32;Multi\-word text needs no special quoting\.

There are no&#32;`/tan`&#32;flags in the supplied implementation\.

Main can already be streaming when you launch a tan\.&#32;The launch path does not require aborting Main’s current turn\.

### Fictional practice project

Imagine a disposable checkout containing these files\.

**`data/catalog.csv`&#32;— fictional fixture\:**

~~~csv
title,shelf,label
The Paper Moon,green,amber
A Map of Clouds,green,blue
The Quiet Atlas,silver,amber
~~~

**`docs/label-guide.md`&#32;— fictional fixture\:**

~~~text
Green-shelf books use amber labels.
Silver-shelf books use silver labels.
~~~

**`docs/visitor-faq.md`&#32;— fictional fixture\:**

~~~text
Visitors may browse the green and silver shelves.
Ask a librarian if a shelf label is unclear.
~~~

You can work through the examples without creating anything\.&#32;For a live rehearsal\,&#32;have Main prepare these fixtures in a disposable practice directory—not by overwriting similarly named files in an existing project\.

### Worked scenario\:&#32;launch a bounded audit

First confirm that you are in Main and that the fictional files are available\.

**OMP slash command — Main only\:**

~~~text
/tan Audit the fictional Lantern Library label rules. Read data/catalog.csv and docs/label-guide.md. Report mismatched labels with their expected labels. Do not edit any file, run project-wide repairs, or change Main's work.
~~~

**Expected observations**

- A dispatch status identifies a background job\,&#32;such as the generated job ID shown after&#32;`Dispatched background tan`\.
- The tan appears in the runtime Hub under a generated&#32;`Tan-…`&#32;agent ID\.
- Main remains the underlying conversation\;&#32;launch does not automatically focus the tan\.
- If Main was already working\,&#32;its turn can continue alongside the tan\.

The compact dispatch breadcrumb may show only a shortened work preview\.&#32;While Main is streaming\,&#32;the controller queues that breadcrumb for later context rather than steering it into Main’s current response\.

**Fictional acceptance check**

Against the unchanged fixtures\,&#32;a complete audit should identify\:

- **A Map of Clouds\:**&#32;blue → amber\.
- **The Quiet Atlas\:**&#32;amber → silver\.

That is a correctness check for the assignment—not a promised model response\.

If the tan finishes before you reach it\,&#32;use&#32;[Continue a finished tan](<https://present-sketch-tp94.here.now/chapters/tan-7-continue-a-finished-tan#tan-7-continue-a-finished-tan>)\.&#32;Finishing quickly is not a control failure\.

### Prerequisites and launch failures

| Observation | Meaning and recovery |
| --- | --- |
| `Usage: /tan <work>` | No nonblank work was supplied\.&#32;Return to Main and provide a concrete assignment\. |
| `No active model available for /tan.` | Main has no active model to pass to the clone\.&#32;Resolve model availability in Main before launching again\. |
| `Background jobs are disabled; enable async jobs to use /tan.` | The actual check is that this session has an async job manager\.&#32;See the correction below\. |
| `/tan requires a persisted session.` | An in\-memory session is insufficient\.&#32;Use a normal persisted interactive session\. |
| Background\-job limit reached | Wait for an appropriate running job to finish\,&#32;or deliberately cancel an identified job\.&#32;Do not cancel an arbitrary row to make room\. |
| A filesystem\,&#32;session\,&#32;authentication\,&#32;or provider error | Determine whether dispatch happened before retrying\.&#32;A launch acknowledgment does not prove that the first model request succeeded\. |

A persisted session need not already contain an assistant answer\:&#32;the controller calls&#32;`ensureOnDisk()`&#32;and flushes it before forking\.&#32;An in\-memory session\,&#32;however\,&#32;has no persisted parent path to fork\.

> **Important recovery correction**
>
> Although the missing\-manager error says “enable async jobs\,” this&#32;`/tan`&#32;controller does not test&#32;`async.enabled`\.&#32;The shown SDK constructs the primary async manager independently of that toggle\.
>
> Therefore\,&#32;changing&#32;`async.enabled`&#32;is&#32;**not an established repair for a missing manager**\,&#32;nor is that toggle a proven&#32;`/tan`&#32;on\/off switch here\.&#32;Have Main diagnose the current host\/session setup and implementation version\.
>
> The SDK does read&#32;`async.maxJobs`&#32;when constructing the manager\.&#32;That is not evidence of live resizing of an existing manager\.

Keep a copy of an important launch request\.&#32;The slash\-command handler clears its text before starting\;&#32;the focused\-chat restoration behavior described later is not a blanket guarantee for failed slash\-command launches\.

### What is inherited—and what is not synchronized

| At initial launch | Boundary |
| --- | --- |
| Persisted conversation entries | The child reconstructs context from a journal copy\.&#32;Unfinished streaming text\,&#32;unsent drafts\,&#32;and pending user queues are not a synchronized continuation of Main\.&#32;Compaction can affect what history becomes active model context\. |
| Main’s current model and configured thinking selector | These are passed at launch\,&#32;including an&#32;`auto`&#32;thinking selector when configured\.&#32;Main’s later choices are not a live model mirror for the tan\. |
| Main’s current system\-prompt blocks | A snapshot is passed\,&#32;not a continuously synchronized prompt\. |
| Main’s enabled tool names | This includes enabled tools that are not top\-level\-visible\.&#32;The SDK rebuilds tools\;&#32;names alone do not guarantee identical availability or implementations\. |
| A settings snapshot | The subagent helper applies its own overrides\.&#32;Shared services and storage still exist\. |
| Model registry and authentication infrastructure | The tan is not a separate account or permission sandbox\. |
| Working directory and inherited workspace context | No isolated repository copy is created\. |
| Main’s initial&#32;`local://`&#32;mapping | It is captured for the initial background run\;&#32;later Main session changes do not retarget that captured mapping\. |

**Permission caveat\:**&#32;the subagent settings helper sets the default approval mode to&#32;**`yolo`**&#32;for unattended execution and disables the advisor by default\.&#32;Explicit per\-tool approval policies are still inherited\.&#32;Do not assume Main’s interactive approval behavior is reproduced unchanged\.

The initial tan is created headlessly\.&#32;Focusing it later does not recreate it as a new Main session with all interactive\-only facilities\.

It also does not inherit Main’s live extension instances\,&#32;editor\,&#32;shell\/kernel state\,&#32;browser ownership\,&#32;or active provider request as one cloned runtime\.&#32;Initial extension discovery is disabled\,&#32;but that is&#32;**not**&#32;equivalent to “all custom capabilities are disabled”\:&#32;SDK custom\-tool discovery and supplied MCP proxy tools are separate paths\.

### Newly staged images are not forwarded by the launch command

The current&#32;`/tan`&#32;slash handler forwards&#32;**work text only**\.&#32;`TanCommandController.start()`&#32;does not accept an image argument\,&#32;and its initial&#32;`clone.prompt()`&#32;supplies no images\.

Consequently\:

- An image already recorded in inherited conversation history may be part of the forked history\.
- An image newly staged beside&#32;`/tan …`&#32;is&#32;**not forwarded as a new attachment by this launch path**\.
- A textual image marker in the work request is not evidence that its image bytes arrived\.

For a new image\,&#32;launch first\,&#32;focus the verified Tan ID\,&#32;then attach it to an ordinary chat message\.&#32;Alternatively\,&#32;provide an authorized\,&#32;accessible file and ask the tan to read it\.

Inspect the composer after launch\.&#32;Do not assume staged attachments were either sent or safely discarded\.

---

## 3\.&#32;Find and focus the right tan

### Learn your actual bindings

**OMP slash command — Main only\:**

~~~text
/hotkeys
~~~

Use the displayed bindings rather than treating this workbook’s defaults as immutable\.

On macOS\,&#32;OMP may label&#32;**Alt**&#32;as&#32;**Option**\.&#32;Terminal applications\,&#32;multiplexers\,&#32;and custom bindings can intercept or change delivery of a chord\.&#32;Ctrl\+Enter is especially dependent on terminal support\;&#32;Ctrl\+Q is the alternative default\.

**A current documentation gap\:**&#32;the generated&#32;`/hotkeys`&#32;sheet shows the resolved Hub shortcuts and many other application actions\,&#32;but the supplied generator omits follow\-up and dequeue rows\.&#32;Their absence from that sheet does not mean the actions are disabled\.

For those bindings\,&#32;ask Main to inspect the effective configuration for\:

- `app.message.followUp`
- `app.message.dequeue`

The keybinding loader supports&#32;`keybindings.yml`\,&#32;`keybindings.yaml`\,&#32;and legacy&#32;`keybindings.json`\,&#32;with profile inheritance\.&#32;Do not infer an effective binding from one file without considering overrides\.

### Default controls by surface

| Surface | Default control | Effect |
| --- | --- | --- |
| Ordinary editor | **Alt\+A**&#32;or&#32;**Ctrl\+S** | Open the runtime Agent Hub\. |
| Hub roster | **Up\/Down**&#32;or&#32;**k\/j** | Select a row\. |
| Hub roster | **Enter** | Focus a local\,&#32;usable agent\;&#32;revive a parked one if possible\.&#32;Successful activation closes the Hub\. |
| Hub roster | **t** | Switch between Flat and By parent views\. |
| Narrow Hub | **Tab** | Toggle the selected\-agent details view\. |
| Visible Hub details | **Page Up\/Page Down** | Scroll details\. |
| Narrow details view | **Escape** | Return to the roster\.&#32;Use Escape rather than Left when the Hub is open over a focused tan\. |
| Hub roster | **Escape**\,&#32;or its Hub toggle key | Close the Hub\,&#32;revealing the prior chat view\. |
| Hub\,&#32;selected parked row | **r** | Attempt revival without submitting a new task\. |
| Hub\,&#32;selected agent row | **x** | Explicit kill\/release\.&#32;Inspect the target first\. |
| Focused tan chat | **Enter** | New prompt when idle\;&#32;steering when streaming\. |
| Focused tan chat | **Ctrl\+Q**&#32;or&#32;**Ctrl\+Enter** | Follow\-up when streaming\;&#32;ordinary new prompt when idle\. |
| Focused tan chat | **Escape** | Ordinary behavior\:&#32;clear text draft\,&#32;otherwise return to Main\. |
| Empty focused editor | Deliberate&#32;**Left\,&#32;Left** | Return directly to Main\. |

**Focused\-Hub caution\:**&#32;with a tan focused and an empty composer\,&#32;the global Left handler consumes the key before the Hub details view receives it\.&#32;Repeating Left can return the underlying chat to Main instead of dismissing details\.&#32;Left is a details\-back action only when that focused\-agent interceptor is inactive\;&#32;use Escape here and recheck the prompt recipient after closing the Hub\.

The Hub’s By parent view is&#32;**agent lineage**\,&#32;not Main’s&#32;`/tree`&#32;conversation\-branch selector\.&#32;Changing that display does not move files\,&#32;merge conversations\,&#32;or change the current prompt recipient\.

Main is the ambient chat and is excluded from the runtime Hub roster\.&#32;Do not look for a Main row as the normal way back\.

### Worked scenario\:&#32;inspect\,&#32;then focus

1. Open the runtime Hub\.
2. Select the row for the label audit\.
3. Read its&#32;**full Tan ID**\,&#32;lifecycle state\,&#32;task information when available\,&#32;and lineage\.
4. If the information is ambiguous\,&#32;use Main’s discovery procedure in&#32;[Observe jobs and read results](<https://present-sketch-tp94.here.now/chapters/tan-6-observe-jobs-and-read-results#tan-6-observe-jobs-and-read-results>)\.
5. Press Enter only once the intended row is identified\.
6. Wait for the focus notice identifying that exact agent\.

**Expected observation\:**&#32;the Hub closes and that agent’s conversation becomes the visible chat\.

**Failure recovery\:**&#32;if activation fails\,&#32;the Hub keeps the failure visible as a notice\.&#32;Do not type the task into an uncertain surface\.&#32;Diagnose the reported revival or session problem first\.

A parked row is not automatically a promise of revivability\.&#32;An aborted row opens a read\-only transcript viewer rather than becoming a writable focused session\.&#32;Advisor rows are also observation\-only\.

### The “parent” banner is misleading

The current focus notice includes\:

~~~text
Viewing agent <agent-id> — Esc returns to main, ←← hops to parent
~~~

The second clause is not the implemented key behavior\.

The focused double\-left handler calls&#32;`unfocusSession()`\.&#32;It goes&#32;**directly to Main**\,&#32;including when the focused agent has a non\-Main parent\.

To inspect another parent agent\,&#32;select its exact row in the Hub\.&#32;Do not use double\-left as a one\-level\-up navigation instruction\.

### Optional detail\:&#32;double\-left is a gesture\,&#32;not key repeat

From Main\,&#32;deliberate double\-left on an empty editor opens the Hub only when it has agent content to show\,&#32;including saved agents\.&#32;The explicit Hub shortcuts can open an empty roster\.

The editor detector rejects very rapid synthesized bursts and held\-arrow repetition\.&#32;Its accepted second\-tap interval is at least 40 ms and less than 500 ms\.&#32;You do not need to time this precisely\;&#32;use two deliberate taps\,&#32;or use the explicit Hub key\.

These are source\-backed input rules\.&#32;The supplied runtime proof did not test physical keyboard decoding\.

---

## 4\.&#32;Steer and queue work

### Choose “change this” or “do this next”

While the tan is streaming\:

- **Enter\:**&#32;“Adjust the work you are doing\.”
- **Ctrl\+Q&#32;\/&#32;Ctrl\+Enter\:**&#32;“After the current work would otherwise finish\,&#32;do this next\.”

When the tan is idle\,&#32;both submission paths start a normal prompt\.&#32;A follow\-up key does not create an indefinite holding queue behind nonexistent work\.

### Worked scenario\:&#32;correct the audit mid\-flight

The label audit is running\.&#32;You now want only green\-shelf mismatches\.

Confirm the focused Tan ID\.

**Plain\-language message — submit to the verified tan with Enter while streaming\:**

~~~text
Correction: report only green-shelf label mismatches. Exclude silver-shelf books from this audit. Keep both input files read-only, and do not repair anything Main is changing. State the narrowed scope in your answer.
~~~

**Expected observations**

- The submission is accepted into the tan’s steering path\,&#32;not Main’s user queue\.
- Pending\-message state may be visible before the message appears in the transcript\.
- A later response should acknowledge the green\-only scope\.

**Fictional acceptance check\:**&#32;the narrowed report should identify&#32;**A Map of Clouds\:&#32;blue → amber**\,&#32;without treating the silver\-shelf mismatch as part of this requested audit\.

Earlier output may still contain the original broader scope\.&#32;Steering does not retract text already produced or reverse tool effects\.

### Why steering may not act instantly

Steering is delivered at agent\-loop boundaries\.&#32;Its timing depends on queue and interruption settings and on what is currently executing\.

In the shown loop\:

- Interruptible waits can be interrupted for steering\.
- Tools with side effects are not indiscriminately hard\-killed just because steering arrived\.
- Cooperative tools may step aside\.
- Tools not yet started may be skipped\.
- Otherwise\,&#32;the message waits for a safe boundary\.

Even the setting named&#32;`immediate`&#32;does not mean “every provider request and external process stops immediately\.”

If the correction is urgent enough to stop execution rather than wait for steering\,&#32;use the distinctions in&#32;[Interrupt\,&#32;cancel\,&#32;or kill](<https://present-sketch-tp94.here.now/chapters/tan-8-interrupt-cancel-or-kill#tan-8-interrupt-cancel-or-kill>)\.

### Worked scenario\:&#32;queue the next task

Keep the same tan focused while it is still working\.

**Plain\-language message — submit with Ctrl\+Q or Ctrl\+Enter\:**

~~~text
After the audit, list the green-shelf book titles in alphabetical order. Read the current catalog again before answering. Do not edit any files.
~~~

**Expected observation\:**&#32;this is queued as a follow\-up rather than steering\.

**Fictional acceptance check\:**&#32;with the original fixtures unchanged\,&#32;the later list is\:

1. A Map of Clouds
2. The Paper Moon

Steering is handled ahead of follow\-up work in the normal queue flow\.&#32;Multiple messages can be drained one at a time or together according to the session’s queue settings\.

**Important result boundary\:**&#32;the initial tan job returns the&#32;**last assistant text**&#32;after its run settles\.&#32;If a follow\-up becomes the last turn\,&#32;the job’s delivered text may be the title list rather than the earlier audit\.&#32;Read the conversation when you need both\.

### Queued does not mean completed

A queued message is live scheduling state\,&#32;not a durable task ticket or proof that the model acted on it\.

After an interruption\,&#32;error\,&#32;parking transition\,&#32;or uncertain submission\:

1. Recheck the current focus\.
2. Check the queue and transcript\.
3. Determine whether the instruction was accepted and answered\.
4. Only then decide whether to submit it again\.

Repeatedly sending the same instruction can create duplicate work\.

### Images in focused chat

Focused chat forwards pending image payloads for both Enter and follow\-up submissions\,&#32;including image\-only drafts represented by their actual attachment chips\.

Keep the image’s chip or marker intact\.&#32;At submission\,&#32;the input controller drops attachments whose inline markers were removed and renumbers the survivors\.

Attachment delivery to the session is not the same as native vision support\.&#32;The session may normalize images\,&#32;describe them through another model\,&#32;or omit them from provider input according to capabilities and settings\.

**Recovery when dispatch rejects\:**&#32;the focused\-submit catch path restores the submitted text\,&#32;pending images\,&#32;and image links\,&#32;and surfaces the error\.

That guarantee concerns a&#32;**rejected submission call**\.&#32;It does not mean every later model error restores a draft\,&#32;or that an accepted message was never processed\.&#32;If focus changed while the call was pending\,&#32;verify the recipient again before resubmitting the restored draft\.

### Focused chat is not a command console

Do not issue slash commands while focused on a tan\.

The focused input path blocks\:

- Slash commands\.
- Bash input beginning with&#32;`!`&#32;or&#32;`!!`\.
- Recognized Python command input such as&#32;`$ …`&#32;or&#32;`$$ …`\.

The draft is left in place with a notice that commands run in Main\.&#32;This does&#32;**not**&#32;prevent the tan itself from using its enabled tools\.

Main’s&#32;`.`&#32;\/&#32;`c`&#32;continue shortcuts and queue shorthand are not the focused\-chat control path\.&#32;Use an explicit plain\-language prompt instead\.

Model\/thinking cycling is also refused while focused\.&#32;Other configuration surfaces remain Main\-owned\;&#32;this workbook provides no live Tan model\-switch recipe\.&#32;Changing Main’s model\,&#32;or editing an agent definition in&#32;`/agents`\,&#32;is not a supported way to retarget an already\-running tan\.

**A useful exception\:**&#32;the default&#32;**Alt\+R**&#32;retry action targets&#32;`viewSession`\,&#32;so it can retry an idle focused tan’s failed turn\.&#32;Preserve an unrelated draft first\:&#32;a started retry clears the draft and attachments\.&#32;The&#32;`/retry`&#32;slash command is still not something to enter while focused\.

### Optional detail\:&#32;the dequeue shortcut currently targets Main

The default dequeue keys are&#32;**Alt\+Up&#32;\/&#32;Shift\+Up**\,&#32;but the supplied&#32;`restoreQueuedMessagesToEditor()`&#32;implementation drains&#32;`ctx.session`—Main—not the focused&#32;`viewSession`\.

**Do not use those keys as a Tan queue\-editing control\.**

They can restore Main’s queued text into the shared editor while a tan is visible\.&#32;If that happens\,&#32;do not submit it to the tan\.&#32;Return to Main and reconcile the restored text with its actual owner\.

No Tan\-specific queue\-edit slash command is established by these sources\.

---

## 5\.&#32;Leave and switch safely

### Leaving is a view change

In ordinary focused chat\,&#32;Escape does not cancel the tan\.

- **Nonempty text draft\:**&#32;Escape clears the text and leaves you focused on the tan\.
- **Empty text draft\:**&#32;Escape returns you to Main\.

This is not a universal “press Escape twice” macro\.&#32;If you started with an empty draft\,&#32;one Escape already returned you\.&#32;Another press can affect Main\.

### Worked scenario\:&#32;leave with an unfinished draft

You are focused on the label\-audit tan and have typed—but not submitted\:

~~~text
Please also rewrite the visitor FAQ.
~~~

You decide not to send it\.

1. Press Escape once\.
2. Confirm the text is gone and you are still viewing the tan\.
3. Inspect any attachment state\.
4. With an empty draft\,&#32;press Escape again\.
5. Confirm&#32;`Returned to main session`&#32;or the corresponding return notice\.

**Expected outcome\:**&#32;no FAQ request was submitted\.&#32;Leaving did not cancel the running audit\.

If you want to preserve the draft instead\,&#32;copy its text before clearing it\.&#32;Do not assume separate per\-agent draft buffers\.

### Worked scenario\:&#32;switch between Main and two tans

- **Main → tan\:**&#32;open Hub\,&#32;select exact ID\,&#32;Enter\.
- **Tan → Main\:**&#32;ordinary empty\-draft Escape\,&#32;or empty\-editor double\-left\.
- **Tan A → Tan B\:**&#32;open Hub\,&#32;select Tan B’s exact ID\,&#32;Enter\.
- **Hub → prior chat\:**&#32;close the Hub\.&#32;Closing the overlay does not by itself mean “return to Main\.”

Recheck the focus notice after every transition\.

### Automatic returns need the same target check

The focus controller returns to Main when the focused agent is\:

- Parked\.
- Marked aborted\.
- Removed from the registry\.

The initial&#32;`/tan`&#32;run normally parks at completion\,&#32;so completion normally causes this automatic return\.

A later revived tan can finish a prompt and become&#32;**idle**&#32;without immediately returning you to Main\.&#32;An idle transition alone is not the auto\-unfocus trigger\.

**Draft safety rule\:**&#32;a draft intended for the tan is not permanently addressed to it\.&#32;If the tan parks while you type and the view returns to Main\,&#32;the next submission is now a Main submission\.

Do not replay an already\-submitted message merely because the screen returned to Main afterward\.&#32;First check whether that submission reached the tan\.

### Escape has higher\-priority owners

The ordinary focused\-chat rule is conditional\.

The input controller handles certain owners before the focused Escape branch\,&#32;including\:

- Active MCP\-test cancellation handlers\.
- Side panels such as&#32;`/btw`\,&#32;`/omfg`\,&#32;and&#32;`/cleanse`\.
- Speech playback\.
- Global loop mode\.

**Loop mode is particularly important\:**&#32;if enabled\,&#32;Escape can act on&#32;**Main’s**&#32;streaming loop iteration even while a tan is focused\.&#32;If Main is idle\,&#32;it can pause the loop and cancel its pending submission instead\.&#32;It may not unfocus the tan\.

Resolve the topmost surface or mode\,&#32;then inspect where you are\.&#32;Do not keep pressing Escape until something disappears\.

### Do not use whole\-application controls to leave one tan

Ctrl\+C\,&#32;Ctrl\+D\,&#32;closing the terminal\,&#32;and process suspension are not Tan\-navigation controls\.

In this input controller\,&#32;a first Ctrl\+C clears the editor\;&#32;a rapid second press requests application shutdown\.&#32;Ctrl\+D exits\.&#32;Shutdown can cancel owned background work\.

Returning to Main is also different from creating\,&#32;clearing\,&#32;deleting\,&#32;or switching Main’s logical session\.&#32;Session\-boundary operations can affect jobs\,&#32;delivery\,&#32;and retained files\.&#32;Use focus controls when you only want to change the view\.

---

## 6\.&#32;Observe jobs and read results

### Keep three identities separate

| Identity | What it names | Where to discover it |
| --- | --- | --- |
| **Tan agent ID** | The conversation\/agent registration\,&#32;generated as&#32;`Tan-…` | Runtime Hub roster or agent\-facing Hub list |
| **Background job ID** | The initial managed run\,&#32;commonly generated as&#32;`bg_…` | Dispatch acknowledgment\,&#32;`/jobs`\,&#32;Hub job inspection |
| **Supervised process name** | A server\,&#32;watcher\,&#32;debugger\,&#32;or other broker\-managed process | Hub process listing and description |

A display label such as&#32;`tan`\,&#32;a shortened task preview\,&#32;or “the second row” is not an identity\.

The internal background\-job record has an&#32;`agentId`&#32;association\.&#32;The persisted tan dispatch record also contains the job ID\,&#32;full work text\,&#32;and a transcript filename based on the Tan ID\.&#32;**Not every rendered or tool\-facing job snapshot exposes that association\.**

Do not claim to have mapped the two IDs merely because a list contains one plausible label\.

### Copyable discovery request to Main

**Plain\-language message — Main only\:**

~~~text
Main, inspect the current live and parked agent roster and your background jobs. Find the tan assigned to the Lantern Library label audit.

Report:
- its exact Tan agent ID;
- any backing background job ID, distinguishing running from merely retained;
- its owner or parent;
- its agent lifecycle state and job state separately;
- the evidence connecting the task, Tan ID, and job ID.

Use current roster, dispatch, and authorized transcript information. Do not infer the mapping from a shared "tan" label, a truncated preview, row order, or a remembered job number. Do not send, revive, or cancel anything. If the mapping is unavailable or ambiguous, say so.
~~~

This is a discovery request\,&#32;not a special OMP command\.&#32;Main must use the inspection surfaces actually available to it\.

### Status without messaging

**OMP slash command — Main only\:**

~~~text
/jobs
~~~

This shows Main’s owner\-scoped running jobs and a small recent\-job list\.&#32;It is a status snapshot\;&#32;it does not itself retrieve and consume settled job\-result bodies\.

For a currently parked tan\,&#32;messaging is not passive observation\:&#32;sending can revive it and start a turn\.&#32;Read its transcript instead when you only want to inspect prior work\.

### Hub tool arguments are not terminal commands

The following are&#32;**argument objects for the agent’s&#32;`hub`&#32;tool**\.&#32;They are not shell commands\,&#32;slash commands\,&#32;or HTTP requests\.

Main normally supplies them through its available tool interface\.

**Discover live peers\:**

~~~json
{"op":"list"}
~~~

**Discover parked peers in the current session root\:**

~~~json
{"op":"list","status":"parked","limit":100}
~~~

**Inspect owner\-scoped jobs and additional running\-agent activity\:**

~~~json
{"op":"jobs"}
~~~

The default peer list includes running and idle peers\,&#32;excludes the caller and advisor transcripts\,&#32;and does not list aborted agents\.&#32;Parked peers require the parked filter\.

Lists default to 32 rows and allow at most 100\.&#32;Inspect&#32;`shown`&#32;and&#32;`truncated`&#32;counts\.&#32;Increasing the limit is not a guarantee of an exhaustive roster\,&#32;and no pagination parameter is supplied here\.

The runtime UI Hub can also show parked\,&#32;killed\,&#32;and advisor transcript rows\.&#32;These surfaces intentionally have different visibility rules\.

### Checking status can consume delivery

A completed tan normally delivers its final text to the&#32;**owning session**\,&#32;ordinarily Main\.&#32;Owner\-routed delivery can enter an active run at a boundary or wake an idle owner\.

However\,&#32;if the agent’s Hub&#32;**`jobs`&#32;or&#32;`wait`**&#32;operation observes a settled result first\,&#32;that snapshot becomes the recovered delivery and suppresses a duplicate automatic&#32;`async-result`\.

Later snapshots may say\:

~~~text
Delivery: already delivered or recovered.
~~~

…and omit the body\.

That does not mean the tan produced no output\.&#32;Look in the earlier delivery or Hub result\,&#32;then follow any actual artifact link\.

This is why repeated status polling is not a reliable way to repeatedly fetch the same full report\.

### Waiting is not “wait until all work succeeds”

After discovering an actual job ID\,&#32;Main can narrow a wait\.

**Hub tool arguments — replace the placeholder only with a discovered current job ID\:**

~~~json
{"op":"wait","ids":["<discovered-job-id>"],"timeoutMs":30000}
~~~

The unified wait can return on the first relevant message\,&#32;watched job settlement\,&#32;timeout\,&#32;or interruption\.&#32;It is not an all\-jobs\-complete barrier\.

A timeout\:

- Does not cancel the job\.
- Does not prove the tan is stuck\.
- Does not prove that a message was not delivered\.

Default job waits can use an adaptive window\.&#32;An explicit&#32;`timeoutMs`&#32;is in&#32;**milliseconds**\;&#32;do not confuse it with process\-operation timeout fields measured in seconds\.

### Messaging from Main is a different input path

**Plain\-language message — Main only\:**

~~~text
Main, rediscover the exact Tan ID for the Lantern Library label audit. If there is one unambiguous match, send it this correction: report only green-shelf mismatches, keep the input files read-only, and leave Main's work alone.

Report the exact recipient and delivery receipt. Do not describe the receipt as proof that the correction has already been followed.
~~~

For illustration\,&#32;the corresponding tool arguments have this shape\:

~~~json
{"op":"send","to":"<discovered-tan-id>","message":"Report only green-shelf mismatches. Keep the input files read-only and leave Main's work alone."}
~~~

The source messaging path delivers a peer message to a busy agent or wakes\/revives an idle\/parked one\.&#32;This is&#32;**not the same queue\-control gesture as focused Enter**\,&#32;and it is not the same as Ctrl\+Q follow\-up ordering\.

If an answer is essential\,&#32;`send`&#32;supports&#32;`await:true`\.&#32;That waits for a reply\;&#32;it is not a guarantee of task completion\.&#32;If the send succeeded but the reply wait timed out or was interrupted\,&#32;check the inbox or wait again rather than blindly resending\.

### Read the transcript\,&#32;not an invented result path

`history://`&#32;is an&#32;**OMP internal resource scheme**\,&#32;read through OMP’s read tooling\.

- `history://`&#32;provides an index\.
- `history://<discovered-tan-id>`&#32;reads that agent’s transcript\.

For live agents\,&#32;the handler reads live session messages\.&#32;For parked or unregistered agents\,&#32;it can use retained JSONL files found through its discovery paths\.&#32;It does not need to revive the agent just to read history\.

The representation is concise Markdown\,&#32;not a promise that every large tool payload is reproduced verbatim\.

**Plain\-language message — Main only\:**

~~~text
Main, discover the exact Tan ID for the Lantern Library label audit and read its history:// transcript without reviving it. Summarize the audit, the later correction, and any queued follow-up that actually ran. Distinguish completed evidence from proposed or skipped work. Follow only artifact links that were actually returned.
~~~

Ordinary task result URLs such as&#32;`agent://<agent-id>`&#32;resolve a corresponding&#32;`<agent-id>.md`&#32;output artifact\.&#32;**The initial&#32;`/tan`&#32;controller does not automatically write that ordinary task result artifact\.**

Do not assume an&#32;`agent://`&#32;route for a Tan ID will work merely because its transcript exists\.

### Full text versus previews

A Hub preview or compact dispatch line is not the whole result\.

Owner delivery keeps text inline up to the current 12\,000\-character threshold\.&#32;Larger text is normally represented by a 4\,000\-character preview plus an&#32;`artifact://`&#32;link&#32;**if artifact persistence succeeds**\.&#32;If persistence fails\,&#32;only the preview may be available through that delivery\.

Read the returned full\-output artifact when present\.&#32;If it is missing\,&#32;report the gap\;&#32;do not silently treat the preview as a complete report\.

### Job IDs are short\-lived handles

Background\-job rows are process\-local and normally retained for roughly five minutes after settlement\.&#32;They can be removed earlier by lifecycle operations\.&#32;Generated job IDs can be reused after eviction\.

Therefore\:

- Rediscover before cancelling\.
- Do not use a job number from an old screenshot or a previous OMP process\.
- A missing job row does not mean its Tan transcript disappeared\.
- A revived tan can be working without a new job row for its prompt\.

Hub job snapshots include additional running\-agent activity not represented by the caller’s running jobs\.&#32;That can include a revived agent or work owned by another agent\.&#32;It is not blanket authority for Main to cancel that work\.

---

## 7\.&#32;Continue a finished tan

### Parked is not idle

| Agent state | Practical meaning |
| --- | --- |
| **Running** | The registration claims active work\.&#32;Check live activity if it appears stale\. |
| **Idle** | A live session is attached and awaiting another prompt\. |
| **Parked** | The live session has been disposed and detached\;&#32;a record and transcript path may remain\.&#32;Revival is required for new work\. |
| **Aborted** | A terminal agent record\,&#32;notably after explicit kill\.&#32;It is not a writable paused session\. |
| **Absent** | No current registry entry\.&#32;A transcript may still remain on disk\. |

Normal completion of the initial&#32;`/tan`&#32;job sets the agent to parked\,&#32;disposes the live session\,&#32;and detaches it\.

This is separate from the job’s&#32;`completed`\,&#32;`failed`\,&#32;or&#32;`cancelled`&#32;state\.&#32;A parked tan can have encountered an error\.&#32;A completed job is not proof the assignment succeeded\.

### Worked scenario\:&#32;ask a finished tan one more question

The label audit has finished and its row is parked\.

1. Discover the exact Tan ID again\.
2. Open the runtime Hub\.
3. Select that parked row\.
4. Press Enter\.
5. Wait for successful focus on that exact ID\.
6. Submit a new\,&#32;scoped prompt\.

**Plain\-language message — verified tan chat\:**

~~~text
Continue the Lantern Library tangent only. Main owns all other work. Read the current data/catalog.csv again and report how many green-shelf books it contains. Do not edit files or resume Main's earlier checklist.
~~~

**Fictional acceptance check\:**&#32;the unchanged fixture contains&#32;**two**&#32;green\-shelf books\.

**Expected lifecycle observation\:**&#32;successful revival attaches a new live session to the same Tan identity and retained transcript\.&#32;After this prompt\,&#32;it can become idle\.

The old background job remains settled or may already have expired\.&#32;This prompt does not recreate that job\,&#32;and its answer is not promised to flow through the old job’s automatic delivery\.&#32;Read the Tan conversation or have Main retrieve the answer\.

### Revival is not task resubmission

- **r**&#32;attempts to make a parked session live while leaving you in the Hub\.
- **Enter**&#32;attempts revival as needed and then focuses its chat\.
- A&#32;**new prompt**&#32;asks it to do work\.
- A&#32;**Hub send**&#32;can combine revival with delivery of a new message\.

None of these means “rerun the original&#32;`/tan`&#32;command automatically\.”

### When revival fails

Common boundaries include\:

- No usable transcript file\.
- No saved session\-initialization contract\.
- The recorded working directory is gone\.
- No persisted reviver installed by the host\.
- Unavailable model\/authentication or rebuilt tool dependencies\.
- The agent was killed\,&#32;removed\,&#32;or replaced during revival\.

A failed cold revival is not necessarily sticky\:&#32;the lifecycle manager drops a failed cold adoption so a later attempt can rebuild from fresher context\.

**Recovery**

1. Read and preserve the exact error\.
2. Inspect history without reviving\,&#32;if available\.
3. Repair the actual dependency through Main\.
4. Rediscover and retry only after that repair\.
5. If the agent is transcript\-only or terminal\,&#32;start a&#32;**new**&#32;bounded tangent from a reviewed summary instead of pretending the old agent resumed\.

Do not delete a tombstone to force a killed agent back into service\.

### Optional detail\:&#32;cold revival has new boundaries

The initial tan writes a saved contract containing its system prompt\,&#32;work text\,&#32;and enabled tool names\.&#32;It does not record a complete live runtime\.

The cold\-revive path\:

- Reopens the transcript\.
- Rebuilds settings from the host’s current base settings\,&#32;with subagent overrides\.
- Resolves models through the SDK’s restoration and availability logic\.
- Rebuilds tools and clamps the enabled set to available saved names\.
- Uses current host resources for parts of initialization\.
- Denies respawning when no saved spawn policy exists\;&#32;the initial tan contract does not record one\.

The recorded working directory is checked and used when reopening the transcript\,&#32;while current Main context is also supplied for SDK discovery and shared artifacts\.&#32;After a Main move or session transition\,&#32;inspect paths and resource mappings rather than assuming perfect reconstruction\.

The initial tan’s special fork reminder and compaction subscription are also not a permanently serialized control package\.&#32;The original controller installs that subscription for its background run and removes it afterward\;&#32;the generic cold reviver does not reinstall the Tan\-specific subscription\.

**Practical consequence\:**&#32;after revival\,&#32;restate the tangent’s scope\,&#32;file ownership\,&#32;and current inputs\.&#32;Check the effective model and tools when they matter\.&#32;Do not interpret successful revival as proof of an identical model\,&#32;settings snapshot\,&#32;cache lineage\,&#32;or environment\.

---

## 8\.&#32;Interrupt\,&#32;cancel\,&#32;or kill

These operations stop&#32;**different things**\.

> **None of them rolls back file edits\.&#32;None proves that every external process\,&#32;tool side effect\,&#32;or provider request has stopped\.**

### A\.&#32;Interrupt the current turn

Use this when the goal is to stop the current attempt and continue the conversation—not to remove the agent\.

The demonstrated runtime primitive was&#32;`AgentSession.abort()`\,&#32;applied to a&#32;**revived live tan**\.&#32;In that proof\,&#32;the tan remained attached and idle\,&#32;then answered another prompt\.

That primitive is not an OMP slash command\.

#### Worked scenario\:&#32;advance a queued correction

The focused tan is streaming and your correction is still queued as steering\.

1. Verify the exact Tan focus\.
2. Verify that a message is actually queued\.
3. Ensure the editor contains&#32;**no text and no images**\.
4. Press Enter\.

The focused input handler requests a turn abort and refreshes pending\-message display\.

**What to check next**

- The interrupted turn settles\.
- Pending steering may drain into a fresh run\.
- The correction appears in the intended Tan conversation\.
- The resulting work obeys the new scope\.

**Important limits**

- Empty Enter with no queued message does nothing here\.
- Empty Ctrl\+Q\/Ctrl\+Enter does not invoke this abort behavior\.
- The queue is not automatically erased\.
- A follow\-up\-only queue can remain waiting after a deliberate user interrupt\;&#32;do not promise that empty Enter flushes every queue\.
- On the initial&#32;`/tan`&#32;run\,&#32;settling can lead to ordinary job finalization and parking\.&#32;Do not assume every turn interruption leaves the original live session attached\.

If it parks or returns you to Main\,&#32;inspect the transcript and queue outcome before continuing it\.

### B\.&#32;Cancel the background job

Use this when the initial managed background run is no longer wanted\.

**Plain\-language message — Main only\:**

~~~text
Main, refresh the current roster and your background jobs. Identify the exact Tan and backing job for the Lantern Library label audit, with evidence connecting them.

Cancel only that job if it is still running, using its discovered background job ID—not its Tan agent ID. If it has already settled or the mapping is ambiguous, take no destructive action and report the state.

Afterward, report the cancellation outcome, the Tan's current lifecycle state, and any file or external-process effects that still need inspection. Do not roll back files.
~~~

**Hub tool arguments — only after current discovery\:**

~~~json
{"op":"cancel","ids":["<discovered-job-id>"]}
~~~

**Expected observation\:**&#32;a successfully cancelled running job becomes&#32;`cancelled`\.&#32;Cleanup then unwinds\.

In the supplied runtime proof\,&#32;cancelling the initial Tan job resulted in\:

- A cancelled job\.
- The Tan registration becoming absent\.
- Its transcript remaining on disk at observation\.

**Crucial boundary\:**&#32;this initial Tan signal\-abort path does not establish the same durable tombstone as explicit kill\.&#32;Later persisted discovery can therefore differ\.&#32;Do not say “a cancelled tan can never reappear\.”

If cancellation reports not found or already completed\,&#32;refresh discovery\.&#32;Do not increment the job number and try again\.

### C\.&#32;Explicitly kill the agent

Use this when you want the Tan registration to be terminal\,&#32;not merely its current job cancelled\.

#### Worked scenario\:&#32;kill a finished or revived tan

1. Open the runtime Agent Hub\.
2. Select the exact Tan ID\.
3. Inspect its assignment and lineage\.
4. Press&#32;**x**&#32;only when that row is the intended target\.

The source handler attempts to abort a running session\,&#32;then calls explicit lifecycle release with&#32;`tombstone:true`\.

Treat x as immediately actionable\;&#32;the shown handler does not add a separate confirmation step\.

**Expected result of a successful explicit kill**

- The agent becomes terminal&#32;`aborted`\.
- Its live session is detached and disposed\.
- A&#32;`.tombstone`&#32;sidecar is written beside its transcript\.
- The transcript is retained for reading\.
- Revival is refused\.

The durable marker is what lets later persisted discovery preserve the terminal decision rather than treating the remaining transcript as a fresh parked agent\.

If the Hub displays an error\,&#32;or the row changed during the operation\,&#32;recheck the outcome\.&#32;Do not infer a successful durable kill from pressing the key alone\.

**The job can tell a different story\:**&#32;x does not itself call background\-job cancellation\.&#32;An initial job may settle separately\,&#32;and a previously completed job stays completed\.&#32;A later settlement notice is not by itself proof that the killed agent is live again\.

### D\.&#32;Understand the Hub cancel fallback

There is an additional behavior worth knowing before supplying an ID to&#32;`hub cancel`\.

When the supplied ID does not identify a visible job\,&#32;the Hub can try to cancel an&#32;**agent registration with that exact ID**\.&#32;For a caller with an identity\,&#32;the shown check allows its&#32;**direct children**\,&#32;not arbitrary descendants or other agents’ children\.

This fallback aborts\/disposes and performs a plain release or unregister\.&#32;It does&#32;**not**&#32;request&#32;`tombstone:true`\.

Therefore\:

- The lower\-level job manager rejects a Tan ID as the wrong job ID\;&#32;the runtime proof demonstrated that\.
- The higher\-level&#32;**Hub tool may still act on that Tan ID through its registration fallback**\.
- A “cancelled agent” receipt is not proof that the original&#32;`bg_…`&#32;job was cancelled\.
- A plain registration removal is not equivalent to the explicit x\/tombstone path\.

For an intentionally removed\,&#32;jobless child\,&#32;this fallback is available\:

~~~json
{"op":"cancel","ids":["<discovered-direct-child-tan-id>"]}
~~~

This is a&#32;**Hub tool argument example**\,&#32;not a recommended substitute for explicit kill when durable terminal intent matters\.

### E\.&#32;“Pause\,” process shutdown\,&#32;and undo are separate needs

No Tan\-specific pause\/resume slash interface is established here\.

A message saying “stop editing and wait” is a behavioral request to the model—not a guaranteed runtime freeze\.&#32;If you need stronger stopping\,&#32;choose job cancellation or explicit kill and verify the outcome\.

If the tan started a supervised process\:

1. Have Main discover the actual process name\.
2. Inspect its description\,&#32;project\,&#32;command\,&#32;and ownership evidence\.
3. Stop it through the process\-control surface if that is also intended\.

Hub process operations such as&#32;`ps`\,&#32;`describe`\,&#32;`logs`\,&#32;and&#32;`stop`&#32;address&#32;**`name`**\,&#32;not a Tan ID or background\-job ID\.&#32;Process restart\/persistence options are not Tan options\.

To undo edits\,&#32;inspect the shared working tree and make a separate\,&#32;reviewed recovery plan\.&#32;Never assume cancelling a tan restores the pre\-launch files\.

---

## 9\.&#32;Coordinate several tans

### Allocate ownership before parallel work

Every tan starts in the same working directory\.&#32;Disjoint conversations do not make disjoint files\.

A useful fictional ownership plan is\:

| Worker | Write ownership | Read\-only inputs | Must leave alone |
| --- | --- | --- | --- |
| Main | `data/catalog.csv`&#32;and integration decisions | Both documentation files | Documentation while its assigned tan is editing |
| Label\-guide tan | `docs/label-guide.md` | `data/catalog.csv` | FAQ\,&#32;catalog edits\,&#32;unrelated repairs |
| FAQ tan | `docs/visitor-faq.md` | Catalog and label guide | Label\-guide edits\,&#32;catalog edits\,&#32;unrelated repairs |

Agree on when shared inputs are stable\.&#32;Even a read\-only worker can produce stale conclusions if Main changes its inputs mid\-audit\.

### Worked scenario\:&#32;two independent documentation passes

Enter each launch separately from Main\.

**OMP slash command — Main only\:**

~~~text
/tan For the fictional Lantern Library, improve only docs/label-guide.md for clarity. Read data/catalog.csv as an input, but do not edit it. Do not change docs/visitor-faq.md or repair unrelated files. Report the exact edits and any assumptions about the catalog.
~~~

**OMP slash command — Main only\:**

~~~text
/tan For the fictional Lantern Library, improve only docs/visitor-faq.md for visitors. Read the catalog and label guide as inputs, but do not edit them. Do not change any other file. Report the exact edits and any assumptions that need Main's review.
~~~

After each launch\,&#32;record the discovered identity pair before adding more similar work\:

- Full Tan ID\.
- Initial job ID\.
- Assignment and write scope\.
- Current agent state\.
- Current job state\.

The label&#32;`tan`&#32;is not enough to distinguish them\.

### Worked scenario\:&#32;Main changes an input

Suppose Main changes the label policy while the FAQ tan is still writing\.

**Plain\-language message — Main only\:**

~~~text
Main, discover the exact Tan ID currently responsible for docs/visitor-faq.md. Tell it that the label policy has changed and ask it to reread the current docs/label-guide.md before finalizing.

Do not message every tan. Report the exact recipient and whether it acknowledged the new input.
~~~

Do not rely on the tan learning the change from Main’s conversation\.&#32;The file can be shared while the explanation of its change is not\.

### Worked scenario\:&#32;two workers touched the same file

1. Stop assigning additional overlapping work\.
2. Discover which exact agents and jobs are involved\.
3. Steer or stop the relevant worker according to urgency\.
4. Inspect the&#32;**current file and diff**\,&#32;not just each agent’s report\.
5. Establish one writer for the conflicted file\.
6. Have that writer reread current contents before making a repair\.
7. Run the specific project checks that cover the reconciled change\.

A failed build during Main’s refactor is not an invitation for a tan to repair unrelated work\.&#32;Ask it to report the conflict or blocker\.

**Acceptance check\:**&#32;the final shared file contains the intended combined change\,&#32;no unrelated edits\,&#32;and passes the relevant checks selected from the actual project\.&#32;There is no tan merge command to perform this review for you\.

Use unique scratch paths too\.&#32;Two workers writing&#32;`local://notes.md`&#32;can conflict even when their repository files are disjoint\.

---

## 10\.&#32;Protect context and recover

### A tangent is not a privacy boundary

A tan can inherit conversation content that has nothing to do with its narrow assignment\:

- Prior user and assistant messages\.
- Tool results and file contents\.
- Previously attached images\.
- Instructions or memory\-derived content already present in the inherited context\.

It can also access the shared workspace through its available tools\.

Before launching\,&#32;consider whether the inherited content is appropriate for the model\/provider and tool access involved\.&#32;A narrow task prompt does not remove unrelated inherited information\.

Do not assume that Main’s extension\-based safeguards or interactive approval prompts are reproduced identically\.&#32;Verify protections that matter before unattended work\.

This public workbook never needs your real transcripts\,&#32;credentials\,&#32;or private instructions\.

### Cost\:&#32;concurrent does not mean free

A tan can add model requests\,&#32;tool work\,&#32;retries\,&#32;and auxiliary model activity while Main is also active\.

The initial controller copies Main’s effective prompt\-cache routing key while giving the child a separate provider request lineage\.&#32;That creates&#32;**eligibility for provider cache reuse**\,&#32;not a cache\-hit or savings guarantee\.

The supplied mock\-provider proof reported zero usage by design\.&#32;Those zeros are not pricing evidence\.

Also be careful with transcript\-derived totals\:&#32;a fork includes historical messages and their usage records\.&#32;Such totals are not necessarily the tan’s incremental new spend\.

### Shared scratch space and artifact boundaries

During its initial run\,&#32;the tan uses a captured mapping to Main’s&#32;`local://`&#32;scratch root\.&#32;A file such as&#32;`local://lantern-label-audit.md`&#32;can therefore be shared with Main\.

The Tan transcript is nested in the parent session’s artifact tree\,&#32;and the launch does not recursively copy that tree\.

But sharing&#32;`local://`&#32;does&#32;**not**&#32;mean every artifact allocator is identical\.&#32;The initial controller does not adopt Main’s&#32;`ArtifactManager`\;&#32;tools can create child\-session artifacts\.&#32;The cold reviver\,&#32;in contrast\,&#32;adopts the current Main artifact manager\.

Use paths and links actually returned by the tools\.&#32;Do not construct guessed artifact IDs or assume that all resources keep the same mapping after Main moves or a tan is cold\-revived\.

### Compaction preserves intent only within its actual wiring

During the initial background run\,&#32;the Tan controller reasserts the fork boundary after successful reported compaction\.

That is a useful guard\,&#32;not a guarantee that every later revived conversation carries the same live reminder machinery\.&#32;After compaction or revival\,&#32;a concise restatement of scope and ownership is a sensible operating practice\.

### Recovery guide

| Symptom | Check | Recovery |
| --- | --- | --- |
| “I cannot find it in&#32;`/jobs`\.” | Did the job expire\?&#32;Is it owned by another agent\?&#32;Is this a revived\,&#32;jobless prompt\? | Check the live and parked roster and&#32;`history://`\;&#32;do not guess a new job ID\. |
| “Hub list says no actionable peers\.” | Are there parked peers\?&#32;Is the list truncated\? | Request the parked filter and inspect counts\;&#32;use the runtime Hub for further inspection\. |
| “The row says running\,&#32;but nothing seems active\.” | Is startup still wiring up\?&#32;Does live session activity corroborate the status\? | Inspect current state and history before treating it as a stale registration\. |
| “My correction did not affect the answer\.” | Was the right Tan focused\?&#32;Was it queued\,&#32;processed\,&#32;or submitted after auto\-return\? | Locate the message in the correct transcript before resending\. |
| “I got returned to Main unexpectedly\.” | Did the focused tan park\,&#32;become aborted\,&#32;or disappear\? | Recheck both state tracks and the draft recipient\. |
| “The command was refused in tan chat\.” | Did the draft begin with slash\,&#32;shell\,&#32;or Python command syntax\? | Preserve it if needed\,&#32;return to Main\,&#32;and perform only the intended Main action there\. |
| “An image was missing\.” | Was it staged beside&#32;`/tan`\,&#32;or sent later as an actual focused attachment\?&#32;Was image input allowed\? | Attach explicitly to the verified tan\,&#32;or provide an authorized accessible file\. |
| “The report disappeared from later job snapshots\.” | Was it already auto\-delivered or recovered\? | Read the earlier result\,&#32;actual artifact link\,&#32;or transcript\. |
| “Revival failed\.” | Transcript\,&#32;saved contract\,&#32;workspace\,&#32;runtime factory\,&#32;model\/auth\,&#32;and tool dependencies | Repair the actual dependency\;&#32;otherwise use retained history to prepare a new tangent\. |
| “I cancelled it\,&#32;but a parked row appeared later\.” | Was this job cancellation or plain release rather than explicit tombstoned kill\? | Reconcile the transcript and lifecycle\.&#32;Use explicit kill if terminal intent is still required\. |
| “Stopping it did not stop a server\.” | Was the server a separately supervised or external process\? | Discover and control that process separately\;&#32;verify ownership first\. |
| “A test failed during concurrent edits\.” | Did another worker change the tested files or dependencies\? | Establish a stable input point and one writer\,&#32;then rerun the relevant check\. |

### Restart and retention are not the same as live control

Source supports discovering retained child transcripts under a session’s artifact tree and rebuilding eligible parked agents\.&#32;It does not mean a running Tan keeps executing through an OMP process exit\.

Discovery also has boundaries\:

- It is tied to reachable session roots\,&#32;not an unlimited search of every file on the computer\.
- Saved\-agent scans and metadata hydration can fail or be incomplete\.
- A long inherited transcript can place Tan\-specific initialization beyond the small prefix used for roster metadata\.
- A row’s preview may therefore be missing or insufficient to identify its assignment\.
- File existence alone does not prove a live\,&#32;controllable agent\.

The supplied runtime proof exercised cold revival&#32;**without proving restart rediscovery**\.

Transcripts remained on disk at the proof’s observation points\.&#32;That is not permanent retention\.&#32;Session deletion\,&#32;artifact cleanup\,&#32;archive\/GC policy\,&#32;storage errors\,&#32;and loss of image\/blob dependencies can change later availability\.&#32;The supplied evidence does not establish a guaranteed retention period\.

Completed journal entries and in\-flight streaming text also have different durability\.&#32;Do not assume an abrupt exit preserves every partial token\.

### Optional detail\:&#32;do not borrow ordinary\-task recovery settings blindly

The initial Tan controller does not run through the ordinary task executor’s full driver\.

In particular\,&#32;do not present these as established controls for the initial Tan\:

- `task.maxRuntimeMs`&#32;as its automatic wall\-clock stop\.
- `task.softRequestBudget`&#32;as its run budget\.
- `task.agentIdleTtlMs`&#32;as a way to prevent its immediate completion parking\.
- `task.isolation.*`&#32;as automatic&#32;`/tan`&#32;isolation\.
- Agent\-definition model overrides as a live Tan model selector\.

Those settings have uses elsewhere\.&#32;Their existence does not establish the same behavior in&#32;`TanCommandController.start()`\.

---

## 11\.&#32;Operate through Main

### Use an identity\-first brief

Main can inspect and coordinate through the tools available in its session\.&#32;An agent with authorized terminal\-interface control can also operate the UI\.

But a prose request does not magically expose every internal lifecycle method\.&#32;The native&#32;`/tan`&#32;launch is an interactive Main\-composer path\;&#32;Hub&#32;`start`&#32;launches a&#32;**process**\,&#32;not a tan\.&#32;Do not substitute an ordinary task subagent and silently call it a tan\.

For each control request\,&#32;specify\:

1. **Discovery\:**&#32;identify the exact current agent and any job\.
2. **Intent\:**&#32;inspect\,&#32;message\,&#32;continue\,&#32;cancel the job\,&#32;or explicitly kill the agent\.
3. **Scope\:**&#32;which files and processes must remain untouched\.
4. **Evidence\:**&#32;report the actual receipt and resulting state\.
5. **Uncertainty\:**&#32;stop short of destructive action when identity is unresolved\.

### Copyable request\:&#32;inspect without waking

~~~text
Main, rediscover the Lantern Library label-audit tan, including parked entries. Read its available transcript and report the latest completed result, current agent lifecycle state, and any current backing job.

Do not send it a message, revive it, cancel it, or edit files. Note any missing history, truncated result, or ambiguous identity.
~~~

Remember that Main’s Hub job\-result inspection can consume settled automatic delivery\,&#32;as explained earlier\.

### Copyable request\:&#32;continue through a message

~~~text
Main, find the exact retained Tan ID for the Lantern Library label audit. If it is usable or revivable, send it one new request: continue this tangent only, reread the current catalog, count green-shelf books, and do not edit files or resume Main's old checklist.

Report the recipient and delivery outcome. Retrieve its answer when available. Do not assume the original background job has restarted, and do not invent a replacement ID if revival fails.
~~~

This uses peer messaging\.&#32;For the precise user steering\-versus\-follow\-up distinction\,&#32;use the focused composer controls instead\.

### Copyable request\:&#32;explicit terminal intent

~~~text
Main, rediscover the exact Lantern Library label-audit tan. I want an explicit agent kill with a durable terminal tombstone, not merely a turn interruption, background-job cancellation, or plain registration removal.

Use only an installed control that actually provides that behavior. If it is not exposed to your tools, identify the exact runtime Agent Hub row for the operator's x action and state the limitation. Do not silently substitute another stopping operation.

Preserve the transcript, do not roll back files, and identify any separate processes that still require inspection.
~~~

### Before accepting “done”

Ask Main to distinguish\:

- The control request was accepted\.
- The targeted turn or job settled\.
- The agent is idle\,&#32;parked\,&#32;absent\,&#32;or terminal\.
- The requested project outcome was actually verified\.
- External processes and already\-written files were inspected where relevant\.

These are separate claims\.

**Your operating habit**

> Identify → focus or address → submit once → observe → leave safely → review the result or stop the exact target\.

---

## 12\.&#32;State and control reference

Three state tracks remain independent\:

- **View\:**&#32;Main\,&#32;a focused Tan chat\,&#32;or an overlay\.
- **Agent\:**&#32;running\,&#32;idle\,&#32;parked\,&#32;aborted\,&#32;or absent\.
- **Job\:**&#32;running\,&#32;completed\,&#32;failed\,&#32;cancelled\,&#32;or no longer retained\.

| Actor&#32;\/&#32;control | Target | Preconditions | Effect | Does&#32;**not**&#32;imply |
| --- | --- | --- | --- | --- |
| Operator\:&#32;`/tan <work>` | New child conversation and initial job | Main\;&#32;work\;&#32;model\;&#32;manager\;&#32;persisted parent | Forks and dispatches background work | Isolation\,&#32;automatic focus\,&#32;successful task |
| Operator\:&#32;Hub Enter | Selected exact agent ID | Usable live agent or successful revival | Focuses chat | New assignment or old job restart |
| Operator\:&#32;focused Enter | Current Tan session | Nonempty prompt or actual attachment | Prompt if idle\;&#32;steering if streaming | Immediate hard stop |
| Operator\:&#32;Ctrl\+Q&#32;\/&#32;Ctrl\+Enter | Current Tan session | Prompt\/attachment | Follow\-up if streaming\;&#32;prompt if idle | A paused queue when idle |
| Operator\:&#32;empty Enter | Current Tan turn | Streaming\;&#32;queued message\;&#32;no text\/images | Requests turn abort\;&#32;leaves queue handling to session | Job cancellation\,&#32;kill\,&#32;universal queue flush |
| Operator\:&#32;ordinary focused Escape | Draft or view | No higher\-priority handling | Clears text\,&#32;otherwise returns Main | Cancelling the tan |
| Operator\:&#32;focused double\-left | View | Empty editor\;&#32;recognized gesture | Returns directly Main | A one\-level parent hop |
| Main\:&#32;`/jobs` | Main\-owned jobs | Main composer | Status snapshot | Result consumption or cancellation |
| Main\:&#32;Hub&#32;`jobs`&#32;\/&#32;`wait` | Visible owner\-scoped jobs | Available manager\;&#32;valid IDs when supplied | Observes\;&#32;can consume settled delivery | All work succeeded\,&#32;perpetual result retrieval |
| Main\:&#32;Hub&#32;`send` | Exact agent ID in&#32;`to` | Addressable recipient | Delivers\;&#32;may wake\/revive | Focused steering timing or acknowledged compliance |
| Main\:&#32;Hub&#32;`cancel`&#32;with job ID | Running owned job | Current verified job ID | Marks cancelled and signals abort | Durable Tan tombstone |
| Main\:&#32;Hub cancel registration fallback | Exact direct\-child agent ID | Applicable registration\;&#32;ownership check | Aborts\/disposes and removes registration | Same behavior as x |
| Operator\:&#32;Hub x | Selected exact Tan ID | Matching row\;&#32;successful release | Explicit kill with tombstone\;&#32;transcript retained | Job necessarily says cancelled\;&#32;rollback |
| Operator\:&#32;Hub r | Selected parked agent | Successful reviver | Rebuilds live session | Reissues original task |
| Main\:&#32;read&#32;`history://…` | Discovered transcript | Reachable live history or retained file | Read\-only transcript access | A live controllable agent |
| Main\:&#32;Hub process control | Verified process&#32;`name` | Available process supervision | Inspects or controls that process | Tan conversation control |

---

## Evidence and method

### What was actually observed

The supplied proof used real SDK sessions\,&#32;`TanCommandController`\,&#32;installed&#32;`InputController`&#32;callbacks\,&#32;`SessionFocusController`\,&#32;the registry\,&#32;`AsyncJobManager`\,&#32;and the persisted cold reviver\.

Model transport was the repository’s&#32;**mock provider**\.&#32;The UI was a&#32;**recording adapter**\.&#32;The launcher denied network access\,&#32;restricted writes to a fresh runtime area\,&#32;and removed that area afterward\.

Local verification recorded the following nine successful scenarios\.&#32;Ultima received the first eight\;&#32;the publisher subsequently added the initial\-running\-tan kill check before publication\:

| Scenario | Bounded observation |
| --- | --- |
| Persisted parent and startup | Main and the tan were streaming concurrently\;&#32;their session identities differed\;&#32;their working directory matched\;&#32;the child received Main’s effective prompt\-cache key\. |
| Focused steering\,&#32;follow\-up\,&#32;and Escape | Steering and follow\-up entered the child’s queues\,&#32;not Main’s\.&#32;With loop mode off and no higher\-priority panels\/TTS\,&#32;the first Escape cleared draft text and the next returned to Main without cancelling the job\. |
| Queue delivery and normal parking | Steering preceded follow\-up in the child conversation\.&#32;The job completed with the last follow\-up text\;&#32;the agent parked and detached\;&#32;focus returned to Main\. |
| Main finishing | Main’s recorded turn finished without an aborted assistant result\. |
| Cold revival and continuation | The same Tan identity and transcript were reopened through the real cold\-revive path\,&#32;and a new prompt received a mock response\. |
| Turn interruption | Direct&#32;`AgentSession.abort()`&#32;on the revived session left it idle and attached\;&#32;a subsequent prompt worked\.&#32;This was not an Escape\-key test\. |
| Explicit kill | Direct&#32;`AgentLifecycleManager.release(..., {tombstone:true})`&#32;created the sidecar\,&#32;retained the transcript\,&#32;and caused revival to be refused\.&#32;The physical x key was not exercised\. |
| Running\-job cancellation | Direct&#32;`AsyncJobManager.cancel(jobId)`&#32;cancelled the job and the Tan registration became absent\;&#32;its transcript remained\.&#32;Passing the Tan ID to that lower\-level job manager did not cancel the job\. |
| Killing an initial running tan | The runtime Hub\'s abort\-then\-release primitives produced an aborted\,&#32;detached\,&#32;tombstoned agent\.&#32;Its original job separately became&#32;`completed`&#32;with&#32;`(no output)`&#32;and an un\-aborted job signal\.&#32;The physical x key was not exercised\. |

The supplied focused\-test report records&#32;**136 passed\,&#32;0 failed**&#32;across\:

- `packages/coding-agent/test/slash-commands/tan.test.ts`
- `packages/coding-agent/test/modes/controllers/tan-command-controller.test.ts`
- `packages/coding-agent/test/session-focus-controller.test.ts`
- `packages/coding-agent/test/input-controller-escape.test.ts`
- `packages/coding-agent/test/input-controller-keybindings.test.ts`
- `packages/coding-agent/test/input-controller-focused-submit-restore.test.ts`
- `packages/coding-agent/test/agent-hub-activate.test.ts`
- `packages/coding-agent/test/registry/agent-lifecycle.test.ts`

Other supplied test bodies were used as contract evidence\,&#32;but are not silently included in that reported pass count\.

### What this evidence does not prove

It does not establish\:

- Physical terminal key delivery or actual OMP renderer appearance\.
- Real model quality\,&#32;task success\,&#32;provider cache hits\,&#32;or savings\.
- Real LSP\/MCP\/tool side effects or complete external\-process cancellation\.
- Worktree isolation\.
- Restart rediscovery\.
- Permanent transcript or artifact retention\.
- Upstream\-release parity\.
- Browser verification or an official agentic score for this workbook\.

The proof’s cold\-revive fixture deliberately had no enabled tools and disabled several auxiliary systems\.&#32;Its successful continuation is not evidence that every production tool configuration reconstructs identically\.

The teaching recommendations—identity\-first requests\,&#32;disjoint file ownership\,&#32;scope restatement\,&#32;and post\-control inspection—are operating practices derived from these boundaries\.

This manuscript reports supplied evidence\;&#32;it is not an additional execution or verification run\.

### Source map

| Subject | Supplied project\-relative paths and symbols |
| --- | --- |
| Slash syntax and routing | `packages/coding-agent/src/slash-commands/builtin-lifecycle.ts`&#32;—&#32;`BUILTIN_LIFECYCLE_SLASH_COMMANDS`\,&#32;`tan.handleTui`\;&#32;`packages/coding-agent/src/modes/interactive-mode.ts`&#32;—&#32;`InteractiveMode.handleTanCommand` |
| Initial fork\,&#32;inheritance\,&#32;images\,&#32;parking | `packages/coding-agent/src/modes/controllers/tan-command-controller.ts`&#32;—&#32;`TanCommandController.start`\,&#32;`extractAssistantText`\;&#32;`packages/coding-agent/src/session/session-manager.ts`&#32;—&#32;`SessionManager.forkFrom`\,&#32;`ensureOnDisk` |
| Rebuilt tools and settings | `packages/coding-agent/src/sdk.ts`&#32;—&#32;`createAgentSession`\,&#32;`createAgentSessionScoped`\,&#32;`unregisterUnlessParked`\;&#32;`packages/coding-agent/src/task/executor.ts`&#32;—&#32;`createSubagentSettings`\,&#32;`createMCPProxyTools` |
| Focused submissions\,&#32;Escape\,&#32;double\-left\,&#32;retry\,&#32;dequeue | `packages/coding-agent/src/modes/controllers/input-controller.ts`&#32;—&#32;`setupKeyHandlers`\,&#32;`setupEditorSubmitHandler`\,&#32;`#submitToFocusedSession`\,&#32;`handleFollowUp`\,&#32;`#handleFocusedLeftTap`\,&#32;`handleRetry`\,&#32;`restoreQueuedMessagesToEditor` |
| Focus notices and automatic return | `packages/coding-agent/src/modes/controllers/session-focus-controller.ts`&#32;—&#32;`SessionFocusController.focusAgent`\,&#32;`unfocus`\,&#32;`#onRegistryEvent`\,&#32;`#attach` |
| Default keys and hotkeys\-sheet limitation | `packages/coding-agent/src/config/keybindings.ts`&#32;—&#32;`KEYBINDINGS`\,&#32;`KeybindingsManager`\;&#32;`packages/coding-agent/src/modes/utils/hotkeys-markdown.ts`&#32;—&#32;`buildHotkeysMarkdown` |
| Runtime Hub navigation\,&#32;revival\,&#32;kill | `packages/coding-agent/src/modes/components/agent-hub.ts`&#32;—&#32;`AgentHubOverlayComponent.#handleTableInput`\,&#32;`#activateAgent`\,&#32;`#reviveSelected`\,&#32;`#killSelected` |
| `/jobs`&#32;versus&#32;`/agents` | `packages/coding-agent/src/slash-commands/builtin-session.ts`&#32;—&#32;`BUILTIN_SESSION_SLASH_COMMANDS`\;&#32;`packages/coding-agent/src/modes/controllers/command-controller.ts`&#32;—&#32;`handleJobsCommand` |
| Job identity\,&#32;ownership\,&#32;retention\,&#32;delivery consumption | `packages/coding-agent/src/async/job-manager.ts`&#32;—&#32;`AsyncJob`\,&#32;`AsyncJobManager.register`\,&#32;`cancel`\,&#32;`consumeJobResults`\,&#32;`#resolveJobId`\,&#32;`#scheduleEviction` |
| Hub argument shapes and waits | `packages/coding-agent/src/tools/hub/index.ts`&#32;—&#32;`hubSchema`\,&#32;`HubTool.execute`\,&#32;`#executeWait`\;&#32;`packages/coding-agent/src/tools/hub/types.ts`&#32;—&#32;`JobSnapshot`\,&#32;roster limits |
| Cancellation fallback and snapshots | `packages/coding-agent/src/tools/hub/jobs.ts`&#32;—&#32;`executeCancel`\,&#32;`cancelAgentRegistration`\,&#32;`buildJobResult`\,&#32;`runningAgentsOutsideJobs` |
| Peer messaging and list scope | `packages/coding-agent/src/tools/hub/messaging.ts`&#32;—&#32;`executeList`\,&#32;`executeSend`\,&#32;`executeMessageWait` |
| Process\-name distinction | `packages/coding-agent/src/tools/hub/launch.ts`&#32;—&#32;`LaunchParams`\,&#32;`executeLaunch`\,&#32;`operationFor` |
| Queue timing and turn abort | `packages/agent/src/agent-loop.ts`&#32;—&#32;`runLoopBody`\,&#32;`executeToolCalls`\;&#32;`packages/agent/src/agent.ts`&#32;—&#32;`steer`\,&#32;`followUp`\,&#32;`continue`\;&#32;`packages/coding-agent/src/session/agent-session.ts`&#32;—&#32;`prompt`\,&#32;`abort`\,&#32;`#canAutoContinueForFollowUp` |
| Owner delivery and large results | `packages/coding-agent/src/session/agent-session.ts`&#32;—&#32;`#deliverAsyncJobResult`\,&#32;`#formatAsyncResultForFollowUp`\;&#32;`packages/coding-agent/src/session/async-job-delivery.ts`&#32;— inline and preview thresholds |
| Lifecycle and durable terminal intent | `packages/coding-agent/src/registry/agent-lifecycle.ts`&#32;—&#32;`AgentLifecycleManager.ensureLive`\,&#32;`release`\,&#32;`persistAgentTombstone`\;&#32;`packages/coding-agent/src/registry/agent-registry.ts`&#32;—&#32;`AgentStatus`\,&#32;`getAgentTombstonePath` |
| Saved discovery and cold reconstruction | `packages/coding-agent/src/registry/persisted-agents.ts`&#32;—&#32;`ensurePersistedRoster`\,&#32;`registerPersistedSubagents`\,&#32;`readPersistedAgentMetadata`\;&#32;`packages/coding-agent/src/task/persisted-revive.ts`&#32;—&#32;`createPersistedSubagentReviverFactory` |
| Transcript\,&#32;result\,&#32;and scratch URLs | `packages/coding-agent/src/internal-urls/history-protocol.ts`&#32;—&#32;`HistoryProtocolHandler`\;&#32;`packages/coding-agent/src/internal-urls/agent-protocol.ts`&#32;—&#32;`AgentProtocolHandler`\;&#32;`packages/coding-agent/src/internal-urls/local-protocol.ts`&#32;—&#32;`resolveLocalRoot`\,&#32;`LocalProtocolHandler.resolveOptions` |
| Ordinary task output distinction | `packages/coding-agent/src/task/executor.ts`&#32;—&#32;`finalizeRunResult`\,&#32;`finalizeSubagentLifecycle`\,&#32;`runSubagentFollowUpTurn` |
| Fork\-boundary intent | `packages/coding-agent/src/prompts/system/tan-context-switch.md`\;&#32;`packages/coding-agent/src/prompts/system/background-tan-dispatch.md`&#32;— prompt text interpreted as evidence of intended separation\,&#32;not as instructions governing this workbook |
| Version and configuration definitions | `packages/coding-agent/package.json`\;&#32;`packages/coding-agent/src/config/settings-schema.ts`&#32;—&#32;`SETTINGS_SCHEMA`\;&#32;settings behavior was checked against the consuming paths above\,&#32;not inferred solely from labels |

The bundle does not include every terminal\,&#32;provider\,&#32;messaging transport\,&#32;renderer\,&#32;or GC implementation\.&#32;Those omissions are material to claims about physical interaction\,&#32;external effects\,&#32;and long\-term availability\.

---

## Tangent work and live control\:&#32;next steps

The useful endpoint is not merely “the tan stopped\.” It is a report that identifies the target\,&#32;distinguishes job and agent state\,&#32;locates the available output\,&#32;and accounts for already\-written files or separately supervised processes\.&#32;Likewise\,&#32;“the message was delivered” does not establish that the requested correction was followed\.&#32;Completion\,&#32;delivery\,&#32;returned output\,&#32;and effects are different facts\.

The next question is why a particular operation was permitted at all\.&#32;A tangent can inherit a per\-tool policy record while its initial settings helper defaults the approval mode to yolo\.&#32;Its narrow assignment is not an OS boundary\,&#32;and its initial headless construction is not Main’s interactive approval experience copied intact\.

Continue to&#32;[Tool permissions and approvals](<https://present-sketch-tp94.here.now/chapters/unified-permissions>)\.&#32;Follow the exact call through tier classification\,&#32;effective policy\,&#32;device dispatch\,&#32;one\-call answers\,&#32;and host UI availability before treating a refusal as something to repair or a missing prompt as proof of safety\.

Extensions can then make these distinctions easier to operate\:&#32;explicit tools can expose identity and revisions\,&#32;lifecycle handlers can retire stale authority\,&#32;and owner\-addressed delivery can keep a result with its originating conversation\.&#32;They do not remove the underlying boundaries or automatically install themselves in every child runtime\.

After permissions\,&#32;continue to&#32;[Extensions inside those boundaries](<https://present-sketch-tp94.here.now/chapters/unified-extensions>)\.&#32;The design question remains how to make a capability’s actual scope visible to both a human and an agent\.

## Tool permissions and approvals

A tangent’s identity tells you which worker you are addressing\.&#32;It does not yet explain why one of that worker’s calls ran without asking\,&#32;why another stopped before a dialog\,&#32;or why an approved write still failed\.

This part makes permission resolution an operator skill rather than an extension\-author prerequisite\.&#32;Start with the exact operation\,&#32;follow its applicable policy\,&#32;and keep permission separate from execution and effects\.

### Read the three desks in sequence

**Approval Desk**&#32;establishes the tier matrix\,&#32;then works through conflicts among explicit tool policy\,&#32;effective user policy\,&#32;mode defaults\,&#32;and ordered bash rules\.&#32;Its command strings are classification data only\.

**Dispatch Desk**&#32;follows a&#32;`write`&#32;envelope into the fictional&#32;`seed_slot`&#32;device\.&#32;You will distinguish generic write policy from device policy\,&#32;an outer admission from an inner check\,&#32;and two prompts from two executions\.

**Boundary Desk**&#32;explains the actual one\-call Approve\/Deny choice\,&#32;dismissal and unavailable UI\,&#32;RPC and ACP capability differences\,&#32;launch precedence\,&#32;and the later OS\/host boundary\.&#32;It also connects Task and Tan construction to the yolo\-default settings helper without pretending that a conversation fork clones every safeguard\.

The public starting points are&#32;[Approval Desk’s cases](<https://present-sketch-tp94.here.now/examples/approval-desk/cases.json>)\,&#32;[Dispatch Desk’s cases](<https://present-sketch-tp94.here.now/examples/dispatch-desk/cases.json>)\,&#32;and&#32;[Boundary Desk’s cases](<https://present-sketch-tp94.here.now/examples/boundary-desk/cases.json>)\.&#32;They are inert JSON\,&#32;not configuration to install or tools to submit\.&#32;The recording actions change no real inventory or publication service\.

### Keep the default route on paper

Predict\,&#32;inspect\,&#32;explain\,&#32;and compare with the supplied recorded outcomes\.&#32;No provider call\,&#32;credentials\,&#32;personal settings change\,&#32;remote connection\,&#32;privileged broker\,&#32;or executable simulator is required\.&#32;Browser milestone selection changes reading state only\.

The source snapshot and new evidence are dated 30 August 2026\.&#32;The two existing focused test runs\,&#32;the separate private scenario report\,&#32;and the installed configuration CLI observation retain distinct scopes\.&#32;Use&#32;[Evidence and limitations](<https://present-sketch-tp94.here.now/chapters/permissions-evidence-and-limitations>)&#32;before promoting a passing classification into a claim about physical UI or OS enforcement\.

## Orientation

A tool call can stop without offering a dialog\.&#32;Another call can run without asking\,&#32;even in a mode named&#32;`always-ask`\.&#32;A call displayed as&#32;`write`&#32;can actually dispatch a different tool\,&#32;with a different tier and policy key\.&#32;None of these observations is explained adequately by saying that permissions are simply on or off\.

This part teaches an operator’s method\:&#32;**identify the exact operation\,&#32;follow the policy that applies to that operation\,&#32;and distinguish permission to proceed from evidence of an effect\.**&#32;You will read enough TypeScript to understand the decision\,&#32;but you do not need to build an extension or make a provider request\.

The practice setting is the fictional&#32;**Cedar Seed Library**\.&#32;Its three desks are a sequence of reading exercises\:

- **Approval Desk**&#32;classifies declarations\,&#32;modes\,&#32;policies\,&#32;and ordered shell rules\.
- **Dispatch Desk**&#32;follows an operation through a&#32;`write`&#32;envelope into an argument\-sensitive device tool\.
- **Boundary Desk**&#32;separates one\-call answers\,&#32;UI capabilities\,&#32;provider safety acknowledgements\,&#32;and host authority\.

The complete practice data is in&#32;[Approval Desk’s cases](<https://present-sketch-tp94.here.now/examples/approval-desk/cases.json>)\,&#32;[Dispatch Desk’s cases](<https://present-sketch-tp94.here.now/examples/dispatch-desk/cases.json>)\,&#32;and&#32;[Boundary Desk’s cases](<https://present-sketch-tp94.here.now/examples/boundary-desk/cases.json>)\.&#32;These are inert JSON records\,&#32;not OMP configuration\,&#32;executable extensions\,&#32;or shell scripts\.&#32;`seed_note`&#32;and&#32;`seed_slot`&#32;are fictional recording tools used by the supplied private verifier\;&#32;this workbook does not install them\.&#32;An action string named&#32;`publish`&#32;does not contact a publishing service\.&#32;Shell\-shaped strings are objects of classification only\:&#32;**do not execute them or submit them to OMP\.**

### Use the paper route

For each worked case\,&#32;cover the answer\,&#32;predict the decision\,&#32;inspect the fictional inputs\,&#32;and compare your explanation with the recorded outcome\.&#32;Keep three labels separate\:

- **Source\-backed\:**&#32;follows the supplied implementation snapshot of 30 August 2026\.
- **Recorded\:**&#32;describes a completed check in a supplied report\,&#32;under that report’s conditions\.
- **Paper checkpoint\:**&#32;a falsifiable question for the reader\;&#32;answering it is not a new runtime test\.

The website’s example controls select authored reading milestones\.&#32;They do not evaluate your settings\,&#32;resolve a real permission policy\,&#32;answer an OMP dialog\,&#32;or run a fixture\.&#32;The default route requires only the text and fictional data\.&#32;No credentials\,&#32;provider prompts\,&#32;personal settings changes\,&#32;SSH connection\,&#32;privileged broker\,&#32;or new simulator are needed\.

## Start with the operation

Nia\,&#32;the fictional desk operator\,&#32;receives two reports\:&#32;“the tool was blocked” and “the tool did not ask\.” Before changing a setting\,&#32;she asks what operation each report actually describes\.

That question prevents a common repair mistake\.&#32;A missing tool cannot be enabled by approving a call\.&#32;An explicit policy denial is not an unanswered question\.&#32;A successful approval cannot make an unwritable file writable\.&#32;Each failure belongs to a different boundary\.

### Five gates that must not be collapsed

The following is a diagnostic map\,&#32;not a promise that every host implements five checks in this exact order\.

| Gate | Question it answers | What passing it does not establish |
| --- | ---: | --- |
| Tool availability | Is this capability enabled and reachable through the current session’s actual tool surface\? | That any particular invocation is approved\. |
| Approval policy | Does the applicable declaration\,&#32;user policy\,&#32;and effective wrapper mode allow\,&#32;prompt\,&#32;or deny this call\? | A domain grant\,&#32;provider acknowledgement\,&#32;or OS privilege\. |
| Domain authority | Does the application permit this action on this object and revision\? | That host approval or filesystem access will succeed\. |
| Provider safety | Are applicable provider requirements satisfied\,&#32;including pending computer safety checks handled by the wrapper\? | That the operation is correct or allowed by the OS\. |
| OS or host authority | Can the actual process or client perform the requested effect at the actual destination\? | That the effect was wanted\,&#32;completed\,&#32;or correctly reported\. |

The distinction between availability and presentation is especially important\.&#32;A discoverable tool can be enabled without appearing as a top\-level schema\.&#32;The supplied&#32;`xd://`&#32;implementation can reach enabled mounted tools and enabled top\-level tools through its canonical map\.&#32;Conversely\,&#32;a name in old conversation text does not establish present availability\.

`ToolLoadMode`&#32;in&#32;`packages/agent/src/types.ts`&#32;describes presentation\.&#32;`resolveXdevTool()`&#32;in&#32;`packages/coding-agent/src/tools/xdev.ts`&#32;checks the enabled union before returning a tool\.&#32;Neither is a grant for an arbitrary operation\.&#32;The SDK’s restricted\-session options are also separate construction controls\;&#32;do not treat a familiar launch flag or a displayed name as a complete inventory of every runtime path\.

### Predict two different refusals

Inspect these inputs from Boundary Desk as data\:

~~~json
{
  "case": "boundary-no-ui",
  "approval": "exec",
  "args": { "action": "publish" },
  "mode": "always-ask",
  "ui": false
}
~~~

Now compare&#32;`boundary-deny-before-handler`\.&#32;Its declaration includes&#32;`policy: "deny"`\,&#32;its original action is&#32;`blocked`\,&#32;and its proposed handler replacement is&#32;`inspect`\.

**Prediction\:**&#32;will either case display a selection\?&#32;Will the replacement rescue the denied call\?

**Recorded answer\:**&#32;neither displayed a selection and neither reached the inert executor\.&#32;Their traces nevertheless differed\.&#32;`boundary-no-ui`&#32;recorded one&#32;`tool_call`\,&#32;then approval requested and approval resolved false with reason&#32;`no interactive UI available`\.&#32;`boundary-deny-before-handler`&#32;recorded no&#32;`tool_call`&#32;and no approval lifecycle pair\:&#32;the direct wrapper’s original\-input policy denial stopped before its handler could supply a replacement\.

Thus zero prompts does not mean automatic approval\.&#32;It can mean that the call was denied before prompting\,&#32;that prompting was required but unavailable\,&#32;or that policy admitted the call without a prompt\.&#32;The decision and execution evidence distinguish those cases\.

### Make a small decision record

For a blocked or unexpectedly unprompted call\,&#32;begin with the exact observed tool name\,&#32;call ID if available\,&#32;arguments\,&#32;path or device address\,&#32;and current session\/cwd scope\.&#32;Then identify the effective mode\,&#32;applicable policy key\,&#32;tool declaration\,&#32;and host UI capability\.&#32;Finally\,&#32;record whether the underlying operation executed and what effect was actually inspected\.

For this workbook\,&#32;those facts come from the supplied cases and recorded outcomes—not from a request to an agent to investigate your installation\.&#32;In later operational work\,&#32;keep complete sensitive arguments and diagnostics private\.

**Paper checkpoint\:**&#32;explain why&#32;`boundary-no-ui`&#32;is not an OS permission\-denied file write\.&#32;**Worked answer\:**&#32;it stops in&#32;`ExtensionToolWrapper.execute()`&#32;before the inert tool runs\;&#32;no file primitive is involved\.&#32;An OS denial belongs to a later operation\,&#32;such as the separate fallback case studied at Boundary Desk\.

**Failure boundary\:**&#32;the private scenario report uses a structural runner adapter and explicitly instrumented executors\.&#32;It is not a record of a provider choosing a tool\,&#32;a real person answering\,&#32;or every possible dispatch path\.

**Source anchors\:**&#32;`packages/agent/src/types.ts`&#32;—&#32;`AgentTool`\,&#32;`ToolLoadMode`\,&#32;`ToolApprovalDecision`\;&#32;`packages/coding-agent/src/sdk.ts`&#32;—&#32;`createAgentSessionScoped`\;&#32;`packages/coding-agent/src/tools/xdev.ts`&#32;—&#32;`resolveXdevTool`\;&#32;`packages/coding-agent/src/extensibility/extensions/wrapper.ts`&#32;—&#32;`ExtensionToolWrapper.execute`\.

## Read\,&#32;write\,&#32;and exec are tiers

Nia’s next mistake would be to infer behavior from a tool’s name\.&#32;A tool called&#32;`read`&#32;is not always read\-tier\.&#32;A tool carried through&#32;`write`&#32;is not always write\-tier\.&#32;The declaration can be a function of arguments\.

A&#32;**tier**&#32;is a tool’s approval classification\.&#32;It is not a sandbox\,&#32;a syscall monitor\,&#32;or proof that the implementation has no other effects\.

### Learn the baseline matrix first

The resolver ranks&#32;`read`&#32;below&#32;`write`&#32;below&#32;`exec`\.&#32;With no explicit policy or active override changing the decision\,&#32;the matrix is exact\:

| Approval mode | Read tier | Write tier | Exec tier |
| --- | ---: | --- | --- |
| `always-ask` | allow | prompt | prompt |
| `write` | allow | allow | prompt |
| `yolo` | allow | allow | allow |

The name&#32;`always-ask`&#32;therefore does&#32;**not**&#32;mean every call prompts\.&#32;It automatically admits read\-tier calls at this baseline layer\.

The schema default for&#32;`tools.approvalMode`&#32;is&#32;`yolo`\.&#32;The wrapper also falls back to&#32;`yolo`&#32;when the execute\-time settings context supplies no mode\.&#32;That is a source default\,&#32;not a statement about a reader’s installation\.&#32;A tool with no approval declaration defaults to&#32;**exec**\,&#32;not read\.

**Recorded\:**&#32;Approval Desk exercised all nine combinations through the actual&#32;`resolveApproval()`&#32;and&#32;`requiresApproval()`&#32;functions\.&#32;The cases are&#32;`approval-always-ask-read`\,&#32;`approval-always-ask-write`\,&#32;`approval-always-ask-exec`\,&#32;`approval-write-read`\,&#32;`approval-write-write`\,&#32;`approval-write-exec`\,&#32;`approval-yolo-read`\,&#32;`approval-yolo-write`\,&#32;and&#32;`approval-yolo-exec`\.&#32;They classified calls\;&#32;they did not execute tools or show dialogs\.

### An omitted declaration is a useful counterexample

Cover the expected result for&#32;`approval-undeclared-exec`\.&#32;It supplies the fictional tool name&#32;`seed_note`\,&#32;no declaration\,&#32;and mode&#32;`write`\.

**Prediction\:**&#32;does the absence of an execution\-related name make this an automatically allowed call\?

**Recorded answer\:**&#32;the result was&#32;`policy: "prompt"`\,&#32;`tier: "exec"`\,&#32;`override: false`\,&#32;`source: "mode"`\.&#32;Omission is deliberately conservative\.&#32;`requiresApproval()`&#32;returned&#32;`required: true`\;&#32;that is still a classification result\,&#32;not evidence that a UI appeared\.

The same default applies to an unannotated MCP tool at this resolver\.&#32;An MCP\-looking name does not itself establish a lower tier\.&#32;The existing&#32;`approval.test.ts`&#32;tests both unannotated and explicitly annotated MCP subjects\.

### Read tier does not mean universally read\-only

Several supplied declarations make the limit concrete\:

| Source\-backed operation | Declaration | Boundary to retain |
| --- | ---: | --- |
| Ordinary&#32;`ReadTool`&#32;path | Usually read | URL reads can make network requests\;&#32;some specialized reads escalate\. |
| `ReadTool`&#32;or&#32;`GrepTool`&#32;targeting&#32;`ssh://` | exec | The higher tier does not grant remote access\. |
| `HubTool`&#32;peer&#32;`send`\,&#32;inbox\,&#32;waits\,&#32;and job&#32;`cancel` | read for these declared forms | Messaging\,&#32;consuming an inbox\,&#32;or cancelling a job can change agent\/job state\. |
| `HubTool`&#32;process&#32;`send`&#32;with a process name and no peer target | exec | Process input is classified differently from a peer message\. |
| Ordinary filesystem&#32;`WriteTool`&#32;target | write | The tier is not a workspace\-containment guarantee\. |
| `ManageSkillTool` | write | Its managed\-skill storage is not simply the current workspace\. |
| `EvalTool` | exec | A harmless\-looking cell does not lower this static declaration\. |

These rows are source examples\,&#32;not new executed workbook scenarios\.&#32;The&#32;`hubApproval()`&#32;implementation\,&#32;rather than its broad comment about read\-only operations\,&#32;establishes the classifications\.&#32;`HubTool.execute()`&#32;then routes those operations to messaging and lifecycle functions\.

Likewise\,&#32;`ReadTool.approval()`&#32;returns read for ordinary HTTP URL reads\,&#32;while its execution path can reach&#32;`executeReadUrl()`&#32;or&#32;`fetchReadUrl()`\.&#32;Local\-looking or read\-tier work is not a general no\-network promise\.

A declaration can also distinguish handler\-backed paths and session artifacts\.&#32;Later chapters follow those paths without pretending that read\-tier artifact work has zero effects\.

### Use tiers to predict a gate\,&#32;not to certify a program

A capable operator can use the matrix to predict the default policy\.&#32;To assess the effect\,&#32;the operator still needs the actual implementation and destination\.&#32;A tool author can declare a tier incorrectly\,&#32;and trusted in\-process code can do work outside a particular wrapped call\.

**Paper checkpoint\:**&#32;predict the baseline policy for an unannotated&#32;`seed_note`&#32;in&#32;`write`&#32;mode\,&#32;then for a read\-tier peer message in&#32;`always-ask`\.&#32;**Worked answer\:**&#32;prompt\,&#32;then allow\.&#32;Neither answer establishes what a real implementation would change or disclose\.

**Failure boundary\:**&#32;the matrix is the fallback after the resolver’s higher\-precedence branches\.&#32;Do not apply it before checking explicit denies\,&#32;explicit tool policies\,&#32;overrides\,&#32;or the effective launch mode\.

**Source anchors\:**&#32;`packages/coding-agent/src/tools/approval.ts`&#32;—&#32;`normalizeDecision`\,&#32;`resolveToolTier`\,&#32;`APPROVAL_MODE_MAX_TIER`\,&#32;`resolveApproval`\;&#32;`packages/coding-agent/src/config/settings-schema.ts`&#32;—&#32;`tools.approvalMode`\;&#32;`packages/coding-agent/src/tools/hub/index.ts`&#32;—&#32;`hubApproval`\,&#32;`HubTool.execute`\;&#32;`packages/coding-agent/src/tools/read.ts`&#32;—&#32;`ReadTool.approval`\,&#32;`#executeInner`\;&#32;`packages/coding-agent/src/tools/grep.ts`&#32;—&#32;`GrepTool.approval`\;&#32;`packages/coding-agent/src/tools/manage-skill.ts`&#32;—&#32;`ManageSkillTool`\;&#32;`packages/coding-agent/src/tools/eval.ts`&#32;—&#32;`EvalTool`\.

## Approval Desk\:&#32;modes and policies

The baseline matrix explains defaults\.&#32;It does not explain every decision\.&#32;Nia now compares two plausible rules\:&#32;“user policy always wins” and “yolo allows everything\.” Both are too broad\.

The actual resolver gives special treatment to&#32;**denial**\,&#32;then distinguishes explicit tool policy from an override\-only prompt and from the mode fallback\.

### Follow the implementation’s precedence

First\,&#32;the tool’s declaration is evaluated against the arguments and normalized\.&#32;A declaration can be a bare tier\,&#32;an object carrying a tier and additional fields\,&#32;or a function returning either form\.

The resolver then identifies the effective user policy\.&#32;Its key is the declaration’s&#32;`policyKey`\,&#32;if present\,&#32;otherwise the invoking tool’s name\.&#32;If a distinct keyed policy is absent or invalid\,&#32;the invoking tool’s policy is the fallback\.

After that lookup\,&#32;the following order applies\:

| Order | Condition | Result |
| --- | ---: | --- |
| 1 | Tool decision says&#32;`deny` | Deny from tool policy\. |
| 2 | Effective user policy says&#32;`deny` | Deny from user policy\. |
| 3 | Mode is&#32;`yolo` | An explicit tool policy wins\;&#32;otherwise use the effective user policy or allow\.&#32;Override is reported false\. |
| 4 | Non\-yolo decision has&#32;`override: true` | Explicit tool allow remains allow\;&#32;otherwise prompt\.&#32;Source is tool and override remains true\. |
| 5 | Tool decision explicitly says allow or prompt | Use that tool policy\. |
| 6 | A valid effective user policy remains | Use that user policy\. |
| 7 | No earlier branch decided | Compare the tier with the mode matrix\. |

This table describes&#32;`resolveApproval()`\.&#32;The wrapper can add provider safety requirements and has a specific forwarded\-dispatch prompt predicate\,&#32;examined later\.

An&#32;**override\-only prompt**&#32;is a decision such as&#32;`{ "tier": "exec", "override": true }`\,&#32;with no explicit policy\.&#32;It prompts in a non\-yolo mode even if a user allow would otherwise admit the call\.&#32;In yolo\,&#32;the override\-only prompt is ignored\.&#32;By contrast\,&#32;`{ "tier": "exec", "policy": "prompt" }`&#32;remains a prompt in the resolver’s yolo branch\.

Even&#32;`override: true`&#32;is not literally “always force a prompt”\:&#32;a decision that also explicitly says allow returns allow\,&#32;after the deny checks\.&#32;Read the fields together\.

### Work through four policy conflicts

**Prediction\:**&#32;for each case below\,&#32;name the winning source as well as allow\,&#32;prompt\,&#32;or deny\.

| Case | Important inputs | Recorded resolution |
| --- | ---: | --- |
| `approval-tool-deny-wins` | Tool write\-tier deny\;&#32;user allow\;&#32;yolo | deny\,&#32;source tool\,&#32;reason&#32;`Fictional desk is closed` |
| `approval-user-deny-wins` | Tool read\-tier allow\;&#32;user deny\;&#32;yolo | deny\,&#32;source user\,&#32;policy key&#32;`seed_note` |
| `approval-explicit-tool-prompt` | Explicit tool prompt\;&#32;user allow\;&#32;yolo | prompt\,&#32;source tool\,&#32;override false |
| `approval-tool-allow-before-user-prompt` | Explicit tool allow\;&#32;user prompt\;&#32;always\-ask | allow\,&#32;source tool |

The last row is easy to misread\.&#32;A valid user prompt is not a universal veto over an explicit tool allow\.&#32;A valid&#32;**effective user deny**&#32;is checked earlier and does stop it\.&#32;Consequently\,&#32;the short settings description cannot replace the resolver’s actual order\.

These records also show why denial provenance matters\.&#32;`denyError()`&#32;distinguishes a tool\-owned refusal from a user\-policy refusal\.&#32;A tool\-policy error can include the tool’s reason\.&#32;A user\-policy error names the effective user key\.&#32;The stock error’s repair hint is not a workbook instruction to remove the policy\.

### A policy key is a lookup identity\,&#32;not another cumulative grant

Compare these complete fictional resolver inputs\.&#32;They are teaching data\,&#32;not configuration to install\.

~~~json
{
  "tool": "write",
  "approval": { "tier": "exec", "policyKey": "seed_slot" },
  "mode": "yolo",
  "userPolicies": {
    "seed_slot": "not-a-policy",
    "write": "deny"
  }
}
~~~

This is the input shape of&#32;`approval-policy-key-fallback`\.&#32;The keyed value is invalid\,&#32;so the valid invoking\-tool policy supplies the denial\.

**Recorded answer\:**&#32;deny\,&#32;source user\,&#32;policy key&#32;`write`\.

Now inspect&#32;`approval-policy-key-specific`\:

~~~json
{
  "tool": "write",
  "approval": { "tier": "exec", "policyKey": "seed_slot" },
  "mode": "always-ask",
  "userPolicies": {
    "seed_slot": " ALLOW ",
    "write": "deny"
  }
}
~~~

**Recorded answer\:**&#32;allow\,&#32;source user\,&#32;policy key&#32;`seed_slot`\.&#32;The valid device policy replaces the invoking&#32;`write`&#32;fallback\,&#32;including that fallback’s deny\.&#32;The resolver is not performing an intersection of every policy entry in the record\.

That does not contradict deny precedence\.&#32;The&#32;`write`&#32;entry is no longer the effective user policy for this keyed decision\.&#32;A tool\-owned deny or a deny at a later applicable inner gate is a different matter\.

### Keep normalization claims local

`normalizePolicy()`&#32;accepts strings\,&#32;trims them\,&#32;lowercases them\,&#32;and recognizes only&#32;`allow`\,&#32;`deny`\,&#32;and&#32;`prompt`\.&#32;Other values do not become policies at that resolver layer\.&#32;A missing or invalid tier in a returned decision defaults to exec\.&#32;A function\-valued declaration can still throw\;&#32;the general resolver does not promise to catch every throwing declaration and convert it into a safe result\.

These facts are not a claim that every raw settings object is validated everywhere\.&#32;The wrapper separately uses raw own\-property presence when deciding whether an inner prompt is explicit\.&#32;An invalid entry can therefore be ignored as a policy by the resolver while still mattering to that later predicate\.&#32;Nor does&#32;`resolveApproval()`&#32;normalize arbitrary invalid mode names into one of the three supported modes\.

Use the documented record shape and exact mode values\.&#32;Do not intentionally depend on malformed settings as a safety mechanism\.

### Read the result as an explanation

A resolved result contains policy\,&#32;tier\,&#32;and override\,&#32;with provenance fields where the selected branch supplies them\.&#32;`policyKey`&#32;is useful when user policy won\.&#32;`reason`&#32;is optional\,&#32;not a complete audit record\.&#32;For example\,&#32;ignoring an override\-only critical prompt in yolo can also omit its reason from the resolved result\.

**Paper checkpoint\:**&#32;does&#32;`write: deny`&#32;necessarily deny a call whose declaration has&#32;`policyKey: seed_slot`\?&#32;**Worked answer\:**&#32;no\.&#32;A valid keyed policy is selected first\;&#32;the invoking&#32;`write`&#32;policy is consulted only when the keyed value supplies no valid policy\.&#32;Then the ordinary precedence applies\.

**Failure boundary\:**&#32;an allow result is permission at this resolver\,&#32;not a successful call\,&#32;a persisted human answer\,&#32;a domain grant\,&#32;or proof that another gate agrees\.

**Source anchors\:**&#32;`packages/coding-agent/src/tools/approval.ts`&#32;—&#32;`normalizePolicy`\,&#32;`normalizeDecision`\,&#32;`resolveApproval`\,&#32;`requiresApproval`\,&#32;`denyError`\;&#32;the fixture references identify the resolver section as lines 104–233\.&#32;Recorded cases are in&#32;`proof/report.json`\,&#32;under the Approval Desk IDs above\.&#32;Existing contract coverage is in&#32;`packages/coding-agent/test/tools/approval.test.ts`\.

## Approval Desk\:&#32;ordered shell rules

Nia now reviews a shell\-policy list\.&#32;She expects a deny anywhere in the list to win\.&#32;The implementation instead chooses the&#32;**first applicable rule**\.&#32;Whether a rule is applicable depends partly on its approval value\.

This makes ordering part of the policy\,&#32;not merely its formatting\.

### Inspect strings without running them

The following is a complete fictional classification input from&#32;`bash-first-applicable-rule`\.&#32;It is not a shell exercise and is not a configuration recommendation\.

~~~json
{
  "command": "printf seed-card",
  "patterns": [
    { "match": "printf *", "approval": "allow" },
    { "match": "*", "approval": "deny" }
  ],
  "mode": "always-ask"
}
~~~

**Prediction\:**&#32;does the final catch\-all deny block this string\?

**Recorded answer\:**&#32;no\.&#32;The first rule applies\.&#32;`BashTool.approval()`&#32;returns an explicit tool allow at write tier\,&#32;and the resolver returns allow from tool policy\.&#32;No shell command was executed by this scenario\.

The existing&#32;`approval.test.ts`&#32;also covers a broad early&#32;`*`&#32;allow shadowing a later matching deny\.&#32;Therefore “there is a deny later” is not sufficient evidence of an effective denial\.

### Understand what matching means

`bash.patterns`&#32;is an ordered array of objects with&#32;`match`&#32;and&#32;`approval`\.&#32;In the supplied implementation\:

- Only&#32;`*`&#32;is a wildcard\.&#32;Other regular\-expression\-looking punctuation is escaped\.
- Pattern and command whitespace is normalized before matching\.
- Policy values are normalized\;&#32;malformed rule entries are discarded by the rule reader\.
- An allow must match the whole command and pass the shell\-control heuristic\.
- A deny or prompt may match the whole command or a tokenized segment of a compound command\.
- The first rule that applies under those semantics is selected\.

The matching is not a shell security parser\.&#32;It does not certify executable identity\,&#32;the behavior of scripts or binaries\,&#32;environment\-dependent behavior\,&#32;or every form of shell interpretation\.

The allow heuristic does distinguish some literal quoted metacharacters from active shell control\.&#32;The existing tests cover a quoted literal pattern that remains allowable\,&#32;as well as expansion\,&#32;redirection\,&#32;compound syntax\,&#32;and arguments reinterpreted through command\/eval options that do not ride a narrow allow\.&#32;Those are useful classification contracts\,&#32;not an exhaustive shell\-language proof\.

### Change the command shape\,&#32;not the rule order

Now read&#32;`bash-compound-deny-segment`\:

~~~json
{
  "command": "printf seed-card && printf closed",
  "patterns": [
    { "match": "printf *", "approval": "allow" },
    { "match": "printf closed", "approval": "deny" }
  ],
  "mode": "yolo"
}
~~~

**Prediction\:**&#32;does the first allow still apply because the text begins with&#32;`printf`\?

**Recorded answer\:**&#32;no\.&#32;The compound line cannot use that allow\.&#32;The second rule matches a segment\,&#32;so the decision is tool\-owned deny with reason&#32;`Blocked by bash pattern: printf closed`\.&#32;Yolo does not remove the selected explicit denial\.

This explains why “first matching” must include the rule’s semantics\.&#32;It does not mean “first textual prefix\.”

### Critical heuristics have another ordering boundary

After selecting a rule\,&#32;`BashTool.approval()`&#32;does the following\:

1. If the selected rule says deny\,&#32;return explicit deny immediately\.
2. Otherwise\,&#32;check&#32;`CRITICAL_BASH_PATTERNS`\.
3. If a critical pattern matches\,&#32;return exec with&#32;`override: true`&#32;and reason&#32;`Critical pattern detected`\,&#32;but no explicit prompt policy\.
4. Only then return the selected allow or prompt\,&#32;if any\.
5. With none of those decisions\,&#32;return exec\.

For the next three cases\,&#32;the command field is the inert string&#32;`chmod -R 700 /fictional-seed-library`\.&#32;It is included to classify a recursive absolute\-path permission change\.&#32;**Never execute it\.**

| Case | Rule and mode | Recorded resolution |
| --- | ---: | --- |
| `bash-critical-before-allow` | `chmod *`&#32;allow\;&#32;write mode\;&#32;user bash allow | prompt\,&#32;exec\,&#32;override true\,&#32;source tool |
| `bash-critical-yolo` | Same allow rule\;&#32;yolo | allow\,&#32;exec\,&#32;override false\,&#32;source mode |
| `bash-selected-deny-before-critical` | `chmod *`&#32;deny\;&#32;yolo | deny\,&#32;exec\,&#32;override true\,&#32;source tool |

The critical result in the first two rows is an override\-only decision\.&#32;That is why yolo can bypass it\.&#32;The third row is an explicit denial returned before the heuristic and remains denied\.

There is a further consequence worth predicting from source\:&#32;a selected prompt rule does not necessarily survive as an explicit prompt decision when the critical branch returns first\.&#32;The selected rule is found first\,&#32;but the critical branch is evaluated before selected allow\/prompt is returned\.&#32;Do not turn a catch\-all prompt rule into a guarantee that every critical string will prompt in yolo\.

For comparison\,&#32;`bash-explicit-prompt-yolo`&#32;uses the noncritical&#32;`printf seed-card`&#32;string and a&#32;`printf *`&#32;prompt rule\.&#32;**Recorded\:**&#32;it remains a tool\-sourced prompt in yolo\,&#32;even with user bash allow\.&#32;Here the explicit prompt branch was actually reached\.

### Repair an explanation before repairing a policy

When a result surprises you\,&#32;write down the selected rule and the branch reached after selection\.&#32;If you cannot identify the first applicable rule\,&#32;rearranging or broadening the list is premature\.&#32;Keep approval patterns separate from&#32;`bashInterceptor`&#32;rules\:&#32;the latter are another execution\-time mechanism for directing work toward dedicated tools\,&#32;not the same ordered approval array\.

**Paper checkpoint\:**&#32;why does a selected deny survive yolo while the critical override\-only result can be allowed\?&#32;**Worked answer\:**&#32;explicit denial is resolved before the yolo branch\;&#32;an override flag without an explicit policy is ignored by that branch\.

**Failure boundary\:**&#32;the six private bash cases called the real declaration and resolver only\.&#32;They did not call&#32;`BashTool.execute()`\.&#32;Neither their passing classifications nor the existing pattern tests establish a shell sandbox\.

**Source anchors\:**&#32;`packages/coding-agent/src/tools/bash.ts`&#32;—&#32;`getBashApprovalPatternRules`\,&#32;`bashApprovalRuleMatches`\,&#32;`findBashApprovalPatternRule`\,&#32;`hasBashApprovalShellControl`\,&#32;`CRITICAL_BASH_PATTERNS`\,&#32;`BashTool.approval`\;&#32;`packages/coding-agent/src/tools/approval.ts`&#32;—&#32;`resolveApproval`\;&#32;`packages/coding-agent/test/tools/approval.test.ts`&#32;— ordered\-pattern\,&#32;compound\-command\,&#32;and critical\-pattern tests\.

## Dispatch Desk\:&#32;device and path gates

At Dispatch Desk\,&#32;Nia sees a call named&#32;`write`\.&#32;She initially assumes it is a generic filesystem write\.&#32;The address changes the question\:&#32;the call targets&#32;`xd://seed_slot`\,&#32;so&#32;`write`&#32;is carrying an invocation of another tool\.

The operator must identify both the&#32;**outer transport**&#32;and the&#32;**inner operation**\.

### Read the envelope and its payload separately

This is the fictional envelope used for a reservation case\.&#32;Inspect it\;&#32;do not submit it to a running agent\.

~~~json
{
  "path": "xd://seed_slot",
  "content": "{\"action\":\"reserve\"}"
}
~~~

The&#32;`content`&#32;field is a JSON string containing the inner argument object\.&#32;In the private verifier\,&#32;the only mounted recording tool is&#32;`seed_slot`\.&#32;Its declaration returns read for&#32;`inspect`\,&#32;write for&#32;`reserve`\,&#32;an explicit exec\-tier deny for&#32;`blocked`\,&#32;and exec for other action strings\,&#32;including&#32;`publish`\.&#32;The executor merely appends an in\-memory record\.

`WriteTool.approval()`&#32;parses the device payload and calls&#32;`resolveToolTier()`&#32;on the target\.&#32;It returns the borrowed tier with&#32;`policyKey: "seed_slot"`\.&#32;It does&#32;**not**&#32;copy the target’s entire policy decision into the outer declaration\.

During execution\,&#32;`dispatchXdevTool()`&#32;resolves the enabled canonical tool and validates the decoded arguments before invoking the executable instance\.&#32;The inner wrapper can still enforce a tool\-owned deny or other applicable gate\.

### Predict an inspection and a reservation

`dispatch-inspect-no-ui`&#32;supplies&#32;`action: inspect`\,&#32;always\-ask mode\,&#32;and no UI\.&#32;`dispatch-reserve-once`&#32;supplies&#32;`action: reserve`&#32;in always\-ask and one recorded&#32;`Approve`&#32;answer\.

**Recorded answers\:**&#32;the inspection produced no prompt and one inert execution\.&#32;The reservation produced one prompt and one inert execution\.&#32;Both produced two&#32;`tool_call`&#32;events\:&#32;one for&#32;`write`\,&#32;one for&#32;`seed_slot`\,&#32;under the same outer call ID\.

The reservation’s outer prompt was\:

~~~text
Allow tool: write
Path: xd://seed_slot
Content:
{"action":"reserve"}
~~~

The available responses were exactly&#32;`Approve`&#32;and&#32;`Deny`\.

After the outer gate\,&#32;`WriteTool.execute()`&#32;forwards&#32;`xdevApproved: true`&#32;when a context is available\.&#32;For unchanged inner input\,&#32;the wrapper can suppress the duplicate mode\-tier prompt\.&#32;Two wrapper layers therefore do not necessarily mean two questions\,&#32;and two events do not mean two executions\.

### Policy identity can replace the generic write fallback

Compare these recorded cases\:

| Case | Applicable policy data | Recorded outcome |
| --- | ---: | --- |
| `dispatch-fallback-deny` | No device policy\;&#32;`write: deny`\;&#32;yolo | Outer throw\;&#32;zero prompts\,&#32;zero&#32;`tool_call`&#32;events\,&#32;zero inert executions\. |
| `dispatch-specific-allow` | `seed_slot: allow`\;&#32;`write: deny`\;&#32;always\-ask\;&#32;no UI | Exec\-tier fictional action returned\;&#32;zero prompts\,&#32;two&#32;`tool_call`&#32;events\,&#32;one inert execution\. |
| `dispatch-explicit-prompt-twice` | `seed_slot: prompt`\;&#32;yolo\;&#32;two Approve answers | Two prompts\,&#32;two&#32;`tool_call`&#32;events\,&#32;one inert execution\. |
| `dispatch-inner-tool-deny` | Inner action&#32;`blocked`\;&#32;yolo | Error result with tool\-policy refusal\;&#32;one outer&#32;`tool_call`\,&#32;no inner execution\. |

The device\-specific allow is a valid replacement for the invoking\-tool fallback\.&#32;It is not an instruction to widen a real device policy\.&#32;The explicit device prompt is consulted at both outer and inner layers in the recorded path\,&#32;so the current implementation can ask twice for one action\.&#32;Do not interpret that as two successful reservations\.

The inner\-deny case explains why borrowing a tier is not borrowing the whole decision\.&#32;The outer gate permits exec under yolo\,&#32;but the inner declaration still denies\.&#32;The dispatcher converts that ordinary inner error into a result carrying&#32;`isError: true`\.&#32;A returned result is not necessarily success\.

### Know the exact forwarded\-prompt predicate

The relevant source expressions in&#32;`ExtensionToolWrapper.execute()`&#32;are\:

~~~ts
const explicitPrompt = resolved.override || Object.hasOwn(userPolicies, resolved.policyKey ?? this.tool.name);
const xdevBypass = context?.xdevApproved === true && effectiveParams === params;
~~~

An ordinary resolved prompt is required when&#32;`explicitPrompt`&#32;is true or&#32;`xdevBypass`&#32;is false\.&#32;Pending provider safety checks independently require approval\.

Two limits follow directly from this source\:

- Raw own\-property presence is not the same as a normalized valid policy\.&#32;An invalid entry can still affect this predicate for the key it checks\.
- The unchanged\-input test is object identity\,&#32;not a deep content comparison or revision hash\.&#32;The recorded replacement cases use new argument objects\.

There is also a source\-derived edge case\:&#32;an unchanged forwarded inner decision that resolves to prompt but has neither a surviving override nor an own user\-policy entry can be suppressed by this predicate\.&#32;Do not generalize the tested explicit&#32;**user**&#32;prompt case into a claim that every possible tool\-owned prompt produces an inner dialog\.&#32;This edge case is not a separately executed Dispatch Desk scenario\.

### A revised input must be explained again

In&#32;`dispatch-rewrite-prompts`\,&#32;the outer input is&#32;`inspect`\.&#32;The recording handler supplies this replacement for the inner tool\:

~~~json
{
  "action": "publish",
  "note": "Revised fictional card"
}
~~~

**Prediction\:**&#32;can the replacement ride the original read\-tier admission\?

**Recorded answer\:**&#32;it cannot use the unchanged\-input bypass\.&#32;The inner wrapper reclassifies it as exec\,&#32;prompts with the revised action\,&#32;and records one inert execution after Approve\.&#32;The returned dispatch tier is exec\.

A useful diagnostic qualification appears in the same record\:&#32;`details.xdev.args`&#32;still contains the dispatcher’s original validated&#32;`inspect`&#32;input\,&#32;while the effective tier and inert execution ledger reflect the replacement\.&#32;The tier callback updates the tier\;&#32;it is not a general rewrite of every dispatch metadata field\.&#32;Do not treat that nested metadata as a universal final\-input audit\.

`dispatch-rewrite-denies`&#32;replaces&#32;`inspect`&#32;with&#32;`blocked`\.&#32;**Recorded\:**&#32;two&#32;`tool_call`&#32;events\,&#32;no prompt\,&#32;no inert execution\,&#32;and an error result naming tool policy\.&#32;A previously admitted input does not authorize a newly denied one\.

`dispatch-malformed-json`&#32;carries&#32;`{not-json`&#32;in yolo\.&#32;**Recorded\:**&#32;one outer event\,&#32;no prompt\,&#32;no inner execution\,&#32;and an error result explaining that the device expects a JSON args object\.&#32;The outer declaration falls back to exec for malformed JSON\;&#32;yolo admitting that tier does not make the payload valid\.&#32;In a prompting mode\,&#32;the outer gate can be reached before this dispatch validation failure\.

### Ordinary paths have different rules

The device story does not replace path analysis\.

- `WriteTool`&#32;unwraps a hashline path header before classification\.&#32;SSH\-shaped targets escalate to exec before ordinary handler\-backed write classification\.
- `resolveFileWriteApprovalTier()`&#32;returns write for ordinary filesystem paths\.&#32;For recognized internal\-resource paths\,&#32;a writable handler keeps write tier\;&#32;absence of a handler write method can yield read tier\.
- `local://`&#32;can subsequently resolve to session artifact storage and be written\.&#32;Thus this classification is not proof of zero file effects\.
- A device\-only&#32;`write`&#32;transport is not a general file\-write grant\.&#32;The existing dispatch tests verify refusal of filesystem targets\,&#32;with the source’s specific active\-plan local\-sandbox exception\.&#32;A fuller displayed description alone does not relax that execution guard\.
- `ReadTool`&#32;and&#32;`GrepTool`&#32;use&#32;`pathTargetsSsh()`&#32;to escalate SSH\-containing arguments\.&#32;The substring scan can catch a remote entry before later path\-list expansion\.&#32;It does not authenticate to the host or grant access there\.

The existing&#32;`ssh-url-approval-gate.test.ts`&#32;rejects remote\-shaped read\,&#32;grep\,&#32;and write calls at the wrapper before an SSH connection\,&#32;while exercising local counterparts\.&#32;That proves the tested gate boundary\,&#32;not remote execution\.

**Paper checkpoint\:**&#32;explain&#32;`dispatch-reserve-once`&#32;as one outer prompt\,&#32;two tool\-call events\,&#32;and one inert execution\.&#32;Then explain why&#32;`dispatch-explicit-prompt-twice`&#32;has two prompts without two executions\.&#32;**Worked answer\:**&#32;the first uses forwarded duplicate\-tier suppression\;&#32;the second retains an explicit device user policy at the inner gate\.

**Failure boundary\:**&#32;this route is not a model for every nested call\.&#32;Same\-tool native delegation through&#32;`ExtensionRunner.invokeNativeTool()`&#32;calls an unwrapped native implementation without another approval gate\.&#32;Other bridges and direct calls have their own wiring\.&#32;`xd://`&#32;is a dispatch address\,&#32;not a new source of authority\.

**Source anchors\:**&#32;`packages/coding-agent/src/tools/write.ts`&#32;—&#32;`WriteTool.approval`\,&#32;`WriteTool.execute`\;&#32;`packages/coding-agent/src/tools/xdev.ts`&#32;—&#32;`parseDeviceArgs`\,&#32;`resolveXdevTool`\,&#32;`dispatchXdevTool`\;&#32;`packages/coding-agent/src/extensibility/extensions/wrapper.ts`&#32;—&#32;`ExtensionToolWrapper.execute`\;&#32;`packages/coding-agent/src/tools/path-utils.ts`&#32;—&#32;`resolveFileWriteApprovalTier`\,&#32;`pathTargetsSsh`\;&#32;`packages/coding-agent/test/write-xdev-dispatch.test.ts`\;&#32;`packages/coding-agent/test/tools/ssh-url-approval-gate.test.ts`\.

## Boundary Desk\:&#32;one call at a time

Boundary Desk begins with a deceptively small question\:&#32;what did the word Approve authorize\?

For the generic tool gate in the supplied wrapper\,&#32;the answer is the current call\.&#32;There is no built\-in choice here named “allow for session” or “always allow\,” and selecting Approve does not write a per\-tool policy\.

### Inspect the actual binary choice

The wrapper calls\:

~~~ts
choice = await uiContext.select(safetyPrompt, ["Approve", "Deny"]);
const approved = choice === "Approve";
~~~

Only the exact returned label&#32;`Approve`&#32;is positive\.&#32;`Deny`\,&#32;`undefined`\,&#32;and other nonmatching values are not approval\.&#32;A dialog exception also stops this path and is reported as a false resolution before being rethrown\.

These are the generic tool\-approval choices\.&#32;They are not Review Desk’s local review outcomes\,&#32;an ACP plan\-review menu\,&#32;or a domain delegation dialog\.

### Predict the second call

Inspect&#32;`boundary-approval-does-not-persist`\:

~~~json
{
  "approval": "exec",
  "args": { "action": "publish" },
  "mode": "always-ask",
  "choices": ["Approve", "Deny"],
  "repeat": 2
}
~~~

**Prediction\:**&#32;does the first answer eliminate the second question\?

**Recorded answer\:**&#32;no\.&#32;The same wrapped tool\,&#32;with unchanged settings\,&#32;was invoked twice\.&#32;There were two prompts and two&#32;`tool_call`&#32;events\,&#32;but only one successful call and one inert executor entry\.&#32;The second invocation threw&#32;`Tool call denied by user: seed_note`\.

The first inert result remains present in the report even though the final outcome is a throw\.&#32;That is not evidence that the second invocation ran\.&#32;Read&#32;`successfulCalls`\,&#32;the execution ledger\,&#32;and the per\-call lifecycle together\.

The simpler cases establish the alternatives independently\:

| Case | Recorded response | Prompts | Inert executions | Outcome |
| --- | ---: | --- | --- | --- |
| `boundary-approve-one-call` | Approve | 1 | 1 | return |
| `boundary-deny-one-call` | Deny | 1 | 0 | throw |
| `boundary-dismiss-one-call` | `undefined`\,&#32;represented by fixture null | 1 | 0 | throw |

A declined call is not a persisted deny policy\.&#32;Conversely\,&#32;a user\-policy deny does not require a dialog in which a person declines this particular call\.

### Follow the order\,&#32;not just the final banner

For the directly exercised wrapper path\,&#32;the useful sequence is\:

1. Consume any existing loop\-emission marker and sample approval inputs from the execute\-time context\.
2. Resolve the original input\;&#32;an effective deny stops this wrapper path before its own&#32;`tool_call`&#32;handler emission\.
3. If not already emitted by the loop\,&#32;run applicable&#32;`tool_call`&#32;handlers\,&#32;which can block or return replacement input\.
4. Resolve approval against the effective input and report an effective xdev tier when that callback exists\.
5. If approval is required\,&#32;wait for the applicable scheduled\-call preview when such a waiter is installed\.
6. Emit&#32;`tool_approval_requested`&#32;when approval lifecycle handlers are present\.
7. Obtain the selection\,&#32;or resolve false because no UI is available\.
8. Emit&#32;`tool_approval_resolved`\;&#32;execute the underlying tool only after a positive required decision\.

`ui.select`&#32;and the executor are steps in this explanation\,&#32;not extra OMP event names\.&#32;The existing&#32;`extensions-runner.test.ts`&#32;records requested → UI selection → resolved order and tests preview waiting for canonical and wire\-aliased tool names\.&#32;The private fixture report records the lifecycle pairs\,&#32;prompt data\,&#32;and executor counts through its structural adapter\;&#32;it does not exercise that transcript preview waiter\.

An approval requested event can therefore exist even when no dialog was displayed\.&#32;It records a required decision path\,&#32;not proof of a human interaction\.&#32;The empty&#32;`sessionId`&#32;in several private scenario events comes from their deliberately partial execute\-time context\,&#32;which has no session manager\.&#32;It is not a real reader’s conversation identity\.

### Reclassification has a supported scope

The wrapper’s supported replacement path applies a returned&#32;`input`&#32;before the full approval gate\.&#32;The existing runner tests verify that a revised input can become denied\,&#32;that a prompt describes revised rather than original input\,&#32;and that an xdev replacement forfeits the bypass\.

For normal model\-loop preparation\,&#32;`AgentLoopConfig.beforeToolCall`&#32;documents replacement argument revalidation and propagation into scheduling and tool\-call history\.&#32;The retained&#32;[Tools\,&#32;interception and native delegation](<https://present-sketch-tp94.here.now/chapters/extensions-tools-interception-and-native-delegation>)&#32;chapter explains the session wiring and its distinction from direct execution\.&#32;A direct&#32;`tool.execute()`&#32;call is not proof that the loop’s schema revalidation occurred\.

The runner’s handlers are not a field\-by\-field replacement pipeline\:&#32;they receive the normalized event\,&#32;and the last returned result object is retained unless a block short\-circuits\.&#32;Computer\-provider calls expose a synthetic actions\/safety\-check view\,&#32;so the wrapper does not apply ordinary returned input replacements to those execution parameters\.

Do not extend the supported replacement proof to arbitrary in\-process mutation\,&#32;to code that changes data after approval\,&#32;or to every nested executor\.&#32;The wrapper samples mode and user policies before awaiting handlers and selection\;&#32;it is not a continuous policy\-revalidation transaction while a dialog remains open\.

### Cancellation is not one universal mechanism

The recorded dismissal case and synthetic RPC cancellation both produce&#32;`undefined`\,&#32;which the wrapper refuses\.&#32;Existing runner tests also cover a throwing approval selector and cancellation while an extension\-owned&#32;`tool_call`&#32;confirmation is pending\.

Those are different seams\.&#32;The runner supplies signals and active\-work timeout accounting to extension\-handler dialogs\.&#32;The generic approval selector call shown above does not itself pass dialog options containing the execution signal\.&#32;Do not claim from these records that every external abort automatically cancels every native approval selection or defeats every possible late response\.&#32;A cancelled presentation\,&#32;a blocked tool\,&#32;and rollback of an earlier effect remain separate claims\.

**Paper checkpoint\:**&#32;after the Approve\-then\-Deny repeat case\,&#32;what is the execution count\?&#32;**Worked answer\:**&#32;one\.&#32;The second prompt did not persist or reuse the first answer\,&#32;and the denial did not undo the first inert record\.

**Failure boundary\:**&#32;the wrapper’s result hooks can transform returned content\,&#32;details\,&#32;and error presentation after execution\.&#32;They cannot undo an external effect\.&#32;An approval event\,&#32;a nonthrowing result\,&#32;or a visible success label is not a substitute for checking the operation’s own postcondition\.

**Source anchors\:**&#32;`packages/coding-agent/src/extensibility/extensions/wrapper.ts`&#32;—&#32;`ExtensionToolWrapper.execute`\;&#32;`packages/coding-agent/src/extensibility/extensions/runner.ts`&#32;—&#32;`emitToolCall`\,&#32;`markToolCallEmitted`\,&#32;`consumeToolCallEmitted`\,&#32;`waitForToolApprovalPreview`\;&#32;`packages/agent/src/types.ts`&#32;—&#32;`AgentLoopConfig.beforeToolCall`\;&#32;`packages/coding-agent/test/extensions-runner.test.ts`&#32;— tool approval lifecycle\,&#32;input replacement\,&#32;timeout\,&#32;and cancellation tests\.

## Boundary Desk\:&#32;when the host can ask

Nia next hears that RPC is headless and concludes it cannot ask for approval\.&#32;That conclusion confuses a physical terminal with a semantic UI adapter\.

The wrapper asks its&#32;**extension runner**&#32;whether UI is available\.&#32;In the supplied runner\,&#32;`hasUI()`&#32;is implemented as a comparison against the built\-in no\-op UI context\.&#32;It is not a test for a keyboard\,&#32;TTY\,&#32;window\,&#32;or working client display\.

### Separate three questions

Ask these independently\:

1. What host mode is running\?
2. Which UI context was installed on the runner or tool context\?
3. Does that adapter actually support the requested selection operation\?

The SDK’s initial&#32;`hasUI`&#32;and&#32;`interactivePrompts`&#32;options\,&#32;the tool context’s UI state\,&#32;and&#32;`ExtensionRunner.hasUI()`&#32;serve related but distinct consumers\.&#32;A boolean from one is not proof of every capability in the others\.

| Host path | Source\-backed approval surface | Important limit |
| --- | ---: | --- |
| TUI | `ExtensionUiController`&#32;installs selector\-based UI\. | Component\/controller evidence is not physical key\-delivery proof\. |
| RPC | `runRpcMode()`&#32;installs&#32;`RpcExtensionUIContext`&#32;on extension initialization\;&#32;select uses RPC request\/response helpers\. | A connected client must handle the semantic request\.&#32;Native custom components and raw terminal input are not provided\. |
| ACP | `createAcpExtensionUiContext()`&#32;translates supported dialogs into form elicitations\. | Support depends on negotiated&#32;`elicitation.form`\;&#32;other methods remain stubbed\. |
| Default print\/JSON extension initialization | No UI context is supplied to the runner\. | A required approval fails closed\;&#32;automatically allowed calls need not prompt\. |

In&#32;`runRpcMode()`\,&#32;the optional&#32;`setToolUIContext`&#32;callback is called with the adapter and true when supplied\.&#32;`main.ts`&#32;supplies that callback for&#32;`rpc-ui`\,&#32;not ordinary&#32;`rpc`\.&#32;Nevertheless\,&#32;both use the RPC adapter in&#32;`initializeExtensions()`\,&#32;which is the runner UI consulted by this approval wrapper\.&#32;This is why the initial tool\-UI flag does not justify flattening ordinary RPC into no\-UI\.

### Read the two RPC cases

`boundary-rpc-approve`&#32;and&#32;`boundary-rpc-cancel`&#32;use the same exec\-tier fictional call in always\-ask mode\.&#32;Their&#32;`ui`&#32;value is&#32;`rpc`\.

**Prediction\:**&#32;what should a positive correlated response and a cancelled response do\?

**Recorded answer\:**&#32;each produced one select request through the real&#32;`requestRpcSelect()`&#32;helper and was answered through the real&#32;`dispatchRpcControlFrame()`&#32;helper\.&#32;The positive response produced one inert execution\.&#32;The cancelled response became&#32;`undefined`\,&#32;produced no execution\,&#32;and threw the user\-denial error\.&#32;Both left zero pending requests in the helper map\.

This is stronger than merely printing an expected frame\:&#32;the supplied verifier exercised the actual helper functions and wrapper\.&#32;It is narrower than launching a full RPC process or proving a real client rendered a dialog\.&#32;The runner itself was a structural adapter\,&#32;and the responses were supplied in\-process\.

The source also gives&#32;`RpcPendingExtensionRequests.rejectAll()`&#32;a disconnect failure path\.&#32;That is source\-backed here\,&#32;not an additional workbook disconnect scenario\.

### ACP requires capability\-specific language

The ACP adapter checks&#32;`clientCapabilities.elicitation.form`\.&#32;Without form support\,&#32;`select()`&#32;returns&#32;`undefined`\,&#32;`confirm()`&#32;returns false\,&#32;and editor\/input paths return their unavailable values\.

`AcpAgent.#configureExtensions()`&#32;still passes a distinct UI adapter to the extension runner\.&#32;As a result\,&#32;the runner can report&#32;`hasUI: true`&#32;even when a particular form operation is unavailable\.&#32;For a required generic tool approval\,&#32;that unavailable selection can be refused as a non\-Approve response rather than taking the runner’s literal no\-UI branch\.

This is not evidence that a person selected Deny\.&#32;It is an adapter capability boundary\.&#32;The supplied ACP setup also marks the tool UI available through its callback when form support is present\,&#32;and exposes extension mode as&#32;`rpc`\;&#32;neither means terminal components work\.

ACP plan\-proposal approval has its own control flow and capability fallback\.&#32;It is not the generic two\-choice tool gate\.&#32;Do not borrow its behavior or menu to explain a&#32;`tool_approval_requested`&#32;event\.

### No UI is a reason to stop\,&#32;not permission to skip

Compare&#32;`boundary-no-ui`&#32;with&#32;`dispatch-inspect-no-ui`\.&#32;Both have no UI\.&#32;The first needs a prompt and refuses\;&#32;the second is read\-tier at the applicable gates and returns without a prompt\.&#32;Absence of UI does not automatically deny all tools\,&#32;and it does not automatically approve a required prompt\.

The stock no\-UI error lists configuration\-widening possibilities\.&#32;This workbook does not recommend them as the generic repair\.&#32;First establish why a prompt is required\,&#32;whether the operation is still intended\,&#32;and whether the owning host has a supported approval surface\.&#32;On this route\,&#32;simply record the refusal and continue reading\.

**Paper checkpoint\:**&#32;is&#32;`hasUI: true`&#32;sufficient proof that a native panel or an ACP form can be used\?&#32;**Worked answer\:**&#32;no\.&#32;It can indicate only a non\-no\-op adapter\.&#32;Check the specific mode and negotiated operation capability\.

**Failure boundary\:**&#32;no physical terminal\,&#32;keyboard\,&#32;full RPC client process\,&#32;or ACP client flow was exercised by the new private scenarios\.&#32;Transport\-helper success does not upgrade itself into those claims\.

**Source anchors\:**&#32;`packages/coding-agent/src/extensibility/extensions/runner.ts`&#32;—&#32;`hasUI`\,&#32;`initialize`\;&#32;`packages/coding-agent/src/modes/rpc/rpc-mode.ts`&#32;—&#32;`runRpcMode`\,&#32;`requestRpcSelect`\,&#32;`requestRpcDialog`\,&#32;`dispatchRpcControlFrame`\,&#32;`RpcPendingExtensionRequests`\;&#32;`packages/coding-agent/src/modes/acp/acp-agent.ts`&#32;—&#32;`createAcpExtensionUiContext`\,&#32;`AcpAgent.#configureExtensions`\;&#32;`packages/coding-agent/src/modes/runtime-init.ts`&#32;—&#32;`initializeExtensions`\;&#32;`packages/coding-agent/src/modes/print-mode.ts`&#32;—&#32;`runPrintMode`\;&#32;`packages/coding-agent/src/main.ts`&#32;—&#32;`runRootCommand`\.

## Configuration and launch precedence

A displayed setting answers a configuration question\.&#32;It does not necessarily answer the wrapper’s final policy question\.

Nia’s fictional settings view says always\-ask\,&#32;yet an exec\-tier recording call proceeds without a question\.&#32;Before assuming a broken gate\,&#32;she checks whether the launch supplied&#32;`autoApprove`&#32;separately\.

### Keep persisted configuration and runtime overrides separate

The supplied&#32;`Settings`&#32;implementation merges the active profile’s persisted layer\,&#32;project settings\,&#32;explicit configuration overlays\,&#32;and runtime overrides\,&#32;in that increasing order of precedence\.&#32;`get()`&#32;resolves a value from the merged view\,&#32;falling back to the schema default when it is absent\.

`Settings.set()`&#32;changes the persisted layer and queues saving\.&#32;`Settings.override()`&#32;changes a non\-persisted runtime layer\.&#32;An ordinary session transition is not a factory reset of those settings\.

For well\-formed object layers\,&#32;maps are deep\-merged\;&#32;arrays such as&#32;`bash.patterns`&#32;are replaced by the higher layer rather than concatenated\.&#32;Therefore the effective ordered rule list must be inspected as a list\,&#32;not imagined as every rule from every file in sequence\.

The wrapper reads mode and user policies from its execute\-time&#32;`context.settings`\.&#32;A tool’s own classifier can also read settings bound to its tool instance—for example\,&#32;`BashTool.approval()`&#32;reads its session’s&#32;`bash.patterns`\.&#32;A dump from an unrelated settings instance cannot establish both inputs\.

### Read the isolated CLI observation

The supplied configuration report used the installed&#32;**`omp/18.0.7`**&#32;CLI\,&#32;an exclusively owned fresh named profile with no linked authentication\,&#32;and an empty temporary working directory\.&#32;It did not inspect or change the reader’s personal settings\.

This section reproduces the observation\,&#32;not an executable recipe\.&#32;The report does not include a complete profile\-creation preamble\,&#32;so no launch or profile\-creation command is invented here\.&#32;The default route is to read the recorded roundtrip\.

Setting&#32;`tools.approvalMode`&#32;to&#32;`write`&#32;with JSON output returned\:

~~~json
{"key":"tools.approvalMode","value":"write"}
~~~

Setting the&#32;`tools.approval`&#32;record to the JSON object&#32;`{"bash":"deny"}`&#32;returned\:

~~~json
{"key":"tools.approval","value":{"bash":"deny"}}
~~~

The saved configuration was YAML\:

~~~yaml
tools:
  approvalMode: write
  approval:
    bash: deny
~~~

Getting each setting with JSON output returned an object\,&#32;not merely its value\:

~~~json
{
  "key": "tools.approvalMode",
  "value": "write",
  "type": "enum",
  "description": "Default approval behavior for tool calls. 'Always ask' auto-approves read-only tools only. 'Write' auto-approves read and workspace-write tools. 'Yolo' auto-approves all tiers; user policy may still prompt or block."
}
~~~

~~~json
{
  "key": "tools.approval",
  "value": { "bash": "deny" },
  "type": "record",
  "description": "Per-tool approval policies. Set to 'allow' to auto-approve, 'prompt' to require confirmation, or 'deny' to block. Overrides are honored in every approval mode."
}
~~~

Those descriptions are the observed CLI copy\.&#32;Their shorthand about read\-only\/workspace\-write behavior does not establish containment or override the more precise resolver precedence\.&#32;The supplied capture also contains wall\-time annotations\;&#32;those are not fields in either JSON setting object\.

**Recorded boundary\:**&#32;the roundtrip demonstrates configuration serialization and inspection\.&#32;It does not demonstrate an approval dialog\,&#32;a live session override\,&#32;effective wrapper policy\,&#32;or a model invocation\.&#32;`commands/config.ts`&#32;delegates to&#32;`runConfigCommand()`\;&#32;the full delegated CLI implementation is not in this selected pack\,&#32;so the observed JSON shapes are kept attached to the report\.

### Launch flags add a second source of precedence

The supported built\-in long forms are&#32;`--approval-mode`\,&#32;`--auto-approve`\,&#32;and&#32;`--yolo`\.&#32;The latter two set&#32;`autoApprove: true`\.&#32;There is no listed built\-in&#32;`-y`&#32;alias in the supplied parser\.

These are flag fragments to interpret\,&#32;not commands to launch for this exercise\:

| Launch input | Settings value after root\-command override | Wrapper mode when the corresponding context is supplied |
| --- | ---: | --- |
| No approval flag | Existing resolved setting | That setting\,&#32;with the wrapper’s documented fallback if absent |
| `--approval-mode always-ask` | always\-ask\,&#32;runtime only | always\-ask |
| `--auto-approve`&#32;or&#32;`--yolo`\,&#32;without an explicit mode | yolo\,&#32;runtime only | yolo |
| `--approval-mode always-ask`&#32;together with&#32;`--auto-approve`&#32;or&#32;`--yolo` | always\-ask remains the displayed runtime setting | yolo\,&#32;because&#32;`context.autoApprove === true`&#32;wins in the wrapper |

`main.ts`&#32;deliberately preserves the explicit mode in settings when both inputs exist\.&#32;`buildSessionOptions()`&#32;separately forwards&#32;`autoApprove`\,&#32;and the SDK supplies it in the tool context\.&#32;The wrapper chooses yolo from that boolean before calling&#32;`resolveApproval()`\.

Changing the order of those two different flags is not a way to make the explicit mode outrank the boolean at the wrapper\.&#32;They populate separate inputs\.

**Recorded comparison\:**&#32;`boundary-auto-approve-mode`&#32;injected configured always\-ask\,&#32;`autoApprove: true`\,&#32;and no UI\.&#32;It returned with zero prompts and one inert execution\.&#32;`boundary-auto-approve-still-denied`&#32;added an effective user deny and stopped before handlers\,&#32;prompts\,&#32;or execution\.&#32;These cases verify execute\-time wrapper behavior\,&#32;not CLI parsing or a full launch\.

### Invalid input does not create a new safe mode

The&#32;`--approval-mode`&#32;setter accepts exactly the three documented values\.&#32;For an invalid supplied value it logs a warning and does not install that value as the parsed mode\.&#32;That is not the same as a hard launch refusal\,&#32;nor proof that it selected always\-ask\.&#32;Other parsed inputs and the resolved settings remain relevant\.

Similarly\,&#32;a type annotation on&#32;`Settings.get()`&#32;is not runtime validation of every hand\-edited value\.&#32;Keep the resolver’s normalization of individual policy strings separate from raw record shape\,&#32;launch parsing\,&#32;and other consumers\.

**Paper checkpoint\:**&#32;a settings view says always\-ask\,&#32;but the execute\-time context has&#32;`autoApprove: true`\.&#32;What mode does this wrapper use\,&#32;and can an effective deny remain\?&#32;**Worked answer\:**&#32;yolo\;&#32;yes\,&#32;the resolver’s explicit deny branches still apply\.

**Failure boundary\:**&#32;no configuration change is required to finish this chapter\.&#32;Do not use a personal profile to recreate the observation or treat a config getter as a diagnostic endpoint for another process’s effective policy\.

**Source anchors\:**&#32;`packages/coding-agent/src/config/settings.ts`&#32;—&#32;`Settings.get`\,&#32;`set`\,&#32;`override`\,&#32;`#rebuildMerged`\;&#32;`packages/coding-agent/src/config/settings-schema.ts`&#32;—&#32;`tools.approval`\,&#32;`tools.approvalMode`\,&#32;`bash.patterns`\;&#32;`packages/coding-agent/src/commands/config.ts`&#32;—&#32;`Config.run`\;&#32;`packages/coding-agent/src/cli/args.ts`&#32;—&#32;`parseArgs`\;&#32;`packages/coding-agent/src/cli/flag-tables.ts`&#32;—&#32;`STRING_SETTERS["--approval-mode"]`\,&#32;`VALUELESS_FLAGS`\;&#32;`packages/coding-agent/src/main.ts`&#32;—&#32;`runRootCommand`\,&#32;`buildSessionOptions`\;&#32;`packages/coding-agent/src/sdk.ts`&#32;— execute\-time context construction\.&#32;CLI observations\:&#32;`proof/cli-config-proof.json`\.

## Subagents and inherited policies

The preceding Tan part established that a conversation fork is not a cloned runtime\.&#32;Permission construction supplies a concrete reason\.

Nia expects an unattended child to reproduce Main’s interactive approval mode\.&#32;The initial helper does something more specific\:&#32;it snapshots settings\,&#32;applies child defaults\,&#32;and permits explicit helper overrides afterward\.

### Read the helper in its actual callers

`createSubagentSettings()`&#32;in&#32;`packages/coding-agent/src/task/executor.ts`&#32;reads every key in&#32;`SETTINGS_SCHEMA`&#32;from the base settings\.&#32;It creates an in\-memory settings instance with that snapshot\,&#32;then applies these defaults before spreading any explicit overrides\:

- `tools.approvalMode: "yolo"`\;
- `advisor.enabled: false`\.

It also passes the base storage handle to the isolated settings instance\.&#32;“In\-memory settings overrides” therefore does not mean every shared runtime resource or storage service has been duplicated or removed\.

`runSubprocess()`&#32;uses this helper for Task construction\.&#32;**`TanCommandController.start()`&#32;uses the same helper\.**&#32;Its call is&#32;`createSubagentSettings(this.ctx.settings)`\,&#32;with no explicit approval\-mode override at that call site\.

The initial Tan SDK construction sets&#32;`hasUI: false`&#32;and disables extension discovery\.&#32;It passes enabled\-tool names and other captured context through specific options\;&#32;it does not pass Main’s live extension instances as a cloned safeguard set\.&#32;Supplied MCP proxies and custom\-tool discovery are separate mechanisms\.

### Predict a child from a small configuration

Use this fictional base configuration on paper\:

~~~json
{
  "tools.approvalMode": "always-ask",
  "tools.approval": { "bash": "deny" }
}
~~~

**Prediction\:**&#32;after the helper’s ordinary defaults\,&#32;must the child prompt for every exec\-tier call\?&#32;Does bash become allowed\?

**Source\-backed worked answer\:**&#32;the child settings mode defaults to yolo\,&#32;so tier\-only exec prompting is not inherited unchanged\.&#32;The per\-tool policy record remains relevant\:&#32;an effective bash deny still resolves to denial\.&#32;A user prompt policy can likewise leave a child needing a UI it does not have\,&#32;subject to the resolver’s tool\-policy precedence\.

This example is a source\-derived calculation\,&#32;not an executed child scenario\.&#32;None of the 46 private cases launches Task or Tan\.

### Separate three meanings of inheritance

| Layer | What is established | What not to infer |
| --- | ---: | --- |
| Initial helper defaults | Snapshot values are used\,&#32;then yolo and advisor\-off defaults are applied\. | The parent’s interactive mode was copied unchanged\. |
| Explicit helper overrides | The final&#32;`overrides`&#32;spread can replace the helper’s default mode\. | Ordinary Tan launch supplies such an override\,&#32;or a new Tan flag exists\. |
| Live runtime inputs | The wrapper samples its execute\-time settings and autoApprove\;&#32;specific shared callbacks\/resources can remain live\. | Every later parent setting or safeguard automatically mirrors into every child\. |

The helper’s optional inherited service\-tier argument is another example of an explicit input\.&#32;It concerns provider service tiers\,&#32;not the read\/write\/exec approval tiers\.&#32;Likewise\,&#32;the SDK can carry a live extension\-root provider for certain child construction paths\.&#32;Neither establishes a universal live approval\-policy inheritance mechanism\.

If another host later applies runtime inheritance\,&#32;that is a separate transition to inspect\.&#32;An initial helper result is not proof that a child remains permanently at that value\,&#32;and a conversation relationship is not proof of a later synchronization\.&#32;The complete&#32;`packages/coding-agent/src/session/agent-session.ts`&#32;implementation and host\-specific live inheritance adapters are outside this selected permissions pack\;&#32;no blanket statement about those transitions is warranted\.

### Parent approval is not an OS delegation

The helper’s source explains the unattended design in terms of the parent Task approval boundary\.&#32;That does not make the child a sandbox or erase its per\-tool policies\.&#32;It also does not make a human&#32;`/tan`&#32;command identical to a model\-issued Task call\:&#32;they are different entry surfaces\.

A “do not edit” assignment remains behavioral guidance\.&#32;The child’s actual tool surface\,&#32;policy record\,&#32;domain rules\,&#32;and host authority determine what it can do\.&#32;Focusing a headlessly created Tan later is not evidence that it was reconstructed with every interactive facility\.

For the original launch and lifecycle qualifications\,&#32;retain&#32;[Start from Main](<https://present-sketch-tp94.here.now/chapters/tan-2-start-from-main>)&#32;and&#32;[Interrupt\,&#32;cancel\,&#32;or kill](<https://present-sketch-tp94.here.now/chapters/tan-8-interrupt-cancel-or-kill>)\.&#32;Their historical observations are not rerun by this chapter\.

**Paper checkpoint\:**&#32;locate the Tan helper call\,&#32;then the helper’s merge order\.&#32;Explain how an explicit helper override could differ from the ordinary Tan call without inventing a CLI or slash\-command option\.&#32;**Worked answer\:**&#32;the helper API accepts overrides after its defaults\;&#32;the supplied Tan call does not provide them\.

**Failure boundary\:**&#32;neither a conversation fork\,&#32;a settings snapshot\,&#32;nor shared storage proves identical live permissions\,&#32;loaded safeguards\,&#32;remote authority\,&#32;or cross\-process enforcement\.

**Source anchors\:**&#32;`packages/coding-agent/src/task/executor.ts`&#32;—&#32;`createSubagentSettings`\,&#32;`runSubprocess`\,&#32;`createMCPProxyTools`\;&#32;`packages/coding-agent/src/modes/controllers/tan-command-controller.ts`&#32;—&#32;`TanCommandController.start`\;&#32;`packages/coding-agent/src/sdk.ts`&#32;—&#32;`CreateAgentSessionOptions`\,&#32;`createAgentSessionScoped`\;&#32;`packages/coding-agent/src/extensibility/extensions/wrapper.ts`&#32;— execute\-time approval inputs\.

## Approval is not a sandbox

Boundary Desk ends by separating two claims that often get compressed into one\:&#32;“the application admitted the call” and “the environment allowed the effect\.”

Approval is an application\-level decision\.&#32;It neither changes the process’s OS authority nor certifies every action the implementation may perform\.&#32;A successful call can still have effects outside the boundary its name suggests\,&#32;and an approved call can still fail later\.

### Provider safety is an independent required decision

Inspect&#32;`boundary-provider-check-no-ui`\.&#32;It combines a read declaration\,&#32;yolo\,&#32;`autoApprove: true`\,&#32;user allow\,&#32;no UI\,&#32;and synthetic pending computer safety metadata\.

**Prediction\:**&#32;does any of that automatic approval acknowledge the pending safety check\?

**Recorded answer\:**&#32;no\.&#32;The wrapper emitted an approval lifecycle pair ending false\,&#32;executed nothing\,&#32;and reported that pending provider safety checks had no interactive UI\.&#32;`providerSafetyApproved`&#32;remained false\.

In&#32;`boundary-provider-check-approved`\,&#32;the recording adapter selected Approve\.&#32;The prompt included&#32;`Fictional seed shelf check`\;&#32;the context flag became true and the inert executor recorded one call\.

The source makes pending checks independently require approval\,&#32;regardless of yolo\,&#32;ordinary user allow\,&#32;or forwarded xdev admission\.&#32;It obtains computer actions and pending checks from provider metadata\,&#32;not by treating arbitrary prose as a safety grant\.

The proof here is deliberately narrow\:&#32;synthetic metadata passed through the actual wrapper\.&#32;No provider delivered a request\,&#32;no screenshot was taken\,&#32;and no desktop input occurred\.&#32;This local acknowledgement mechanism is also not the entirety of a provider’s safety policy or refusal behavior\.

### An OS error arrives at another seam

Now inspect&#32;`boundary-fallback-no-handler`\.&#32;The actual&#32;`writeFileWithFallback()`&#32;helper received an injected BunFile\-shaped primitive whose&#32;`write()`&#32;throws a synthetic&#32;`EACCES`&#32;error\.&#32;No fallback handler was registered\.

**Recorded answer\:**&#32;one primitive attempt\,&#32;zero successful effects\,&#32;and the identical error object rethrown\.&#32;There was no privileged channel and no recovered write\.

Compare the two error\-classification cases\:

| Case | Error data | Recorded classification |
| --- | ---: | --- |
| `boundary-fallback-code-precedence` | Structured code&#32;`ENOENT`\;&#32;message mentions a path containing&#32;`EACCES` | Not permission\-denied\. |
| `boundary-fallback-message` | No structured code\;&#32;Error message contains&#32;`EACCES` | Permission\-denied\. |

`isPermissionDeniedError()`&#32;treats a structured code as authoritative\.&#32;Only when that structured information is absent does its message fallback apply\.&#32;A word in a pathname is not an OS denial\,&#32;and a classified denial is not an acquired grant\.

### What a file fallback contract actually provides

The source supports a later host seam for selected native ordinary\-file byte writes and unlinks after permission errors\.&#32;Direct&#32;`EPERM`\,&#32;`EACCES`\,&#32;and&#32;`EROFS`&#32;can qualify\.&#32;A registered write fallback also enables a special check for a denied parent\-directory creation that Bun initially presents as&#32;`ENOENT`\;&#32;an ordinary missing or invalid path is not automatically a permission boundary\.

The destination is resolved according to the primitive’s semantics\.&#32;Writes follow the final symlink target\;&#32;unlinks leave the final link itself as the object to remove\.&#32;An unverifiable write destination is not handed to a handler\.&#32;Delete handlers use a separate registry\,&#32;and&#32;`confirmedFile: false`&#32;does not authorize recursive removal\.

Those registries are process\-wide\.&#32;A request’s origin session can differ from the session that registered a handler and owns its UI\.&#32;Returning true tells native code to proceed as though the supported primitive succeeded\;&#32;it is not itself proof that bytes became durable\.

These are host contracts to understand\,&#32;**not a broker exercise for this route**\.&#32;The existing&#32;[Permission\-denied file fallbacks](<https://present-sketch-tp94.here.now/chapters/extensions-permission-denied-file-fallbacks>)&#32;chapter retains the complete adapter and its missing real\-broker prerequisites\.&#32;No elevated writer is supplied here\.

### Several effects remain outside this particular seam

A native byte\-write fallback is not exhaustive syscall interception\.&#32;It does not universally cover arbitrary extension filesystem calls\,&#32;shell or subprocess writes\,&#32;archive\-member rewrites\,&#32;SQLite row operations\,&#32;ACP client\-side writes\,&#32;independent LSP workspace edits\,&#32;or formatter subprocess mutations\.

Similarly\,&#32;the approval wrapper is not a restriction on arbitrary imported JavaScript\.&#32;The supplied&#32;`isProjectTrusted()`&#32;compatibility method returns true\;&#32;it does not establish a per\-directory consent prompt or OS sandbox\.&#32;Tool selection\,&#32;approval\,&#32;and trust in loaded code must remain separate subjects\.

Canonical path handling reduces particular misrouting risks\.&#32;Do not promote it into a general claim that every race\,&#32;host process\,&#32;network route\,&#32;or external side effect is constrained by the same policy\.

The private verifier’s launcher used a clean environment and an OS sandbox with named restrictions\.&#32;Those restrictions describe that evidence\-producing child\,&#32;not an OMP feature installed for the reader and not a full sandbox validation suite\.&#32;Its effect counters cover instrumented seams\,&#32;not every possible effect of arbitrary imports\.

**Paper checkpoint\:**&#32;can Approve repair&#32;`EACCES`\,&#32;and does a fallback returning true independently prove durable bytes\?&#32;**Worked answer\:**&#32;neither\.&#32;Approval admits an application call\.&#32;The OS\/host still governs the primitive\;&#32;a handler’s success is a contract that a real implementation must satisfy and verify\.

**Failure boundary\:**&#32;the private fallback case was injected\,&#32;while the existing focused tests include local kernel\-permission fixtures and stand\-ins\.&#32;Neither establishes a real privileged broker or blanket OS isolation\.

**Source anchors\:**&#32;`packages/coding-agent/src/extensibility/extensions/wrapper.ts`&#32;—&#32;`computerSafetyChecks`\,&#32;`approvalArgs`\,&#32;`ExtensionToolWrapper.execute`\;&#32;`packages/coding-agent/src/tools/file-write-fallback.ts`&#32;—&#32;`isPermissionDeniedError`\,&#32;`writeFileWithFallback`\,&#32;`deleteFileWithFallback`\,&#32;`withFileMutationSession`\;&#32;`packages/coding-agent/src/tools/path-utils.ts`&#32;—&#32;`resolveSyscallTarget`\;&#32;`packages/coding-agent/src/extensibility/extensions/runner.ts`&#32;—&#32;`createContext`\,&#32;fallback initialization\/disposal\.

## Recovery without widening permission

Nia can now replace “permissions are broken” with a specific account\:&#32;the target\,&#32;the applicable gate\,&#32;the observed refusal or admission\,&#32;and the effect count\.

Recovery begins with that account\.&#32;It does not begin by changing the whole session to yolo\.

### Start with the exact observed target and current scope

Before any later retry\,&#32;establish\:

1. The current persistent conversation and cwd when relevant—not merely a title from an old screenshot\.
2. The exact tool and call\,&#32;including any outer&#32;`write`&#32;envelope and inner device name\.
3. The original arguments\,&#32;supported replacements\,&#32;and actual destination or action under review\.
4. The tool declaration\,&#32;effective policy key\,&#32;mode\,&#32;and execute\-time autoApprove input\.
5. The host’s relevant UI capability and any independent provider safety requirement\.
6. Whether execution occurred\,&#32;which result belongs to which call\,&#32;and which effects remain uninspected\.

On the paper route\,&#32;use only the supplied fictional records\.&#32;If a fact is not in the record\,&#32;mark it unknown\.&#32;Do not create certainty by substituting your own settings or making a provider request\.

### Use the recovery matrix

| Observation | Boundary to investigate | Bounded recovery | What not to do |
| --- | ---: | --- | --- |
| Tool absent or&#32;`No such tool` | Enabled registry and actual direct\/mounted route | Confirm the exact known name and current availability\;&#32;use an already available appropriate surface or report the missing capability\. | Assume a user allow installs or discovers a tool\. |
| `blocked by tool policy` | The declaration’s effective deny\,&#32;including a selected bash deny | Read the exact reason and arguments\;&#32;preserve the refusal unless the responsible policy owner identifies an authorized correction\. | Expect yolo or a generic user allow to erase a tool deny\. |
| `blocked by user policy` | The effective normalized key\,&#32;including dispatcher fallback | Identify whether the key is the device or invoking tool\;&#32;compare the policy with the intended scope\. | Remove unrelated denies or treat the stock hint as authorization to change policy\. |
| `Tool call denied by user`&#32;after a selection | One\-call response\,&#32;dismissal\,&#32;or adapter returning no positive value | Retain the declined outcome\;&#32;reconsider the operation and input before any separately authorized retry\. | Treat it as a persisted deny\,&#32;or retry just to pressure another answer\. |
| Required approval but no UI | Runner UI installation and required operation | Stop\;&#32;identify whether the owning host has an appropriate supported approval surface\.&#32;Reading the refusal completes this workbook case\. | Widen the mode merely to make unattended execution continue\. |
| Prompt or result refers to revised input | Supported handler replacement and reclassification | Review the effective operation again\;&#32;correlate prompt\,&#32;tier\,&#32;and execution evidence\. | Approve by remembering the original input or assume every metadata field was rewritten\. |
| Domain stale\-revision refusal | The domain’s own object and revision contract | Inspect the current object and reconsider the action\. | Substitute a fresh token into an unchanged request without review\. |
| Malformed device JSON or schema error | Envelope decoding and inner validation | Compare the fictional payload with the actual schema\;&#32;repair only the intended input in a separately authorized real workflow\. | Loosen approval policy to repair invalid data\. |
| Pending provider safety checks | Provider metadata and explicit acknowledgement path | Keep the required decision separate from ordinary allow\/yolo behavior\;&#32;stop if it cannot be obtained appropriately\. | Treat autoApprove or xdev forwarding as acknowledgement\. |
| `EPERM`\,&#32;`EACCES`\,&#32;or&#32;`EROFS`&#32;after execution begins | Actual OS\/host primitive and resolved target | Preserve the error and relevant cause\;&#32;hand the exact authorized operation to the host’s normal diagnostic process outside this workbook\. | Assume another tool approval grants privilege or activate an elevated writer as practice\. |
| Extension handler block or timeout | Pre\-execution handler and its active\-work failure | Identify the failing handler and preserve the blocked call\;&#32;investigate compatibility or the supported domain path\. | Interpret a stalled gate as silent consent\. |
| A call ran without asking | The full resolution path | Check tier defaults\,&#32;tool allow\,&#32;selected rule\,&#32;policy key\,&#32;launch autoApprove\,&#32;child construction\,&#32;and supported forwarding\. | Infer either safety or a bypass from zero prompts alone\. |

The matrix deliberately distinguishes a policy denial from a declined call\.&#32;It also distinguishes a stale domain object from a handler\-revised tool input\:&#32;the generic approval wrapper is not itself a domain revision system\.

### Worked recovery\:&#32;an unexpected no\-prompt result

Read&#32;`dispatch-specific-allow`&#32;again\.&#32;The outer name is&#32;`write`\,&#32;generic write policy is deny\,&#32;and the fictional action is exec\-tier\.&#32;A superficial account says the gate ignored deny\.

A complete account says\:

- The target is&#32;`xd://seed_slot`&#32;and the outer declaration supplies that policy key\.
- The valid&#32;`seed_slot: allow`&#32;replaces the invoking&#32;`write`&#32;fallback\.
- The inner declaration does not deny this fictional action\;&#32;its user allow applies\.
- The report records zero prompts and one inert execution\,&#32;not a real publication\.

The immediate repair is the explanation\.&#32;Whether a real device\-specific allow is intended is a separate policy\-owner decision\.&#32;It is not permission to rewrite the reader’s configuration\.

Now compare&#32;`boundary-auto-approve-mode`\.&#32;Its no\-prompt result has another cause\:&#32;execute\-time autoApprove selects wrapper yolo despite configured always\-ask\.&#32;Do not diagnose it as a device\-policy issue\.

### Worked recovery\:&#32;an error result after outer admission

In&#32;`dispatch-inner-tool-deny`\,&#32;the outer transport reaches dispatch\.&#32;The inner policy then refuses\,&#32;and the dispatcher returns an error result\.&#32;There is one outer&#32;`tool_call`&#32;and no inert inner execution\.

A retry with the same denied action is not a missing\-dialog repair\.&#32;The operator must address the actual inner\-policy reason or leave the action blocked\.&#32;A transport return and an outer event are not evidence that the inner effect occurred\.

For a later real file error\,&#32;inspect the relevant effect before retrying\.&#32;Approval and cancellation are not rollback\,&#32;and a sequence of primitives can have partial results\.&#32;The prior Tan and Continuity chapters retain their own phase\-aware recovery boundaries\;&#32;this permission chapter does not replace them with a universal transaction guarantee\.

### Finish the exercise without resetting anything

Nothing must be cleared\,&#32;dropped\,&#32;or reset to complete these cases\.&#32;The fixtures are reading material\.&#32;Retain your written predictions and corrections if useful\.&#32;Do not use session resets\,&#32;memory deletion\,&#32;policy removal\,&#32;or personal\-profile changes as workbook cleanup\.

**Paper checkpoint\:**&#32;write a three\-sentence recovery note for&#32;`boundary-no-ui`\,&#32;naming the fictional target\,&#32;the actual stopped gate\,&#32;and the effect count\.&#32;**Worked answer\:**&#32;the exec\-tier&#32;`seed_note`&#32;call requires approval in always\-ask\.&#32;The supplied runner has no UI\,&#32;so the wrapper resolves false before execution\.&#32;The inert execution count is zero\;&#32;changing mode is not required to learn or explain the result\.

**Failure boundary\:**&#32;a useful diagnosis may end at an unavailable host capability or missing observation\.&#32;That is more accurate than manufacturing success through a wider grant\.

**Source trail\:**&#32;`packages/coding-agent/src/tools/approval.ts`&#32;—&#32;`denyError`\;&#32;`packages/coding-agent/src/extensibility/extensions/wrapper.ts`&#32;— refusal and selection paths\;&#32;`packages/coding-agent/src/tools/xdev.ts`&#32;— dispatch errors\;&#32;`packages/coding-agent/src/tools/file-write-fallback.ts`&#32;— primitive failure handling\.&#32;For separate domain recovery\,&#32;see&#32;[Review Desk](<https://present-sketch-tp94.here.now/chapters/extensions-review-desk-edit-and-decide-locally>)\;&#32;for session\-state boundaries\,&#32;see&#32;[Decision Desk resetting deliberately](<https://present-sketch-tp94.here.now/chapters/continuity-decision-desk-resetting-deliberately>)\.

## Evidence and limitations

A permission explanation is useful only when its evidence reaches the claimed boundary\.&#32;This part combines source reading\,&#32;two runs of existing focused tests\,&#32;a separate private scenario report executed by Main\,&#32;and an isolated installed\-CLI configuration observation\.&#32;Those are four distinguishable evidence sources\,&#32;not one end\-to\-end product test\.

### Source snapshot and exact anchors

The new permissions source snapshot is dated&#32;**30 August 2026**\.&#32;Older Memory\,&#32;Tan\,&#32;Extension\,&#32;and Continuity reports retain their original dates and qualifications\.&#32;A shared version string does not establish identical custom APIs or behavior across installations\.

| Subject | Supplied source anchors |
| --- | ---: |
| Declaration and loop contracts | `packages/agent/src/types.ts`&#32;—&#32;`ToolTier`\,&#32;`ToolApprovalDecision`\,&#32;`ToolApproval`\,&#32;`AgentLoopConfig.beforeToolCall` |
| Resolution and provenance | `packages/coding-agent/src/tools/approval.ts`&#32;—&#32;`normalizeDecision`\,&#32;`normalizePolicy`\,&#32;`resolveToolTier`\,&#32;`resolveApproval`\,&#32;`requiresApproval`\,&#32;`denyError` |
| Per\-call gate and forwarding | `packages/coding-agent/src/extensibility/extensions/wrapper.ts`&#32;—&#32;`ExtensionToolWrapper.execute`\,&#32;`approvalArgs`\,&#32;`computerSafetyChecks` |
| Runner\/UI and handler behavior | `packages/coding-agent/src/extensibility/extensions/runner.ts`&#32;—&#32;`initialize`\,&#32;`hasUI`\,&#32;`emitToolCall`\,&#32;marker methods\,&#32;preview waiter\,&#32;`invokeNativeTool` |
| Approval event and UI types | `packages/coding-agent/src/extensibility/extensions/types.ts`&#32;—&#32;`ToolApprovalRequestedEvent`\,&#32;`ToolApprovalResolvedEvent`\,&#32;`ExtensionUIContext` |
| Shell classification | `packages/coding-agent/src/tools/bash.ts`&#32;—&#32;`BashTool.approval`\,&#32;ordered\-rule helpers\,&#32;`CRITICAL_BASH_PATTERNS` |
| Path\/device classification | `packages/coding-agent/src/tools/write.ts`&#32;—&#32;`WriteTool.approval`\,&#32;`execute`\;&#32;`tools/xdev.ts`&#32;—&#32;`resolveXdevTool`\,&#32;`parseDeviceArgs`\,&#32;`dispatchXdevTool`\;&#32;`tools/path-utils.ts`&#32;—&#32;`pathTargetsSsh`\,&#32;`resolveFileWriteApprovalTier` |
| Nonuniform read effects | `packages/coding-agent/src/tools/read.ts`&#32;—&#32;`ReadTool`\;&#32;`tools/grep.ts`&#32;—&#32;`GrepTool`\;&#32;`tools/hub/index.ts`&#32;—&#32;`hubApproval`\,&#32;`HubTool.execute`\;&#32;`tools/manage-skill.ts`&#32;—&#32;`ManageSkillTool`\;&#32;`tools/eval.ts`&#32;—&#32;`EvalTool` |
| Configuration and launch | `packages/coding-agent/src/config/settings.ts`&#32;—&#32;`Settings`\;&#32;`config/settings-schema.ts`&#32;— approval settings\;&#32;`commands/config.ts`&#32;—&#32;`Config.run`\;&#32;`cli/args.ts`&#32;—&#32;`parseArgs`\;&#32;`cli/flag-tables.ts`&#32;— approval setters\;&#32;`main.ts`&#32;—&#32;`runRootCommand`\,&#32;`buildSessionOptions`\;&#32;`sdk.ts`&#32;— session\/context construction |
| Protocol and print hosts | `packages/coding-agent/src/modes/rpc/rpc-mode.ts`&#32;—&#32;`runRpcMode`\,&#32;select\/control helpers\;&#32;`modes/acp/acp-agent.ts`&#32;—&#32;`createAcpExtensionUiContext`\,&#32;`#configureExtensions`\;&#32;`modes/runtime-init.ts`&#32;—&#32;`initializeExtensions`\;&#32;`modes/print-mode.ts`&#32;—&#32;`runPrintMode`\;&#32;`modes/controllers/extension-ui-controller.ts`&#32;—&#32;`ExtensionUiController` |
| Child construction | `packages/coding-agent/src/task/executor.ts`&#32;—&#32;`createSubagentSettings`\,&#32;`runSubprocess`\;&#32;`modes/controllers/tan-command-controller.ts`&#32;—&#32;`TanCommandController.start` |
| Later OS\/host seam | `packages/coding-agent/src/tools/file-write-fallback.ts`&#32;— write\/delete fallback helpers and error classifier\;&#32;`tools/path-utils.ts`&#32;—&#32;`resolveSyscallTarget` |

Paths abbreviated within a table cell retain that cell’s&#32;`packages/coding-agent/src/`&#32;prefix\.&#32;The fixtures supply line references for selected anchors\:&#32;approval lines 104–233\;&#32;wrapper lines 177–346\;&#32;bash lines 264–300 and 553–579\;&#32;write lines 515–560 and 1104–1205\;&#32;xdev lines 406–474\.&#32;These are supplied snapshot references\,&#32;not newly measured line numbers\.

Implementation takes precedence over broad comments\.&#32;In particular\,&#32;a comment calling coordination operations read\-only cannot override&#32;`hubApproval()`&#32;assigning read tier to cancellation and messaging\.&#32;A stale UI comment cannot override RPC adapter construction\.

### Recorded evidence\:&#32;existing focused tests

`proof/existing-tests.json`&#32;records&#32;**96 passes\,&#32;zero failures\,&#32;230 assertions across four files**\:

- `packages/coding-agent/test/tools/approval.test.ts`\;
- `packages/coding-agent/test/tools/approval-mode.test.ts`\;
- `packages/coding-agent/test/tools/ssh-url-approval-gate.test.ts`\;
- `packages/coding-agent/test/tools/file-write-fallback.test.ts`\.

`proof/additional-tests.json`&#32;records a&#32;**separate run of 105 passes\,&#32;zero failures\,&#32;362 assertions across two files**\:

- `packages/coding-agent/test/write-xdev-dispatch.test.ts`\;
- `packages/coding-agent/test/extensions-runner.test.ts`\.

These are&#32;**201 passing existing tests in two runs**\.&#32;They are not one newly rerun aggregate of earlier workbook suites\.&#32;The existing tests include actual local tool and file contracts as well as injected seams\;&#32;they should not all be described as inert classification cases\.&#32;SSH\-shaped calls in the dedicated gate tests are rejected before connection\.&#32;File\-permission fixtures do not supply a real elevated broker\.

### Recorded evidence\:&#32;the separate private scenario report

After Main’s execution\,&#32;`proof/report.json`&#32;reports&#32;`passed: true`\:&#32;**46 executed cases\,&#32;46 passes\,&#32;zero failures**\,&#32;four separately labeled source\-only items\,&#32;ten inert tool executions\,&#32;and one injected denied\-primitive attempt\.&#32;The launcher reports exit code zero and no timeout\.

| Public fixture | Executed cases | Meaning of the executed route |
| --- | ---: | --- |
| `source-workbooks/permissions/site/examples/approval-desk/cases.json` | 22 | Actual resolver calls\,&#32;including actual BashTool declarations\;&#32;no bash execution\. |
| `source-workbooks/permissions/site/examples/dispatch-desk/cases.json` | 9 | Actual write\/device dispatch and wrappers around a fictional in\-memory executor\. |
| `source-workbooks/permissions/site/examples/boundary-desk/cases.json` | 15 | Wrapper responses\,&#32;RPC helpers\,&#32;synthetic safety metadata\,&#32;and a denied primitive\/error classifier\. |

The report captures the exact fixture bytes and their supplied hashes\.&#32;The public fixtures still describe authored expected outcomes\;&#32;reading their&#32;`expected`&#32;fields is not itself execution evidence\.&#32;The separate report is what records the completed checks\.

The earlier&#32;`proof/report-contract.json`&#32;label&#32;`READY_TO_RUN_NOT_EXECUTED_BY_AUTHOR`&#32;is historical preparation state\.&#32;The completed report supersedes it for these 46 named cases only\.&#32;It does not turn its four source\-only items into executed scenarios\.

The private runner did not launch a provider\,&#32;real shell command\,&#32;SSH route\,&#32;real remote device\,&#32;auth store\,&#32;privileged broker\,&#32;or desktop input operation\.&#32;Its tool counters mean in\-memory ledger appends\.&#32;Its denied writer throws before placing bytes\.&#32;Full&#32;`runRpcMode()`&#32;initialization\,&#32;actual runner UI construction\,&#32;and a real client display were not exercised by this scenario harness\.

### Recorded evidence\:&#32;installed configuration CLI

`proof/cli-config-proof.json`&#32;records the installed&#32;`omp/18.0.7`&#32;set\/get JSON roundtrip in an owned named profile and empty temporary cwd\.&#32;It establishes the shown output objects and saved YAML\,&#32;not effective policy in a live session\.&#32;The combined launch\-flag behavior is source\-backed\;&#32;the injected autoApprove cases verify the wrapper half separately\.

No personal settings\,&#32;credentials\,&#32;private memory\,&#32;hidden reasoning\,&#32;or unrelated session material is needed for the public practice route\.&#32;Private runner contents and generated private reports are not additional downloadable workbook exercises\.

### What remains unproved

No supplied result establishes physical keyboard behavior\,&#32;every TUI\/ACP\/RPC capability\,&#32;actual provider tool delivery\,&#32;remote SSH permission\,&#32;durable privileged writing\,&#32;a full OS sandbox\,&#32;or blanket cross\-process enforcement\.&#32;Source hashes identify bytes\;&#32;they do not prove that every branch in a file executed\.

The selected pack does not provide the complete live session inheritance\/revival implementation or a reader’s current child context\.&#32;Configuration inspection does not fill that gap\.&#32;Likewise\,&#32;a nested replacement’s tier report does not make every returned argument field a final\-input record\.

The finished manuscript and its editorial connections add explanations\,&#32;not new runtime results\.&#32;Conversion\,&#32;catalog generation\,&#32;browser behavior\,&#32;preservation of existing routes\,&#32;and publication require the owning publisher’s separate verification\;&#32;no such action is asserted here\.

**Final paper checkpoint\:**&#32;assign these claims to their evidence\:&#32;the mode matrix\,&#32;two prompts for an explicit device policy\,&#32;Tan’s initial helper mode\,&#32;the JSON config getter shape\,&#32;and a working physical approval dialog\.&#32;**Worked answer\:**&#32;recorded resolver cases\;&#32;recorded dispatch case\;&#32;supplied source construction\;&#32;isolated CLI report\;&#32;not established by this evidence\.&#32;That last answer is a successful boundary judgment\,&#32;not an incomplete lesson\.

## Tool permissions and approvals\:&#32;next steps

You can now explain a permission outcome without treating every refusal as a request for a wider grant\.&#32;You can distinguish an absent tool\,&#32;an effective deny\,&#32;a declined call\,&#32;unavailable UI\,&#32;revised input\,&#32;and a later host error\.&#32;You can also explain why no prompt is not proof of either safety or a broken gate\.

### Carry the operator contract into design

The next part asks how a capability should expose those boundaries\.&#32;Host approval is only one layer\.&#32;Seed Desk’s branch\-persisted authority\,&#32;Review Desk’s one\-action revision grant\,&#32;and Field Notes’ factory\-local selection have different lifetimes\.&#32;None should be silently replaced by a generic approval answer\.

Continue to&#32;[Extensions inside those boundaries](<https://present-sketch-tp94.here.now/chapters/unified-extensions>)\.&#32;Read its complete examples as designs whose domain checks\,&#32;host wiring\,&#32;delivery\,&#32;and verification must agree—not as a way to bypass an operator’s policy\.&#32;The shared connection&#32;[Permission belongs to an operation\,&#32;a revision\,&#32;and a lifetime](<https://present-sketch-tp94.here.now/chapters/connection-authority-and-lifetimes>)&#32;compares the layers without merging their grants\.

## Extensions inside those boundaries

The earlier parts established what the runtime keeps\,&#32;what it shares\,&#32;and what an operation does not prove\.&#32;This part turns those lessons into interface design\.

Start with the smallest appropriate surface\.&#32;A human command can answer locally\.&#32;A model\-callable tool needs a schema and a useful result contract\.&#32;A skill supplies guidance\,&#32;not executable enforcement\.&#32;An SDK host owns initialization and disposal\.&#32;A plugin selects distribution entries\;&#32;installing it and executing a factory are different events\.

### Follow the progressive projects

**Seed Desk**&#32;begins with a welcome command and a read\-only tool over the same fictional information\.&#32;It grows into inventory queries\,&#32;then branch\-local reservations with exact IDs\,&#32;revision checks\,&#32;human\-granted authority\,&#32;and persisted state\.&#32;Load only one complete Seed Desk stage at a time\:&#32;all stages intentionally own&#32;`/seeds`\.

**Review Desk**&#32;keeps a different promise\.&#32;A person can edit and accept a release note locally\;&#32;acceptance does not publish it\.&#32;The agent’s one\-action grant is held in memory and tied to the session and revision\.&#32;Pending dialogs must be aborted and generation\-invalidated so a late positive response cannot restore revoked authority\.&#32;Standard dialogs and a native panel expose the same small domain through different presentation capabilities\.

**Package Lab**&#32;follows Field Notes from one file to helpers\,&#32;a manifest\,&#32;optional features\,&#32;and an embedded SDK host\.&#32;Its selection is factory\-local\,&#32;not transcript\-local\:&#32;a new conversation can reuse the same closure\.&#32;The package chapters make that lifetime correction explicit rather than concealing it behind the word session\.

All supplied files remain available under their original paths in&#32;[the complete archive](<https://present-sketch-tp94.here.now/examples.zip>)\,&#32;including adjacent descriptions\,&#32;JSON fixtures\,&#32;manifests\,&#32;and helper modules\.&#32;Additional complete exercises printed in the chapters remain printed exercises\;&#32;they are not silently relabelled as separately observed downloads\.

### Match the host before claiming the behavior

The examples target the recorded custom build\.&#32;Bun and the named runtime packages are real prerequisites where used\.&#32;Do not replace an unavailable custom dependency with an unrelated upstream package and call the original example verified\.

Binding a factory is not initializing its live actions\.&#32;A session reload is not necessarily a factory reimport\.&#32;`hasUI`&#32;is not proof that a native component works in RPC or ACP\.&#32;A provider appearing in a catalog is not proof of authentication or inference\.&#32;A file\-fallback adapter is not an elevated writer without a real host broker and policy\.

Those distinctions are part of the teaching\,&#32;not unfinished work to paper over with successful no\-ops\.

### Keep authority inside the domain

Host approval\,&#32;the example’s human grant\,&#32;and operating\-system authority are separate gates\.&#32;In\-process extension code is trusted code\;&#32;`isProjectTrusted()`&#32;is not a sandbox in the described build\.&#32;Revisions prevent stale actions only when the domain validates them\,&#32;and cancellation before commit is different from cancellation after a stored effect\.

Read the focused laboratories after the progressive projects\,&#32;then use the complete public feature inventory and all 46 event descriptions as references\.&#32;The final debugging and distribution chapters explain what evidence is needed at each layer\.&#32;No real provider\,&#32;authentication\,&#32;privileged\-broker\,&#32;or publication exercise is added by this unified edition\.

## Orientation

An independent\,&#32;source\-grounded workbook for the current custom Oh My Pi build identified in the recorded checks as&#32;**OMP 18\.0\.7**\.&#32;It is not a compatibility promise for every upstream or npm release carrying that version\.

[Download the complete example bundle](<https://present-sketch-tp94.here.now/downloads/extensions-examples.zip>)

## Choose what to build

Start with the outcome\,&#32;not the API\.

### “I want my agent to do X—build Y”

| I want… | Build… | Why this surface fits |
| --- | --- | --- |
| A human to type&#32;`/seeds hours`&#32;and receive a local answer | An&#32;**extension slash command** | The command handler can answer without asking a model\. |
| The model to query inventory or reserve a specific item | A&#32;**model\-callable tool** | It has a parameter schema\,&#32;structured results\,&#32;cancellation and errors\. |
| Several commands\,&#32;tools and lifecycle handlers to share one domain | An&#32;**executable extension** | One default factory registers the related capabilities\. |
| A key chord to insert a useful phrase or open a local view | An&#32;**extension keyboard shortcut** | It is an operator convenience\,&#32;not a model tool\. |
| A startup option such as&#32;`--seed-quiet` | An&#32;**extension CLI flag** | The extension declares the flag before the CLI’s extension\-aware argument pass\. |
| To inspect\,&#32;block or transform a tool call | An&#32;**extension event handler** | `tool_call`&#32;and&#32;`tool_result`&#32;are the supported interception surfaces\. |

A slash command is&#32;**not**&#32;a tool the model can call\.&#32;Register a tool when the model needs a permanent domain capability\.&#32;Share a domain function between the command and tool rather than asking the model to “run the slash command\.”

“Human command” also does not mean “cryptographically proven human keystroke\.” An SDK or RPC host can deliberately submit slash\-command text through the command\-dispatch path\.&#32;Extensions run in\-process\;&#32;this workbook’s authorization examples are domain checks\,&#32;not a sandbox\.

### When code is unnecessary

| I want… | Build… | Important boundary |
| --- | --- | --- |
| A reusable instruction such as “summarize a fictional field observation” | A&#32;**prompt template** | It expands text\;&#32;it does not itself execute a TypeScript factory\. |
| A named text workflow invoked through a slash command | A&#32;**file\-based slash command** | Markdown command expansion is different from&#32;`registerCommand()`&#32;executing code locally\. |
| A reusable body of expertise\,&#32;instructions and supporting files | A&#32;**skill** | Discovery makes guidance available\.&#32;It does not automatically execute every script in the skill directory\. |
| Standing or conditionally applied instructions | A&#32;**rule** | Rules guide model behavior\.&#32;They are not filesystem or network enforcement\. |
| Different colors and visual styling | A&#32;**theme** | A theme changes presentation\,&#32;not tool authority\. |
| A different input\-editor frame | An&#32;**extension composer shape** | A shape is executable rendering code\,&#32;separate from a theme\. |

### When the integration belongs elsewhere

| I want… | Build… | Important boundary |
| --- | --- | --- |
| Only a model\-callable function\,&#32;especially in an existing tool package | A&#32;**standalone custom tool** | Its factory returns tools\,&#32;and its legacy&#32;`execute`&#32;argument order differs from native extension tools\. |
| To maintain an existing event\-only integration | A&#32;**legacy hook**\,&#32;or migrate it to an extension | JS\/TS hook factories can enter the extension loading pipeline\.&#32;New combined integrations are usually clearer as extensions\. |
| A new model endpoint\,&#32;transport or login flow | A&#32;**model\-provider registration** | This requires real provider configuration\.&#32;Registering metadata does not prove inference works\. |
| Tools or resources supplied by a separate process or remote service | An&#32;**MCP server** | The server owns a protocol endpoint\;&#32;the OMP client connects to it\. |
| To distribute extensions\,&#32;tools\,&#32;commands and optional features together | A&#32;**plugin bundle** | A package manifest selects entries\.&#32;Installation\,&#32;discovery and execution remain distinct steps\. |
| To embed OMP in an application | An&#32;**SDK host with inline extension factories** | The host owns session creation\,&#32;runtime initialization\,&#32;UI and disposal\. |
| To expose a Gemini\-style extension description | A&#32;**declarative&#32;`gemini-extension.json`&#32;manifest** | In this snapshot\,&#32;it is a metadata capability—not an automatic launcher for tools\,&#32;factories\,&#32;context or MCP servers named inside it\. |

There is overlap by design\.&#32;A plugin may contain an executable extension\,&#32;a skill and a prompt\.&#32;An executable extension may register a tool and a human command\.&#32;An MCP tool may ultimately appear in the same tool registry as a local tool\.

The useful question is always\:

> Which component discovers this file\,&#32;which component executes it\,&#32;and which interface can actually invoke the resulting capability\?

### The workbook’s route

Three projects carry the main progression\:

1. [Seed Desk\:&#32;welcome and inventory](<https://present-sketch-tp94.here.now/chapters/extensions-seed-desk-welcome-and-inventory#extensions-seed-desk-welcome-and-inventory>)\:&#32;a volunteer and an agent learn the same fictional desk information\.
2. [Seed Desk\:&#32;reservations](<https://present-sketch-tp94.here.now/chapters/extensions-seed-desk-reservations-on-the-active-branch#extensions-seed-desk-reservations-on-the-active-branch>)\:&#32;reads become revision\-checked\,&#32;branch\-aware actions\.
3. [Review Desk](<https://present-sketch-tp94.here.now/chapters/extensions-review-desk-edit-and-decide-locally#extensions-review-desk-edit-and-decide-locally>)\:&#32;a release note is inspected\,&#32;edited\,&#32;accepted\,&#32;rejected or cancelled—never published\.
4. [Package Lab](<https://present-sketch-tp94.here.now/chapters/extensions-package-lab-one-file-to-an-embedded-host#extensions-package-lab-one-file-to-an-embedded-host>)\:&#32;a notebook moves from one file to helpers\,&#32;a manifest and SDK embedding\.

Focused laboratories then cover native UI\,&#32;interception\,&#32;session navigation\,&#32;background delivery\,&#32;providers\,&#32;resources and file fallbacks\.

Use the&#32;[public feature reference](<https://present-sketch-tp94.here.now/chapters/extensions-public-feature-reference#extensions-public-feature-reference>)&#32;and&#32;[all 46 events](<https://present-sketch-tp94.here.now/chapters/extensions-all-46-extension-events#extensions-all-46-extension-events>)&#32;as lookup chapters\.

### How to read claims

This workbook uses three labels\:

- **Observed\:**&#32;a completed check is recorded in the supplied execution report\.
- **Source\-backed\:**&#32;the behavior follows from the supplied implementation or public type contract\.
- **Exercise\:**&#32;a proposed experiment or new small example\.&#32;Its expected result is explained\,&#32;but it is not added to the recorded proof count\.

The stories use fictional people and data\.&#32;The observations describe the recorded checks\,&#32;not real seed\-library operations\,&#32;a real release\,&#32;or a provider account\.

The workbook website and its downloads do not operate your OMP session\.&#32;There is no tutorial\-browser control API implied by these lessons\.

## Prepare a reversible lab

Before adding convenience\,&#32;make failure inexpensive\.

Mara\,&#32;a seed\-library volunteer\,&#32;wants to experiment without filling her normal project with extension state\.&#32;She chooses an explicit entry path and a scratch working directory\.&#32;That gives her a reproducible launch command and a clear place to inspect the session file\.

### Prerequisites

You need\:

- The workbook\-compatible custom&#32;`omp`&#32;on&#32;`PATH`\.
- Bun\;&#32;the recorded checks used&#32;**Bun 1\.3\.14**\.
- The complete example directory structure\,&#32;preferably from&#32;[the ZIP](<https://present-sketch-tp94.here.now/downloads/extensions-examples.zip>)\.
- Matching runtime dependencies where an example imports them\.

Check the installed versions\,&#32;but do not treat a matching version string as proof that an unrelated distribution contains the same custom APIs\.

**Terminal shell—inspect your installed programs\:**

~~~sh
omp --version
bun --version
~~~

The Seed Desk stages are complete\,&#32;independent entries\.&#32;Load&#32;**one at a time**\:&#32;all three intentionally own&#32;`/seeds`\.

Package Lab’s command\-line entries use type\-only SDK imports and injected schema builders\.&#32;Seed Desk stage 3 additionally imports matching&#32;`@oh-my-pi/omptype`&#32;and&#32;`@oh-my-pi/pi-tui`&#32;runtime packages\.&#32;Review Desk imports the TUI package through the compatible host’s extension loader\.

Do not fix a missing custom package by silently substituting an incompatible upstream package\.

### Establish the workspace once

Start in the extracted directory that contains&#32;`seed-desk`\,&#32;`review-desk`&#32;and&#32;`package-lab`\.

**Terminal shell—create the workbook workspace\:**

~~~sh
EXAMPLES="$PWD"
LAB="$(mktemp -d)"
mkdir -p "$LAB/work" "$LAB/agent"
export PI_CODING_AGENT_DIR="$LAB/agent"
export PI_PROFILE=
cd "$LAB/work"
~~~

Later commands assume these shell variables remain available\.

This is a&#32;**reversible workspace**\,&#32;not complete isolation\:

- `PI_CODING_AGENT_DIR`&#32;selects the lab’s agent directory\.
- `--no-extensions`&#32;suppresses ambient extension\-factory discovery\,&#32;while explicit entries still load\.
- Other discovery families\,&#32;environment credentials\,&#32;package installation and host startup activity have their own behavior\.
- Neither an empty working directory nor&#32;`--no-session`&#32;is an OS security boundary\.

The recorded isolated scenarios used an additional OS policy denying network access and restricting writes\.&#32;Ordinary reader launch commands below do not install that policy\.

### An extension is a factory\,&#32;not a command\-line program

A module loaded with&#32;`-e`&#32;exports a&#32;**default function**\.&#32;OMP calls that function with&#32;`ExtensionAPI`\.

**Complete TypeScript exercise—save as&#32;`lab-status.ts`&#32;in the lab working directory\:**

~~~ts
import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent";

export default function labStatus(pi: ExtensionAPI): void {
    pi.setLabel("Workbook lab");

    pi.on("session_start", (_event, ctx) => {
        pi.logger.debug("Workbook lab session started");
        if (ctx.hasUI) {
            ctx.ui.notify("Workbook lab loaded.", "info");
        }
    });
}
~~~

**Terminal shell—load that exercise\:**

~~~sh
omp --no-extensions -e ./lab-status.ts
~~~

**Expected checkpoint\:**&#32;in a TUI\,&#32;the session\-start handler produces&#32;`Workbook lab loaded.`&#32;In a default headless context\,&#32;the notification is absent\;&#32;that absence is not a load failure\.

Running&#32;`bun lab-status.ts`&#32;merely evaluates a module that exports a function\.&#32;It does not supply OMP’s extension runtime\.

### Registration first\,&#32;initialized actions later

The lifecycle has two important phases\:

1. **Factory binding\:**&#32;register commands\,&#32;tools\,&#32;flags\,&#32;handlers\,&#32;renderers and fallback handlers\.
2. **Runtime initialization\:**&#32;the host wires session actions and UI\,&#32;then dispatches events and invocations\.

Methods such as&#32;`sendMessage()`\,&#32;`appendEntry()`\,&#32;`getAllTools()`&#32;and&#32;`setModel()`&#32;depend on the initialized runtime\.&#32;Calling them during factory loading raises&#32;`ExtensionRuntimeNotInitializedError`\.

An async factory is valid\.&#32;Seed Desk uses one to read&#32;`tool.txt`&#32;before registering a tool\.&#32;Async initialization does not make runtime actions available early\.

Register providers at factory time if needed\:&#32;their registrations are queued for the model registry\.&#32;That is a special registration path\,&#32;not an exception allowing arbitrary session actions during loading\.

### Keep the lifetimes separate

| Lifetime | What belongs here | What does not follow automatically |
| --- | --- | --- |
| Process | Loaded libraries\,&#32;process\-wide registries\,&#32;explicitly shared buses | One user\,&#32;one session or one authorization scope |
| Module evaluation | Static fixtures and helper definitions | Fresh mutable state for every SDK session |
| Factory binding | Closures created when the factory is called | Automatic reset on&#32;`/new`&#32;or session switching |
| Session\/transcript | A session ID\,&#32;header and journal | A global shared database |
| Current branch | The path from root to the active leaf | Every entry in the session file |
| Invocation | The current handler\/tool context and abort signal | A context object safe to cache indefinitely |

The Field Notes selection later demonstrates the distinction\:&#32;it survives a transcript change because the factory binding survives\.

### Trust is already being granted

**Source\-backed\:**&#32;`ExtensionContext.isProjectTrusted()`&#32;always returns&#32;`true`&#32;in this build\.&#32;It is a compatibility method reflecting that project\-local inputs are already trusted by default\.&#32;It is not a prompt\,&#32;sandbox or per\-directory permission store\.

Review the selected module’s entire import graph\.&#32;Package dependencies and helper modules have the same in\-process JavaScript authority as the entry\.

**Exercise\:**&#32;move&#32;`pi.sendMessage()`&#32;into the factory body of a copy of&#32;`lab-status.ts`\.&#32;What should happen\?

**Answer\:**&#32;loading should report an uninitialized\-runtime error\.&#32;Move the action into a handler\;&#32;do not add a delay and hope startup finishes first\.

*Source\,&#32;snapshot 2026\-08\-29\:&#32;`packages/coding-agent/src/extensibility/extensions/loader.ts`\,&#32;`getExtensionFactory`\,&#32;`ExtensionRuntime`\,&#32;`ConcreteExtensionAPI`\;&#32;`packages/coding-agent/src/extensibility/extensions/runner.ts`\,&#32;`initialize`\,&#32;`createContext`\.*

## Seed Desk\:&#32;welcome and inventory

Mara’s first goal is modest\:&#32;stop repeating the fictional seed desk’s opening hours\.&#32;Her obstacle is that a friendly human command alone would leave the agent without a supported way to retrieve the same information\.

She chooses two entrances to one small domain\:&#32;a human command and a read\-only tool\.

### Milestone\:&#32;a welcoming desk for both readers

**Starting state\:**&#32;no reservation state\,&#32;no inventory mutation and no external service\.

Files\:

- [Stage 1 entry](<https://present-sketch-tp94.here.now/examples/seed-desk/01-welcome/index.ts>)
- [Tool description](<https://present-sketch-tp94.here.now/examples/seed-desk/01-welcome/tool.txt>)
- [Seed Desk instructions](<https://present-sketch-tp94.here.now/examples/seed-desk/README.md>)

**Terminal shell—launch stage 1\:**

~~~sh
omp --no-extensions --no-skills -e "$EXAMPLES/seed-desk/01-welcome/index.ts"
~~~

**Human OMP slash commands—enter in the composer\:**

~~~text
/seeds welcome
/seeds hours
~~~

The hours response is\:

**Expected local output\:**

~~~text
Our fictional desk opens Saturday, 10:00-12:00. No booking has been made.
~~~

The machine interface is&#32;`seed_welcome`\.

**Model tool arguments—call&#32;`seed_welcome`\:**

~~~json
{"op":"discover"}
~~~

**Model tool arguments—call&#32;`seed_welcome`\:**

~~~json
{"op":"inspect","topic":"hours"}
~~~

The first operation advertises&#32;`discover`&#32;and&#32;`inspect`\,&#32;the topics\,&#32;scope and quiet flag in structured&#32;`details`\.&#32;The second returns the same hours text used by the slash command\.

Important distinction\:&#32;`details`&#32;is useful to SDK callers\,&#32;hosts and renderers\.&#32;It is not automatically model\-visible\.&#32;This stage’s ordinary text content tells the model which topics it can inspect\;&#32;the quiet flag is in&#32;`details`\,&#32;not duplicated into that text\.

#### What the code changes

The async factory reads its description from a real adjacent file\.&#32;It registers\:

- `seed_welcome`\,&#32;with&#32;`approval: "read"`&#32;and&#32;`loadMode: "essential"`\;
- the boolean flag&#32;`seed-quiet`\;
- a&#32;`session_start`&#32;notification\;
- `/seeds`\,&#32;including argument completion\.

It does&#32;**not**&#32;register a reservation operation\.

**Exact excerpt—completion behavior in&#32;[stage 1’s entry](<https://present-sketch-tp94.here.now/examples/seed-desk/01-welcome/index.ts>)\;&#32;not a standalone replacement file\:**

~~~ts
getArgumentCompletions(prefix) {
    const matches = choices.filter(value => value.startsWith(prefix.trimStart()));
    // A completed sole match MUST release Enter to submit the command.
    if (matches.length === 1 && matches[0] === prefix.trim()) return null;
    return matches.length ? matches.map(value => ({ value: `${value} `, label: value })) : null;
},
~~~

Mara types&#32;`/seeds wel`\.&#32;The completion offers the full argument text&#32;`welcome `\.

When&#32;`welcome`&#32;is already the sole exact match\,&#32;returning&#32;`null`&#32;lets Enter submit rather than continually reaccepting the completion\.&#32;A completion\-array equality test is helpful\,&#32;but it is not a terminal\-key\-dispatch test\.

#### Inspect progress

**Observed\:**

- `wel`&#32;produced&#32;`welcome `\.
- `welcome`&#32;and&#32;`welcome `&#32;produced no completion\.
- The tool’s greeting matched the headless command’s greeting\.
- Quiet startup produced zero startup notices\.
- A headless command produced one custom message\.

The supplied Seed Desk proof simulated confirmation\/notification UI\.&#32;It did not exercise actual terminal autocomplete and Enter behavior\.

#### Quiet is a startup choice\,&#32;not a permission

Exit the stage and restart it with the flag\.

**Terminal shell—suppress the startup notice\:**

~~~sh
omp --no-extensions --no-skills -e "$EXAMPLES/seed-desk/01-welcome/index.ts" --seed-quiet
~~~

This suppresses only the startup notification\.&#32;Commands and tools still work\.

Register flag names without the leading&#32;`--`\;&#32;use the leading&#32;`--`&#32;in the shell\.&#32;In this parser\,&#32;a boolean flag’s presence means&#32;`true`\.&#32;Do not assume&#32;`--seed-quiet=false`&#32;means false\;&#32;omit the flag to use its false default\.

#### Failure and mode boundaries

- An inspect request without a topic throws an error\.
- An unknown human verb produces guidance and changes nothing\.
- The tool checks its&#32;`AbortSignal`&#32;before doing work\.
- With UI\,&#32;the command uses&#32;`notify()`\.
- Without UI\,&#32;it uses&#32;`sendMessage(..., { triggerTurn: false })`\,&#32;so the answer is not lost in an inert notification\.
- Reloading code is not accomplished by directly running the TypeScript file\.&#32;Restart the explicit launch when testing an edit\.

**Exercise\:**&#32;ask the agent to reserve basil through&#32;`seed_welcome`\.

**Checkpoint\:**&#32;it should report that no stock\-changing operation exists\,&#32;not invent&#32;`reserve`&#32;or attempt to invoke&#32;`/seeds`\.

### Milestone\:&#32;the desk gains a real query surface

The next Saturday\,&#32;Mara’s fictional desk has three seed varieties\.&#32;Repeating a prose list has become awkward\:&#32;the agent needs stable IDs\,&#32;family filters and a clear error for an invented ID\.

She replaces stage 1 with stage 2\.&#32;These stages are independent\,&#32;not cumulative imports\.

Files\:

- [Stage 2 entry](<https://present-sketch-tp94.here.now/examples/seed-desk/02-catalog/index.ts>)
- [Shared catalog functions](<https://present-sketch-tp94.here.now/examples/seed-desk/02-catalog/catalog.ts>)
- [Fictional inventory](<https://present-sketch-tp94.here.now/examples/seed-desk/02-catalog/inventory.json>)
- [Tool description](<https://present-sketch-tp94.here.now/examples/seed-desk/02-catalog/tool.txt>)

**Terminal shell—after exiting stage 1\:**

~~~sh
omp --no-extensions --no-skills -e "$EXAMPLES/seed-desk/02-catalog/index.ts"
~~~

**Human OMP slash commands\:**

~~~text
/seeds query
/seeds query herb
/seeds inspect basil-genovese
~~~

**Model tool arguments—call&#32;`seed_catalog`\:**

~~~json
{"op":"query","family":"herb"}
~~~

**Model tool arguments—call&#32;`seed_catalog`\:**

~~~json
{"op":"inspect","id":"basil-genovese"}
~~~

Stage 2 supports&#32;**`query`&#32;and&#32;`inspect`&#32;only**\.&#32;It does not have a&#32;`discover`&#32;operation merely because stage 1 had one\.

#### One shared domain\,&#32;two entrances

This is the complete small helper used by both interfaces\.

**TypeScript source—[stage 2’s&#32;`catalog.ts`](<https://present-sketch-tp94.here.now/examples/seed-desk/02-catalog/catalog.ts>)\:**

~~~ts
import fixture from "./inventory.json";

export interface Seed {
    id: string;
    name: string;
    family: string;
    packets: number;
}

export const inventory: readonly Seed[] = fixture;

export function inspect(id: string): Seed {
    const seed = inventory.find(item => item.id === id);
    if (!seed) throw new Error("Unknown seed ID. Query the catalog for valid IDs. Nothing changed.");
    return seed;
}

export function query(family?: string): readonly Seed[] {
    return family ? inventory.filter(seed => seed.family === family) : inventory;
}

export function summary(seeds: readonly Seed[]): string {
    return seeds.length
        ? seeds.map(seed => `${seed.id}: ${seed.name}, ${seed.packets} fictional packets (${seed.family})`).join("\n")
        : "No fictional seeds match that family.";
}
~~~

**Expected output for the herb query\:**

~~~text
basil-genovese: Genovese basil, 8 fictional packets (herb)
~~~

The tool returns that summary in&#32;`content`\,&#32;and records in\:

- `details.scope: "fictional-fixture"`
- `details.seeds`

This is better than asking the agent to scrape a notification\.&#32;The tool gives it stable targets\,&#32;while the host gets structured records\.

#### Inspect progress and failure

**Observed\:**&#32;the real loader\,&#32;runner and intercepted tool adapter returned only&#32;`basil-genovese`&#32;for the herb query\.&#32;Slash\/tool summaries matched\.&#32;Unknown inspection IDs threw\.&#32;A pre\-aborted call was refused through the intercepted path\.

The family match is exact and case\-sensitive\.&#32;An unknown family gives an empty query result\;&#32;an unknown inspect ID is an error\.

No read appends reservation state\.

**Exercise\:**&#32;compare these two requests\:

**Model tool arguments—call&#32;`seed_catalog`\,&#32;one request at a time\:**

~~~json
{"op":"query","family":"unknown-family"}
~~~

~~~json
{"op":"inspect","id":"unknown-seed"}
~~~

**Answer\:**&#32;the query reports no matches\.&#32;The inspection fails because it promises to identify one existing target\.

### What changes next\?

Mara can now ask\,&#32;“Which herbs are available\?” But the eight packets are still a static fixture\.&#32;To represent a reservation\,&#32;she needs a state model\,&#32;an ownership scope and a concurrency check—not merely another button\.

That is the next chapter\.

*Source\,&#32;snapshot 2026\-08\-29\:&#32;the linked Seed Desk files\;&#32;`packages/coding-agent/src/extensibility/extensions/types.ts`\,&#32;`ToolDefinition`\;&#32;`packages/coding-agent/src/extensibility/extensions/wrapper.ts`\,&#32;`RegisteredToolAdapter`\,&#32;`ExtensionToolWrapper`\.*

## Seed Desk\:&#32;reservations on the active branch

Mara wants the agent to help hold fictional packets while she works through a conversation\.&#32;Her obstacle is no longer retrieval\.&#32;It is stale decisions\:&#32;a reservation based on an old availability view must not silently overwrite a newer one\.

She chooses&#32;**current\-branch reconstruction plus revision\-checked actions**\.

This is still not a stock system\.&#32;Separate sessions and processes do not coordinate physical inventory\.

### Milestone\:&#32;a confirmed human reservation

**Starting state\:**&#32;the stage\-3 fixture has eight basil packets\,&#32;five bean packets and three marigold packets\.&#32;Reservations are empty\.&#32;Agent actions are disabled\.

Files\:

- [Stage 3 entry](<https://present-sketch-tp94.here.now/examples/seed-desk/03-reservations/index.ts>)
- [Reservation domain and reconstruction](<https://present-sketch-tp94.here.now/examples/seed-desk/03-reservations/desk.ts>)
- [Inventory](<https://present-sketch-tp94.here.now/examples/seed-desk/03-reservations/inventory.json>)
- [Package manifest](<https://present-sketch-tp94.here.now/examples/seed-desk/03-reservations/package.json>)
- [Tool description](<https://present-sketch-tp94.here.now/examples/seed-desk/03-reservations/tool.txt>)

For a standalone download\,&#32;install the matching dependencies in this stage\.&#32;This is package\-manager work and may access your registry\.&#32;If those custom versions are unavailable\,&#32;use the matching installed source workspace instead\.

**Terminal shell—dependency installation\,&#32;then launch\:**

~~~sh
cd "$EXAMPLES/seed-desk/03-reservations"
bun install
cd "$LAB/work"
omp --no-extensions --no-skills -e "$EXAMPLES/seed-desk/03-reservations/index.ts" --session "$LAB/seed-session.jsonl"
~~~

**Human OMP slash commands\:**

~~~text
/seeds query
/seeds reserve basil-genovese 2
~~~

Cancel the first confirmation\.&#32;Then repeat the reservation command and confirm\.

**Expected state after confirmation on a fresh branch\:**

~~~text
basil-genovese: 6 available, 2 reserved
~~~

The query also supplies a revision\.&#32;Its concrete value comes from your session\,&#32;so it will not match a printed workbook token\.

#### What changed in the code\?

The domain separates\:

- the immutable fictional fixture\;
- a persisted snapshot containing&#32;`version`\,&#32;`agentEnabled`&#32;and reservation&#32;`quantities`\;
- a derived revision\;
- domain validation in&#32;`changeReservation()`\.

A successful change appends a custom entry named&#32;`seed-desk-state-v1`\.&#32;It then reconstructs state\,&#32;refreshes the status and emits&#32;`workbook:seed-desk:changed`\.

The extension does not write a separate inventory file\.

#### Why the second revision check matters

A confirmation dialog is an&#32;`await`\.&#32;During that wait\,&#32;another action or a session\-tree movement may occur\.

**Exact excerpt—post\-dialog check in&#32;[stage 3’s entry](<https://present-sketch-tp94.here.now/examples/seed-desk/03-reservations/index.ts>)\;&#32;read in its surrounding command handler\:**

~~~ts
// Re-read after the dialog: another action or tree navigation may have happened.
const current = reconstruct(ctx);
if (current.revision !== initial.revision) throw new DeskRefusal("stale-revision", "The branch changed during confirmation; try again.");
const snapshot: Snapshot = grant
    ? { version: 1, quantities: current.quantities, agentEnabled: value === "on" }
    : changeReservation(current, verb, value ?? "", Number(count), initial.revision, "human");
const saved = commit(ctx, snapshot, grant ? `agent-${value}` : verb);
~~~

After validation\,&#32;the commit path has no asynchronous gap before&#32;`appendEntry()`\.&#32;That prevents JavaScript invocations from interleaving inside this small validation\-and\-append segment\.&#32;It is not a cross\-process transaction or a durable database lock\.

**Observed\:**&#32;cancellation appended no state entry\.&#32;A confirmation\-race scenario changed state while the dialog was open\;&#32;the waiting human reservation was then refused\,&#32;leaving bean reservations at zero\.

**Exercise\:**&#32;reserve two basil packets\,&#32;then cancel a request to release one\.

**Checkpoint\:**&#32;basil remains at two reserved\.&#32;Cancellation before commit is not a release\.

### Milestone\:&#32;grant the agent a bounded domain capability

Mara now wants the agent to make fictional reservations without opening a dialog for every domain decision\.&#32;She does not want the tool to grant itself that authority\.

**Human OMP slash command\:**

~~~text
/seeds agent on
~~~

Confirm the scoped dialog\.

The model\-facing interface is now&#32;`seed_desk`\.

**Model tool arguments—call&#32;`seed_desk`\:**

~~~json
{"op":"discover"}
~~~

~~~json
{"op":"query"}
~~~

~~~json
{"op":"inspect","id":"basil-genovese"}
~~~

The tool supports\:

| Operation | Purpose |
| --- | --- |
| `discover` | Explain supported operations\,&#32;action prerequisites and the current state revision |
| `query` | Return valid IDs\,&#32;available\/reserved quantities and a revision\;&#32;optionally filter by exact family |
| `inspect` | Read one exact seed ID |
| `act` | Reserve or release a positive integer quantity using the current revision and a human\-granted permission |

The structured response includes&#32;`ok`\,&#32;`scope`\,&#32;and\,&#32;when available\,&#32;`revision`\,&#32;`agentEnabled`\,&#32;`code`&#32;and&#32;`data`\.

The text response includes the domain outcome and revision\.&#32;Do not assume every field in&#32;`details`\,&#32;including permission status\,&#32;is automatically sent to the model\;&#32;a host exposing structured details has more information than ordinary text\-only model replay\.

#### Ask the agent for the right sequence

**Agent request—use in an authorized model session\:**

~~~text
Use seed_desk to query current fictional availability.
Choose bean-scarlet only if at least one packet is available.
Reserve one packet using the exact revision returned by that query.
If the operation is refused, explain the refusal and query again before
deciding whether another action is appropriate. Do not retry blindly.
~~~

A model session requires your normal provider configuration and may incur charges\.&#32;The recorded example checks did not use provider inference\.

For readers inspecting the schema\,&#32;this is the action shape\:

**Illustrative model\-tool argument template—not usable until the revision is replaced with the actual latest returned value\:**

~~~json
{
  "op": "act",
  "action": "reserve",
  "id": "bean-scarlet",
  "packets": 1,
  "expectedRevision": "COPY_THE_LATEST_RETURNED_REVISION"
}
~~~

A successful action returns a new revision\.&#32;Reusing the old one is a refusal\,&#32;not a second reservation\.

#### Refusals are part of the interface

| Condition | Domain code | Correct next action |
| --- | --- | --- |
| No human grant | `agent-disabled` | Ask the human whether to enable the domain capability |
| Old revision | `stale-revision` | Query again\;&#32;reconsider the intended action |
| Invented ID | `invalid-target` | Use an ID from query results |
| Zero\,&#32;negative or unsafe quantity | `invalid-quantity` | Supply a positive safe integer |
| More than available | `insufficient-availability` | Reduce the quantity or choose another item |
| Release exceeds this branch’s holding | `insufficient-reservation` | Inspect current reservations |
| Incompatible or invalid persisted snapshot | `corrupt-state` | Stop and navigate to a valid earlier branch |
| Human mutation without UI | `ui-required` | Use a supported interactive confirmation path |

The schema can reject malformed inputs before the domain runs\.&#32;The domain also validates action\,&#32;quantity and target\.&#32;These are complementary checks\.

Seed Desk returns ordinary domain refusals as&#32;`details.ok: false`&#32;with explanatory text\.&#32;It does not mark every such refusal as a thrown transport\/tool error\.&#32;An SDK caller must inspect&#32;`ok`\,&#32;not merely whether a promise resolved\.

**Observed\:**&#32;the recorded checks covered disabled authority\,&#32;stale revision\,&#32;invalid target\,&#32;invalid quantity\,&#32;insufficient availability and excess release without appending state\.

#### Host approval is a separate gate

The mixed read\/write&#32;`seed_desk`&#32;tool is conservatively declared&#32;`approval: "write"`&#32;for all operations\.

That host approval tier and Mara’s&#32;`/seeds agent on`&#32;grant are different\:

- Host approval controls whether the wrapped tool call may execute under the configured approval policy\.
- Seed Desk’s grant controls whether its own&#32;`act`&#32;operation may mutate this domain state\.
- Neither prevents other extension JavaScript or independently permitted tools from acting elsewhere\.

Do not weaken a host’s approval configuration merely to make a test pass\.

### Milestone\:&#32;reconstruct the branch\,&#32;not the whole file

Mara tries a different plan from an earlier point in the conversation\.&#32;Her first instinct is to scan all saved entries and select the latest snapshot\.&#32;That would mix sibling branches\.

Instead\,&#32;`reconstruct()`&#32;walks&#32;`ctx.sessionManager.getBranch()`\.

Its revision is\:

- session ID plus&#32;`root`\,&#32;before any Seed Desk state entry\;
- session ID plus the latest Seed Desk state entry’s ID afterward\.

Consequences\:

- Ordinary conversation does not stale the domain revision\.
- A reservation or permission change does\.
- A sibling state snapshot cannot be reused as the current branch’s revision\.
- A new session has a distinct identity\.
- A branch inherits the reservations and grants in its ancestry\.

The domain validates every matching stored snapshot it encounters\.&#32;It does not skip a corrupt Seed Desk entry and silently trust a later one\.

#### Inspect the journal

**Terminal shell—inspect only the lab’s Seed Desk state records\:**

~~~sh
grep '"customType":"seed-desk-state-v1"' "$LAB/seed-session.jsonl"
~~~

This is a journal inspection\,&#32;not a request to edit the journal while OMP owns it\.

Exit and reopen with the same launch command and session path\.&#32;Then query again\.

**Observed\:**

- Real JSONL entries survived reopen\.
- The restored reservation was two packets\.
- The revision was preserved\.
- Rewinding before a reservation showed zero reserved\.
- A sibling branch could hold one packet while the original branch retained two\.
- A sibling revision token was rejected\.
- A fresh session had no carried reservation and agent authority was off\.

A plain leaf movement is not the same thing as a newly appended branch record\.&#32;The session loader reconstructs its position from journal structure\;&#32;do not assume every transient UI navigation choice is independently durable without a subsequent journal change\.&#32;Always query after reopen\.

#### Revoke deliberately

**Human OMP slash command\:**

~~~text
/seeds agent off
~~~

This also asks for confirmation and appends a new snapshot\.

Unlike Review Desk’s in\-memory revocation later\,&#32;**Seed Desk refuses both grant and revoke commands without UI**\.&#32;A persisted grant may therefore still permit tool acts after headless reopen\,&#32;subject to host approval\.&#32;That is the supplied stage’s exact policy\,&#32;not a general recommendation for all applications\.

### Persistence and cancellation limits

`appendEntry()`&#32;stores extension state outside model\-visible conversation\.&#32;It returns no persistence receipt\.

The current file session manager normally hands completed appends to the OS synchronously once persistence is materialized\,&#32;but it does not provide a power\-loss\/fsync guarantee through this extension API\.&#32;Storage failures can be latched and surfaced by the host\.&#32;The Seed Desk checks did not exercise disk\-failure recovery\.

Before commit\,&#32;cancellation changes nothing\.&#32;After commit\,&#32;cancellation cannot undo a reservation\.&#32;Query before retrying\.

Status notifications and bus events do not start an agent turn\.&#32;The optional&#32;`renderResult()`&#32;is presentation only\;&#32;it does not replace the result’s text and structured details\.

**Exercise\:**&#32;should a real multi\-user seed library use this journal as its stock database\?

**Answer\:**&#32;no\.&#32;It would need a shared authoritative store\,&#32;atomic stock operations\,&#32;durable request identity and coordination across users and processes\.&#32;This stage deliberately teaches fictional branch\-local reservations\.

*Source\,&#32;snapshot 2026\-08\-29\:&#32;linked stage\-3 files\;&#32;`packages/coding-agent/src/session/session-manager.ts`\,&#32;`ReadonlySessionManager`\,&#32;`appendCustomEntry`\,&#32;`getBranch`\;&#32;`packages/coding-agent/src/tools/approval.ts`\,&#32;`resolveApproval`\.*

## Review Desk\:&#32;edit and decide locally

Imani prepares a fictional release note\.&#32;Her goal is to let a person review the wording before it is treated as accepted\.&#32;Her obstacle is that “accept” is often confused with “publish\.”

She makes a narrower decision\:&#32;acceptance will be&#32;**a local status change only**\.&#32;The extension contains no send\-to\-service or publish operation\.

Review Desk is one complete downloadable module set\.&#32;The milestones below explain its progression\;&#32;they are not separate downloadable versions\.

### The project

Files\:

- [Entry and runtime coordination](<https://present-sketch-tp94.here.now/examples/review-desk/index.ts>)
- [Domain transitions](<https://present-sketch-tp94.here.now/examples/review-desk/domain.ts>)
- [Native panel and bounded text](<https://present-sketch-tp94.here.now/examples/review-desk/panel.ts>)
- [UI and tool copy](<https://present-sketch-tp94.here.now/examples/review-desk/copy.json>)
- [Fictional release\-note fixture](<https://present-sketch-tp94.here.now/examples/review-desk/release-note.txt>)
- [Story and protocol notes](<https://present-sketch-tp94.here.now/examples/review-desk/story.json>)

**Terminal shell—launch the complete Review Desk\:**

~~~sh
omp --no-extensions -e "$EXAMPLES/review-desk/index.ts" --session "$LAB/review-session.jsonl"
~~~

The compatible loader resolves the runtime TUI import\.&#32;If that fails\,&#32;investigate host\/package compatibility rather than replacing&#32;`panel.ts`&#32;with an inert mock\.

### Milestone\:&#32;inspect without changing the draft

**Starting state\:**&#32;fixture text\,&#32;revision&#32;`0`\,&#32;status&#32;`draft`\,&#32;no agent grant\.

**Human OMP slash command\:**

~~~text
/review-desk show
~~~

**Model tool arguments—call&#32;`review_desk`\:**

~~~json
{"op":"discover"}
~~~

~~~json
{"op":"inspect"}
~~~

~~~json
{"op":"query"}
~~~

`inspect`&#32;returns the complete draft\,&#32;status\,&#32;revision and authorization status\.

`query`&#32;is smaller\:&#32;revision\,&#32;status\,&#32;authorization\,&#32;whether a dialog is busy\,&#32;and the extension mode\.

**Expected query result in a fresh TUI session\:**

~~~json
{
  "revision": 0,
  "status": "draft",
  "authorized": false,
  "busy": false,
  "mode": "tui"
}
~~~

The tool duplicates its structured result as JSON text in&#32;`content`\,&#32;so its machine contract is also model\-visible\.

`show`&#32;refreshes the status\/widget and sends a custom review message\.&#32;It does not increment the draft revision\,&#32;but it is not completely journal\-inert\:&#32;the custom message is conversation data\.

**Observed\:**&#32;the loaded SDK tool exposed&#32;`discover`\,&#32;`inspect`\,&#32;`query`&#32;and&#32;`act`\,&#32;and refused an unauthorized mutation without changing the draft\.

**Exercise\:**&#32;is a status widget a substitute for&#32;`inspect`\?

**Answer\:**&#32;no\.&#32;The widget is a short human summary\.&#32;`inspect`&#32;is the supported way to retrieve the actual domain state\.

### Know the mode before opening a review

The same property can exist in every mode while doing useful work in only some of them\.

| Host | What Review Desk can use | Degraded behavior |
| --- | --- | --- |
| TUI | Standard editor\/selector dialogs\,&#32;status\,&#32;string widget\,&#32;custom message renderer and native overlay | Requires real terminal presentation and input |
| RPC | Editor\,&#32;confirm and labeled select requests answered by a connected client\;&#32;passive status\/widget frames | No native component factory or synchronous composer read |
| ACP | The supplied ACP adapter exposes semantic forms and initializes extensions with&#32;`mode: "rpc"` | Form support depends on negotiated&#32;`elicitation.form`\;&#32;terminal presentation remains unavailable |
| Default print\/JSON context | Read tools and explanatory custom messages | The example refuses its interactive review\,&#32;overlay and delegation paths |

**Source\-backed ACP nuance\:**&#32;the ACP extension context can report UI availability even when form elicitation is unsupported\.&#32;Its dialog methods then return defaults\.&#32;Because Review Desk accepts&#32;`mode: "rpc"`\,&#32;an ACP&#32;`review`&#32;with no form capability can receive&#32;`undefined`&#32;from&#32;`editor()`&#32;and record a&#32;**cancelled review outcome**\.&#32;Delegation receives false and grants nothing\.

That differs from the explicit print\-mode guard\,&#32;which changes neither draft nor authorization\.&#32;The recorded Review Desk protocol scenario exercised RPC\,&#32;not a Review Desk\-specific ACP client flow\.

### Milestone\:&#32;edit and accept locally

Imani asks for a review rather than directly replacing the draft\.

**Human OMP slash command\:**

~~~text
/review-desk review
~~~

The standard flow is\:

1. Open a multiline editor with sanitized draft text\.
2. Edit the note\.
3. Submit the editor\.
4. Choose&#32;**Accept locally**\,&#32;**Reject**\,&#32;or&#32;**Cancel**\.

In the TUI multiline editor\,&#32;Enter inserts a newline\.&#32;Submit with the editor’s configured follow\-up chord—by default Ctrl\+Q or Ctrl\+Enter\.&#32;The core editor also exposes its normal Ctrl\+G external\-editor shortcut\;&#32;this extension does not invoke an external editor programmatically\.

For the following checkpoint\,&#32;use this text\:

**Text to enter in the review editor\:**

~~~text
Release 1.4
A human-reviewed local note. Nothing is published.
~~~

Choose&#32;**Accept locally**\.

**Observed RPC checkpoint\:**&#32;the edited text became revision&#32;`1`\,&#32;status&#32;`accepted`\,&#32;with&#32;`authorized: false`\.&#32;The selector carried labels and aligned descriptions\.&#32;Status and string\-widget frames were emitted\.

The original&#32;`release-note.txt`&#32;fixture was not overwritten\.&#32;The draft is reconstructed from custom session entries named&#32;`workbook-review-desk-state`\.

### Milestone\:&#32;rejection and cancellation are distinct outcomes

Imani next tries an edit she does not like\.&#32;Rather than hiding what happened\,&#32;the domain records a review outcome while preserving the pre\-dialog text\.

| Human outcome | Stored text | Status | Revision |
| --- | --- | --- | --- |
| Accept locally | Edited text | `accepted` | Increases once |
| Reject | Original pre\-dialog text | `rejected` | Increases once |
| Escape from editor | Original pre\-dialog text | `cancelled` | Increases once |
| Cancel or dismiss selector | Original pre\-dialog text | `cancelled` | Increases once |

**Observed\:**&#32;after the accepted revision\,&#32;rejection produced revision&#32;`2`\,&#32;and editor cancellation produced revision&#32;`3`\.&#32;Both retained the accepted note’s text\.

This is an important vocabulary distinction\:

- **Cancel a review\:**&#32;record that the review was cancelled\;&#32;revision increases\.
- **Abort presentation because authority was invalidated or the session moved\:**&#32;do not record a review on a different branch\.
- **Revoke agent authority\:**&#32;leave draft text\,&#32;status and revision unchanged\.

Blank or over\-12\,000\-character replacement text fails domain validation\.&#32;The tool additionally constrains its&#32;`text`&#32;parameter\.&#32;Rejection and cancellation cannot be used to smuggle replacement text through the domain function\.

**Exercise\:**&#32;after accepting revision&#32;`1`\,&#32;open the editor\,&#32;replace all text\,&#32;submit\,&#32;then choose Reject\.&#32;Which text remains\?

**Answer\:**&#32;the pre\-dialog revision\-1 text\,&#32;now with status&#32;`rejected`&#32;and revision&#32;`2`\.

### Milestone\:&#32;authorize one agent action on one revision

Imani wants the agent to shorten the draft once\.&#32;She does not want a permanent permission recovered from the transcript\.

**Human OMP slash command\:**

~~~text
/review-desk delegate
~~~

Confirm the dialog\.&#32;The grant is\:

- in memory only\;
- associated with the current session ID\;
- associated with the inspected numeric revision\;
- consumed by a successful mutation\.

The tool cannot grant itself permission\.

If you followed the accepted\/rejected\/cancelled sequence and&#32;`inspect`&#32;reports revision&#32;`3`\,&#32;the following is an exact next action\.

**Model tool arguments—call&#32;`review_desk`&#32;only after checking that the current revision is&#32;`3`&#32;and the human grant is active\:**

~~~json
{
  "op": "act",
  "action": "revise",
  "expectedRevision": 3,
  "text": "Agent revision, still local."
}
~~~

**Observed result\:**

~~~json
{
  "revision": 4,
  "status": "draft",
  "text": "Agent revision, still local.",
  "authorized": false,
  "published": false
}
~~~

If your revision differs\,&#32;inspect and use that revision instead\.&#32;Never substitute a guessed counter\.

Other agent actions are&#32;`accept`\,&#32;`reject`&#32;and&#32;`cancel`\.&#32;They preserve the inspected text and do not accept a&#32;`text`&#32;argument\.&#32;To change text\,&#32;use&#32;`revise`\.

A stale action fails without mutation\.&#32;A failed stale attempt does not consume the valid grant\;&#32;a successful action does\.&#32;A second successful attempt requires another human grant\.

An open dialog blocks tool mutations with&#32;`Review dialog open. Nothing changed.`

### Milestone\:&#32;revocation defeats a late positive answer

The first implementation of revocation had a classic asynchronous bug\:&#32;clearing the current grant did not invalidate a confirmation already awaiting a response\.&#32;A late positive answer could restore authority\.

Imani’s decision changes from “clear the value” to “retire the pending decision\.”

**Exact excerpt—authority invalidation in&#32;[Review Desk’s entry](<https://present-sketch-tp94.here.now/examples/review-desk/index.ts>)\:**

~~~ts
function invalidateAuthority(): void {
  generation++;
  grant = undefined;
  pending?.abort();
}

pi.on("session_before_switch", invalidateAuthority);
pi.on("session_before_branch", invalidateAuthority);
pi.on("session_before_tree", invalidateAuthority);
~~~

The handler remembers the generation before awaiting the dialog\.

**Exact excerpt—the delegation result is accepted only in the same generation\:**

~~~ts
if (verb === "delegate") {
  const approved = await ctx.ui.confirm(copy.grantTitle, copy.grantMessage, { signal: controller.signal });
  if (epoch !== generation) return;
  grant = approved ? { sessionId: ctx.sessionManager.getSessionId(), revision: state.revision } : undefined;
  present(ctx, state);
  ctx.ui.notify(approved ? `One agent change authorized for revision ${state.revision}.` : "No agent change authorized. Draft unchanged.");
  return;
}
~~~

**Human OMP slash command\:**

~~~text
/review-desk revoke
~~~

**Observed correction\:**&#32;revoking during a real pending RPC confirmation emitted a matching cancel frame\.&#32;A delayed positive response to the retired request did not restore authority\.&#32;The draft was unchanged\,&#32;and&#32;`busy`&#32;became false after the pending handler settled\.

The same invalidation machinery handles session switching\,&#32;branching\,&#32;tree navigation and shutdown\.&#32;A same\-session reload that uses the switch path also invalidates authority\.

#### Honest limits

- The numeric revision is a domain counter\,&#32;not Seed Desk’s session\-plus\-entry\-ID token\.
- Navigation hooks invalidate the grant so sibling revisions cannot retain it through the supported flow\.
- This is not protection against arbitrary in\-process code rewriting domain entries\.
- `stateFor()`&#32;skips stored entries that fail&#32;`isReviewState()`\.&#32;Unlike Seed Desk\,&#32;it does not fail closed on every malformed matching snapshot\.&#32;Treat this as a small teaching fixture\,&#32;not a complete corruption\-recovery system\.

**Exercise\:**&#32;revoke while a delegate confirmation is pending\,&#32;then have the client answer the old request positively\.

**Checkpoint\:**&#32;no authorization\,&#32;no draft mutation\,&#32;and no revived dialog\.

*Source\,&#32;snapshot 2026\-08\-29\:&#32;linked Review Desk files\;&#32;`packages/coding-agent/src/modes/rpc/rpc-mode.ts`\,&#32;`requestRpcDialog`\,&#32;`requestRpcEditor`\;&#32;`packages/coding-agent/src/modes/acp/acp-agent.ts`\,&#32;`createAcpExtensionUiContext`\,&#32;`#configureExtensions`\.*

## Review Desk\:&#32;a native panel and portable dialogs

Imani’s standard editor works in both terminal and RPC review flows\.&#32;Now she wants a compact local panel for quickly accepting or rejecting a long note\.

Her obstacle is portability\.&#32;A terminal component is not automatically a remotely inspectable form\.

She keeps the domain tool and standard dialogs\,&#32;then adds the panel as an optional TUI presentation\.

### Milestone\:&#32;open a focused native overlay

**Human OMP slash command—TUI only\:**

~~~text
/review-desk overlay
~~~

The supplied&#32;`ReviewPanel`&#32;supports\:

- Up\/Down to scroll\;
- `a`&#32;to accept locally\;
- `r`&#32;to reject\;
- Escape to cancel\.

The overlay does not edit the note\.&#32;Accepting from it retains the existing text\.

The body shows an eight\-line scrolling window\.&#32;Text rows are bounded to the smaller of the available width and 100 columns\.

**Exact excerpt—untrusted text handling in&#32;[panel\.ts](<https://present-sketch-tp94.here.now/examples/review-desk/panel.ts>)\:**

~~~ts
export function safeText(text: string): string {
  return replaceTabs(Bun.stripANSI(text)).replace(/[\u0000-\u0008\u000b-\u001f\u007f-\u009f\u202a-\u202e\u2066-\u2069]/g, "");
}
~~~

The note is data\.&#32;It is not trusted ANSI styling\,&#32;terminal control\,&#32;clipboard control or bidirectional layout instruction\.

#### Inspect progress

**Observed\:**

- The loader\-registered expanded custom renderer was checked at widths&#32;`0`\,&#32;`1`\,&#32;`4`\,&#32;`24`\,&#32;`80`&#32;and&#32;`160`\.
- Every rendered row stayed within the applicable bound\.
- Injected control sequences were removed\.
- Real controller\/TUI focus dispatch through a terminal emulator accepted the overlay\.
- Cleanup hid and disposed it\.
- The surrounding composer text remained unchanged\.
- Aborting a custom UI signal rejected the promise and removed the overlay\.

This was real controller and terminal\-emulator behavior with a surrounding mode fixture\.&#32;It was not a physical\-terminal visual audit or a full Seed Desk composer smoke\.

**Exercise\:**&#32;open the overlay\,&#32;scroll\,&#32;then Escape\.

**Checkpoint\:**&#32;the panel closes\;&#32;the domain records a cancelled review\.&#32;An externally aborted presentation caused by revocation\/navigation instead must not commit to another branch\.

### Choose semantic dialogs before custom controls

The portable baseline is\:

| Method | Result | Cancellation distinction |
| --- | --- | --- |
| `select(title, options, dialogOptions?)` | Selected&#32;**label**\,&#32;even for an option object | `undefined`&#32;on dismissal\/cancellation |
| `confirm(title, message, dialogOptions?)` | Boolean | Decline and cancellation both resolve false |
| `input(title, placeholder?, dialogOptions?)` | Text | `undefined`&#32;means no answer\;&#32;an empty string is a separate value |
| `editor(title, prefill?, dialogOptions?, editorOptions?)` | Multiline text | `undefined`&#32;means cancelled |
| Optional&#32;`askDialog(questions, dialogOptions?)` | A structured ask result | Can be cancelled\,&#32;submitted\,&#32;or redirected to chat |

`input`’s second argument is a placeholder\,&#32;not an initial document\.&#32;`editor`’s second argument is the prefill\.&#32;Its fourth argument may set&#32;`promptStyle`\.

Feature\-detect&#32;`askDialog`\;&#32;do not assume it exists because&#32;`hasUI`&#32;is true\.

### Rich ask data

A rich ask question contains\:

- `id`\:&#32;stable question identity\;
- `question`\:&#32;full question text\;
- optional&#32;`header`\;
- `options`\;
- optional&#32;`multi`\;
- optional zero\-based&#32;`recommended`\.

Each option has&#32;`label`\,&#32;optional&#32;`description`\,&#32;and optional&#32;`preview`\.

A submitted result has&#32;`kind: "submit"`&#32;and ordered&#32;`results`\.&#32;Each result item contains\:

- `id`\,&#32;`question`\;
- `options`&#32;as labels\;
- `multi`\;
- `selectedOptions`\;
- optional&#32;`customInput`\,&#32;`note`\,&#32;`timedOut`\.

`kind: "chat"`&#32;means the user chose to discuss the question\.&#32;It is not cancellation and it is not an answer\.

ACP’s rich ask adapter can produce recommended fallback answers marked&#32;`timedOut: true`\.&#32;A permission system must not interpret such a fallback as explicit consent\.

Descriptions and previews are presentation capabilities\,&#32;not a guarantee that every protocol adapter transmits them\.&#32;For example\,&#32;the supplied ACP basic select translates labels to an enum\;&#32;its rich ask path carries richer descriptions\.

### Dialog options are not universally portable

`ExtensionUIDialogOptions`&#32;includes all of the following\:

| Family | Members | Use |
| --- | --- | --- |
| Cancellation and timing | `signal`\,&#32;`timeout`\,&#32;`onTimeout`\,&#32;`onTimeoutStart`\,&#32;`onTimeoutReset` | Cancel work and observe a host\-managed timeout |
| Initial selector presentation | `initialIndex`\,&#32;`outline`\,&#32;`helpText` | Position and explain a TUI selector |
| Selector actions | `onLeft`\,&#32;`onRight`\,&#32;`onExternalEditor` | TUI\-specific callbacks |
| Selection markers | `selectionMarker`\,&#32;`checkedIndices`\,&#32;`markableCount` | Radio\/checkbox presentation for leading options |

Timeouts are milliseconds\.&#32;`timeoutStartsOnPresentation`&#32;is an optional UI capability\:&#32;the TUI sets it true so a queued selector’s timeout need not expire before it appears\.

Do not infer that every option applies to every method\.&#32;In particular\,&#32;the supplied standard multiline editor path honors cancellation but does not implement the same dialog timeout plumbing as the selector\/input paths\.

### RPC is semantic UI\,&#32;not a terminal

The current wired RPC extension context reports&#32;`hasUI: true`&#32;and&#32;`mode: "rpc"`\.&#32;This outranks the stale type comment saying RPC has no UI\.

The protocol uses requests and correlated responses\.

**RPC client input—request the human review command\:**

~~~json
{"type":"prompt","id":"review-1","message":"/review-desk review"}
~~~

The client must continue reading output\,&#32;present the emitted editor\/selector requests\,&#32;and reply using each request’s actual ID\.&#32;It must not wait for the review to finish before processing the dialog requests needed to finish it\.

**Illustrative RPC response shapes—`dialog-1`&#32;represents an ID received from the server\,&#32;not a reusable workbook ID\:**

~~~json
{"type":"extension_ui_response","id":"dialog-1","value":"Accept locally"}
~~~

~~~json
{"type":"extension_ui_response","id":"dialog-1","confirmed":true}
~~~

~~~json
{"type":"extension_ui_response","id":"dialog-1","cancelled":true}
~~~

Use the appropriate variant\,&#32;not all three\.

RPC specifics\:

- Select sends string&#32;`options`\;&#32;optional&#32;`optionDetails`&#32;align descriptions by position\.
- Select returns a label\,&#32;not an index\.
- Select\,&#32;confirm and input transmit timeout information\.
- Editor transmits&#32;`title`\,&#32;`prefill`&#32;and optional&#32;`promptStyle`\;&#32;it has no timeout field or local editor timeout scheduler in this adapter\.
- Aborting an active dialog emits&#32;`method: "cancel"`&#32;with&#32;`targetId`&#32;equal to the original request ID\.
- Pre\-aborted signals suppress presentation\.
- Disconnect rejects pending and future requests\.
- Local select\/confirm\/input timer expiry can settle without a cancel frame\,&#32;so clients must honor the transmitted timeout too\.

RPC supports fire\-and\-forget notifications\,&#32;status\,&#32;string\-array widgets and editor\-text requests\.&#32;Title emission is opt\-in through&#32;`PI_RPC_EMIT_TITLE=1`\.

It does not serialize TUI component factories\.&#32;`custom()`&#32;returns without invoking the factory\.&#32;Component widgets\,&#32;terminal listeners\,&#32;custom editor replacement and autocomplete factories are unsupported there\.

### Passive presentation is not persistence

| UI method | Useful role | Current implementation boundary |
| --- | --- | --- |
| `notify` | Brief result or warning | Not a durable domain record |
| `setStatus(key, text)` | Small named status indicator | Clear with&#32;`undefined` |
| `setWorkingMessage(message?)` | Streaming activity wording | TUI support\;&#32;RPC\/ACP inert |
| `setWidget(key, content, options?)` | Summary above\/below editor | TUI supports strings or factories\;&#32;RPC supports strings only |
| `setTitle` | Terminal\/window title | Not the persisted session name |
| `setFooter`\,&#32;`setHeader` | Advertised component replacement surfaces | No\-op in the supplied TUI controller\,&#32;as well as RPC\/ACP |
| `custom(factory, options?)` | Focused terminal component | Guard with&#32;`ctx.mode === "tui"` |

For string widgets\,&#32;the TUI takes the first ten supplied strings and adds a truncation notice when necessary\.&#32;This is not a promise that arbitrary long strings wrap into only ten terminal rows\.

`ExtensionCustomOptions`&#32;contains\:

- `overlay`\;
- static or lazy&#32;`overlayOptions`\;
- `onHandle`\,&#32;receiving an overlay handle\;
- `signal`\.

A component should implement&#32;`render(width)`&#32;and&#32;`invalidate()`\,&#32;optionally input handling and&#32;`dispose()`\.&#32;Cleanup must be idempotent\.&#32;Review Desk’s panel becomes inert after disposal\.

### What changes next\?

If Imani needs screen\-reader\-friendly or remotely operated review controls\,&#32;she should extend the&#32;**semantic domain interface**&#32;or use host\-supported forms\.&#32;A custom terminal drawing does not become accessible merely because it is visible\.

`review_desk inspect/query/act`&#32;is the agent\-facing interface\.&#32;The panel is one human\-facing view of that interface\.

*Source\,&#32;snapshot 2026\-08\-29\:&#32;`packages/coding-agent/src/extensibility/extensions/types.ts`\,&#32;UI interfaces\;&#32;`packages/coding-agent/src/modes/controllers/extension-ui-controller.ts`\,&#32;`showHookCustom`\,&#32;`#presentDialog`\;&#32;`packages/coding-agent/src/modes/rpc/rpc-mode.ts`\;&#32;linked Review Desk panel\.*

## Editors\,&#32;themes and composer shapes

Luis likes Review Desk’s separation between data and presentation\.&#32;His next goal is a more comfortable authoring environment\:&#32;a phrase shortcut\,&#32;a theme picker and a quieter composer frame\.

He chooses independent UI conveniences rather than changing the review tool’s meaning\.

The examples in this chapter are&#32;**new complete exercises**\,&#32;not additional files in the observed downloadable suite\.

### Milestone\:&#32;insert text without submitting it

**Complete TypeScript exercise—save as&#32;`phrase-key.ts`\:**

~~~ts
import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent";

export default function phraseKey(pi: ExtensionAPI): void {
    pi.registerShortcut("ctrl+shift+y", {
        description: "Paste a fictional-observation prefix",
        handler(ctx) {
            if (ctx.mode !== "tui") return;
            ctx.ui.pasteToEditor("Fictional observation: ");
        },
    });
}
~~~

**Terminal shell—from the directory containing that exercise\:**

~~~sh
omp --no-extensions -e ./phrase-key.ts
~~~

**Expected checkpoint\:**&#32;the key chord pastes text through the editor’s paste handling\.&#32;It does not submit a prompt or call a provider\.

The custom host already uses&#32;`ctrl+shift+x`&#32;for its built\-in autoresearch overlay\.&#32;This exercise uses&#32;`ctrl+shift+y`&#32;instead\:&#32;a valid chord can still lose to a later registration\.&#32;Verify the final handler in your host\,&#32;not merely that your factory registered it\.

`setEditorText()`&#32;replaces the editor text\.&#32;`pasteToEditor()`&#32;follows the TUI paste path\,&#32;including large\-paste handling\.&#32;`getEditorText()`&#32;reads the current TUI text\.

RPC’s&#32;`pasteToEditor()`&#32;falls back to a&#32;`set_editor_text`&#32;request\.&#32;Its synchronous&#32;`getEditorText()`&#32;returns an empty string\;&#32;the remote host must track its own composer state\.

**Exercise\:**&#32;should a timer call&#32;`pasteToEditor()`&#32;to force an agent turn\?

**Answer\:**&#32;no\.&#32;Editor mutation is not prompt submission\.&#32;Use an explicit message API if an agent turn is intended\,&#32;with the appropriate ownership and cost policy\.

### Milestone\:&#32;select a theme that actually exists

Luis does not hard\-code a theme name or invent a theme\-file schema\.&#32;He asks the host for its available themes\.

**Complete TypeScript exercise—save as&#32;`theme-picker.ts`\:**

~~~ts
import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent";

export default function themePicker(pi: ExtensionAPI): void {
    pi.registerCommand("workbook-theme", {
        description: "Choose an available TUI theme",
        async handler(_args, ctx) {
            if (ctx.mode !== "tui") {
                pi.sendMessage(
                    { customType: "workbook.theme", content: "Theme selection requires the TUI.", display: true },
                    { triggerTurn: false },
                );
                return;
            }
            const themes = await ctx.ui.getAllThemes();
            const choice = await ctx.ui.select("Workbook theme", themes.map(theme => theme.name));
            if (choice === undefined) return;
            const result = await ctx.ui.setTheme(choice);
            ctx.ui.notify(
                result.success ? `Theme selected: ${choice}` : result.error ?? "Theme selection failed.",
                result.success ? "info" : "warning",
            );
        },
    });
}
~~~

**Human OMP slash command\,&#32;after loading the file\:**

~~~text
/workbook-theme
~~~

`getTheme(name)`&#32;loads a theme without selecting it\.&#32;`ui.theme`&#32;is the current styling object\.

Although the type permits&#32;`setTheme(Theme)`\,&#32;the supplied TUI implementation accepts names and returns failure for a direct theme object\.&#32;RPC and ACP return theme\-switching failures and no available theme list\.

For authoring a new declarative theme file\,&#32;start from the matching host’s actual theme schema and a real theme file\.&#32;That schema is not included in the supplied extension evidence\,&#32;so this workbook does not invent theme JSON keys\.

**Exercise\:**&#32;cancel the picker\.

**Checkpoint\:**&#32;no theme change\.&#32;A present method or a successful headless no\-op would not prove theme switching\.

### Milestone\:&#32;register a composer shape

A composer shape owns the editor’s frame\,&#32;not its domain state\.

**Complete TypeScript exercise—save as&#32;`workbook-dock.ts`\:**

~~~ts
import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent";
import type { ComposerStyle } from "@oh-my-pi/pi-tui";

const style: ComposerStyle = {
    id: "workbook-field-dock",
    sideBorders: false,
    verticalChrome: 1,
    statusAttachment: "none",
    bottomBar: "full",
    bottomBarGap: true,
    defaultPromptGutter: "> ",
    defaultPaddingX: () => 0,
    sideChromeWidth: () => 0,
    renderTop: ({ box, width, borderColor }) =>
        borderColor(box.horizontal.repeat(width)),
    renderRow: ({ gutter, text, pad }) => [gutter + text + pad],
    renderBottom: () => undefined,
};

export default function workbookDock(pi: ExtensionAPI): void {
    pi.registerComposerShape({
        label: "Workbook Field Dock",
        description: "A single rule above the prompt",
        style,
    });
}
~~~

Load it explicitly\,&#32;then choose&#32;**Workbook Field Dock**&#32;in Appearance → Composer Shape\.

Registration adds a choice\;&#32;it does not automatically select it\.

The shape contract includes\:

- `id`\,&#32;persisted as&#32;`composer.shape`\;
- `sideBorders`\,&#32;which affects cursor reserve\,&#32;IME behavior and scrollbar layout\;
- `verticalChrome`\,&#32;the exact fixed chrome\-row count\;
- `statusAttachment`\:&#32;`top-border`\,&#32;`top-rule-chip`&#32;or&#32;`none`\;
- `bottomBar`\:&#32;`none`\,&#32;`left`&#32;or&#32;`full`\;
- `bottomBarGap`\;
- `defaultPromptGutter`\;
- `defaultPaddingX()`&#32;and&#32;`sideChromeWidth()`\;
- `renderTop()`\,&#32;`renderRow()`&#32;and&#32;`renderBottom()`\.

Renderer contexts supply width\,&#32;padding\,&#32;box glyphs and border\/accent\/surface styling functions\.&#32;Row contexts also supply the already\-rendered&#32;`gutter`\,&#32;`text`\,&#32;`pad`\,&#32;last\-row information\,&#32;cursor overflow\,&#32;IME\-safe\-tail state and scrollbar\-thumb state\.

Preserve those prepared content pieces\.&#32;Do not reflow or arbitrarily truncate them inside frame code\.&#32;Normal rows must occupy the expected visible width\;&#32;ANSI bytes are not visible columns\.

Built\-in IDs cannot be replaced\:

`box`\,&#32;`claude`\,&#32;`pi`\,&#32;`borderless`\,&#32;`rule`\,&#32;`field`\,&#32;`rail`\.

If the configured extension shape is unavailable\,&#32;the editor falls back to&#32;`box`\.

**Exercise\:**&#32;test the shape at a narrow width and with a long line ending at the cursor\.

**Checkpoint\:**&#32;no unexpected wrapping caused by frame\-width miscalculation\.&#32;A pure&#32;`renderRow()`&#32;unit test alone does not establish live editor\/IME behavior\.

### Milestone\:&#32;replace the editor only when a shortcut is insufficient

A custom editor must be a&#32;**`CustomEditor`&#32;subclass**\,&#32;not merely a plain TUI&#32;`Editor`&#32;or an arbitrary component\.&#32;The host expects application action keys and callbacks\.

This source\-checkout exercise uses the supplied class’s exact repository location\.&#32;Save it at the matching repository root and use a host built from that same checkout\;&#32;do not mix a copied source class with an unrelated binary\.

**Complete TypeScript source\-checkout exercise—save as&#32;`workbook-editor.ts`\:**

~~~ts
import type { ExtensionAPI } from "./packages/coding-agent/src/extensibility/extensions/types";
import { CustomEditor } from "./packages/coding-agent/src/modes/components/custom-editor";
import { matchesKey } from "@oh-my-pi/pi-tui";

class FictionalEditor extends CustomEditor {
    override handleInput(data: string): void {
        if (matchesKey(data, "ctrl+shift+y")) {
            this.insertText("[fictional] ");
            this.tui?.requestRender();
            return;
        }
        super.handleInput(data);
    }
}

export default function workbookEditor(pi: ExtensionAPI): void {
    pi.registerCommand("workbook-editor", {
        description: "Use the fictional-prefix editor: on | off",
        async handler(args, ctx) {
            if (ctx.mode !== "tui") {
                ctx.ui.notify("Custom editors require the TUI.", "warning");
                return;
            }
            if (args.trim() === "on") {
                ctx.ui.setEditorComponent(
                    (tui, theme, keybindings) => new FictionalEditor(tui, theme, keybindings),
                );
            } else if (args.trim() === "off") {
                ctx.ui.setEditorComponent(undefined);
            } else {
                ctx.ui.notify("Use /workbook-editor on or /workbook-editor off.", "warning");
            }
        },
    });
}
~~~

**Terminal shell—from the matching source repository root\:**

~~~sh
omp --no-extensions -e ./workbook-editor.ts
~~~

Do not load this and the phrase\-key exercise together\:&#32;they intentionally demonstrate two ways to own the same chord\.

**Expected checkpoint\:**&#32;ordinary typing and application shortcuts still pass to&#32;`super.handleInput()`\.&#32;Turning the editor off restores the default editor\.

Custom input behavior needs real composer tests\,&#32;including paste\,&#32;Escape\,&#32;autocomplete and submission\.&#32;Avoid adding a global raw\-input listener merely to implement an editor\-local key\.

### Autocomplete\,&#32;terminal listeners and renderers

`addAutocompleteProvider(factory)`&#32;wraps the current provider\.&#32;A good wrapper\:

- handles only its own prefix\;
- delegates other suggestions and completion application\;
- preserves built\-in slash\/file completion\;
- tolerates being reapplied when command metadata refreshes\.

The supplied controller stacks factories in registration order\.&#32;Contract tests cover healthy wrappers surviving a broken sibling factory\.&#32;Headless adapters accept and ignore these factories\.

`onTerminalInput(handler)`&#32;is lower\-level\.&#32;A handler may return&#32;`{ consume: true }`&#32;or replacement&#32;`data`\;&#32;it returns an unsubscribe function\.&#32;Use it only in TUI mode\,&#32;clean it up\,&#32;and do not confuse terminal input with desktop\-wide keystroke control\.

`getToolsExpanded()`&#32;and&#32;`setToolsExpanded()`&#32;control TUI tool\-output expansion\.&#32;RPC\/ACP return false and ignore the setter\.

A supplemental thinking renderer can add presentation after already\-visible thinking text\.

**Complete TypeScript exercise—save as&#32;`thinking-length.ts`\:**

~~~ts
import type { ExtensionFactory } from "@oh-my-pi/pi-coding-agent";
import { Container, Text } from "@oh-my-pi/pi-tui";

const extension: ExtensionFactory = pi => {
    pi.registerAssistantThinkingRenderer((context, theme) => {
        const container = new Container();
        container.addChild(new Text(theme.fg("dim", `thinking chars: ${context.text.length}`), 0, 0));
        return container;
    });
};

export default extension;
~~~

Its context includes&#32;`contentIndex`\,&#32;`thinkingIndex`\,&#32;visible&#32;`text`&#32;and&#32;`requestRender()`\.&#32;It must not mutate the message\.&#32;It does not reveal otherwise unavailable reasoning\,&#32;and it does not change provider context\.

Custom message renderers\,&#32;thinking renderers and tool renderers are different extension points\.&#32;Review Desk uses the first\;&#32;Seed Desk stage 3 uses a tool\-result renderer\.

*Source\,&#32;snapshot 2026\-08\-29\:&#32;`packages/tui/src/components/composer/types.ts`\,&#32;`ComposerStyle`\;&#32;`packages/coding-agent/src/modes/components/custom-editor.ts`\,&#32;`CustomEditor`\;&#32;`packages/coding-agent/src/modes/controllers/extension-ui-controller.ts`\;&#32;`packages/coding-agent/test/issue-4919-extension-autocomplete-provider.test.ts`\;&#32;`packages/coding-agent/examples/extensions/thinking-note.ts`\.*

## Package Lab\:&#32;one file to an embedded host

Theo maintains fictional field observations\.&#32;His first notebook fits in one file\.&#32;Later he wants reusable helpers\,&#32;optional package features and an application that embeds OMP\.

His obstacle is architectural\:&#32;an imported helper\,&#32;an extension entry and an installed plugin are not the same thing\.

[Package Lab’s complete instructions](<https://present-sketch-tp94.here.now/examples/package-lab/README.md>)&#32;accompany the&#32;[source bundle](<https://present-sketch-tp94.here.now/downloads/extensions-examples.zip>)\.

### Milestone\:&#32;one file is enough

Files\:

- [Single\-file entry](<https://present-sketch-tp94.here.now/examples/package-lab/single/field-notes.ts>)
- [Package Lab root manifest](<https://present-sketch-tp94.here.now/examples/package-lab/package.json>)

**Terminal shell\:**

~~~sh
omp --no-extensions -e "$EXAMPLES/package-lab/single/field-notes.ts"
~~~

**Human OMP slash command\:**

~~~text
/field-notes reed
~~~

**Model tool arguments—call&#32;`field_notes`\:**

~~~json
{"op":"discover"}
~~~

~~~json
{"op":"inspect","value":"reed"}
~~~

~~~json
{"op":"query","value":"marsh"}
~~~

**Observed discovery details\:**

~~~json
{
  "operations": ["discover", "inspect", "query"],
  "ids": ["reed", "fern"],
  "writes": false
}
~~~

Inspection returns the fictional reed beside the footbridge\.

The tool lowercases and trims its search value\.&#32;Query searches ID\,&#32;habitat and text\.&#32;The slash command is narrower\:&#32;it lists all notes or matches an exact lowercased ID\.

Unknown tool inspection IDs throw\.&#32;Unknown human IDs produce a warning\.

This entry does not declare&#32;`approval`&#32;or&#32;`loadMode`\.&#32;Therefore the current defaults apply\:&#32;an execution\-tier approval declaration and discoverable presentation\,&#32;despite the domain being read\-only\.&#32;Descriptions do not set approval policy\.

The command uses notifications without a headless message fallback\.&#32;Its machine tool remains useful without the notification\.

**Exercise\:**&#32;query&#32;`woodland`\.

**Checkpoint\:**&#32;the tool finds&#32;`fern`\.&#32;Do not assume&#32;`/field-notes woodland`&#32;performs the same full\-text query\;&#32;it is an ID\-oriented human command\.

### Milestone\:&#32;helpers become ordinary imports

Theo adds a current selection\.&#32;He wants both&#32;`/notebook fern`&#32;and a tool act to change the same selection\,&#32;without duplicating lookup logic\.

Files\:

- [Multi\-file entry](<https://present-sketch-tp94.here.now/examples/package-lab/multi/index.ts>)
- [Notebook helper](<https://present-sketch-tp94.here.now/examples/package-lab/multi/notebook.ts>)

**Terminal shell—after exiting the single\-file launch\:**

~~~sh
omp --no-extensions -e "$EXAMPLES/package-lab/multi"
~~~

The directory resolves to&#32;`index.ts`\.&#32;`notebook.ts`&#32;is imported as a helper\;&#32;it is not independently bound as another extension\.

**Complete helper source—[`multi/notebook.ts`](<https://present-sketch-tp94.here.now/examples/package-lab/multi/notebook.ts>)\:**

~~~ts
export const notes = [
    { id: "reed", text: "Fictional reeds border the marsh trail." },
    { id: "fern", text: "Fictional ferns shade the woodland path." },
] as const;

export function inspectNote(id: string): (typeof notes)[number] {
    const note = notes.find(candidate => candidate.id === id);
    if (!note) throw new Error(`Unknown note id: ${id}. Discover ids before selecting.`);
    return note;
}
~~~

**Model tool arguments—call&#32;`notebook`\,&#32;one at a time\:**

~~~json
{"op":"discover"}
~~~

~~~json
{"op":"inspect","id":"reed"}
~~~

~~~json
{"op":"act","id":"reed"}
~~~

~~~json
{"op":"query"}
~~~

**Human OMP slash commands\:**

~~~text
/notebook fern
/notebook
~~~

**Observed\:**&#32;command and tool shared the factory’s state\.&#32;An invalid action left the earlier selection intact\.&#32;Exact completed arguments returned no completion\.

#### The lifetime correction

The selection is declared inside the factory\:

**Exact excerpt from&#32;[multi\/index\.ts](<https://present-sketch-tp94.here.now/examples/package-lab/multi/index.ts>)\:**

~~~ts
// Factory-local state belongs to one binding, not Bun's cached module.
let selected: string | null = null;
~~~

That prevents this variable from being shared merely because a cached module is rebound\.&#32;It does&#32;**not**&#32;make it transcript\-local\.

**Observed SDK lifecycle checks\:**

| Operation | Transcript identity | Factory selection |
| --- | --- | --- |
| Select&#32;`reed` | Unchanged | `reed` |
| `newSession()` | Changes | Still&#32;`reed` |
| Select&#32;`fern`\,&#32;then&#32;`switchSession()` | Changes to a real target header | Still&#32;`fern` |
| Construct a separate SDK host | Separate binding | Starts at&#32;`null` |
| Rebind prepared factories | Fresh runtime and extension objects | Starts at&#32;`null` |

The corrected teaching language is&#32;**factory\-local**&#32;or&#32;**binding\-local**\,&#32;not session\-local\.

The public helpers deliberately do not reset on&#32;`session_switch`&#32;and do not append selection entries\.

**Exercise\:**&#32;select&#32;`fern`\,&#32;use&#32;`/new`\,&#32;then run&#32;`/notebook`\.

**Checkpoint\:**&#32;within the reused binding\,&#32;it remains selected\.&#32;If you want transcript\-local selection\,&#32;add explicit lifecycle reset or branch reconstruction in a new exercise\;&#32;do not claim the supplied stage already does that\.

### Milestone\:&#32;a manifest declares several entries

Theo now wants a catalog and a reusable prompt\.&#32;An optional summary tool should be available only when selected as a package feature\.

Files\:

- [Manifest](<https://present-sketch-tp94.here.now/examples/package-lab/manifest/package.json>)
- [Catalog entry](<https://present-sketch-tp94.here.now/examples/package-lab/manifest/entries/catalog.ts>)
- [Resource entry](<https://present-sketch-tp94.here.now/examples/package-lab/manifest/entries/resources.ts>)
- [Optional summary entry](<https://present-sketch-tp94.here.now/examples/package-lab/manifest/entries/summary.ts>)
- [Prompt file](<https://present-sketch-tp94.here.now/examples/package-lab/manifest/prompts/field-observation.md>)

**Exact config file—[`manifest/package.json`](<https://present-sketch-tp94.here.now/examples/package-lab/manifest/package.json>)\:**

~~~json
{
  "name": "omp-workbook-field-notes",
  "version": "1.0.0",
  "private": true,
  "type": "module",
  "description": "Fictional field notes with an optional plugin summary",
  "omp": {
    "extensions": ["./entries/catalog.ts", "./entries/resources.ts"],
    "features": {
      "summary": {
        "description": "Add the fictional habitat summary tool",
        "default": false,
        "extensions": ["./entries/summary.ts"]
      }
    }
  }
}
~~~

**Terminal shell—load the base manifest entries\:**

~~~sh
omp --no-extensions -e "$EXAMPLES/package-lab/manifest"
~~~

The base catalog registers&#32;`field_catalog`&#32;and&#32;`/field-catalog`\.

**Model tool arguments—call&#32;`field_catalog`\:**

~~~json
{"op":"discover"}
~~~

~~~json
{"op":"query","habitat":"marsh"}
~~~

Explicit&#32;`-e`&#32;directory loading reads the base&#32;`extensions`&#32;list\.&#32;It does&#32;**not**&#32;select plugin features\.

To try the optional entry without installation\:

**Terminal shell\:**

~~~sh
omp --no-extensions -e "$EXAMPLES/package-lab/manifest" -e "$EXAMPLES/package-lab/manifest/entries/summary.ts"
~~~

**Model tool arguments—call&#32;`field_summary`\:**

~~~json
{}
~~~

**Expected result details\:**

~~~json
{"fictional":true,"habitats":["marsh","woodland"]}
~~~

`field_summary`&#32;has no&#32;`op`&#32;parameter\.

**Observed\:**&#32;default installed\-plugin feature resolution selected two entries\;&#32;enabling&#32;`summary`&#32;selected three\.&#32;Explicit base\-manifest directory discovery did not include the optional entry\.

Resource registration is explained in&#32;[Resources\,&#32;event buses\,&#32;MCP and Gemini manifests](<https://present-sketch-tp94.here.now/chapters/extensions-resources-event-buses-mcp-and-gemini-manifests#extensions-resources-event-buses-mcp-and-gemini-manifests>)\.&#32;Its runner\-level proof does not establish automatic prompt\-menu rendering\.

**Exercise\:**&#32;why not import&#32;`summary.ts`&#32;for its side effects from the base catalog\?

**Answer\:**&#32;that would undermine the feature\-selection boundary\.&#32;A feature is meaningful only if its code is not activated through another unconditional path\.

### Milestone\:&#32;embed a factory in an SDK host

Theo’s final goal is to include the notebook in another application without installing a global plugin\.

Files\:

- [Inline factory creator](<https://present-sketch-tp94.here.now/examples/package-lab/inline/factory.ts>)
- [Session construction](<https://present-sketch-tp94.here.now/examples/package-lab/inline/session.ts>)
- [Runnable host script](<https://present-sketch-tp94.here.now/examples/package-lab/inline/run.ts>)

`createFieldNotesExtension(name)`&#32;returns an&#32;`ExtensionFactory`\.&#32;The SDK’s&#32;`extensions`&#32;option takes&#32;**functions**\,&#32;not file paths\.&#32;File paths belong in&#32;`additionalExtensionPaths`\.

**Exact excerpt—session options in&#32;[inline\/session\.ts](<https://present-sketch-tp94.here.now/examples/package-lab/inline/session.ts>)\;&#32;use the linked complete module\:**

~~~ts
const result = await createAgentSession({
    cwd: scratchDirectory,
    agentDir: path.join(scratchDirectory, "agent"),
    authStorage: auth,
    modelRegistry: new ModelRegistry(auth, path.join(scratchDirectory, "models.yml")),
    settings: Settings.isolated(),
    sessionManager: SessionManager.inMemory(scratchDirectory),
    disableExtensionDiscovery: true,
    extensions: [createFieldNotesExtension("Fictional field notebook")],
    enableMCP: false,
    enableLsp: false,
    skipPythonPreflight: true,
    preloadedCustomToolPaths: [],
    skills: [], rules: [], contextFiles: [], promptTemplates: [], slashCommands: [],
    toolNames: [],
});
~~~

The complete module constructs an in\-memory auth store\,&#32;closes auth on failure\,&#32;and returns a&#32;`close()`&#32;function that disposes the session before closing auth\.

Install the&#32;**workbook\-compatible**&#32;SDK package into the environment that runs the script\.&#32;Package Lab marks it as an optional peer\;&#32;the example is private and is not presented as a registry\-published package\.

**Terminal shell—with that runtime dependency available\:**

~~~sh
bun "$EXAMPLES/package-lab/inline/run.ts"
~~~

The script prints metadata containing&#32;`providerPromptSent: false`\,&#32;the&#32;`inline_notebook`&#32;tool and the registered command names\,&#32;then disposes the host\.&#32;It never calls&#32;`session.prompt()`\.

That is not a blanket no\-network guarantee for arbitrary host startup\:&#32;model registries and other host configuration can perform discovery\.&#32;The recorded isolated run had network denied\.

#### Binding is not initialization

`createAgentSession()`&#32;builds the session and binds factories\.&#32;A mode adapter subsequently initializes the extension runner’s live actions\/UI\.

The public metadata script does not pretend to be a full TUI\/RPC host\.&#32;Its notebook tool uses closure state\,&#32;so the proof could exercise it through the real adapter without needing message actions\.&#32;An embedding that uses&#32;`appendEntry()`\,&#32;message delivery\,&#32;dialogs or session actions must wire the real runtime—not replace those methods with successful no\-ops\.

The inline tool’s&#32;`discover`\,&#32;`inspect`&#32;and&#32;`query`&#32;all return its notebook contract and current selection\.&#32;It does not implement a separate per\-note inspection record\.&#32;`act`&#32;requires&#32;`reed`&#32;or&#32;`fern`\.

#### Prepared factories versus bound instances

Prepared factories can be rebound to a fresh runtime\.&#32;Already\-bound extension instances close over their original API and must not be forwarded to an independently constructed SDK session\.

The CLI’s early preload is a special same\-owner optimization for flag parsing\.&#32;It is not a general pattern for sharing a parent’s loaded instances with a child\.

**Exercise\:**&#32;share a module\-scope mutable selection between two SDK hosts\.&#32;What risk have you introduced\?

**Answer\:**&#32;the module may be cached\,&#32;so both factory bindings can reach the same mutable variable\.&#32;Put binding state inside the factory\,&#32;and use explicit storage for any state intentionally shared across bindings\.

*Source\,&#32;snapshot 2026\-08\-29\:&#32;linked Package Lab files\;&#32;`packages/coding-agent/src/sdk.ts`\,&#32;`CreateAgentSessionOptions`\,&#32;`createAgentSessionScoped`\;&#32;`packages/coding-agent/src/extensibility/extensions/loader.ts`\,&#32;`loadExtensions`\,&#32;`bindPreparedExtensions`\;&#32;recorded loading\/rebinding scenarios\.*

## Discovery\,&#32;installation and reload

Theo can now distribute a working folder\,&#32;but another maintainer cannot find the tool\.&#32;The obstacle is not TypeScript\:&#32;it is discovery scope\.

He separates four questions\:

1. Is the package installed or linked\?
2. Is its entry selected by discovery\?
3. Did the factory bind successfully\?
4. Is the tool enabled and presented in the expected way\?

### Creation and loading formats

| Format | Exact entry contract | Typical use |
| --- | --- | --- |
| Single&#32;`.ts`&#32;or&#32;`.js`&#32;file | Default factory | First extension or tightly focused integration |
| Async default factory | Returns&#32;`Promise<void>` | Read adjacent assets before registration |
| Directory with&#32;`index.ts`&#32;\/&#32;`index.js` | Default factory in the selected index | Entry plus ordinary helpers |
| Directory with&#32;`package.json` | `omp.extensions`\;&#32;legacy&#32;`pi.extensions`&#32;fallback | Several explicit entries |
| Explicit&#32;`.mjs`&#32;\/&#32;`.cjs`&#32;file | ESM default factory or compatible CommonJS factory export | JavaScript distribution without TypeScript |
| Installed plugin manifest | Base entries plus enabled feature entries | Managed distribution |
| SDK inline factory | Function passed to&#32;`extensions` | Host\-owned composition |
| Prepared factory rebinding | Imported factory called against a fresh API\/runtime | Avoid repeated module evaluation without sharing bound state |

For normal ESM modules\,&#32;an arbitrary named export is not enough\.

**Complete JavaScript exercise—save as&#32;`format-note.mjs`\:**

~~~js
export default function formatNote(pi) {
    pi.registerCommand("format-note", {
        description: "Identify the explicitly loaded module format",
        async handler(_args, ctx) {
            ctx.ui.notify("Explicit ESM extension loaded.", "info");
        },
    });
}
~~~

**Complete CommonJS exercise—alternative file&#32;`format-note.cjs`\;&#32;do not load both alternatives together\:**

~~~js
module.exports = function formatNote(pi) {
    pi.registerCommand("format-note", {
        description: "Identify the explicitly loaded module format",
        async handler(_args, ctx) {
            ctx.ui.notify("Explicit CommonJS extension loaded.", "info");
        },
    });
};
~~~

**Terminal shell—choose one explicit file\:**

~~~sh
omp --no-extensions -e ./format-note.mjs
~~~

### Scanning and manifest resolution are not identical

Native and configured directory scanning automatically discover direct&#32;`.ts`\/`.js`&#32;files and immediate child entry packages\.&#32;They do not recursively import arbitrary helpers\.

Installed\-plugin manifest expansion is broader\:&#32;it supports&#32;`.ts`\,&#32;`.js`\,&#32;`.mjs`&#32;and&#32;`.cjs`\,&#32;and corresponding index files\.

There are additional implementation differences worth knowing\:

- Native discovery uses globbing with gitignore\/hidden filtering\;&#32;explicit configured\-directory scanning uses&#32;`readdir`&#32;and does not apply that same filtering\.
- Native child manifests with declared entries suppress index fallback\.
- Installed\-plugin directory manifests are authoritative even when a declared entry is missing\;&#32;a stale index is not substituted\.
- The explicit\-directory resolver falls back to an index when no declared entry survives its existence checks\.
- Installed\-plugin scanning excludes declaration files\.&#32;Do not place&#32;`.d.ts`&#32;files in loose native\/configured scan directories and assume every scanner excludes them\.
- For portable manifests\,&#32;list concrete entry files\.&#32;Do not depend on every loader resolving directory\-valued manifest entries in exactly the same way\.

### Where native extension factories come from

The ambient native project root is&#32;**cwd\-only**\:

- `<cwd>/.omp/extensions`
- the active agent directory’s&#32;`extensions`&#32;folder

The default user agent directory is&#32;`~/.omp/agent`\.&#32;A named profile uses&#32;`~/.omp/profiles/<name>/agent`\;&#32;`PI_CODING_AGENT_DIR`&#32;can override it\.

Native discovery also reads legacy&#32;`settings.json`&#32;extension lists\.&#32;The main startup path adds merged&#32;`extensions`&#32;settings\,&#32;normally from the active agent&#32;`config.yml`&#32;and project configuration\.

Plain&#32;`<project>/extensions`&#32;is not the same as&#32;`<project>/.omp/extensions`\.&#32;The former needs explicit\/configured loading\;&#32;the latter is a native ambient root\.

Legacy&#32;`pi`&#32;package metadata and some&#32;`.pi`&#32;compatibility lookups remain supported\.&#32;`.pi/extensions`&#32;is not a native ambient module root in this snapshot\.

Skill ancestor traversal must not be used to infer extension\-module traversal\.

### Ordered inputs and two layers of deduplication

The module discovery pipeline appends\:

1. native extension\-module capability items\;
2. discovered JS\/TS hook factories\;
3. enabled installed\-plugin extension entries\;
4. explicit\/configured paths\.

Within the SDK’s configured lane\,&#32;CLI additional paths precede settings paths\.

There are two different identity operations\:

- Capability discovery can deduplicate by derived extension&#32;**name**\.&#32;Native project items precede user items\.
- The final path list deduplicates normalized absolute&#32;**paths**\,&#32;first seen wins\.

The final path deduplication is not a universal realpath\-equivalence guarantee\.&#32;Two different symlink spellings are not necessarily one path\-list identity\.

Imports may be prepared concurrently\,&#32;but factories are bound sequentially in path order\.&#32;Do not rely on module\-scope side effects occurring in factory order\.

### Explicit\-only and trusted\-file loading

`--no-extensions`&#32;means explicit\-only for this extension\-factory path and its OMP extension\-package sibling roots\.&#32;Explicit&#32;`-e`\,&#32;`--extension`&#32;and&#32;`--hook`&#32;entries remain eligible\.

It is not full isolation\.&#32;Other tools\,&#32;skills\,&#32;rules\,&#32;prompts and MCP discovery families have their own controls\.

`--trusted-extension`&#32;is stricter about&#32;**entry selection**\:

- it requires an existing&#32;**absolute module file path**\;
- it rejects directories\;
- it canonicalizes the selected file’s real path\;
- it cannot be combined with&#32;`-e`\,&#32;`--extension`&#32;or&#32;`--hook`\;
- a trusted entry load failure aborts that startup path rather than merely becoming an ordinary per\-module warning\.

**Terminal shell—trusted\-file selection using the actual absolute example path\:**

~~~sh
omp --trusted-extension "$EXAMPLES/package-lab/single/field-notes.ts"
~~~

Do not copy the relative trusted\-path form from older example prose\:&#32;the supplied current&#32;`parseArgs()`&#32;requires an absolute path\.

Trusted\-file selection does&#32;**not**\:

- sandbox the factory\;
- authenticate its dependencies\;
- restrict ordinary imports\;
- remove in\-process filesystem or network authority\;
- create a universal prohibition on unrelated discovery systems\.

### Disable an entry without deleting it

A derived ID looks like\:

- `extension-module:field-notes`&#32;for&#32;`field-notes.ts`\;
- `extension-module:multi`&#32;for&#32;`multi/index.ts`\.

**Config file—an example setting in the active agent’s&#32;`config.yml`\:**

~~~yaml
disabledExtensions:
  - extension-module:multi
~~~

In normal settings\-aware discovery\,&#32;this filters the directory\-discovered&#32;`multi/index.ts`&#32;entry\.&#32;An explicitly configured&#32;**file**&#32;remains an override in this build\.

**Observed\:**&#32;the disabled directory case selected zero entries\;&#32;the explicit file case selected one\.

Under&#32;`disableExtensionDiscovery`\,&#32;the SDK does not use the normal ambient disabled\-ID list for the explicit lane\.&#32;Do not use&#32;`--no-extensions`&#32;to test whether normal ambient disabling works\.

Other capability families use their own IDs in&#32;`disabledExtensions`\.&#32;A hook’s disabled identity is not automatically its extension\-module name\.

### Installation is an opt\-in change to your environment

The workbook requires no plugin installation\.&#32;Explicit&#32;`-e`&#32;is enough\.

If you intentionally want to try native plugin management\,&#32;these commands change the user plugin installation state\.&#32;The scratch agent directory above is not a guarantee that plugin storage is project\-local\.

**Terminal shell—optional developer installation\:**

~~~sh
omp plugin link "$EXAMPLES/package-lab/manifest"
omp plugin list --json
omp plugin features omp-workbook-field-notes
omp plugin features omp-workbook-field-notes --enable summary
~~~

A linked plugin starts with feature defaults\.&#32;`summary`&#32;defaults off\.

Launch normally\,&#32;without&#32;`--no-extensions`\,&#32;when checking ambient installed\-plugin discovery\.

`omp plugin install ./local-directory`&#32;routes to the same link operation\.&#32;It is not a package copy into the current project\.

Current installation boundaries\:

| Operation | What it does |
| --- | --- |
| `plugin link <directory>` | Symlinks into the user plugin&#32;`node_modules`&#32;tree and records runtime state |
| `plugin install <local-directory>` | Routes to link |
| npm or git installation | Runs package\-manager work and may fetch dependencies |
| `plugin install name@marketplace --scope project` | Uses the distinct marketplace project\-scope route |
| `plugin disable <name>` | Disables an installed plugin without removing its source |
| `plugin enable <name>` | Re\-enables it |
| `plugin uninstall <name>` | Removes installation\/runtime registration through the appropriate route |

Adding&#32;`--scope project`&#32;does not make local\/npm installation project\-scoped\.&#32;The local\/npm install handler warns that scope is supported only for marketplace installs\.

Runtime discovery can read both user and project plugin roots\.&#32;Enabled project packages shadow user packages with the same package name\.&#32;That does not imply every installer writes to the project root\.

### Features and settings

Install\-spec feature syntax is\:

- `package`\:&#32;default features\;
- `package[summary]`\:&#32;selected features\;
- `package[*]`\:&#32;all optional features\;
- `package[]`\:&#32;no optional features\.

Quote bracketed shell arguments so your shell does not expand them\.&#32;Use a real package spec\;&#32;the workbook’s private teaching package is not claimed to exist in a registry\.

Plugin feature metadata supports&#32;`description`\,&#32;`default`\,&#32;and additional&#32;`extensions`\,&#32;`tools`\,&#32;`hooks`&#32;and&#32;`commands`\.

Plugin settings support\:

- string\,&#32;number\,&#32;boolean and enum types\;
- descriptions and defaults\;
- `secret`&#32;display masking\;
- an&#32;`env`&#32;fallback declaration\;
- numeric&#32;`min`\,&#32;`max`\,&#32;`step`\;
- enum&#32;`values`\.

A settings declaration is not executable behavior\.&#32;The supplied&#32;`getPluginSettings()`&#32;helpers merge saved global\/project values\;&#32;they do not inject a settings object into&#32;`ExtensionAPI`&#32;or magically apply all defaults\/environment fallbacks to extension code\.&#32;A consumer must resolve and validate its settings deliberately\.

The CLI supports&#32;`plugin config list|get|set|delete|validate`\.&#32;In this snapshot\,&#32;the parsed&#32;`--local`&#32;flag is not forwarded into the manager’s setter\:&#32;do not advertise it as a reliable project\-local write route\.

Project overrides have their own file\:

**Config\-file exercise—`.omp/plugin-overrides.json`\,&#32;selecting the real example feature\:**

~~~json
{
  "features": {
    "omp-workbook-field-notes": ["summary"]
  }
}
~~~

The same override structure supports&#32;`disabled`&#32;package names and per\-package&#32;`settings`\.

`secret: true`&#32;is not secure credential storage\.&#32;Human CLI display may mask a value while JSON output still exposes it\.&#32;Keep real credentials out of source\,&#32;screenshots\,&#32;transcript details and published config\.

### Collisions have family\-specific rules

| Registration family | Current behavior |
| --- | --- |
| Same tool\/command name inside one factory | Later Map entry replaces earlier |
| Tools across extension instances | Registrations are retained\;&#32;effective lookup is later\-extension\-wins |
| Commands across extension instances | Effective command lookup is later\-extension\-wins |
| Aggregated built\-in command collision | Filtered with diagnostics |
| Event handlers | Append and run in registration\/load order\,&#32;subject to event\-specific short\-circuiting |
| Custom message renderer lookup | First extension with that type wins |
| Assistant thinking renderers | All returned components append in registration order |
| Flags and composer\-shape IDs | Aggregate last\-wins |
| Nonreserved shortcut collision | Warning\;&#32;later normalized key wins |
| Reserved shortcut | Rejected from the effective shortcut set |

Do not load all Seed Desk stages together and infer which version won from one notification\.

Registered CLI flags can shadow same\-named built\-ins in the extension\-aware parser\.&#32;Prefer namespaced flags so startup behavior is not surprising\.

The current CLI validates requested tool names against the fully discovered session registry\.&#32;Older custom\-tool documentation saying only built\-in names are validated is stale\.&#32;Still\,&#32;`--tools`\/`--no-tools`&#32;is not a universal isolation switch\:&#32;unrestricted extension\/custom tools have their own inclusion behavior\.

### Reload means what the host actually reloads

This snapshot contains a particularly important distinction\:

- `AgentSession.reload()`&#32;reopens the current session file through&#32;`switchSession()`\.
- The supplied TUI command\-context&#32;`reload()`&#32;calls that session reload and rebuilds presentation\.
- RPC\/ACP plugin\-refresh code clears caches and refreshes skills\/file\-command metadata\,&#32;but does not itself reimport every extension factory\.
- The module loader supports cache\-busted imports when a host actually invokes it again\.

Therefore\:

> For editing extension code\,&#32;restarting the explicit launch is the reliable workbook procedure\.&#32;Do not promise that&#32;`ctx.reload()`&#32;hot\-rebinds the factory\.

Treat&#32;`await ctx.reload(); return;`&#32;as the end of the current command handler\.&#32;Factory\-local notebook state need not reset on a session reload\.

The loader’s same\-process source cache\-busting also has platform\-specific behavior\.&#32;In particular\,&#32;the supplied compatibility loader notes a Windows&#32;`file://`&#32;query limitation\.&#32;A fresh process avoids treating an unverified hot\-reload path as proof\.

### Failure recovery and removal

An ordinary invalid export\,&#32;import failure or factory exception becomes a path\-specific loader error\;&#32;later modules still bind\.

Factory failure restores the queued provider\-registration list\,&#32;including earlier registrations the failed factory removed\.&#32;It is&#32;**not**&#32;a transaction over arbitrary side effects\.&#32;Recorded checks showed a failed factory’s flag default surviving that rollback\.

Inline&#32;`loadExtensionFromFactory()`&#32;rejects its promise instead of returning a per\-path loader error record\.

For recovery\:

1. Restart with only one explicit reviewed entry\.
2. Read the named load error\.
3. Check adjacent assets and dependencies\.
4. Restore a known\-good source version\.
5. Restart and inspect tool\/command metadata before invoking a mutation\.

For removal\:

- remove the explicit\/configured path\,&#32;or move the authored entry outside discovery\;
- disable\/uninstall an installed plugin through its installation route\;
- restart and verify that no second path still loads it\;
- keep session files unless you intentionally want to discard their data\.

**Terminal shell—optional installed\-plugin lifecycle checks\:**

~~~sh
omp plugin disable omp-workbook-field-notes
omp plugin enable omp-workbook-field-notes
omp plugin uninstall omp-workbook-field-notes
omp plugin list --json
~~~

Inspect the result\.&#32;A linked source directory remains your source\;&#32;uninstalling is not a request to delete that checkout\.

**Exercise\:**&#32;an extension remains active after disabling its plugin\.&#32;What is the first likely duplicate route to inspect\?

**Answer\:**&#32;an explicit&#32;`-e`&#32;or&#32;`extensions:`&#32;path\,&#32;or a loose copy in a native extension directory\.

*Source\,&#32;snapshot 2026\-08\-29\:&#32;`packages/coding-agent/src/extensibility/extensions/loader.ts`\;&#32;`packages/coding-agent/src/discovery/builtin.ts`\,&#32;`helpers.ts`\;&#32;`packages/coding-agent/src/extensibility/plugins/loader.ts`\,&#32;`manager.ts`\,&#32;`types.ts`\;&#32;`packages/coding-agent/src/cli/args.ts`\,&#32;`plugin-cli.ts`\;&#32;`packages/coding-agent/src/main.ts`\,&#32;`buildSessionOptions`\;&#32;`packages/coding-agent/src/session/agent-session.ts`\,&#32;`reload`\.*

## Tools\,&#32;interception and native delegation

Rowan wants extensions that an agent can use reliably\,&#32;not merely tools that appear in a list\.&#32;The obstacle is an attractive but weak interface\:&#32;“do something with this text” gives the model no stable targets\,&#32;no revision and no useful failure vocabulary\.

Rowan adopts the patterns already earned by Seed Desk and Review Desk\.

### Design a composable domain interface

A useful capability often separates\:

1. **Discover\:**&#32;what operations and targets exist\?
2. **Inspect\:**&#32;what is the exact object and contract\?
3. **Query\:**&#32;what is currently available\?
4. **Act\:**&#32;make a bounded change with explicit identity and preconditions\.
5. **Wait\:**&#32;observe a real asynchronous operation until a bounded deadline\.
6. **Diagnose\:**&#32;explain unavailable dependencies\,&#32;authority or failed progress\.

These are design patterns\,&#32;not six mandatory OMP method names\.

The supplied synchronous Seed Desk and Review Desk tools do not implement&#32;`wait`&#32;or&#32;`diagnose`\.&#32;Do not invent them in a request\.&#32;When adding real background work\,&#32;define a stable job ID and a truthful pending\/completed\/error contract before adding those operations\.

A good action result says\:

- which target changed\;
- what changed\;
- which revision now applies\;
- what is available next\;
- how to inspect the result\;
- whether retry is safe\.

It should never infer authorization from a note’s prose or from a rendered control\.

### The native tool signature

A registered extension tool executes as\:

**TypeScript API signature—reference\,&#32;not a standalone file\:**

~~~ts
execute(toolCallId, params, signal, onUpdate, ctx)
~~~

The third argument is the abort signal\.&#32;Some older repository examples use legacy parameter names in the wrong positions\;&#32;unused parameters can hide that mistake\.

Parameters may use\:

- `pi.zod`\:&#32;the injected Zod\-compatible omptype builder\;
- `pi.arktype`\:&#32;the injected native omptype builder\;
- `pi.typebox`\:&#32;the compatibility facade\.

Use the compatible injected builders rather than assuming any arbitrary upstream schema\-package version has identical behavior\.

Type validation and provider wire schema generation are related but distinct\.&#32;`strict`&#32;controls provider structured\-output grammar opt\-in\/out\;&#32;it does not replace runtime domain validation\.

### Tool fields\,&#32;without conflation

| Fields | Meaning |
| --- | --- |
| `name`\,&#32;`label`\,&#32;`description` | Machine identity\,&#32;human label and model\-facing contract |
| `parameters` | Runtime\/wire parameter schema |
| `execute` | Actual implementation |
| `hidden` | Excluded from normal initial inclusion unless explicitly selected |
| `defaultInactive` | Registered\,&#32;but not automatically enabled\;&#32;the extension owns later activation |
| `loadMode` | Presentation of an enabled tool\:&#32;**`essential`&#32;or&#32;`discoverable`&#32;only** |
| `deferrable` | The tool may stage changes requiring explicit resolve\/discard\;&#32;separate from discovery |
| `approval` | Static or argument\-dependent tier\/policy declaration |
| `strict` | Provider strict\-grammar choice\;&#32;explicit false is meaningful |
| `mcpServerName`\,&#32;`mcpToolName` | MCP provenance\/search metadata |
| `shellEnv` | Environment contribution for the supported user\-shell seam |
| `sourcePath` | Originating custom\-tool file metadata |
| `onSession` | Host\-dispatched tool lifecycle callback |
| `renderCall`\,&#32;`renderResult` | Optional TUI presentation |

There is no&#32;`loadMode: "deferred"`&#32;in this source\.

Discoverable tools are kept off the normal top\-level schema where the host’s discovery transport permits it—through&#32;`xd://`&#32;mounting or tool search\.&#32;That is presentation\,&#32;not disabled authority\.&#32;An essential tool stays top\-level\.

`getActiveTools()`&#32;is wired by the supplied TUI\/ACP adapters to the enabled set\,&#32;including discoverable tools\.&#32;It is not necessarily identical to&#32;`agent.state.tools`\,&#32;the directly presented top\-level array\.

`setActiveTools(names)`&#32;is asynchronous\.&#32;Await it\.&#32;Unknown names are ignored by the session setter\;&#32;inspect the resulting set rather than assuming every requested name exists\.

### Content\,&#32;details and streaming

Return\:

- `content`\:&#32;text\/image blocks intended for model\-visible results\;
- optional&#32;`details`\:&#32;structured host\/rendering metadata\;
- optional&#32;`isError`\:&#32;a nonthrowing failure indicator\.

Put decision\-critical machine fields in content too when the model needs them\.&#32;Review Desk’s JSON text is one approach\.

`onUpdate()`&#32;emits partial results\.&#32;It is progress\,&#32;not a commit receipt\.&#32;`renderResult()`&#32;receives&#32;`expanded`\,&#32;`isPartial`&#32;and optional&#32;`spinnerFrame`\;&#32;native extension renderers may also receive original arguments as a fourth parameter\.

A tool should\:

- check an already\-aborted signal\;
- pass cancellation into subprocess\/network work\;
- validate again before a side effect when asynchronous work intervenes\;
- distinguish cancellation before and after commit\.

### Milestone\:&#32;a standalone custom tool with real work

Rowan has an existing tool\-only package\.&#32;A custom\-tool factory is appropriate here\;&#32;it returns a tool instead of calling&#32;`registerTool()`\.

This complete exercise requires Git and a Git working tree\.&#32;It counts tracked TypeScript files\;&#32;it does not contact a remote\.

**Complete TypeScript exercise—save as&#32;`.omp/tools/workbook-repo-stats.ts`&#32;in a test repository\:**

~~~ts
import type { CustomToolFactory } from "@oh-my-pi/pi-coding-agent";

const factory: CustomToolFactory = pi => ({
    name: "workbook_repo_stats",
    label: "Workbook repository stats",
    description: "Count tracked TypeScript files in the current Git working tree.",
    approval: "exec",
    loadMode: "essential",
    parameters: pi.zod.object({
        glob: pi.zod.string().optional(),
    }),
    async execute(_callId, params, onUpdate, ctx, signal) {
        signal?.throwIfAborted();
        onUpdate?.({
            content: [{ type: "text", text: "Reading the tracked file list." }],
        });
        const result = await pi.exec("git", ["ls-files", "-z", "--", params.glob ?? "*.ts"], {
            cwd: ctx.sessionManager.getCwd(),
            signal,
            timeout: 5000,
        });
        if (result.killed) throw new Error("Tracked-file query was cancelled.");
        if (result.code !== 0) throw new Error(result.stderr || "git ls-files failed.");
        const files = result.stdout.split("\0").filter(Boolean);
        const details = { count: files.length, files };
        return {
            content: [{ type: "text", text: JSON.stringify(details) }],
            details,
        };
    },
});

export default factory;
~~~

**Model tool arguments—call&#32;`workbook_repo_stats`&#32;after discovery\/loading\:**

~~~json
{}
~~~

The legacy custom\-tool order is\:

**TypeScript API signature—standalone custom\-tool reference\:**

~~~ts
execute(toolCallId, params, onUpdate, ctx, signal)
~~~

The SDK bridge converts that order to the native extension order\.

The conservative&#32;`exec`&#32;approval tier reflects launching a program\.&#32;Host policy may require approval even though this particular Git operation reads metadata\.

`pi.exec()`&#32;takes a program plus argv\,&#32;not an automatically interpreted shell expression\.&#32;Its options are&#32;`signal`\,&#32;`timeout`&#32;in milliseconds and&#32;`cwd`\;&#32;results are&#32;`stdout`\,&#32;`stderr`\,&#32;`code`&#32;and&#32;`killed`\.

For native extensions\,&#32;`pi.exec()`&#32;defaults to the factory\-bound cwd\.&#32;Pass&#32;`ctx.cwd`&#32;explicitly when you need the current workspace after a move\.

**Expected checkpoint\:**&#32;the result’s count matches the returned file array\.&#32;Outside a Git repository\,&#32;the tool fails instead of returning a fake zero\.

**Exercise\:**&#32;abort during a slower subprocess version\.

**Checkpoint\:**&#32;the signal reaches the subprocess and&#32;`killed`&#32;is handled\;&#32;no success result is fabricated\.

#### Standalone\-tool boundaries

The loader also discovers metadata records in some tool directories\.&#32;A&#32;`.md`&#32;or&#32;`.json`&#32;tool record is not automatically an executable factory\.

Standalone custom\-tool name conflicts are rejected against built\-ins and previously loaded custom tools\.&#32;That differs from extension re\-registration\,&#32;which can intentionally replace a built\-in\.

The legacy custom\-tool type includes compatibility members that do not all survive the ordinary bridge\.&#32;In particular\,&#32;its approval\-formatting callback is not propagated through the supplied&#32;`customToolToDefinition()`&#32;path\,&#32;and that bridge does not forward original arguments to the legacy result renderer’s optional fourth argument\.

SDK&#32;`customTools`&#32;uses the legacy contract\;&#32;SDK&#32;`toolDefinitions`&#32;uses the native contract\.&#32;Restricted sessions exclude ambient extensions\/custom tools\.&#32;Explicit SDK tools require&#32;`allowRestrictedCustomTools: true`&#32;and selection in&#32;`toolNames`&#32;when used with&#32;`restrictToolNames: true`\.

### Milestone\:&#32;intercept a tool without rewriting its domain

Rowan wants&#32;`first`&#32;to be a deliberate alias for the reed note when inspecting through&#32;`field_notes`\,&#32;and wants an oversized query refused\.

This is an additional exercise\,&#32;not a change to the downloaded entry\.

**Complete TypeScript exercise—save as&#32;`field-guard.ts`\:**

~~~ts
import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent";

export default function fieldGuard(pi: ExtensionAPI): void {
    pi.on("tool_call", event => {
        if (event.toolName !== "field_notes") return;
        if (event.input.op === "inspect" && event.input.value === "first") {
            return { input: { op: "inspect", value: "reed" } };
        }
        if (
            event.input.op === "query" &&
            typeof event.input.value === "string" &&
            event.input.value.length > 80
        ) {
            return { block: true, reason: "Field-note queries are limited to 80 characters." };
        }
    });

    pi.on("tool_result", event => {
        if (event.toolName !== "field_notes" || !event.isError) return;
        return {
            content: [
                ...event.content,
                { type: "text", text: "Use field_notes discover before choosing an inspection ID." },
            ],
            isError: true,
        };
    });
}
~~~

**Terminal shell—load the real domain and the new middleware\:**

~~~sh
omp --no-extensions -e "$EXAMPLES/package-lab/single/field-notes.ts" -e ./field-guard.ts
~~~

**Model tool arguments—call&#32;`field_notes`\:**

~~~json
{"op":"inspect","value":"first"}
~~~

**Expected checkpoint\:**&#32;the effective tool input becomes inspection of&#32;`reed`\.

For model\-issued calls\,&#32;`tool_call`&#32;runs at argument\-preparation time\.&#32;Replacement input is revalidated and becomes the input used for scheduling\,&#32;execution events\,&#32;persisted assistant tool\-call arguments and approval\.

For direct or nested dispatches not passing through that agent\-loop preparation path\,&#32;the wrapper applies the replacement before its approval gate\.&#32;Do not assume a direct&#32;`tool.execute()`&#32;SDK call reproduces the loop’s schema revalidation\.

All&#32;`tool_call`&#32;handlers see the original normalized event view\,&#32;not a middleware chain of earlier rewrites\.&#32;The current runner retains the last returned result object unless a block short\-circuits\;&#32;a later empty object can discard an earlier rewrite\.&#32;Coordinate policy handlers instead of assuming field\-by\-field merging\.

Normalized event input may contain derived fields\,&#32;such as hashline edit&#32;`path`\/`paths`\,&#32;that are not valid execution parameters\.&#32;Return the tool’s actual input shape\,&#32;not a copied gate\-only view\.&#32;Computer provider calls have a synthetic view and do not apply these input replacements\.

A revised nested&#32;`xd://`&#32;call forfeits an outer approval bypass and faces the appropriate gate again\.

The slash command&#32;`/field-notes first`&#32;does&#32;**not**&#32;traverse this tool middleware\.&#32;Put mandatory shared policy in the shared domain if both entrances must enforce it\.

### Milestone\:&#32;delegate to the original built\-in

Rowan next wants to add logging around one ordinary file write without reimplementing native snapshots and editing bookkeeping\.

`ctx.invokeTool()`&#32;is available only when the registered tool shadows a native built\-in of the&#32;**same name**\.&#32;It is not a general “invoke arbitrary tool” API\.

This deliberately narrow exercise replaces&#32;`write`&#32;and permits only&#32;`workbook-note.txt`\.&#32;Use it in a scratch workspace\;&#32;it will refuse other writes\.

**Complete TypeScript exercise—save as&#32;`one-file-write.ts`\:**

~~~ts
import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent";

export default function oneFileWrite(pi: ExtensionAPI): void {
    const parameters = pi.zod.object({
        path: pi.zod.string(),
        content: pi.zod.string(),
    });
    pi.registerTool<typeof parameters>({
        name: "write",
        label: "Workbook one-file write",
        description: "Write workbook-note.txt through the native write implementation. Other targets are refused.",
        approval: "write",
        loadMode: "essential",
        parameters,
        async execute(_callId, params, signal, _onUpdate, ctx) {
            signal?.throwIfAborted();
            if (params.path !== "workbook-note.txt") {
                throw new Error("This exercise permits only workbook-note.txt.");
            }
            if (!ctx.invokeTool) {
                throw new Error("The native write implementation is unavailable in this host.");
            }
            pi.logger.debug("Delegating workbook file write", { path: params.path });
            return await ctx.invokeTool(params);
        },
    });
}
~~~

The bare delegate inherits the caller’s signal\,&#32;progress callback and relevant native tool context\.&#32;The native call is not re\-gated\;&#32;the outer same\-tool call already passed approval\.&#32;Delegation depth is guarded\.

That narrow delegation mechanism is not a restriction on arbitrary JavaScript imported by the extension\.&#32;Do not describe it as a sandbox\.

**Exercise\:**&#32;rename the tool to&#32;`workbook_write`&#32;without changing the delegate\.

**Checkpoint\:**&#32;it no longer shadows&#32;`write`\,&#32;so&#32;`ctx.invokeTool`&#32;is absent\.&#32;Share a domain function or use an explicit host adapter\;&#32;do not invent an arbitrary\-target overload\.

### Approval\,&#32;result transforms and session hooks

Approval declarations can be\:

- `read`\,&#32;`write`&#32;or&#32;`exec`\;
- an object with&#32;`tier`\,&#32;`reason`\,&#32;`override`\,&#32;`policy`&#32;and optional&#32;`policyKey`\;
- a function of arguments returning such a decision\.

The current tier comparison is\:

- `always-ask`&#32;automatically admits read\-tier operations\;
- `write`&#32;admits read\/write tiers\;
- `yolo`&#32;admits all tiers by default\.

The name&#32;`always-ask`&#32;therefore does not mean every read prompts\.

Explicit deny policies remain important\.&#32;`override: true`&#32;alone is not an unbypassable prompt in yolo mode\.&#32;These host policies must not replace a domain\-specific human grant\.

`tool_result`&#32;patches&#32;`content`\,&#32;`details`&#32;and&#32;`isError`&#32;in sequence\;&#32;later handlers see earlier changes\.&#32;It cannot undo an external side effect\.

One current wrapper detail matters for diagnostics\:&#32;its event&#32;`isError`&#32;is initialized from a thrown execution error\.&#32;A domain refusal\,&#32;or a nonthrowing result’s own failure flag\,&#32;should not be inferred solely from that event flag\.&#32;Seed Desk’s&#32;`details.ok`&#32;is intentionally explicit\.&#32;Test nonthrowing failures before writing result middleware that could erase their meaning\.

`ToolDefinition.onSession`&#32;has reasons&#32;`start`\,&#32;`switch`\,&#32;`branch`\,&#32;`tree`&#32;and&#32;`shutdown`\,&#32;with&#32;`previousSessionFile`\.&#32;It is host\-dispatched\;&#32;the TUI controller has an explicit dispatcher\.&#32;The standalone custom\-tool bridge also maps additional reliability events\.&#32;For cross\-mode domain lifecycle behavior\,&#32;use and test explicit&#32;`pi.on(...)`&#32;handlers as the principal examples do\.

`shellEnv`&#32;receives&#32;`command`\,&#32;`cwd`&#32;and a copy of the shell environment\.&#32;In the supplied&#32;`BashRunner`\,&#32;the registered&#32;`bash`&#32;definition’s hook contributes environment values when&#32;`useUserShell`&#32;is enabled\.&#32;It is not an all\-subprocess environment interceptor and does not automatically affect&#32;`pi.exec()`\.

*Source\,&#32;snapshot 2026\-08\-29\:&#32;`packages/coding-agent/src/extensibility/extensions/types.ts`\,&#32;`ToolDefinition`\;&#32;`wrapper.ts`\;&#32;`runner.ts`\,&#32;`emitToolCall`\,&#32;`emitToolResult`\,&#32;`invokeNativeTool`\;&#32;`packages/coding-agent/src/sdk.ts`\,&#32;`customToolToDefinition`\;&#32;`packages/coding-agent/src/session/bash-runner.ts`\;&#32;`packages/coding-agent/src/exec/exec.ts`\;&#32;`packages/coding-agent/src/tools/approval.ts`\.*

## Session navigation and event\-driven behavior

Lena works through a long fictional review\.&#32;She wants a checkpoint command that can inspect the current session and move to a deliberate place in its history\.

Her obstacle is lifecycle timing\:&#32;a tool executes inside an agent operation\,&#32;while session navigation may need to abort\,&#32;restore history or wait for the operation to settle\.

She keeps navigation on a command context\.

### Milestone\:&#32;explicit session controls

The following is a complete source\-backed exercise\.&#32;It is not a new Seed Desk or Review Desk stage\.

**Complete TypeScript exercise—save as&#32;`session-desk.ts`\:**

~~~ts
import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent";

export default function sessionDesk(pi: ExtensionAPI): void {
    pi.registerCommand("workbook-session", {
        description: "Session controls: status | new | reload | compact | tree <id> | branch <id> | switch <file>",
        async handler(args, ctx) {
            const trimmed = args.trim();
            const split = trimmed.indexOf(" ");
            const verb = split < 0 ? trimmed : trimmed.slice(0, split);
            const target = split < 0 ? "" : trimmed.slice(split + 1).trim();

            if (!verb || verb === "status") {
                const current = ctx.models.current();
                const details = {
                    sessionId: ctx.sessionManager.getSessionId(),
                    sessionFile: ctx.sessionManager.getSessionFile() ?? null,
                    leafId: ctx.sessionManager.getLeafId(),
                    cwd: ctx.cwd,
                    model: current ? `${current.provider}/${current.id}` : null,
                    contextUsage: ctx.getContextUsage() ?? null,
                    asyncJobs: ctx.getAsyncJobSnapshot(),
                    pendingMessages: ctx.hasPendingMessages(),
                };
                pi.sendMessage(
                    { customType: "workbook.session-status", content: JSON.stringify(details), details, display: true },
                    { triggerTurn: false },
                );
                return;
            }

            await ctx.waitForIdle();

            if (verb === "reload") {
                await ctx.reload();
                return;
            }
            if (verb === "compact") {
                await ctx.compact("Preserve concrete decisions, unresolved questions and stable domain IDs.");
                return;
            }

            let result: { cancelled: boolean };
            if (verb === "new") {
                result = await ctx.newSession();
            } else if (verb === "tree" && ctx.sessionManager.getEntry(target)) {
                result = await ctx.navigateTree(target, { summarize: false });
            } else if (verb === "branch") {
                const entry = ctx.sessionManager.getEntry(target);
                if (entry?.type !== "message" || entry.message.role !== "user") {
                    throw new Error("branch requires an existing user-message entry ID.");
                }
                result = await ctx.branch(target);
            } else if (verb === "switch" && target) {
                if (!(await Bun.file(target).exists())) {
                    throw new Error("The target session file does not exist.");
                }
                result = await ctx.switchSession(target);
            } else {
                throw new Error("Use status, new, reload, compact, tree <id>, branch <id>, or switch <file>.");
            }
            ctx.ui.notify(result.cancelled ? "Session operation cancelled." : "Session operation completed.");
        },
    });
}
~~~

**Human OMP slash command—after loading the exercise\:**

~~~text
/workbook-session status
~~~

Use actual entry IDs from the current session manager\/tree\,&#32;not a workbook placeholder\,&#32;for navigation\.

The status command is a snapshot\.&#32;It also appends a custom message\,&#32;so its displayed&#32;`leafId`&#32;is the position just before that status message was appended\.

#### What the controls mean

- `waitForIdle()`&#32;waits through the host’s wired idle operation\.&#32;It is not an exclusive lock against another caller starting work\.
- `newSession()`&#32;starts a fresh transcript and returns&#32;`{ cancelled }`\.
- `newSession({ parentSession, setup })`&#32;can associate a parent and run an initialization callback\.&#32;That callback receives the full session manager for deliberate setup\.
- `branch(entryId)`&#32;uses a user\-message entry to create a new session file from its preceding history\.
- `navigateTree(targetId, { summarize })`&#32;changes the current path in the same file\.
- `switchSession(path)`&#32;restores another session\.
- `reload()`&#32;reopens the current session\,&#32;as discussed earlier\.
- `compact()`&#32;requests context compaction\.

A user\-message tree target normally moves to its parent and offers that message for editing\.&#32;A non\-user target generally lands on the selected node\.&#32;Do not equate every target ID with “include this entry as the new leaf\.”

**Expected checkpoint\:**&#32;navigating before a Seed Desk reservation changes the query result because that extension reconstructs the current branch\.&#32;Field Notes’ binding\-local selection does not change merely because the transcript moves\.

**Failure boundary\:**&#32;pre\-navigation handlers may cancel\.&#32;Missing\/invalid targets fail\.&#32;Summarization and compaction may need a configured model\/provider and may incur costs\.&#32;The exercise uses&#32;`summarize: false`&#32;for ordinary tree navigation\.

**Exercise\:**&#32;call&#32;`ctx.newSession()`&#32;from a tool by casting its context\.

**Answer\:**&#32;do not\.&#32;General tool\/event contexts intentionally omit these command\-only navigation methods\.&#32;A cast does not make the lifecycle operation safe\.

### Compaction is not domain persistence

Compaction changes the model’s retained conversational context\.&#32;It is not a replacement for reconstructing extension state from journal entries\.

The supported compaction surfaces are\:

- `session_before_compact`\:&#32;cancel or supply a complete custom compaction result\;
- `session.compacting`\:&#32;add summary context\,&#32;replace the summarizer prompt or attach&#32;`preserveData`\;
- `session_compact`\:&#32;observe the resulting entry\.

`CompactOptions`&#32;contains\:

- `onComplete`\;
- `onError`\;
- `mode`\;
- `internalGuidance`\.

A string passed to&#32;`ctx.compact()`&#32;is public custom instructions\.&#32;An options object can request a one\-off supported compaction mode\.&#32;The supplied type commentary names&#32;`soft`\,&#32;`remote`&#32;and&#32;`snapcompact`\;&#32;use the matching host’s&#32;`CompactMode`&#32;rather than inventing another value\.

`internalGuidance`&#32;is for native summarizer guidance\,&#32;not the user’s&#32;`customInstructions`&#32;visible to the pre\-compaction hook\.

The general context also exposes&#32;`compact()`\,&#32;but lifecycle\-sensitive compaction is best driven from a command or a host\-controlled safe boundary\.&#32;Awaiting maintenance from inside work that maintenance itself must drain can create a deadlock\.

### Change a prompt through the supported return shape

Lena wants a temporary instruction to label fictional output\,&#32;without permanently editing the base prompt\.

**Complete TypeScript exercise—save as&#32;`fictional-turn.ts`\:**

~~~ts
import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent";

export default function fictionalTurn(pi: ExtensionAPI): void {
    pi.on("before_agent_start", event => ({
        systemPrompt: [
            ...event.systemPrompt,
            "Clearly distinguish fictional workbook data from real observations.",
        ],
    }));
}
~~~

This returns the complete replacement block array for that turn\,&#32;preserving existing blocks\.

There is no current native&#32;`systemPromptAppend`&#32;result member\.&#32;The older pirate example uses that stale name\;&#32;do not copy it as a working contract\.

`context`&#32;is the per\-model\-call message transformation surface\.&#32;Return new arrays and objects\.&#32;The runner attempts a structured clone\,&#32;but falls back to a shallow array copy for noncloneable data\;&#32;in\-place mutation is therefore a poor portability strategy\.

Never remove one half of a tool\-call\/tool\-result pair casually\.

### Handler timing and errors

Most event handlers run in extension order with a&#32;**30\-second**&#32;budget\.

Important exceptions and qualifications\:

- `tool_call`&#32;uses&#32;`extensionHandlers.toolCallTimeoutMs`\,&#32;defaulting to 30 seconds when unset or invalid\.
- `tool_call`&#32;errors\/timeouts block execution\.
- Its budget pauses while awaiting supported human dialogs\;&#32;asynchronous custom\-component setup still consumes budget until the component is ready\.
- Other event handlers do not inherit that same paused human\-dialog budget\.
- `session_shutdown`&#32;handlers run concurrently with a two\-second per\-handler cap\.
- Factory loading and command handlers are not automatically covered by that event\-handler budget\.
- Most other event\-handler failures are reported and dispatch continues\.&#32;A failed pre\-switch handler is not automatically a veto\.
- A timeout cannot preempt synchronous JavaScript or magically cancel an arbitrary detached promise\.&#32;Code must honor cancellation and retire stale work\.

`message_start`\,&#32;`message_update`&#32;and&#32;`message_end`&#32;are notifications\.&#32;`message_end`&#32;receives a detached snapshot\;&#32;changing it does not rewrite the next provider request\.

`agent_end`&#32;is also notification\-only\.&#32;It may indicate an already scheduled continuation through&#32;`willContinue`\.&#32;Use&#32;`session_stop`&#32;for a supported stop\-time continuation request\,&#32;not an assumed return value from&#32;`agent_end`\.

**Exercise\:**&#32;throw from&#32;`user_bash`&#32;to prohibit a shell command\.

**Answer\:**&#32;that is not a reliable veto\.&#32;Its handler errors are isolated\,&#32;and the default path can continue\.&#32;Use a supported interception result or an actual enforcement boundary appropriate to the operation\.

*Source\,&#32;snapshot 2026\-08\-29\:&#32;`packages/coding-agent/src/session/agent-session.ts`\,&#32;session navigation and event mapping\;&#32;`packages/coding-agent/src/extensibility/extensions/runner.ts`\,&#32;`#runHandlerWithTimeout`\,&#32;`emit`\,&#32;`emitBeforeAgentStart`\;&#32;`packages/coding-agent/src/extensibility/shared-events.ts`\;&#32;`packages/coding-agent/src/extensibility/extensions/compact-handler.ts`\.*

## Background work and owner\-addressed delivery

Mina starts a fictional habitat check\,&#32;then moves to another conversation before the result arrives\.&#32;Her goal is to keep the result with its origin\.&#32;Her obstacle is a tempting implementation\:

> “When the promise finishes\,&#32;call&#32;`sendMessage()`\.”

That sends to the runtime’s current session\,&#32;not necessarily the original owner\.

Mina separates&#32;**work lifetime**\,&#32;**result storage**\,&#32;**delivery admission**&#32;and&#32;**agent wake\-up**\.

### Milestone\:&#32;managed timers for a local reminder

Start with a small timer that only notifies the operator\.&#32;It does not pretend to be a durable job\.

**Complete TypeScript exercise—save as&#32;`clock-note.ts`\:**

~~~ts
import type { ExtensionAPI, ExtensionContext } from "@oh-my-pi/pi-coding-agent";

export default function clockNote(pi: ExtensionAPI): void {
    let timer: Timer | undefined;

    function clear(ctx: ExtensionContext): void {
        if (timer !== undefined) ctx.clearTimer(timer);
        timer = undefined;
    }

    pi.registerCommand("clock-note", {
        description: "Schedule or stop a one-second local reminder",
        async handler(args, ctx) {
            clear(ctx);
            if (args.trim() === "stop") {
                ctx.ui.notify("Reminder stopped.");
                return;
            }
            if (args.trim() && args.trim() !== "start") {
                ctx.ui.notify("Use /clock-note start or /clock-note stop.", "warning");
                return;
            }
            const owner = ctx.sessionManager.getSessionId();
            timer = ctx.setTimeout(() => {
                timer = undefined;
                if (ctx.sessionManager.getSessionId() !== owner) return;
                pi.logger.debug("Workbook reminder fired");
                if (ctx.hasUI) ctx.ui.notify("One-second workbook reminder.");
            }, 1000);
            ctx.ui.notify("Reminder scheduled.");
        },
    });

    pi.on("session_before_switch", (_event, ctx) => clear(ctx));
    pi.on("session_before_branch", (_event, ctx) => clear(ctx));
    pi.on("session_before_tree", (_event, ctx) => clear(ctx));
    pi.on("session_shutdown", (_event, ctx) => clear(ctx));
}
~~~

**Human OMP slash commands—after loading\:**

~~~text
/clock-note start
/clock-note stop
~~~

**Expected checkpoint\:**&#32;start produces one reminder if the host stays alive long enough\;&#32;stop or a navigation attempt clears it\.

Managed timers\:

- contain synchronous callback throws and rejected native promises\;
- report failures through the extension error channel\;
- are unref’d\,&#32;so they do not keep the process alive alone\;
- are cleared on shutdown\;
- can be cleared individually with&#32;`clearTimer()`\.

Clearing a timer does not cancel asynchronous work that its callback already started\.&#32;That work needs its own abort controller and stale\-result checks\.

Managed timers do not automatically register jobs in&#32;`getAsyncJobSnapshot()`\.&#32;That snapshot reports the host’s session\-owned async jobs\,&#32;or null when no applicable manager is available\.

**Exercise\:**&#32;remove the explicit pre\-switch clear and start a reminder before&#32;`/new`\.

**Answer\:**&#32;the runner\/binding can survive&#32;`/new`\;&#32;automatic shutdown cleanup is not a session\-switch reset\.&#32;Design the intended lifetime explicitly\.

### Ordinary messages\:&#32;choose the effect deliberately

| API | Intended use | Important semantics |
| --- | --- | --- |
| `appendEntry(type, data)` | Extension state | Not sent to the model |
| `sendMessage(payload, options)` | Custom conversation\/context message | `content`&#32;participates in model context\;&#32;`details`&#32;is metadata |
| `sendUserMessage(content, options)` | User\-style prompt or queued user message | Does not dispatch slash commands or expand prompt templates |
| `captureSessionTarget()`&#32;\+&#32;`deliverMessage()` | Owner\-addressed result delivery | Persistent anchor\,&#32;admission checks and deduplication receipt |

`sendMessage()`&#32;and&#32;`sendUserMessage()`&#32;return&#32;`void`&#32;through&#32;`ExtensionAPI`\.&#32;Awaiting them does not produce a durable acknowledgement\.

For custom messages\:

- Idle\,&#32;no trigger\:&#32;append to conversation\/session without starting a turn\.
- Streaming\,&#32;default delivery\:&#32;queue as steering\.
- Streaming\,&#32;`followUp`\:&#32;queue behind the current work\.
- Streaming\,&#32;`nextTurn`\:&#32;hold as hidden next\-turn context rather than an editable pending\-message chip\.
- `triggerTurn: true`\:&#32;request a turn\;&#32;hosts may defer it\.
- Idle&#32;`nextTurn`&#32;without a trigger appends immediately in this implementation\;&#32;it is not a separate durable outbox\.

For&#32;`sendUserMessage()`\:

- omitted&#32;`deliverAs`&#32;starts prompt flow when idle and steers while streaming\;
- explicit&#32;`steer`&#32;or&#32;`followUp`&#32;enqueues through that queue\,&#32;without synchronously starting a prompt\;
- host queue\-drain behavior may subsequently resume work\;
- **`nextTurn`&#32;is not an option on this API\.**

`display: false`&#32;hides presentation\,&#32;not model visibility\.

The old repository reload\-tool example queues&#32;`"/reload-runtime"`&#32;through&#32;`sendUserMessage()`\.&#32;Current&#32;`AgentSession.sendUserMessage()`&#32;explicitly skips command handling\.&#32;Do not teach that as a supported way for a model to invoke a human command\.

### Milestone\:&#32;capture once\,&#32;deliver to that owner

This complete exercise demonstrates immediate capture\/delivery and a retained retry identity\.&#32;It is not a durable background job engine\.

It requires\:

- an initialized extension runtime\;
- a persistent session\;
- enough known model context capacity for admission\.

No inference is requested because&#32;`triggerTurn`&#32;is false\.&#32;Without a model\/capacity snapshot\,&#32;`context_full`&#32;can be a correct deferred result\.

**Complete TypeScript exercise—save as&#32;`owner-note.ts`\:**

~~~ts
import type { ExtensionAPI, ExtensionSessionTarget } from "@oh-my-pi/pi-coding-agent";

export default function ownerNote(pi: ExtensionAPI): void {
    const namespace = "workbook.owner-note";
    let pending: {
        target: ExtensionSessionTarget;
        deliveryId: string;
        content: string;
    } | undefined;
    let busy = false;

    pi.registerCommand("owner-note", {
        description: "Capture and deliver a local note: new <text> | retry | status",
        async handler(args, ctx) {
            const input = args.trim();
            if (input === "status") {
                ctx.ui.notify(pending ? `Pending delivery: ${pending.deliveryId}` : "No pending delivery.");
                return;
            }
            if (busy) throw new Error("A delivery operation is already in progress.");
            busy = true;
            try {
                if (input.startsWith("new ")) {
                    if (pending) throw new Error("Retry the existing pending delivery before creating another.");
                    const content = input.slice(4).trim();
                    if (!content) throw new Error("A nonblank note is required.");
                    const requestId = crypto.randomUUID();
                    const target = await pi.captureSessionTarget({
                        namespace,
                        requestId,
                        data: { kind: "fictional-workbook-note" },
                    });
                    pending = { target, deliveryId: requestId, content };
                } else if (input !== "retry") {
                    throw new Error("Use new <text>, retry, or status.");
                }

                if (!pending) throw new Error("No captured note is waiting for delivery.");
                const receipt = await pi.deliverMessage(
                    { customType: namespace, content: pending.content, display: true },
                    {
                        target: pending.target,
                        namespace,
                        deliveryId: pending.deliveryId,
                        triggerTurn: false,
                    },
                );
                ctx.ui.notify(
                    `${receipt.state}; reason=${receipt.reason ?? "none"}; wake=${receipt.wake}`,
                    receipt.state === "deferred" ? "warning" : "info",
                );
                if (receipt.state !== "deferred") pending = undefined;
            } finally {
                busy = false;
            }
        },
    });
}
~~~

**Human OMP slash commands—after loading with a persistent session\:**

~~~text
/owner-note new The fictional marsh check is complete.
/owner-note status
/owner-note retry
~~~

Use&#32;`retry`&#32;only if the previous operation retained a pending item\.&#32;A successfully committed item is cleared by this small example\.

The note’s retry state is binding\-local\.&#32;If the process exits before delivery\,&#32;this exercise has no durable outbox from which to recover it\.&#32;A real background system must persist the target\,&#32;stable delivery ID and complete body with its job\.

#### Capture contract

`captureSessionTarget({ namespace, requestId, data? })`&#32;appends an anchor and returns\:

- `sessionId`\;
- `sessionFile`\;
- `anchorEntryId`\.

Identifiers must be nonempty\.&#32;`data`&#32;must be structured\-cloneable\.&#32;Capture requires persistence and can fail during a conflicting transition\.

Capture once before launching work\.&#32;Repeated calls with the same request ID are not an instruction to find and reuse an earlier anchor\.

#### Receipt contract

`deliverMessage(payload, { target, namespace, deliveryId, triggerTurn })`&#32;returns\:

| Receipt field | Meaning |
| --- | --- |
| `deliveryId` | Your stable delivery identity |
| `state: "deferred"` | No new body appended by this attempt |
| `state: "committed"` | Body persisted and published to the owner’s live context |
| `state: "already_committed"` | Existing entry reused\;&#32;no duplicate body |
| `entryId` | Committed journal entry\,&#32;when available |
| `reason` | Admission or wake condition |
| `wake` | `not_scheduled`\,&#32;`scheduled`&#32;or&#32;`pending` |

Reasons are\:

- `owner_inactive`\;
- `branch_inactive`\;
- `session_busy`\;
- `context_full`\;
- `host_defers_turn`\.

Inspect state and wake separately\.&#32;A committed body can still have a pending wake\.

A scheduled wake is not proof that the model has replied\.

#### Ownership and deduplication

The coordinator checks\:

- the original session identity and file lineage\;
- the anchor on the active branch\;
- reset boundaries\;
- busy session work\;
- context capacity\.

A recorded move can preserve owner identity\.&#32;A fork creates a new session ID and does not inherit delivery authority\.&#32;Navigating away from the anchor or crossing a reset boundary can make the branch inactive\.

A namespace\/delivery\-ID retry with different content\,&#32;owner or anchor throws a conflict\.&#32;The hash represents typed content segments\,&#32;not every presentation field in the payload\;&#32;keep the entire payload stable rather than expecting changed details to update an existing delivery\.

Text is split into blocks of at most 65\,536 UTF\-16 code units without splitting surrogate pairs\.&#32;The coordinator does not truncate or compact the result to make it fit\.

`details["omp.delivery"]`&#32;is reserved for delivery identity\.

Already\-compacted messages are not resurrected\.&#32;Retrying a delivery can repair a pending\/failed wake without adding another body\,&#32;but a later successful assistant entry suppresses another wake\.

**Observed contract tests\:**&#32;real session persistence and coordinator scenarios covered duplicate delivery\,&#32;conflict\,&#32;failed persistence\,&#32;owner transitions\,&#32;large complete bodies and wake retry\.&#32;Some lifecycle tests used a mock model to test the agent loop\.&#32;That is not live\-provider evidence\.

### What changes next\?

For a real service job\,&#32;Mina needs an additional durable job\/outbox store and retry policy\.&#32;No public&#32;`ExtensionAPI.startJob()`&#32;or magic shared durable backend exists in this inventory\.

A useful new tool should then expose\:

- a stable job ID\;
- `inspect/query`&#32;for state\;
- a bounded\,&#32;cancellable&#32;`wait`\;
- `diagnose`&#32;for a deferred owner\,&#32;unavailable service or pending wake\;
- explicit action retry rules\.

Independent processes writing the same transcript need an external owner lock\.&#32;Delivery serialization is session\-local\.

**Exercise\:**&#32;a receipt says&#32;`state: "committed"`\,&#32;`reason: "host_defers_turn"`\,&#32;`wake: "pending"`\.&#32;Should the worker resend under a new delivery ID\?

**Answer\:**&#32;no\.&#32;Retain the same identity\.&#32;The body is already committed\;&#32;the outstanding issue is consumption\,&#32;not missing storage\.

*Source\,&#32;snapshot 2026\-08\-29\:&#32;`packages/coding-agent/src/extensibility/extensions/managed-timers.ts`\;&#32;`packages/coding-agent/src/session/extension-delivery.ts`\,&#32;`ExtensionDeliveryCoordinator`\;&#32;`packages/coding-agent/src/session/agent-session.ts`\,&#32;message APIs and async snapshots\;&#32;`packages/coding-agent/test/extension-delivery.test.ts`\,&#32;`extension-delivery-lifecycle.test.ts`\.*

## Models\,&#32;providers\,&#32;credentials and memory

Sora embeds OMP for a fictional research team\.&#32;She wants to select an appropriate model and later connect a real organizational gateway\.

Her obstacle is a false sense of progress\:&#32;a model appearing in a catalog does not establish valid authentication\,&#32;compatible streaming or useful inference\.

She develops these as separate milestones\.

### Milestone\:&#32;query the host’s models without copying its heuristics

`ctx.models`&#32;is the read\-only query facade\:

| Member | Contract |
| --- | --- |
| `list()` | Models considered available by the registry’s configured\-auth\/keyless\-provider rules\;&#32;not a live health test |
| `current()` | Lazily read current model |
| `resolve(spec)` | Resolve provider\/ID\,&#32;bare model selector or configured role alias using host matching preferences |
| `family(model)` | Opaque lineage token for comparison\;&#32;do not persist its vocabulary |

Thinking\/routing suffixes accepted by resolution identify the base model\;&#32;apply effort separately\.

**Complete TypeScript exercise—save as&#32;`model-desk.ts`\:**

~~~ts
import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent";

export default function modelDesk(pi: ExtensionAPI): void {
    pi.registerCommand("workbook-model", {
        description: "List available models or select one by a real host selector",
        async handler(args, ctx) {
            const spec = args.trim();
            if (spec) {
                if (!ctx.isIdle()) throw new Error("Select a model after the current operation settles.");
                const target = ctx.models.resolve(spec);
                if (!target) throw new Error("No available model matches that selector.");
                if (!(await pi.setModel(target))) {
                    throw new Error("No usable API key was available for that model.");
                }
            }
            const current = ctx.models.current();
            const details = {
                current: current ? `${current.provider}/${current.id}` : null,
                available: ctx.models.list().map(model => ({
                    selector: `${model.provider}/${model.id}`,
                    sameFamilyAsCurrent: current
                        ? ctx.models.family(model) === ctx.models.family(current)
                        : null,
                })),
                thinkingLevel: pi.getThinkingLevel() ?? null,
                serviceTiers: pi.getServiceTiers(),
            };
            pi.sendMessage(
                { customType: "workbook.models", content: JSON.stringify(details), details, display: true },
                { triggerTurn: false },
            );
        },
    });
}
~~~

**Human OMP slash command—after loading\:**

~~~text
/workbook-model
~~~

Use an actual returned selector as the argument when selecting\.

`setModel()`&#32;can resolve credentials\,&#32;refresh OAuth or run a configured credential program through host code\.&#32;It is not necessarily an offline metadata\-only call\.&#32;It returns false for unavailable credentials and can throw on other failures\.

The command rereads&#32;`ctx.models.current()`&#32;after selection\.&#32;This matters because the supplied command\-context construction spreads a base context and can snapshot&#32;`ctx.model`\;&#32;the model facade keeps its lazy getter\.&#32;General event contexts preserve the live model accessor\,&#32;except provider hooks deliberately bind it to that request’s model\.

**Exercise\:**&#32;save&#32;`ctx.model`&#32;at factory time for use by every future request\.

**Answer\:**&#32;there is no invocation context at factory time\,&#32;and model choice can change\.&#32;Use the current invocation or the lazy facade\.

### Thinking and service tiers

`getThinkingLevel()`&#32;reads the effective level\.&#32;`setThinkingLevel(level)`&#32;changes the current session’s supported thinking level\.

The extension\-facing setter takes&#32;`ThinkingLevel`\;&#32;the broader session\/settings&#32;`auto`&#32;selector is not an invented additional extension setter mode\.

`getServiceTiers()`&#32;returns a detached snapshot\.&#32;Mutating the returned object does not change the session\.

`setServiceTier(family, tier)`&#32;affects subsequent requests\:

- OpenAI\:&#32;`auto`\,&#32;`default`\,&#32;`flex`\,&#32;`scale`\,&#32;`priority`\;
- Anthropic\:&#32;`priority`\;
- Google\:&#32;`flex`\,&#32;`priority`\;
- `undefined`\:&#32;clear that family’s override\.

Invalid family\/tier combinations throw\.&#32;An older embedding that did not wire these actions throws an unsupported\-action error rather than pretending success\.&#32;Changes do not alter an already in\-flight request\.

A configured tier is not a guarantee of provider availability\,&#32;latency\,&#32;billing or acceptance\.

Session journals also record model\/thinking\/service\-tier state for restoration\.&#32;Do not confuse those snapshots with a provider having successfully served a request\.

### Milestone\:&#32;register a real provider configuration

The public bundle supplies a host adapter\,&#32;not a pretend provider\.

**Complete public TypeScript source—[provider\-registration\.ts](<https://present-sketch-tp94.here.now/examples/package-lab/advanced/provider-registration.ts>)\:**

~~~ts
import type { ExtensionFactory, ProviderConfig } from "@oh-my-pi/pi-coding-agent";

/** Host-owned configuration only. No endpoint, credentials, or fake transport is bundled. */
export function createProviderExtension(name: string, config: ProviderConfig): ExtensionFactory {
    if (!name.trim()) throw new Error("Provider name is required.");
    return pi => {
        pi.registerProvider(name, config);
    };
}
~~~

Import the creator into an SDK host and pass its returned factory in&#32;`extensions`\.&#32;It has no default export and is not a standalone&#32;`-e`&#32;entry\.

**Missing prerequisite\:**&#32;a real&#32;`ProviderConfig`&#32;owned by the host\,&#32;including the actual endpoint\/API choice and valid authentication or a real custom transport\.&#32;No such service is bundled or exercised here\.

Sora can verify registration and rollback without using real credentials\.&#32;She cannot honestly label inference “working” until a genuine provider scenario has been run\.

### `ProviderConfig`&#32;field guide

| Member | Author decision |
| --- | --- |
| `baseUrl` | Actual endpoint base\;&#32;required for defined models under the public contract |
| `apiKey` | Trusted key value or supported configuration reference\,&#32;such as an environment\-variable name |
| `api` | Existing API identifier\,&#32;or custom API identifier paired with a real stream implementation |
| `streamSimple` | Custom stream implementation returning&#32;`AssistantMessageEventStream` |
| `headers` | Provider request headers\;&#32;never publish secrets in examples |
| `authHeader` | Whether the resolved key is added as a Bearer authorization header |
| `models` | Static model definitions |
| `usage` | A normalized&#32;`UsageProvider`&#32;for host usage reporting |
| `oauth` | Login\,&#32;refresh and credential\-aware routing contract |
| `fetchDynamicModels` | Async live\-catalog callback |

The public&#32;`streamSimple`&#32;field returns an event stream\,&#32;not the broader promise\-returning&#32;`StreamFn`&#32;used elsewhere in agent internals\.&#32;Build asynchronous transport work into a correct stream implementation\.

The registry’s internal&#32;`ProviderConfigInput`&#32;has additional members\.&#32;Do not assume every internal field is part of the public extension&#32;`ProviderConfig`\.

#### Replacement versus override

The documented registration intent distinguishes\:

- a nonempty&#32;`models`&#32;list for defining\/redefining provider models\;
- a base\-URL\/header\-only registration for overriding existing model transport\;
- `streamSimple`&#32;for a custom stream API\.

The current implementation replaces that provider’s runtime model\-definition list when the nonempty\-model branch runs\.&#32;It also composes runtime overlays with static\/configured catalogs during lazy rebuilds\.&#32;Therefore do not use&#32;`models: []`&#32;as a delete\-all operation\,&#32;or rely on a one\-time replacement to establish a permanent exclusion filter over every built\-in model\.&#32;Inspect the composed catalog after refresh\.

`unregisterProvider(name)`&#32;removes the runtime provider override and rebuilds static\/configured model state\.&#32;Source cleanup also owns custom API\/OAuth registrations\.&#32;These are registration\-lifetime mechanisms\,&#32;not a permission check limiting an extension to a provider it “owns\.”

### `ProviderModelConfig`&#32;field guide

| Members | Meaning |
| --- | --- |
| `id`\,&#32;`name` | Stable model ID and display name |
| `api` | Optional per\-model API override |
| `reasoning`\,&#32;`thinking` | Whether extended thinking is supported and its canonical capability metadata |
| `input` | Supported&#32;`text`\/`image`&#32;modalities |
| `cost` | Input\/output\/cache\-read\/cache\-write costs per million tokens |
| `premiumMultiplier` | Premium Copilot request accounting metadata\,&#32;not a token price |
| `contextWindow`\,&#32;`maxTokens` | Context and output limits |
| `preferWebsockets` | Codex transport preference where applicable |
| `headers` | Per\-model headers |
| `compat` | Supported OpenAI compatibility metadata |

Do not invent capacities or set costs to zero merely to make a fixture look complete\.&#32;Incorrect limits affect compaction and delivery admission\.

### OAuth is a contract with a real authentication system

The&#32;`oauth`&#32;object contains\:

- `name`\:&#32;login UI label\;
- `login(callbacks)`\:&#32;returns host\-compatible OAuth credentials or a plain API\-key string\;
- optional&#32;`refreshToken(credentials)`\;
- optional&#32;`getApiKey(credentials)`\;
- optional&#32;`modifyModels(models, credentials)`\.

A real implementation needs the provider’s actual authorization flow\,&#32;callback or device\-code requirements\,&#32;expiry\/refresh semantics and credential storage policy\.

Use host callbacks for authorization URLs\,&#32;progress and requested input\.&#32;Do not fabricate a successful login or persist a placeholder token\.

RPC login can emit an&#32;`open_url`&#32;UI request and accept a later pasted code\/redirect through input\.&#32;Its supplied login handler rejects a provider that asks for pre\-URL interactive input it cannot support\.&#32;ACP authentication and extension UI capability negotiation are separate host concerns\;&#32;do not promise an identical login UI in every client\.

`modifyModels()`&#32;receives a clone of the&#32;**whole composed catalog**\,&#32;not merely the provider’s own models\.&#32;Preserve unrelated models\.&#32;It is reapplied during catalog rebuilding when stored OAuth credentials exist\;&#32;a throwing modifier is logged and falls back to the earlier catalog\.

In this registry implementation\,&#32;installation of that modifier occurs in the nonempty static&#32;`models`&#32;branch\.&#32;Do not assume a dynamic\-only registration automatically installs the same modifier path\.

### Dynamic catalogs and usage

`fetchDynamicModels(apiKey)`&#32;receives a resolved key or&#32;`undefined`&#32;and returns model definitions\.&#32;It is driven by registry refresh\,&#32;not by the factory merely declaring the callback\.

The runtime uses the same SQLite model\-cache machinery as built\-ins\,&#32;with a default 24\-hour TTL\.&#32;Static models can remain as fallbacks alongside a live catalog\.&#32;An outage should be represented as an error\,&#32;not silently recast as an authoritative empty successful catalog\.

The callback itself has no public abort\-signal parameter\.&#32;Its transport needs bounded network behavior\;&#32;a host timeout is not a guarantee that an underlying request was cancelled\.

SDK startup first hydrates cached runtime providers offline\,&#32;then starts or awaits online discovery as appropriate for the host and model selector\.&#32;A “not found” result immediately after cold startup can therefore be a catalog\-timing issue\.

`usage`&#32;receives normalized credential information and returns normalized usage data for AuthStorage’s cache\/history\/display path\.&#32;It is distinct from token usage on an assistant turn\.

The recorded checks covered registration\,&#32;replacement and rollback\,&#32;including a synthetic usage\-provider contract\.&#32;They did not contact a real provider\.

### Trusted credential references

Credential references are executable configuration decisions\:

- an environment\-variable reference requires the real variable\;
- command\-backed key\/header configuration can execute a trusted local program\;
- a stored credential reference requires the matching host store\;
- changing a provider URL can redirect where credentials are sent\.

Do not derive endpoint or credential\-program configuration from untrusted model output\,&#32;draft text or a downloaded manifest merely because project inputs are trusted by default\.

### Milestone\:&#32;optional memory without assuming a backend

Sora wants the extension to report whether memory is available\,&#32;not silently pretend every host has the same memory service\.

**Complete TypeScript exercise—save as&#32;`memory-status.ts`\:**

~~~ts
import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent";

export default function memoryStatus(pi: ExtensionAPI): void {
    pi.registerTool({
        name: "workbook_memory_status",
        label: "Workbook memory status",
        description: "Report the configured memory runtime's status without saving anything.",
        approval: "read",
        loadMode: "essential",
        parameters: pi.typebox.Type.Object({}),
        async execute(_id, _params, signal, _update, ctx) {
            signal?.throwIfAborted();
            const details = ctx.memory
                ? await ctx.memory.status()
                : { available: false, message: "This host supplied no memory runtime." };
            signal?.throwIfAborted();
            return { content: [{ type: "text", text: JSON.stringify(details) }], details };
        },
    });
}
~~~

The optional&#32;`MemoryRuntimeContext`&#32;provides\:

- `status()`\:&#32;backend\,&#32;active\/writable\/searchable state and optional scope\/diagnostic fields\;
- `search(query, { limit?, signal? })`\:&#32;count and content items\,&#32;with optional IDs\,&#32;sources\,&#32;timestamps and scores\;
- `save(string | { content, context?, source?, importance? })`\:&#32;stored count\,&#32;optional IDs\,&#32;queued status and message\.

Backend IDs in the supplied contract are&#32;`off`\,&#32;`local`\,&#32;`hindsight`&#32;and&#32;`mnemopi`\.&#32;Search cancellation is best\-effort and backend\-dependent\.

Memory is not automatically a transactional reservation store\,&#32;not automatically branch\-local\,&#32;and not a delivery outbox\.&#32;A selected backend may need local components or an external service not supplied here\.

**Exercise\:**&#32;`ctx.memory`&#32;exists but status says&#32;`writable: false`\.&#32;Should a save button claim success\?

**Answer\:**&#32;no\.&#32;Availability of an object is not availability of every operation\.&#32;Inspect capability status and the real save result\.

*Source\,&#32;snapshot 2026\-08\-29\:&#32;`packages/coding-agent/src/extensibility/extensions/model-api.ts`\;&#32;`types.ts`\,&#32;provider and model interfaces\;&#32;`packages/coding-agent/src/config/model-registry.ts`\,&#32;registration\/refresh\/auth methods\;&#32;`packages/coding-agent/src/memory-backend/types.ts`\;&#32;linked provider adapter\.*

## Resources\,&#32;event buses\,&#32;MCP and Gemini manifests

Theo wants the notebook package to bring along guidance\.&#32;Rowan wants another extension to hear about a selected note\.&#32;Sora wants an external MCP service to contribute tools\.

These are three different integration problems\.&#32;They should not be hidden behind the word “extension\.”

### Milestone\:&#32;return module\-relative resource paths

Theo’s resource entry returns the location of a prompt\,&#32;rather than embedding all guidance inside a tool description\.

**Complete public TypeScript source—[resources\.ts](<https://present-sketch-tp94.here.now/examples/package-lab/manifest/entries/resources.ts>)\:**

~~~ts
import * as path from "node:path";
import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent";

export default function fieldResources(pi: ExtensionAPI): void {
    pi.on("resources_discover", () => ({
        promptPaths: [path.join(import.meta.dir, "../prompts/field-observation.md")],
    }));
}
~~~

**Complete public Markdown resource—[field\-observation\.md](<https://present-sketch-tp94.here.now/examples/package-lab/manifest/prompts/field-observation.md>)\:**

~~~markdown
---
description: Describe a fictional field observation
---
Use field_catalog to query the fictional notebook. Summarize one returned note. Clearly label the observation as fictional; do not claim a real survey occurred.
~~~

The module\-relative absolute path works from a different cwd\.

`resources_discover`&#32;handlers may return\:

- `skillPaths`\;
- `promptPaths`\;
- `themePaths`\.

The event includes&#32;`cwd`&#32;and reason&#32;`startup`&#32;or&#32;`reload`\.&#32;The runner aggregates each path with the originating&#32;`extensionPath`\.

**Observed\:**&#32;explicit runner dispatch returned the real prompt path and retained&#32;`entries/resources.ts`&#32;provenance\.

**Boundary\:**&#32;the supplied&#32;`AgentSession`&#32;startup path does not call&#32;`emitResourcesDiscover()`\.&#32;Registering this handler does not prove that a normal launch consumed its paths or populated a prompt menu\.&#32;A host must emit and consume the results\.

OMP extension\-package sibling discovery is a separate path\:&#32;configured\/explicit package roots can contribute conventional&#32;`skills`\,&#32;`hooks`\,&#32;`tools`\,&#32;`commands`\,&#32;`rules`\,&#32;`prompts`&#32;and MCP resources\.&#32;A prompt appearing through that route would not prove the resource event fired\.

**Exercise\:**&#32;launch from another directory and inspect the runner’s resource result in a host test\.

**Checkpoint\:**&#32;the prompt path still resolves to the package’s file\.&#32;Then separately test whether your actual frontend consumes it\.

### Milestone\:&#32;choose a skill\,&#32;prompt or rule deliberately

Theo decides that a reusable workflow belongs in a skill\,&#32;while a short invocation belongs in a prompt\.

**Complete Markdown exercise—save as&#32;`.omp/skills/fictional-field-guide/SKILL.md`\:**

~~~markdown
---
name: fictional-field-guide
description: Inspect and summarize the workbook's fictional field catalog
---
Use the available field_catalog tool to discover valid note IDs and query a habitat.

Label every observation as fictional. If field_catalog is unavailable, explain
that the extension must be loaded; do not invent inventory or tools.

Do not claim that a real survey occurred.
~~~

The native skill scanner requires a description\.&#32;Native project skills can be discovered through ancestor traversal within its configured boundary\,&#32;unlike cwd\-only extension modules\.

When skill commands are enabled\,&#32;the human command is\:

**Human OMP slash command\:**

~~~text
/skill:fictional-field-guide
~~~

That introduces guidance into the agent flow\.&#32;It is not the same as executing a local&#32;`registerCommand()`&#32;handler\.

For a standing instruction\:

**Complete Markdown exercise—save as&#32;`.omp/rules/fictional-data.md`\:**

~~~markdown
---
description: Keep workbook data distinct from real observations
alwaysApply: true
---
Treat Seed Desk, Review Desk and Field Notes records as fictional fixtures.
Never describe a workbook reservation as a physical stock change.
~~~

Rule files can carry descriptions\,&#32;globs\,&#32;always\-apply state and supported condition\/scope metadata\.&#32;The native top\-level&#32;`RULES.md`&#32;route forces sticky always\-apply behavior\.

A rule cannot enforce permissions against arbitrary extension code\.&#32;Seed Desk still needs real checks in&#32;`changeReservation()`\.

Prompt templates and Markdown commands likewise do not register missing tools\.&#32;Guidance should say what to do when the required capability is absent\.

**Exercise\:**&#32;load only the skill\,&#32;without&#32;`field_catalog`\.

**Checkpoint\:**&#32;the agent should diagnose the missing tool rather than invent a catalog result\.

### Milestone\:&#32;coordinate extensions on a process\-local bus

Rowan wants selection changes to be observable without creating a model turn\.

The public event\-bus entry both publishes and listens to a namespaced channel\.

**Complete public TypeScript source—[event\-bus\.ts](<https://present-sketch-tp94.here.now/examples/package-lab/advanced/event-bus.ts>)\:**

~~~ts
import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent";

const channel = "workbook:field-notes:selection";

export default function fieldNoteEvents(pi: ExtensionAPI): void {
    let selected: string | null = null;
    const unsubscribe = pi.events.on(channel, data => {
        if (data === "reed" || data === "fern") selected = data;
    });
    pi.on("session_shutdown", () => unsubscribe());
    const { Type } = pi.typebox;
    pi.registerTool({
        name: "field_events",
        label: "Field events",
        description: "Discover or inspect the field-notes channel, query last selection, or act to publish reed/fern. In-process extension-runtime bus; selection survives /new and session switches within this binding, without durable delivery.",
        parameters: Type.Object({
            op: Type.Union([Type.Literal("discover"), Type.Literal("inspect"), Type.Literal("query"), Type.Literal("act")]),
            id: Type.Optional(Type.Union([Type.Literal("reed"), Type.Literal("fern")])),
        }),
        async execute(_id, params) {
            if (params.op === "act") {
                if (!params.id) throw new Error("act requires a note id; nothing published.");
                pi.events.emit(channel, params.id);
            }
            const details = { channel, selected, operations: ["discover", "inspect", "query", "act"], delivery: "synchronous emit; asynchronous listeners are not awaited" };
            return { content: [{ type: "text", text: JSON.stringify(details) }], details };
        },
    });
}
~~~

**Terminal shell\:**

~~~sh
omp --no-extensions -e "$EXAMPLES/package-lab/advanced/event-bus.ts"
~~~

**Model tool arguments—call&#32;`field_events`\:**

~~~json
{"op":"act","id":"reed"}
~~~

~~~json
{"op":"query"}
~~~

**Observed\:**&#32;selection became&#32;`reed`\.&#32;After shutdown unsubscribed the listener\,&#32;a later bus emission did not update that selection in the isolated check\.

`pi.events.on()`&#32;returns an unsubscribe function\.&#32;`emit()`&#32;invokes listeners but does not await asynchronous completion\.&#32;Listener failures are caught and logged by the bus wrapper\.

The public selection update happens synchronously before the listener’s first await\,&#32;so this small tool can immediately observe it\.&#32;That does not establish an acknowledgement protocol for an asynchronous subscriber\.

The bus is runtime\/process\-local coordination\.&#32;It is not\:

- cross\-process messaging\;
- a durable queue\;
- an automatic agent turn\;
- a shared backend\;
- a transaction across extensions\.

A host can supply an explicit shared bus to SDK sessions\;&#32;that is a deliberate scope choice\.&#32;Do not call&#32;`events.clear()`&#32;from one extension as ordinary cleanup—it clears everyone’s listeners\.

The entry unsubscribes on shutdown\,&#32;not&#32;`/new`\.&#32;Its selection remains binding\-local across transcript changes\.

**Exercise\:**&#32;make a subscriber save to a slow external store\.&#32;Does&#32;`emit()`&#32;mean the save completed\?

**Answer\:**&#32;no\.&#32;Add an explicit result\/acknowledgement protocol or use a service API whose promise represents completion\.

### Milestone\:&#32;an MCP server is a real prerequisite

Sora chooses MCP when the capability belongs in a separate process\/service rather than in OMP’s JavaScript process\.

The supplied native discovery accepts MCP configuration including\:

- server name\;
- `command`\,&#32;`args`\,&#32;`env`\,&#32;optional&#32;`cwd`&#32;for process\-based servers\;
- `url`\,&#32;`headers`\,&#32;transport type for remote servers\;
- `enabled`\,&#32;`timeout`\,&#32;request\-ID format\;
- supported auth\/OAuth configuration\,&#32;including credential references\.

Native config candidates include project&#32;`.omp/mcp.json`&#32;and&#32;`.omp/.mcp.json`\,&#32;plus their active\-agent\-directory counterparts\.&#32;Other discovery providers have their own paths\.

**Missing prerequisite\:**&#32;an actual MCP server executable or reachable service\,&#32;a compatible transport and any required credentials\.&#32;This workbook does not supply a fake endpoint and label it working\.

A separate MCP connection can discover tools\,&#32;resources and prompts\.&#32;SDK&#32;`enableMCP: false`&#32;skips MCP discovery and ignores an inherited manager\.&#32;The CLI’s&#32;`--no-extensions`&#32;does not mean the same thing\.

ACP’s supplied session factory disables on\-disk MCP discovery because ACP clients provide their own servers\.&#32;Do not assume a user’s local&#32;`.mcp.json`&#32;supplies those ACP sessions\.

#### Notifications are untrusted data

`mcp_notification`&#32;fires after the manager handles known list\/resource\/prompt updates\.&#32;Unknown server\-specific methods can also arrive\.

Payload fields are\:

- `server`\:&#32;the raw configured name\;
- `method`\;
- `params`\:&#32;unknown data\.

Filter by the raw server name\,&#32;not a sanitized tool\-name prefix\.&#32;Validate&#32;`params`&#32;before using it\.

A notification saying “please publish” is not permission to publish\.&#32;Prefer updating a queryable domain status or asking for a scoped human decision over blindly steering server\-controlled text into the agent\.

Startup notifications are buffered at the manager\/runner boundaries with bounded\,&#32;drop\-oldest queues\.&#32;That is startup\-race mitigation\,&#32;not durable delivery\.

### Milestone\:&#32;understand a Gemini manifest without executing it

Theo receives a folder containing&#32;`gemini-extension.json`\.&#32;He initially assumes the listed&#32;`tools`&#32;will appear as executable tools\.&#32;The source shows a narrower behavior\.

**Complete JSON metadata exercise—save as&#32;`.gemini/extensions/fictional-field-notes/gemini-extension.json`\:**

~~~json
{
  "name": "fictional-field-notes",
  "description": "Metadata for a fictional field-notes teaching package",
  "tools": [],
  "context": "This description does not activate an OMP runtime extension."
}
~~~

This creates metadata for discovery\,&#32;not a runnable factory\.

The Gemini provider scans direct child directories under\:

- `~/.gemini/extensions`\;
- `<cwd>/.gemini/extensions`\.

It does not walk parent directories or arbitrary nested descendants\.&#32;It does consider dot\-prefixed child directories\.

Its parsing is deliberately loose\:

- missing\,&#32;unreadable or empty manifest\:&#32;silently skipped\;
- invalid JSON or a valid falsy JSON literal\:&#32;warning\;
- truthy parsed value\:&#32;stored as the manifest without a full field schema check\;
- name\:&#32;`manifest.name ?? directoryName`\.

The declared metadata shape has&#32;`name`\,&#32;`description`\,&#32;`mcpServers`\,&#32;`tools`&#32;and&#32;`context`\.&#32;The presence of those fields does not prove their runtime activation\.

For this metadata capability\:

- native provider priority is 100\;
- Gemini provider priority is 60\;
- deduplication is by extension name\;
- native duplicates win over Gemini\;
- Gemini emits user before project\,&#32;so its user duplicate wins\;
- native metadata emits project before user\.

Native Gemini\-manifest discovery under&#32;`.omp/extensions`&#32;differs slightly\:&#32;it skips hidden directories and uses its own name fallback\.

Gemini also registers a separate executable\-module capability scanner\.&#32;However\,&#32;normal ambient&#32;`discoverExtensionPaths()`&#32;requests only native module\-provider items\.&#32;A neighboring factory does not run merely because a Gemini manifest exists\.&#32;Explicitly load the actual module file when that is your intention\.

**Exercise\:**&#32;put an arbitrary object in&#32;`tools`&#32;and reload metadata discovery\.&#32;Has a callable tool been installed\?

**Answer\:**&#32;no\.&#32;You have demonstrated loose metadata parsing\,&#32;not tool construction or execution\.

*Source\,&#32;snapshot 2026\-08\-29\:&#32;linked resource\/event\-bus files\;&#32;`packages/coding-agent/src/utils/event-bus.ts`\;&#32;`packages/coding-agent/src/discovery/builtin.ts`\,&#32;`gemini.ts`\,&#32;`helpers.ts`\;&#32;`packages/coding-agent/src/capability/extension.ts`\,&#32;`extension-module.ts`\;&#32;`packages/coding-agent/src/extensibility/extensions/runner.ts`\,&#32;`emitResourcesDiscover`\;&#32;`packages/coding-agent/src/sdk.ts`\,&#32;MCP wiring\.*

## Permission\-denied file fallbacks

Ada embeds OMP in a host with a real OS permission boundary\.&#32;Native writes to one approved destination are denied inside the agent process\,&#32;but the host has a privileged channel that can perform them\.

Her obstacle is preserving native tool behavior\.&#32;Reimplementing&#32;`write`&#32;or hashline editing would lose snapshots and bookkeeping\.

She chooses a file fallback\,&#32;while leaving permission policy with the host\.

### Milestone\:&#32;delegate exact denied bytes

The public bundle provides an adapter\,&#32;not an elevated writer\.

**Complete public TypeScript source—[file\-fallback\.ts](<https://present-sketch-tp94.here.now/examples/package-lab/advanced/file-fallback.ts>)\:**

~~~ts
import * as path from "node:path";
import type { ExtensionFactory } from "@oh-my-pi/pi-coding-agent";

export interface FieldNotesWriteBroker {
    /** Real host broker: resolve only after exact bytes are durable at the supplied destination. */
    write(destination: string, content: string): Promise<void>;
}

/** Host supplies the already-canonical allowlisted destination and a real broker. No elevated writer is bundled. */
export function createFieldNotesFallback(destination: string, broker: FieldNotesWriteBroker): ExtensionFactory {
    if (!path.isAbsolute(destination)) throw new Error("The host must supply an absolute, canonical destination.");
    return pi => {
        pi.registerFileWriteFallback(async (request, ctx) => {
            if (request.sessionId !== ctx.sessionManager.getSessionId() || request.dst !== destination) return false;
            await broker.write(request.dst, request.content);
            return true;
        });
    };
}
~~~

**Missing prerequisites\:**

- a real broker implementation\;
- a host\-established canonical allowlisted destination\;
- a real permission\-denied native byte\-write scenario\;
- initialized runtime installation of the fallback\.

The adapter checks that the destination is absolute\.&#32;It does&#32;**not**&#32;itself prove canonicalization\;&#32;the host must perform that policy work\.

It has a named factory creator and no default extension export\.&#32;Import it into an SDK host\;&#32;do not launch it directly with&#32;`-e`\.

#### What reaches the write seam\?

The seam handles native ordinary\-file byte writes used by&#32;`write`\,&#32;`edit`&#32;and&#32;`apply_patch`\,&#32;including a hashline move destination\,&#32;after permission errors\:

- `EPERM`\;
- `EACCES`\;
- `EROFS`\.

A special missing\-parent case recovers a denied&#32;`mkdir`&#32;that Bun originally surfaced as&#32;`ENOENT`\.&#32;The broker may need to create that parent\.&#32;A genuinely invalid\/missing path is not automatically a permission fallback\.

Requests contain\:

- `dst`\;
- `content`\;
- `cause`\;
- `sessionId`\,&#32;possibly undefined outside a tool call\.

Handlers run in order\.&#32;The first true result means the native tool may continue as though its byte write succeeded\,&#32;including native snapshot bookkeeping\.

Throwing handlers are logged and skipped\,&#32;including later handlers in the same extension\.&#32;If none succeeds\,&#32;the original error is rethrown\;&#32;a recovered underlying denial may be attached as its cause\.

Returning true before the bytes actually land is a correctness bug\.

#### Path and session policy

`dst`&#32;is the resolved destination the failed write would target\,&#32;including the final component for writes\.&#32;Do not rederive it from a lexically innocent tool path\.

If the destination cannot be resolved safely enough to identify it\,&#32;the fallback is not consulted\.

Fallback registries are&#32;**process\-wide**\.&#32;A handler can be consulted for another session’s denied write\.&#32;Compare request identity to the handler’s session before prompting\:&#32;`ctx.ui`&#32;belongs to the handler’s session\,&#32;not necessarily the originator\.

The public adapter intentionally refuses other sessions\.

Canonical path resolution reduces specific symlink misrouting risks\.&#32;It does not establish a universal sandbox or remove the need for broker\-side path policy and safe filesystem operations\.

### Milestone\:&#32;deletion remains a separate capability

A write fallback must never receive a delete request and interpret missing content as an empty file\.

`registerFileDeleteFallback()`&#32;is separate\.&#32;It covers ordinary native unlink paths such as hashline&#32;`REM`\,&#32;a move’s source unlink and patch deletion\.

A delete request contains\:

- `dst`\;
- `cause`\;
- `confirmedFile`\;
- `sessionId`\.

The final path component is&#32;**not**&#32;resolved through a symlink\:&#32;unlink removes the link itself\.

A broker must use plain unlink semantics\.&#32;It must never fall back to recursive removal or realpath the final component\.

On Darwin\,&#32;unlinking a directory can report&#32;`EPERM`\.&#32;The seam refuses a known directory\,&#32;but inaccessible metadata can leave the target’s type unknown\.&#32;`confirmedFile: false`&#32;can mean either unknown metadata or a symlink—not permission to recursively remove it\.

This complete adapter exercise chooses an even narrower policy\:&#32;only positively identified regular files\.

**Complete TypeScript host\-adapter exercise—not an elevated implementation\:**

~~~ts
import * as path from "node:path";
import type { ExtensionFactory } from "@oh-my-pi/pi-coding-agent";

export interface PlainUnlinkBroker {
    unlink(destination: string): Promise<void>;
}

export function createSingleFileDeleteFallback(
    destination: string,
    broker: PlainUnlinkBroker,
): ExtensionFactory {
    if (!path.isAbsolute(destination)) throw new Error("An absolute policy destination is required.");
    return pi => {
        pi.registerFileDeleteFallback(async (request, ctx) => {
            if (request.sessionId !== ctx.sessionManager.getSessionId()) return false;
            if (request.dst !== destination || !request.confirmedFile) return false;
            await broker.unlink(request.dst);
            return true;
        });
    };
}
~~~

That stricter&#32;`confirmedFile`&#32;choice deliberately declines symlinks and metadata\-hidden targets\.&#32;A different broker policy may support them with safe plain\-unlink operations\,&#32;but recursive deletion is never the fallback contract\.

`ENOENT`&#32;on delete is not diverted\.

### What is not covered

These APIs do not intercept\:

- arbitrary&#32;`Bun.write()`&#32;or&#32;`fs`&#32;calls made by extensions\;
- shell\/subprocess writes\;
- archive\-member rewrites\;
- SQLite row writes\;
- ACP client\-side&#32;`writeTextFile`\;
- the LSP tool’s independent workspace\-edit\/code\-action writes\;
- formatter subprocess mutations\.

Register fallback handlers during factory loading\.&#32;They are installed at runner initialization and disposed on shutdown\.&#32;A first registration after initialization does not install a previously absent seam\.

Each invocation receives a newly built context\,&#32;so current cwd\/UI information is not frozen at installation\.

**Observed scope\:**&#32;existing contract tests exercised real permission\-denied native writes and follow\-up editing\.&#32;The test’s stand\-in broker temporarily changed permissions to place bytes\;&#32;it was not a real privileged broker\.&#32;The workbook’s example scenarios did not exercise elevation\.

**Exercise\:**&#32;the broker succeeded at the move destination but source unlink failed\.&#32;Is the move an atomic success\?

**Answer\:**&#32;no\.&#32;The two primitives have separate outcomes\.&#32;Inspect the actual files before retrying\.

*Source\,&#32;snapshot 2026\-08\-29\:&#32;`packages/coding-agent/src/tools/file-write-fallback.ts`\;&#32;`packages/coding-agent/src/extensibility/extensions/runner.ts`\,&#32;initialization\/disposal\;&#32;`packages/coding-agent/src/extensibility/extensions/wrapper.ts`\,&#32;file\-mutation session attribution\;&#32;`packages/coding-agent/test/sdk-file-write-fallback-extension.test.ts`\.*

## Public feature reference

This chapter is the compact index to the entire supplied public inventory\.&#32;It is intentionally grouped by purpose rather than presented as one giant type dump\.

### `ExtensionAPI`\:&#32;registration and module access

| Members | Practical contract | Worked example |
| --- | --- | --- |
| `logger` | File logging\;&#32;avoid console output that corrupts TUI\/RPC channels and avoid logging secrets | Lab status\;&#32;native delegation |
| `typebox`\,&#32;`arktype`\,&#32;`zod` | Injected compatible schema authoring | Seed Desk\;&#32;Package Lab |
| `pi` | Injected coding\-agent package exports\;&#32;availability still follows the matching host | SDK\-oriented integrations |
| `on` | Subscribe using the exact supported event name and return shape | [Event reference](<https://present-sketch-tp94.here.now/chapters/extensions-all-46-extension-events#extensions-all-46-extension-events>) |
| `registerTool` | Register a schema\-shaped machine capability | Seed Desk\;&#32;Review Desk |
| `registerCommand` | Register a human\/host slash\-command handler | All principal stories |
| `registerShortcut` | Register a nonreserved operator key binding | Phrase\-key exercise |
| `registerFlag`\,&#32;`getFlag` | Declare\/read a boolean or string flag\;&#32;read your own registered name | `seed-quiet` |
| `setLabel` | Set the extension’s display label in the supplied concrete implementation | Lab status |
| `registerMessageRenderer` | Render a named custom message type | Review Desk |
| `registerAssistantThinkingRenderer` | Add supplemental UI after visible thinking blocks | Thinking\-length exercise |
| `registerComposerShape` | Register a complete composer rendering contract | Workbook Field Dock |
| `registerFileWriteFallback`\,&#32;`registerFileDeleteFallback` | Register separate native permission\-denied mutation seams | [File fallbacks](<https://present-sketch-tp94.here.now/chapters/extensions-permission-denied-file-fallbacks#extensions-permission-denied-file-fallbacks>) |
| `registerProvider`\,&#32;`unregisterProvider` | Register\/remove runtime model\-provider overrides | [Providers](<https://present-sketch-tp94.here.now/chapters/extensions-models-providers-credentials-and-memory#extensions-models-providers-credentials-and-memory>) |
| `events` | Shared runtime event bus with&#32;`on`\,&#32;`emit`\,&#32;`clear` | Field events |

**`setLabel`&#32;compatibility edge\:**&#32;the public type advertises both extension\-label and entry\-label usage\,&#32;but&#32;`ConcreteExtensionAPI.setLabel(label)`&#32;only assigns the extension label in this snapshot\.&#32;Do not claim that the two\-argument form labels a journal entry here\.&#32;Do not cast the read\-only session manager to bypass the public boundary\.

### `ExtensionAPI`\:&#32;live actions

| Members | Practical contract |
| --- | --- |
| `sendMessage` | Custom conversation\/context message\;&#32;optional steer\/follow\-up\/next\-turn and turn trigger |
| `captureSessionTarget`\,&#32;`deliverMessage` | Persistent owner anchor and deduplicated complete delivery with a receipt |
| `sendUserMessage` | User\-style prompt\/queue\;&#32;no slash\-command dispatch |
| `appendEntry` | Custom state entry\,&#32;not model\-visible content |
| `exec` | Program\/argv execution with cwd\,&#32;timeout and cancellation |
| `getActiveTools`\,&#32;`getAllTools`\,&#32;`setActiveTools` | Enabled\-set inspection\,&#32;full registry metadata and asynchronous selection |
| `getCommands` | Dynamic command metadata\,&#32;not a universal list of built\-ins |
| `setModel` | Credential\-aware model selection |
| `getThinkingLevel`\,&#32;`setThinkingLevel` | Effective thinking\-level control |
| `getServiceTiers`\,&#32;`setServiceTier` | Detached per\-family tier snapshot and subsequent\-request override |
| `getSessionName`\,&#32;`setSessionName` | Read\/set persisted session naming\,&#32;distinct from terminal title |

`getCommands()`&#32;aggregates extension commands\,&#32;loaded custom\/MCP prompt commands and enabled skill commands through&#32;`getSessionSlashCommands()`\.&#32;Built\-ins are intentionally excluded from that helper\.&#32;Frontends may advertise a larger command list\.

`RegisteredCommand`&#32;contains&#32;`name`\,&#32;optional&#32;`description`\,&#32;optional&#32;`getArgumentCompletions`\,&#32;and&#32;`handler`\.&#32;Completion values represent the complete argument text\,&#32;not just an appended fragment\.

### `ExtensionContext`

| Members | What to remember |
| --- | --- |
| `ui`\,&#32;`mode`\,&#32;`hasUI` | Mode and actual method support matter\;&#32;`hasUI`&#32;is not “all TUI methods work” |
| `cwd` | Current at context creation\;&#32;do not retain it as a permanent workspace identity |
| `sessionManager` | Read\-oriented session\/journal access |
| `modelRegistry` | Host registry and credential\-resolution access\;&#32;prefer&#32;`models`&#32;for selection queries |
| `model`\,&#32;`models` | Current\/request\-specific model and read\-only query facade |
| `localProtocolOptions` | Calling\-session&#32;`local://`&#32;mapping for compatible bridges |
| `getContextUsage` | Estimated context tokens\/window\/percentage\;&#32;can be undefined |
| `getAsyncJobSnapshot` | Read\-only owner\-scoped job state\;&#32;can be null |
| `isIdle`\,&#32;`hasPendingMessages` | Snapshot predicates\,&#32;not locks |
| `abort` | Request cancellation of the current agent operation |
| `shutdown` | Request host shutdown\;&#32;host\-specific and not an immediate&#32;`process.exit`&#32;guarantee |
| `getSystemPrompt` | Effective prompt blocks\;&#32;treat returned content as sensitive |
| `compact` | Request host maintenance at a safe boundary |
| `memory` | Optional status\/search\/save runtime |
| `setInterval`\,&#32;`setTimeout`\,&#32;`clearTimer` | Managed timer lifecycle |
| `invokeTool` | Optional same\-name native built\-in delegation |
| `isProjectTrusted` | Compatibility method returning true\,&#32;not isolation |

The read\-oriented session manager exposes identity\/cwd\/header\/leaf\/entry\/branch\/tree\/label\/usage queries\.&#32;It also includes artifact\/blob helpers such as&#32;`allocateArtifactPath`\,&#32;`saveArtifact`\,&#32;`getArtifactPath`\,&#32;`putBlob`&#32;and&#32;`putBlobSync`\.

“Readonly” here does not mean every helper is side\-effect\-free\:&#32;artifact\/blob helpers can store data\.&#32;It means extensions are not handed the journal’s general navigation\/mutation API\.

Use\:

- `getBranch()`&#32;for branch\-derived state\;
- `getEntries()`&#32;for deliberately whole\-journal inspection\;
- `getEntry()`&#32;and&#32;`getTree()`&#32;for stable navigation targets\.

Do not mutate returned session records in place\.

### `ExtensionCommandContext`

Commands inherit the general context and additionally expose\:

- `getContextUsage`\;
- `waitForIdle`\;
- `newSession`\;
- `branch`\;
- `navigateTree`\;
- `switchSession`\;
- `reload`\;
- `compact`\.

The duplicated usage\/compaction members remain the same public concepts\.&#32;The navigation methods are command\-only\.

The supplied ACP shutdown action is inert\.&#32;Other hosts request deferred graceful shutdown\.&#32;Do not build essential durable cleanup around an assumption that&#32;`ctx.shutdown()`&#32;terminates every embedding\.

### Tool metadata and provenance

`ToolInfo`&#32;contains\:

- `name`\;
- `description`\;
- `parameters`\;
- optional&#32;`promptGuidelines`\;
- `sourceInfo`\.

`SourceInfo`&#32;contains\:

- `path`\;
- `source`\,&#32;such as builtin\,&#32;SDK\,&#32;MCP or extension\;
- `scope`\:&#32;user\,&#32;project or temporary\;
- `origin`\:&#32;package or top\-level\;
- optional&#32;`baseDir`\.

This metadata answers “where did the tool come from\?” It is not an authorization certificate\.

`ToolRenderResultOptions`&#32;contains&#32;`expanded`\,&#32;`isPartial`\,&#32;`spinnerFrame`\.

`ToolSessionEvent`&#32;contains&#32;`reason`&#32;and&#32;`previousSessionFile`\.

`ToolShellEnvironmentContext`&#32;contains&#32;`command`\,&#32;`cwd`&#32;and&#32;`env`\.

All&#32;`ToolDefinition`&#32;fields are explained in&#32;[Tools\,&#32;interception and native delegation](<https://present-sketch-tp94.here.now/chapters/extensions-tools-interception-and-native-delegation#extensions-tools-interception-and-native-delegation>)\.

### UI inventory index

Every supplied&#32;`ExtensionUIContext`&#32;member belongs to one of these families\:

- Dialogs\:&#32;`timeoutStartsOnPresentation`\,&#32;`select`\,&#32;`confirm`\,&#32;`input`\,&#32;optional&#32;`askDialog`\,&#32;`editor`\.
- Notices and layout\:&#32;`notify`\,&#32;`setStatus`\,&#32;`setWorkingMessage`\,&#32;`setWidget`\,&#32;`setFooter`\,&#32;`setHeader`\,&#32;`setTitle`\.
- Native components\/input\:&#32;`custom`\,&#32;`onTerminalInput`\.
- Composer editing\:&#32;`setEditorText`\,&#32;`pasteToEditor`\,&#32;`getEditorText`\,&#32;`addAutocompleteProvider`\,&#32;`setEditorComponent`\.
- Appearance\:&#32;`theme`\,&#32;`getAllThemes`\,&#32;`getTheme`\,&#32;`setTheme`\,&#32;`getToolsExpanded`\,&#32;`setToolsExpanded`\.

The complete dialog\/select\/ask\/custom option shapes are explained in&#32;[Review Desk’s UI chapter](<https://present-sketch-tp94.here.now/chapters/extensions-review-desk-a-native-panel-and-portable-dialogs#extensions-review-desk-a-native-panel-and-portable-dialogs>)\.&#32;Composer shape fields are explained in&#32;[Editors\,&#32;themes and composer shapes](<https://present-sketch-tp94.here.now/chapters/extensions-editors-themes-and-composer-shapes#extensions-editors-themes-and-composer-shapes>)\.

### Named\-interface coverage index

The supplied inventory’s 27 named interfaces are covered as follows\:

| Family | Interfaces |
| --- | --- |
| Core | `ExtensionAPI`\,&#32;`ExtensionContext`\,&#32;`ExtensionCommandContext`\,&#32;`ExtensionModelQuery` |
| Tools | `ToolDefinition`\,&#32;`ToolRenderResultOptions`\,&#32;`ToolSessionEvent`\,&#32;`ToolShellEnvironmentContext`\,&#32;`ToolInfo`\,&#32;`SourceInfo` |
| Commands | `RegisteredCommand` |
| UI | `ExtensionUIContext`\,&#32;`ExtensionUIDialogOptions`\,&#32;`ExtensionCustomOptions`\,&#32;`ComposerShapeDefinition`\,&#32;`ExtensionUISelectOption` |
| Rich ask | `ExtensionAskDialogOption`\,&#32;`ExtensionAskDialogQuestion`\,&#32;`ExtensionAskDialogResultItem`\,&#32;`ExtensionAskDialogSubmitResult` |
| Maintenance | `CompactOptions` |
| Providers | `ProviderConfig`\,&#32;`ProviderModelConfig` |
| Durable delivery | `ExtensionSessionTarget`\,&#32;`CaptureSessionTargetOptions`\,&#32;`ExtensionDeliveryReceipt`\,&#32;`DeliverExtensionMessageOptions` |

### Reserved shortcuts

The supplied runner rejects these effective extension shortcuts\:

`ctrl+c`\,&#32;`ctrl+d`\,&#32;`ctrl+z`\,&#32;`ctrl+k`\,&#32;`ctrl+p`\,&#32;`ctrl+l`\,&#32;`ctrl+o`\,&#32;`ctrl+t`\,&#32;`ctrl+g`\,&#32;`ctrl+q`\,&#32;`alt+m`\,&#32;`shift+tab`\,&#32;`shift+ctrl+p`\,&#32;`alt+enter`\,&#32;`escape`\,&#32;`enter`\.

Other host keybindings can still conflict\.&#32;Test the actual chord in the actual terminal\.

Built\-in command names are filtered by the host’s reserved command set\.&#32;Do not assume a name is available merely because an older example registered it\.

*Source\,&#32;snapshot 2026\-08\-29\:&#32;`packages/coding-agent/src/extensibility/extensions/types.ts`\,&#32;supplied public inventory\;&#32;`loader.ts`\,&#32;`ConcreteExtensionAPI`\;&#32;`runner.ts`\;&#32;`get-commands-handler.ts`\;&#32;`packages/coding-agent/src/session/session-manager.ts`\,&#32;`ReadonlySessionManager`\.*

## All 46 extension events

These are the complete supplied event names\.&#32;Numbers follow the supplied inventory and make omissions easy to check\.

Unless stated otherwise\,&#32;a notification handler’s returned value does not control the operation\.

### Resources and session lifecycle

| No\. | Event | Payload\/use | Supported result |
| --- | --- | --- | --- |
| 1 | `resources_discover` | `cwd`\,&#32;startup\/reload reason\;&#32;contribute resource paths | `skillPaths`\,&#32;`promptPaths`\,&#32;`themePaths`\;&#32;requires host dispatch\/consumption |
| 2 | `session_start` | Initial initialized session load | Notification |
| 3 | `session_before_switch` | Reason&#32;`new`\,&#32;`resume`&#32;or&#32;`fork`\;&#32;optional target file | `{ cancel }` |
| 4 | `session_switch` | Completed switch\;&#32;reason and previous file | Notification\;&#32;reconstruct current state |
| 5 | `session_before_branch` | Selected user\-message entry ID | `{ cancel, skipConversationRestore }` |
| 6 | `session_branch` | Branch completed\;&#32;previous file | Notification |
| 7 | `session_before_compact` | Preparation\,&#32;branch entries\,&#32;public instructions\,&#32;signal | `{ cancel, compaction }` |
| 8 | `session.compacting` | Session ID and messages about to be summarized | `{ context, prompt, preserveData }` |
| 9 | `session_compact` | Compaction entry and&#32;`fromExtension` | Notification |
| 10 | `session_shutdown` | Teardown | Cleanup\;&#32;concurrent bounded handlers |
| 11 | `session_before_tree` | Tree preparation and signal | `{ cancel, summary }`\;&#32;summary used only when requested |
| 12 | `session_tree` | Old\/new leaf\,&#32;optional summary entry and origin | Notification |

`skipConversationRestore`&#32;means the branch proceeds while in\-memory conversation restoration is skipped\.&#32;It is not cancellation\.

For the cancelable pre\-events\,&#32;cancellation short\-circuits\.&#32;Otherwise the current generic runner retains the last returned result rather than deep\-merging every handler’s object\.&#32;`session.compacting`&#32;likewise uses the last returned result object\;&#32;coordinate cooperating extensions\.

Seed Desk reconstructs after lifecycle events\.&#32;Review Desk invalidates pending authority before navigation\.

### Prompt and provider boundaries

| No\. | Event | Payload\/use | Supported result |
| --- | --- | --- | --- |
| 13 | `context` | Messages before each model call | Replacement&#32;`{ messages }`\,&#32;chained |
| 14 | `before_provider_request` | Provider\-specific logical payload\;&#32;request model in context | Return the replacement payload directly |
| 15 | `provider_request` | Frozen event with final&#32;`payloadJson` | Observation only |
| 16 | `after_provider_response` | Status\,&#32;headers\,&#32;request ID\,&#32;metadata before stream consumption | Observation only |
| 17 | `before_agent_start` | Submitted prompt\,&#32;images\,&#32;current prompt blocks | Custom&#32;`message`&#32;and\/or replacement&#32;`systemPrompt`&#32;blocks |
| 39 | `input` | Input text\,&#32;images and source tag | `{ handled, text, images }`\;&#32;transforms chain\,&#32;handled stops |

`before_provider_request`&#32;replacements chain in load order\.&#32;Provider\-specific payloads are not one universal HTTP schema\.

`provider_request`&#32;is the final logical JSON after those transforms\,&#32;not exact transport bytes or headers\.&#32;Returned values cannot change it\.&#32;Extension observer errors are isolated and are not a reliable dispatch veto\.

An embedding that requires a fail\-closed final\-payload check uses the awaited SDK&#32;`onProviderRequest`&#32;callback\;&#32;rejection there stops dispatch before extension observers\.

The supplied guide identifies&#32;`devin-agent`&#32;as a provider that does not fire the request hook\.&#32;Provider implementation coverage must be checked when relying on these boundaries\;&#32;no live provider was exercised for the workbook examples\.

`after_provider_response`&#32;occurs before the response stream has yielded final assistant usage\.&#32;Use a completed assistant message\,&#32;commonly at&#32;`turn_end`\,&#32;for actual turn token\/cost accounting\.

`input`&#32;source tags are&#32;`interactive`\,&#32;`rpc`&#32;and&#32;`extension`\.&#32;The runner supports all three labels\,&#32;but that does not mean every host path emits the event\.&#32;The supplied RPC\/ACP prompt implementations do not themselves call&#32;`emitInput()`\.&#32;Do not use it as a universal inbound\-policy gateway\.

In typed interactive input flow\,&#32;naming a session from&#32;`input`&#32;can precede the normal first\-message title check\.&#32;That does not imply every initial CLI prompt takes the same path\.

### Agent\,&#32;turn and message notifications

| No\. | Event | Payload\/use | Supported result |
| --- | --- | --- | --- |
| 18 | `agent_start` | Agent\-loop start | Notification |
| 19 | `agent_end` | Messages and optional&#32;`willContinue` | Notification only |
| 20 | `session_stop` | Main\-session settle context\,&#32;turn\/session IDs\,&#32;last assistant\,&#32;`stop_hook_active`\,&#32;signal | `{ continue: true, additionalContext }`&#32;or&#32;`{ decision: "block", reason }` |
| 21 | `turn_start` | Turn index and timestamp | Notification |
| 22 | `turn_end` | Completed turn message and tool results | Notification\;&#32;inspect completed usage here |
| 23 | `message_start` | A message begins | Notification |
| 24 | `message_update` | Assistant message and streaming event\/delta | Notification\;&#32;keep handlers lightweight |
| 25 | `message_end` | Detached completed\-message snapshot | Notification\,&#32;not a rewrite hook |

A turn is one assistant response plus its associated tool results\.&#32;A run can contain several turns and maintenance continuations\.

`session_stop`\:

- is awaited at eligible main\-session settle\;
- does not run for task\/subagent sessions\;
- requires nonempty continuation context\/reason\;
- is capped at eight consecutive continuations\;
- is deferred when automatic continuation or owner\-scoped pending async work means the run is not truly finished\;
- can be cancelled through its signal\.

Use&#32;`stop_hook_active`&#32;to avoid endlessly requesting the same extra pass\.

Streaming notifications can be queued\/detached relative to other host work\.&#32;Do not base an authorization protocol on an assumed universal arrival order of every message and UI frame\.

### Tool execution and approval

| No\. | Event | Payload\/use | Supported result |
| --- | --- | --- | --- |
| 26 | `tool_execution_start` | Call ID\,&#32;name\,&#32;arguments\,&#32;optional intent | Observation |
| 27 | `tool_execution_update` | Call identity\,&#32;arguments and partial result | Observation |
| 28 | `tool_execution_end` | Call identity\,&#32;result and error state | Observation |
| 40 | `tool_approval_requested` | Session\/call\/tool IDs\,&#32;optional reason\,&#32;approval mode | Observation |
| 41 | `tool_approval_resolved` | Session\/call\/tool IDs\,&#32;approved boolean\,&#32;optional reason | Observation |
| 42 | `tool_call` | Call identity and normalized input before execution | `{ block, reason, input }` |
| 43 | `tool_result` | Effective input\,&#32;content\,&#32;details and error state | Patch&#32;`{ content, details, isError }`\,&#32;chained |

A&#32;`tool_execution_start`&#32;event does not prove the side effect happened\;&#32;approval may still be pending\.

Approval events are emitted when the wrapper reaches a required approval gate and relevant handlers are present\.&#32;They are not a way to approve by returning a value\.

Already\-denied calls can short\-circuit before&#32;`tool_call`\.&#32;Schema failures\,&#32;pre\-execution blocks and approval denials do not necessarily traverse the post\-execution&#32;`tool_result`&#32;path\.

`tool_call`&#32;errors\/timeouts fail closed\.&#32;`tool_result`&#32;middleware can change what is reported\,&#32;not reverse external effects\.

### Reliability and domain reminders

| No\. | Event | Payload\/use |
| --- | --- | --- |
| 29 | `auto_compaction_start` | Reason\:&#32;threshold\,&#32;overflow\,&#32;idle or incomplete\;&#32;selected action |
| 30 | `auto_compaction_end` | Action\,&#32;optional result\,&#32;aborted\/willRetry\,&#32;optional error\/skipped |
| 31 | `auto_retry_start` | Attempt\,&#32;maximum attempts\,&#32;delay\,&#32;error message and optional error ID |
| 32 | `auto_retry_end` | Success\,&#32;attempt\,&#32;final error and optional retry\-error presentation updates |
| 33 | `retry_fallback_applied` | From\/to model selectors and configured role |
| 34 | `retry_fallback_succeeded` | Fallback model and role that actually succeeded |
| 35 | `ttsr_triggered` | Rules whose stream matching interrupted generation |
| 36 | `todo_reminder` | Unfinished todos and reminder attempt information |
| 37 | `goal_updated` | Current goal or null and optional goal\-mode state |
| 38 | `credential_disabled` | Provider and truncated diagnostic cause for automatic credential soft\-disable |

These are observations\,&#32;not return\-value control hooks\.

Compaction actions include&#32;`context-full`\,&#32;`remote`\,&#32;`handoff`\,&#32;`shake`&#32;and&#32;`snapcompact`&#32;in the supplied event type\.&#32;A skipped or aborted compaction is not a successful summary\.

`retry_fallback_applied`&#32;means a candidate was selected\.&#32;`retry_fallback_succeeded`&#32;distinguishes actual success\.

`credential_disabled`&#32;is not fired for every user logout\/removal\.&#32;Startup events can be buffered until runtime initialization\;&#32;the runner’s buffer is bounded at 32\.

### Human execution and MCP notifications

| No\. | Event | Payload\/use | Supported result |
| --- | --- | --- | --- |
| 44 | `user_bash` | Command\,&#32;cwd and whether&#32;`!!`&#32;excludes output from model context | Full replacement&#32;`{ result }` |
| 45 | `user_python` | Code\,&#32;cwd and whether&#32;`$$`&#32;excludes output from model context | Full replacement&#32;`{ result }` |
| 46 | `mcp_notification` | Raw server name\,&#32;method and unknown params | Notification |

`user_bash`&#32;concerns human&#32;`!`\/`!!`&#32;execution\,&#32;not every model bash tool or arbitrary&#32;`pi.exec()`&#32;call\.&#32;`user_python`&#32;concerns the corresponding&#32;`$`\/`$$`&#32;user\-code path\.

The first returned user\-execution result replaces default execution\.&#32;Throwing is not a supported blocking result\.

MCP notifications arrive after the manager’s known\-method processing\.&#32;Buffering is bounded at 100 with drop\-oldest behavior at the supplied startup boundaries\.&#32;Validate payloads and do not mistake notifications for durable authority\.

*Source\,&#32;snapshot 2026\-08\-29\:&#32;`packages/coding-agent/src/extensibility/extensions/types.ts`\,&#32;all&#32;`on`&#32;overloads\;&#32;`packages/coding-agent/src/extensibility/shared-events.ts`\;&#32;`runner.ts`\;&#32;`wrapper.ts`\;&#32;`packages/coding-agent/src/session/agent-session.ts`\;&#32;`packages/coding-agent/src/session/bash-runner.ts`\;&#32;`packages/coding-agent/src/modes/rpc/rpc-mode.ts`\.*

## Debugging and verification

A workbook milestone is complete when the intended behavior is observable at the right layer\.

A successful import is not proof of a useful tool\.&#32;A successful tool adapter call is not proof that the model chooses it\.&#32;A headless no\-op is not proof of a TUI\.

### A practical debugging order

#### “The extension did not load”

Check\:

1. The launch selected the intended file or directory\.
2. The module has a default factory\.
3. Adjacent assets such as&#32;`tool.txt`&#32;exist\.
4. Runtime dependencies match the custom host\.
5. The file is not hidden\/ignored by the discovery route\.
6. A disabled derived ID or plugin state did not filter it\.
7. An earlier same\-name capability item did not shadow it\.

Inspect the path\-specific loader error\.&#32;Do not replace a failing factory with an empty function merely to obtain “loaded\.”

**Terminal shell—default\-profile log example\;&#32;use the actual active state\-root log path on your host\:**

~~~sh
tail -f ~/.omp/logs/omp.$(date +%F).*.log
~~~

The log root can differ with host\/state\-root configuration\.&#32;The supplied startup watchdog prints the actual log path\.

**Terminal shell—source\-backed startup diagnostics\:**

~~~sh
PI_DEBUG_STARTUP=1 omp --no-extensions -e "$EXAMPLES/package-lab/single/field-notes.ts"
~~~

This reveals startup phases\;&#32;it is not an isolation flag\.

#### “The command works\,&#32;but the agent cannot use it”

Inspect the tool registry\.

- Was a tool actually registered\?
- Is it enabled\?
- Is it discoverable rather than top\-level\?
- Does its description explain operations and prerequisites\?
- Are important result fields in model\-visible content\?
- Is the agent trying to invoke a slash command\?

Use a direct schema\-valid tool scenario first\.&#32;Then\,&#32;separately\,&#32;test a real model session if useful\.

#### “Enter keeps completing instead of submitting”

Test exact completed input\,&#32;not only partial input\.

- Completion values should contain the full argument text\.
- A sole exact completed match should return null\.
- Test Enter with the completion menu in the state the user actually encounters\.
- Do not always press Escape first and then claim the exact\-match behavior is proven\.

#### “The UI is blank”

Check&#32;`mode`\,&#32;not just&#32;`hasUI`\.

- RPC has semantic UI but no native component factory\.
- ACP needs negotiated form support\.
- Print\/JSON default methods are inert\.
- `setFooter`&#32;and&#32;`setHeader`&#32;are no\-op in the supplied TUI implementation\.
- Theme objects are not accepted by the supplied TUI setter\.
- A tool without a renderer should retain its normal content path\.

For a custom component\,&#32;inspect width bounds\,&#32;focus\,&#32;abort\,&#32;disposal and restoration of the editor\.

#### “State leaked into another session”

Ask which lifetime owns it\.

- Module\-scope mutable variable\?
- Factory\-local closure reused after&#32;`/new`\?
- Whole\-journal scan instead of&#32;`getBranch()`\?
- Stale context cached across a workspace move\?
- Parent\-bound extension instances passed to a new SDK host\?
- Background&#32;`sendMessage()`&#32;sent to the current runtime instead of a captured owner\?

The Field Notes result is not a leak according to its contract\:&#32;its selection is binding\-local\.&#32;Calling it session\-local would be the bug in the explanation\.

#### “Permission came back after revocation”

Look for an in\-flight dialog\.

Clearing the current grant is insufficient\.&#32;Abort the presentation and invalidate its generation\.&#32;A late positive answer must fail the generation check\.

Test the delayed positive response deliberately\;&#32;do not test only revoke\-after\-grant\.

#### “Reload ignored my code edit”

Determine which reload path ran\.

A session reload is not a factory rebind\.&#32;Restart the explicit launch\,&#32;then inspect a changed description or deterministic behavior\.&#32;Do not use an old closure’s output as evidence that new source was imported\.

#### “A result arrived twice—or in the wrong branch”

Inspect\:

- captured target\;
- namespace and delivery ID\;
- owner\/session file lineage\;
- anchor presence on the active branch\;
- reset boundaries\;
- receipt state versus wake state\.

Retry a deferred\/pending delivery with the same identity and body\.&#32;Do not recapture the active session merely because the original owner is inactive\.

#### “A provider is listed but unusable”

Separate\:

- catalog registration\;
- cached discovery\;
- available\/auth\-configured selection\;
- credential resolution\;
- streaming transport\;
- final inference\.

A catalog or rollback test does not prove OAuth or provider behavior\.

#### “A denied write never reaches the fallback”

Check whether it is actually\:

- a supported ordinary byte\-write\/unlink\;
- a permission error\;
- a canonicalizable destination\;
- inside an initialized fallback lifecycle\;
- allowed by the handler’s session\/path policy\.

Archives\,&#32;SQLite\,&#32;subprocesses and remote ACP writes do not enter this seam\.

### Verification levels

| Level | What it proves | What it does not prove |
| --- | --- | --- |
| Schema\/type checking | Public API compatibility and parameter\/result typing | Runtime discovery or useful behavior |
| Pure domain tests | Transition rules\,&#32;validation and data invariants | Host wiring |
| Real loader\/runner\/adapter scenario | Actual binding\,&#32;interception and event contracts | Model choice or physical UI |
| Real session storage scenario | Journal\/reopen\/branch behavior | Power\-loss durability or cross\-process coordination |
| Protocol roundtrip | Requests\,&#32;responses\,&#32;cancellation and degraded modes | A particular remote client’s visual\/accessibility quality |
| Real TUI\/controller\/composer smoke | Actual focus\,&#32;keys\,&#32;rendering and editor preservation | Every physical terminal or IME |
| Real provider\/service integration | Actual endpoint\/auth\/transport behavior | General safety for unrelated providers\/configurations |

### A small test you can run without a provider

The Review Desk domain is a useful offline test target\.&#32;This is a new exercise test file\,&#32;not one of the downloaded proof runners\.

**Complete TypeScript test exercise—save beside Review Desk’s&#32;`domain.ts`&#32;as&#32;`domain.test.ts`\:**

~~~ts
import { expect, test } from "bun:test";
import { MAX_TEXT_LENGTH, transition } from "./domain";

test("accept keeps edited text and increments once", () => {
    const before = { revision: 0, status: "draft" as const, text: "Original." };
    expect(transition(before, "accept", "Reviewed.")).toEqual({
        revision: 1,
        status: "accepted",
        text: "Reviewed.",
    });
});

test("reject and cancel preserve pre-review text", () => {
    const before = { revision: 4, status: "draft" as const, text: "Keep this." };
    expect(transition(before, "reject").text).toBe("Keep this.");
    expect(transition(before, "cancel")).toEqual({
        revision: 5,
        status: "cancelled",
        text: "Keep this.",
    });
});

test("invalid replacement text fails", () => {
    const before = { revision: 0, status: "draft" as const, text: "Original." };
    expect(() => transition(before, "revise", "   ")).toThrow();
    expect(() => transition(before, "revise", "x".repeat(MAX_TEXT_LENGTH + 1))).toThrow();
    expect(before.revision).toBe(0);
});
~~~

**Terminal shell—from the downloaded Review Desk directory\:**

~~~sh
bun test ./domain.test.ts
~~~

**Expected checkpoint\:**&#32;the three domain tests pass without model inference\.&#32;They do not test the permission\-dialog race\;&#32;that requires the real runtime\/protocol scenario\.

### Story\-level acceptance scenarios

Use these as falsifiable checks\:

#### Seed Desk

- Welcome command and tool return the same hours\.
- Exact completion does not trap Enter\.
- Herb query returns only basil with eight fixture packets\.
- Unknown ID fails\.
- Cancelled human grant\/reservation appends no state\.
- Agent act before grant fails\.
- Successful act advances revision\.
- Old revision fails without a second mutation\.
- Reopen restores state\.
- Sibling branches do not mix reservations\.
- New session starts with empty reservations and authority off\.

#### Review Desk

- Fresh inspection reports revision 0 and no grant\.
- Accept stores the edited note once\.
- Reject preserves pre\-dialog text\.
- Editor cancellation records a distinct cancelled outcome\.
- Stale agent act fails\.
- Successful act consumes the grant\.
- Revocation during a pending dialog emits cancellation and defeats a late positive response\.
- Overlay cleanup preserves composer text\.
- Print mode refuses interactive changes\.
- RPC dialogs work without invoking native component factories\.
- ACP with and without form capability has explicitly tested behavior\.

#### Package Lab

- Single file\,&#32;index directory and manifest resolve to the expected entries\.
- Helpers are not accidentally bound as extensions\.
- Optional feature defaults off\.
- Explicit optional entry works separately\.
- Invalid selection preserves the earlier selection\.
- `/new`&#32;and switching preserve binding\-local selection\.
- A new binding resets it\.
- Resource dispatch preserves provenance\.
- Factory failure does not suppress later entries\.
- Provider rollback is not mistaken for rollback of all side effects\.

### The optional local&#32;`extb`&#32;toolkit

The supplied local&#32;`extension-builder`&#32;toolkit is separate from native OMP\.&#32;It is not included among this workbook’s public example download paths\,&#32;and readers should not assume a particular developer’s local installation exists\.

Its&#32;`new`\,&#32;`mock`\,&#32;`smoke`&#32;and&#32;`install`&#32;commands illustrate a useful process\,&#32;but its implementation has a narrower scope\:

- The mock API implements command registration\,&#32;not the full ExtensionAPI\.
- Its factory call does not await async initialization\.
- It cannot validate these async\,&#32;tool\-registering examples as a general OMP loader\.
- Its smoke driver uses a real PTY but inherits environment\/configuration and selects a default model\.
- It presses Escape before Enter\,&#32;so it does not independently prove the exact\-match\-menu case\.
- Its 25\-second subprocess timeout is a template choice\,&#32;not proof that native command handlers have a universal 30\-second budget\.
- Its global install path is a fixed copy target\,&#32;not a complete profile\-aware installer\.
- Plain&#32;`extensions/`&#32;needs explicit loading\;&#32;native&#32;`.omp/extensions/`&#32;does not\.

Use the toolkit’s process idea—small scaffold\,&#32;domain test\,&#32;real host smoke\,&#32;deliberate installation—without treating its hints as stronger than the current runtime\.

*Source\,&#32;snapshot 2026\-08\-29\:&#32;supplied contract tests and observed report\;&#32;`extension-builder/bin/extb.ts`\,&#32;`harness/mock-pi.ts`\,&#32;`harness/tui-smoke.ts`\;&#32;`packages/coding-agent/src/main.ts`\;&#32;linked public examples\.*

## Build and distribution checklist

Finish an extension as an operated capability\,&#32;not just a source file\.

### Domain and authority

- \[&#32;\]&#32;The goal names a real user need and the chosen extension surface\.
- \[&#32;\]&#32;Model\-required capability has a supported tool interface\.
- \[&#32;\]&#32;IDs\,&#32;revisions\,&#32;availability and errors are explicit\.
- \[&#32;\]&#32;Human commands and tools share domain rules where required\.
- \[&#32;\]&#32;No tool can infer authority from untrusted text\.
- \[&#32;\]&#32;Cancellation before\/after commit is documented\.
- \[&#32;\]&#32;Pending permission dialogs cannot restore revoked authority\.
- \[&#32;\]&#32;Host approval labels are not described as a JavaScript sandbox\.

### State and lifecycle

- \[&#32;\]&#32;Module\,&#32;binding\,&#32;transcript\,&#32;branch and process lifetimes are documented\.
- \[&#32;\]&#32;Branch\-derived state uses&#32;`getBranch()`\.
- \[&#32;\]&#32;Stored data is validated and versioned\.
- \[&#32;\]&#32;Session switch\,&#32;tree movement\,&#32;branching and reopen are tested\.
- \[&#32;\]&#32;Reload claims match the actual host path\.
- \[&#32;\]&#32;Background work has cancellation and ownership\.
- \[&#32;\]&#32;Durable work retains target\,&#32;delivery ID\,&#32;body and retry state\.
- \[&#32;\]&#32;No cross\-process coordination is implied without a real backend\/lock\.

### UI and machine access

- \[&#32;\]&#32;Terminal\-only features use&#32;`mode === "tui"`\.
- \[&#32;\]&#32;Generic&#32;`hasUI`&#32;is not treated as complete method support\.
- \[&#32;\]&#32;Standard dialogs handle undefined\/false and optional ask\/chat results\.
- \[&#32;\]&#32;Timeout fallback is not treated as consent\.
- \[&#32;\]&#32;Tool content remains useful without custom rendering\.
- \[&#32;\]&#32;Custom components sanitize data and respect visible width\.
- \[&#32;\]&#32;Abort\/dispose restores focus and composer state\.
- \[&#32;\]&#32;RPC\/ACP degraded behavior is documented and tested\.
- \[&#32;\]&#32;Accessibility gaps are acknowledged\;&#32;semantic alternatives remain available\.

### Loading and packaging

- \[&#32;\]&#32;Entries export a valid default factory\.
- \[&#32;\]&#32;Manifest entries point to shipped files\.
- \[&#32;\]&#32;Adjacent text\/JSON assets are included\.
- \[&#32;\]&#32;Helpers are not placed where loose scanning imports them accidentally\.
- \[&#32;\]&#32;Runtime dependencies and compatible host versions are documented\.
- \[&#32;\]&#32;Optional features do not run through unconditional imports\.
- \[&#32;\]&#32;Name\,&#32;flag\,&#32;renderer and shortcut collisions are checked separately\.
- \[&#32;\]&#32;Configured paths are resolved against the intended cwd\.
- \[&#32;\]&#32;Installation scope is stated accurately\.
- \[&#32;\]&#32;Disable\/removal has been checked against every discovery route\.

### Verification and release

- \[&#32;\]&#32;Types were checked against the matching build\.
- \[&#32;\]&#32;Pure domain tests assert useful state changes\,&#32;not only fixture equality\.
- \[&#32;\]&#32;Real loader\/runner\/session scenarios pass\.
- \[&#32;\]&#32;Actual TUI composer behavior was tested where claimed\.
- \[&#32;\]&#32;Protocol cancellation and late replies were tested\.
- \[&#32;\]&#32;Real service tests are claimed only when actually run\.
- \[&#32;\]&#32;Failure recovery has an operator\-readable path\.
- \[&#32;\]&#32;The distributed archive contains no credentials\,&#32;private sessions or local machine paths\.
- \[&#32;\]&#32;A clean extraction can load the intended entries\.
- \[&#32;\]&#32;A release note states what was tested and what remains unverified\.

The public workbook packages remain marked&#32;`private: true`\.&#32;If you turn a copy into a real distributed package\,&#32;choose your own package identity\,&#32;licensing\,&#32;versioning and publication route\.&#32;No registry publication or install success is claimed by this workbook\.

## Evidence and method

This edition is grounded in the supplied&#32;**2026\-08\-29 source snapshot**\,&#32;complete public example files and a recorded completed verification report\.

Repository citations throughout refer to that snapshot\.&#32;They are not links to an assumed upstream commit\.&#32;The installed custom host can differ from a published npm package or upstream source with a similar version string\.

### Recorded completed checks

The supplied report records\:

- **312 passing contract tests**
- **22 test files**
- **1\,039 assertions**
- **0 failed tests**
- Bun&#32;**1\.3\.14**
- Host reported as&#32;**`omp/18.0.7`**
- Strict typecheck of all supplied public example TypeScript files
- **44 passing isolated example scenarios**
- Two resolved review findings

The two recorded corrections were\:

1. **Permission revocation\:**&#32;an in\-flight Review Desk dialog now gets aborted and generation\-invalidated\;&#32;a late positive RPC response cannot recreate the grant\.
2. **Binding lifetime\:**&#32;Field Notes is accurately described as factory\-local\.&#32;`newSession()`&#32;and&#32;`switchSession()`&#32;reuse its closures\;&#32;a new binding starts fresh\.

### Exact contract\-test command recorded

The following is the completed command from the supplied report\.&#32;It is also a reproduction recipe for a matching installed source checkout—not a command available inside the example ZIP alone\.

**Terminal shell—recorded repository\-root test command\:**

~~~sh
bun test \
  packages/coding-agent/test/extensions-runner.test.ts \
  packages/coding-agent/test/extensions-discovery.test.ts \
  packages/coding-agent/test/extension-delivery.test.ts \
  packages/coding-agent/test/extension-delivery-lifecycle.test.ts \
  packages/coding-agent/test/extension-provider-registration-rollback.test.ts \
  packages/coding-agent/test/extension-prepared-rebind.test.ts \
  packages/coding-agent/test/extension-flag-dispatch.test.ts \
  packages/coding-agent/test/extension-flag-initial-message.test.ts \
  packages/coding-agent/test/cli-explicit-extension-isolation.test.ts \
  packages/coding-agent/test/plugin-extensions-discovery.test.ts \
  packages/coding-agent/test/rpc-extension-ui.test.ts \
  packages/coding-agent/test/sdk-file-write-fallback-extension.test.ts \
  packages/coding-agent/test/sdk-extensions-per-session-binding.test.ts \
  packages/coding-agent/test/sdk-preloaded-extensions-isolation.test.ts \
  packages/coding-agent/test/sdk-restricted-extension-provider.test.ts \
  packages/coding-agent/test/extension-context-project-trust.test.ts \
  packages/coding-agent/test/extension-workspace-package-resolution.test.ts \
  packages/coding-agent/test/issue-4919-extension-autocomplete-provider.test.ts \
  packages/coding-agent/test/extensibility/ext-model-query.test.ts \
  packages/coding-agent/test/discovery/disabled-extensions.test.ts \
  packages/coding-agent/test/acp-agent.test.ts \
  packages/coding-agent/test/status-text-sanitization.test.ts
~~~

The strict public\-example typecheck used&#32;`tsgo`&#32;through a separate validation workspace’s&#32;`bun check`&#32;command\.&#32;The downloadable Package Lab manifest does not declare a&#32;`check`&#32;script\.&#32;Do not assume running&#32;`bun check`&#32;in an arbitrary extracted directory reproduces that validation configuration\.

### What the observations establish

The example scenarios used actual production components where named\:

- discovery and module loading\;
- factory binding and prepared rebinding\;
- runner dispatch and tool adapters\;
- real JSONL session storage\;
- real SDK construction\;
- real RPC request\/response streams\;
- real TUI controller\,&#32;focus dispatch and a terminal emulator\;
- real renderer width\/sanitization behavior\.

Seed Desk’s confirmation responses were simulated at the UI seam\.&#32;Review Desk’s native check used a surrounding mode fixture\.&#32;Some existing lifecycle contract tests used mock models to test scheduling and consumption\.

These distinctions matter\.&#32;A test can exercise a real coordinator while deliberately replacing the provider\.

### What is not claimed

The supplied proof does not establish\:

- live provider inference or OAuth against a real account\;
- actual package installation\,&#32;registry publication or marketplace deployment\;
- a real elevated file broker\;
- physical\-terminal visual correctness across terminals\;
- full Seed Desk autocomplete\/Enter smoke\;
- arbitrary remote\-client accessibility\;
- browser\/UI publication verification for this workbook\;
- complete\-repository test coverage\;
- cross\-process reservation or delivery coordination\.

The newly authored exercises in this manuscript are source\-backed teaching code\,&#32;not additions to the supplied completed\-check totals\.&#32;No new execution is claimed during manuscript authoring\.

### Material prerequisites and missing context

Some outcomes necessarily require evidence outside this bundle\:

- A real provider needs its actual endpoint\,&#32;auth flow and transport behavior\.
- A privileged file fallback needs a real host broker and filesystem policy\.
- A remote UI needs a client that implements the negotiated dialogs and cancellation behavior\.
- Declarative theme creation needs the matching host’s theme\-file schema\.
- Standalone shell\/script\-tool protocols should be checked in their own loader implementation rather than inferred from discovery metadata\.
- Exact hot\-reload behavior depends on the frontend path that performs reload\,&#32;not only on the module loader’s import support\.

Where public types or older prose disagree with current code\,&#32;this workbook uses the narrower implementation\-backed behavior\:&#32;RPC can have semantic UI\,&#32;command handlers do not automatically inherit event timeouts\,&#32;`ctx.reload()`&#32;is not promised to rebind factories\,&#32;and metadata discovery is not execution\.

### Source families used

The principal reference locations are\:

- `packages/coding-agent/src/extensibility/extensions/types.ts`
- `packages/coding-agent/src/extensibility/extensions/loader.ts`
- `packages/coding-agent/src/extensibility/extensions/runner.ts`
- `packages/coding-agent/src/extensibility/extensions/wrapper.ts`
- `packages/coding-agent/src/extensibility/extensions/model-api.ts`
- `packages/coding-agent/src/extensibility/extensions/managed-timers.ts`
- `packages/coding-agent/src/session/agent-session.ts`
- `packages/coding-agent/src/session/session-manager.ts`
- `packages/coding-agent/src/session/extension-delivery.ts`
- `packages/coding-agent/src/sdk.ts`
- `packages/coding-agent/src/main.ts`
- `packages/coding-agent/src/discovery/builtin.ts`
- `packages/coding-agent/src/discovery/helpers.ts`
- `packages/coding-agent/src/discovery/gemini.ts`
- `packages/coding-agent/src/extensibility/plugins/loader.ts`
- `packages/coding-agent/src/extensibility/plugins/manager.ts`
- `packages/coding-agent/src/config/model-registry.ts`
- `packages/coding-agent/src/modes/controllers/extension-ui-controller.ts`
- `packages/coding-agent/src/modes/rpc/rpc-mode.ts`
- `packages/coding-agent/src/modes/acp/acp-agent.ts`
- `packages/coding-agent/src/tools/file-write-fallback.ts`
- `packages/tui/src/components/composer/types.ts`

The public examples are available through&#32;[the complete ZIP](<https://present-sketch-tp94.here.now/downloads/extensions-examples.zip>)&#32;and the individual file links in their chapters\.&#32;Private runners\,&#32;private state\,&#32;credentials and local proof artifacts are not part of those downloads\.

The process to carry forward is simple\:

> Choose the smallest appropriate surface\.&#32;Give humans and models explicit contracts\.&#32;Make state lifetimes visible\.&#32;Test the real boundary that matters\.&#32;Distribute only what another reader can inspect\,&#32;operate and verify\.

## Extensions inside those boundaries\:&#32;next steps

You now have both sides of the contract\:&#32;the operator’s need to identify and verify an effect\,&#32;and the builder’s responsibility to expose identity\,&#32;authority\,&#32;lifetime\,&#32;failure\,&#32;and retry behavior honestly\.

The six connection chapters that follow are the synthesis of the book\.&#32;They compare state stores\,&#32;distinguish kinds of forks\,&#32;attach cancellation to the operation that owns it\,&#32;examine permission lifetimes\,&#32;separate results from disclosure\,&#32;and keep evidence at its actual verification layer\.

Begin with&#32;[An empty context is not an empty history or memory store](<https://present-sketch-tp94.here.now/chapters/connection-which-state-survives>)\.&#32;Return to the complete source chapters whenever an exact command\,&#32;example\,&#32;caveat\,&#32;or implementation reference matters\.&#32;The connections explain overlap\;&#32;they do not replace those contracts\.

## An empty context is not an empty history or memory store

The Memory model and the Continuity ledger answer different questions\.&#32;Memory asks where useful knowledge can live\.&#32;Continuity asks which representation and identity a session operation changes\.&#32;Put them together before interpreting an old answer or choosing a reset\.

### Trace the representation\,&#32;not the word reset

| Representation | What the source chapters establish | What that does not establish |
| --- | --- | --- |
| Rendered transcript | A viewer can collapse\,&#32;filter\,&#32;or hide content\. | Hidden content has been deleted\. |
| Live\/model context | `/clear`&#32;drops live messages and appends a reset boundary used by later context rebuilding\. | Every journal entry\,&#32;memory\,&#32;skill\,&#32;or project instruction has disappeared\. |
| Durable journal | Earlier and alternative entries can remain after a live\-context reset\. | Reopening them restores earlier workspace files\. |
| Provider\-facing state | `/fresh`&#32;closes local cached handles\,&#32;rotates the provider\-facing identity\,&#32;and retains the conversation\. | Remote erasure\,&#32;authentication repair\,&#32;a cache miss\,&#32;or a new persistent journal\. |
| Durable memory | Scoped retrieval can bring retained evidence into a later conversation\. | Retrieval is exhaustive\,&#32;current\,&#32;or guaranteed\. |
| Managed skills | Procedural guidance exists in separately discovered files\. | A skill is a deterministic program or is erased by a conversation reset\. |
| Extension state | Each example chooses a lifetime and reconstruction policy\. | Clearing model messages universally resets extension closures or stored domain entries\. |

These are source\-backed distinctions\.&#32;The editorial consequence is straightforward\:&#32;**an unexpected answer is a provenance problem before it is a reset problem\.**

### Diagnose an old fact without wiping another layer

In Cedar’s correction story\,&#32;the old retry limit might be present in a working memory row\,&#32;another bank\,&#32;a read\-only fact projection\,&#32;a cached injection\,&#32;or retained conversation material\.&#32;Seeing the old value does not identify which representation supplied it\.

[Memory troubleshooting](<https://present-sketch-tp94.here.now/chapters/memory-troubleshooting>)&#32;therefore begins with directory and bank scope\,&#32;then distinguishes stored rows from&#32;`/memory view`’s injected payload\.&#32;The correction story requires exact returned IDs and full content before a replacement edit\.&#32;A successful edit of one eligible row does not establish that transcripts\,&#32;skills\,&#32;exports\,&#32;backups\,&#32;or provider\-held copies changed\.

Likewise\,&#32;[Decision Desk’s reset ledger](<https://present-sketch-tp94.here.now/chapters/continuity-decision-desk-resetting-deliberately>)&#32;does not describe&#32;`/clear`&#32;as an export scrubber\.&#32;A post\-clear context can be empty while a full\-history export still contains the earlier conversation\.&#32;Repeating clear cannot turn that retained representation into a deletion receipt\.

### Scope can change while conversation identity survives

Continuity’s relocation paths can retain the persistent session identity while changing journal location or active cwd\.&#32;Memory’s recorded Mnemopi&#32;`per-project`&#32;bank derives from resolved cwd\,&#32;not Git root\.&#32;The combined operating implication is to recheck memory scope after a move or directory change rather than infer it from the retained session ID\.

That is a scope check\,&#32;not a claim that relocation migrates a memory database\.&#32;The supplied chapters establish no universal migration contract between those systems\.

Extensions add another reason to avoid broad reset assumptions\.&#32;Seed Desk reconstructs domain snapshots from&#32;`getBranch()`\.&#32;Field Notes keeps selection inside a factory binding that can survive&#32;`/new`&#32;and session switching\.&#32;The optional&#32;`ctx.memory`&#32;interface is neither automatically branch\-local nor a transactional domain store\.

### Keep two meanings of fresh separate

Memory sometimes recommends a fresh startup after changing startup\-dependent configuration\.&#32;The named&#32;`/fresh`&#32;command has a narrower provider\-facing purpose\.&#32;Do not use the English phrase as evidence that the command reloads memory backends\,&#32;discovers new tools\,&#32;or rebinds extensions\.

A useful paper check is to predict three independent outcomes\:&#32;which messages will be active\,&#32;which saved records will remain\,&#32;and which runtime configuration will be rebuilt\.&#32;If you cannot answer all three\,&#32;use the relevant source chapter instead of choosing a stronger\-sounding reset\.

**Source trail\:**&#32;[Four places knowledge can live](<https://present-sketch-tp94.here.now/chapters/memory-four-places-knowledge-can-live>)\,&#32;[The continuity ledger](<https://present-sketch-tp94.here.now/chapters/continuity-the-continuity-ledger>)\,&#32;and&#32;[Models\,&#32;providers\,&#32;credentials and memory](<https://present-sketch-tp94.here.now/chapters/extensions-models-providers-credentials-and-memory>)\.&#32;Their cited implementation anchors include&#32;`packages/coding-agent/src/mnemopi/config.ts`&#32;—&#32;`computeMnemopiBankScope`\;&#32;`mnemopi/state.ts`&#32;—&#32;`MnemopiSessionState`\;&#32;`session/agent-session.ts`&#32;—&#32;`freshSession`\,&#32;`resetSessionContext`\;&#32;and&#32;`session/session-context.ts`&#32;—&#32;`buildSessionContext`\.

Related chapters\:

- [The continuity ledger](<https://present-sketch-tp94.here.now/chapters/continuity-the-continuity-ledger>)
- [Return Desk missing\-directory decisions](<https://present-sketch-tp94.here.now/chapters/continuity-return-desk-missing-directory-decisions>)
- [Decision Desk resetting deliberately](<https://present-sketch-tp94.here.now/chapters/continuity-decision-desk-resetting-deliberately>)
- [Four places knowledge can live](<https://present-sketch-tp94.here.now/chapters/memory-four-places-knowledge-can-live>)
- [The configuration this workbook teaches](<https://present-sketch-tp94.here.now/chapters/memory-the-configuration-this-workbook-teaches>)
- [Five fictional lab stories](<https://present-sketch-tp94.here.now/chapters/memory-five-fictional-lab-stories>)
- [The command desk](<https://present-sketch-tp94.here.now/chapters/memory-the-command-desk>)
- [Troubleshooting](<https://present-sketch-tp94.here.now/chapters/memory-troubleshooting>)
- [Package Lab\:&#32;one file to an embedded host](<https://present-sketch-tp94.here.now/chapters/extensions-package-lab-one-file-to-an-embedded-host>)
- [Models\,&#32;providers\,&#32;credentials and memory](<https://present-sketch-tp94.here.now/chapters/extensions-models-providers-credentials-and-memory>)

## A conversation fork is not a worktree or a cloned runtime

The word fork names a relationship between histories\.&#32;It does not\,&#32;by itself\,&#32;tell you which files\,&#32;processes\,&#32;settings\,&#32;tools\,&#32;or permissions became independent\.

Continuity and Tan both preserve earlier conversation material\,&#32;but they do so for different operating purposes\.&#32;Extension branch state adds a third use of ancestry without creating another worker at all\.

### Compare the actual operations

| Operation | Conversation effect | Workspace and runtime boundary |
| --- | --- | --- |
| Session\-tree navigation | Selects a path within the same journal\;&#32;target handling depends on entry type\. | It does not create a checkout or another worker\.&#32;Branch\-aware extensions must reconstruct their state\. |
| Interactive&#32;`/fork` | Creates a new persistent identity from the current session\,&#32;retaining live conversation and ordinary queues on the fork path\. | The workspace remains shared\.&#32;Session artifacts are copied best\-effort\. |
| Startup&#32;`--fork` | Creates a new identity from saved history and rebuilds context using the launch cwd\. | It does not clone an old process’s queues or copy workspace files\. |
| Initial&#32;`/tan` | Creates a contextual child conversation and an initial background run from persisted Main history\. | Main and the child can run concurrently in the same directory\.&#32;Their live runtimes are not one cloned object\. |

The full continuity fork carries all existing non\-header journal entries\,&#32;not only the visible leaf\.&#32;The tangent launch also starts from persisted history\,&#32;not a synchronized stream of Main’s unfinished text\,&#32;unsent draft\,&#32;or pending queues\.&#32;Both facts matter for privacy and for expectations about what the new conversation knows\.

### Task ownership is not isolation

The Tan controller clears the clone’s inherited todo list and supplies a tangent\-specific responsibility boundary\.&#32;That helps keep the child from taking over Main’s unfinished work\.&#32;It does not make the filesystem read\-only\.

Lantern Library’s ownership table is therefore more than prompt\-writing style\.&#32;It assigns one writer to each file\,&#32;identifies shared inputs\,&#32;and tells other workers what to leave alone\.&#32;When Main changes an input\,&#32;the relevant tan must reread it\;&#32;the file can be shared while the explanation of its change is not\.

For review\,&#32;compare the current file and diff with the intended outcome\.&#32;An agent’s report is evidence to inspect\,&#32;not a substitute for inspecting the shared result\.&#32;If actual workspace isolation is required\,&#32;it needs a separately chosen and verified mechanism\.&#32;The source explicitly does not establish&#32;`task.isolation.*`&#32;as an automatic&#32;`/tan`&#32;control\.

### Do not inherit safeguards by assumption

The initial tan receives model\,&#32;prompt\,&#32;settings\,&#32;and enabled\-tool information through specific construction paths\.&#32;Main’s live extension instances\,&#32;editor\,&#32;shell state\,&#32;and active provider request are not copied as a complete runtime\.&#32;Initial extension discovery is disabled\,&#32;but custom\-tool discovery and supplied MCP proxy tools are separate paths\.

The recorded subagent helper defaults unattended approval to&#32;`yolo`\,&#32;with explicit per\-tool policies still inherited\.&#32;A narrow assignment is therefore not proof that Main’s interactive approval experience or extension\-based safeguards are reproduced unchanged\.

Package Lab gives the corresponding builder rule\:&#32;prepared factories can be rebound\,&#32;but already\-bound extension instances close over their original runtime\.&#32;Sharing those instances with an independently constructed SDK session is not equivalent to constructing a fresh capability for the child\.

### Artifacts have their own fork behavior

Continuity’s full fork attempts a recursive copy of the session artifact tree\;&#32;copy failure can leave a valid child journal with missing artifacts\.&#32;The initial Tan instead nests its transcript under the parent artifact tree and captures Main’s&#32;`local://`&#32;mapping without recursively copying that tree\.&#32;Its artifact allocation is not universally identical to Main’s\,&#32;and cold revival introduces further current\-host resource boundaries\.

Use actual returned links and inspect required files\.&#32;Neither a fork acknowledgement nor an existing transcript is a verified backup\.

**Source trail\:**&#32;[Decision Desk forking](<https://present-sketch-tp94.here.now/chapters/continuity-decision-desk-forking>)\,&#32;[Understand the fork](<https://present-sketch-tp94.here.now/chapters/tan-1-understand-the-fork>)\,&#32;[Start from Main](<https://present-sketch-tp94.here.now/chapters/tan-2-start-from-main>)\,&#32;and&#32;[Package Lab](<https://present-sketch-tp94.here.now/chapters/extensions-package-lab-one-file-to-an-embedded-host>)\.&#32;Their anchors include&#32;`packages/coding-agent/src/session/session-manager.ts`&#32;—&#32;`fork`\,&#32;`forkFrom`\,&#32;`copySessionArtifacts`\;&#32;`modes/controllers/tan-command-controller.ts`&#32;—&#32;`TanCommandController.start`\;&#32;and&#32;`extensibility/extensions/loader.ts`&#32;—&#32;`bindPreparedExtensions`\.

Related chapters\:

- [Decision Desk forking](<https://present-sketch-tp94.here.now/chapters/continuity-decision-desk-forking>)
- [Decision Desk resetting deliberately](<https://present-sketch-tp94.here.now/chapters/continuity-decision-desk-resetting-deliberately>)
- [1\.&#32;Understand the fork](<https://present-sketch-tp94.here.now/chapters/tan-1-understand-the-fork>)
- [2\.&#32;Start from Main](<https://present-sketch-tp94.here.now/chapters/tan-2-start-from-main>)
- [9\.&#32;Coordinate several tans](<https://present-sketch-tp94.here.now/chapters/tan-9-coordinate-several-tans>)
- [10\.&#32;Protect context and recover](<https://present-sketch-tp94.here.now/chapters/tan-10-protect-context-and-recover>)
- [Package Lab\:&#32;one file to an embedded host](<https://present-sketch-tp94.here.now/chapters/extensions-package-lab-one-file-to-an-embedded-host>)
- [Session navigation and event\-driven behavior](<https://present-sketch-tp94.here.now/chapters/extensions-session-navigation-and-event-driven-behavior>)

## Changing focus\,&#32;stopping work\,&#32;and cancelling disclosure

Cancellation is meaningful only when attached to an operation and a phase\.&#32;A keyboard gesture\,&#32;a rejected promise\,&#32;a cancelled job row\,&#32;and a hidden late result can describe very different outcomes\.

The shared question is not “Did I press Escape\?” It is&#32;**which component owned that input\,&#32;what work had already started\,&#32;and which effect was actually stopped\?**

### Name the thing being stopped

| Control or event | Intended boundary | Do not infer |
| --- | --- | --- |
| Ordinary Escape in focused Tan chat | Clears nonempty draft text\;&#32;otherwise returns the view to Main\. | The tan or its job was cancelled\.&#32;Higher\-priority panels or loop handling may own Escape instead\. |
| Automatic return from Tan focus | Changes the recipient surface after parking\,&#32;abortion\,&#32;or removal\. | A draft still belongs to the tan\,&#32;or a submitted message needs replaying\. |
| Turn interruption | Requests an end to the current attempt\. | Queue erasure\,&#32;durable agent termination\,&#32;or rollback of tool effects\. |
| Cancellation of the initial background job | Marks and signals the identified running job\. | The explicit\-kill tombstone was written\,&#32;or every external process stopped\. |
| Explicit Hub kill | Requests terminal agent release with a tombstone and retained transcript\. | The job must say cancelled\,&#32;or earlier edits were undone\. |
| Main session reset or transition | Changes session state and may clear queues or cancel owned work according to that path\. | It is merely a view change\,&#32;or every failure phase is transactional\. |
| Sharing\-loader Escape | Restores the editor and suppresses later result display\/opening\. | A started upload or custom\-handler effect was aborted or revoked\. |

These distinctions explain why whole\-application controls are poor substitutes for leaving a focused tangent\.&#32;They also explain why a remembered job ID is unsafe for later cancellation\:&#32;job rows are short\-lived handles\,&#32;whereas the conversation transcript may remain\.

### Compare two asynchronous cancellations

Review Desk explicitly retires pending authority\.&#32;Its&#32;`invalidateAuthority()`&#32;increments a generation\,&#32;clears the grant\,&#32;and aborts the pending presentation\.&#32;A late positive confirmation is accepted only if it still belongs to the current generation\.&#32;The recorded RPC scenario demonstrated that the old response could not recreate authority\.

The sharing controller has a different contract\.&#32;Its loader receives cancellation\,&#32;but the inspected path does not pass that loader signal into default&#32;`shareSession`&#32;or the custom callback\.&#32;The recorded held operation completed after Escape while its late URL was suppressed\.&#32;That observation supports a warning about possible late completion\;&#32;it is not evidence of a real upload or a revocation service\.

The lesson is not that one cancellation word is reliable and another is not\.&#32;It is that&#32;**signal propagation\,&#32;stale\-result rejection\,&#32;committed effects\,&#32;and UI restoration must each be designed and checked**\.

### Use a phase\-aware recovery record

When an operation is uncertain\,&#32;record five things before retrying\:

1. **Surface\:**&#32;which chat\,&#32;overlay\,&#32;loader\,&#32;or mode owned the input\?
2. **Identity\:**&#32;which agent\,&#32;job\,&#32;session\,&#32;process\,&#32;or dialog was targeted\?
3. **Phase\:**&#32;had work only been requested\,&#32;had it started\,&#32;or had an effect already committed\?
4. **Observation\:**&#32;what receipt\,&#32;lifecycle state\,&#32;journal entry\,&#32;or file effect is actually available\?
5. **Remainder\:**&#32;what can still be running or retained outside that component\?

For a tan\,&#32;inspect both agent and job state and any relevant external process\.&#32;For a session switch\,&#32;distinguish settings preflight\,&#32;hook veto\,&#32;guarded target\-load failure\,&#32;and post\-switch reconciliation\.&#32;For a share\,&#32;treat a cancelled or ambiguous result as possibly completed\;&#32;do not resend merely to obtain a visible URL\.

This is a reading and recovery discipline\,&#32;not a request to manufacture a destructive or networked test\.&#32;The original failure cards and intercepted reports provide the comparison safely\.

**Source trail\:**&#32;[Leave and switch safely](<https://present-sketch-tp94.here.now/chapters/tan-5-leave-and-switch-safely>)\,&#32;[Interrupt\,&#32;cancel\,&#32;or kill](<https://present-sketch-tp94.here.now/chapters/tan-8-interrupt-cancel-or-kill>)\,&#32;[Decision Desk refusals and failures](<https://present-sketch-tp94.here.now/chapters/continuity-decision-desk-refusals-and-failures>)\,&#32;and&#32;[Sharing](<https://present-sketch-tp94.here.now/chapters/continuity-sharing-is-a-separate-disclosure-decision>)\.&#32;Relevant cited symbols include&#32;`SessionFocusController`\,&#32;`AgentLifecycleManager.release`\,&#32;`AsyncJobManager.cancel`\,&#32;and&#32;`CommandController.handleShareCommand`\;&#32;the supplied&#32;`examples/review-desk/index.ts`&#32;contains&#32;`invalidateAuthority()`\.

Related chapters\:

- [5\.&#32;Leave and switch safely](<https://present-sketch-tp94.here.now/chapters/tan-5-leave-and-switch-safely>)
- [7\.&#32;Continue a finished tan](<https://present-sketch-tp94.here.now/chapters/tan-7-continue-a-finished-tan>)
- [8\.&#32;Interrupt\,&#32;cancel\,&#32;or kill](<https://present-sketch-tp94.here.now/chapters/tan-8-interrupt-cancel-or-kill>)
- [12\.&#32;State and control reference](<https://present-sketch-tp94.here.now/chapters/tan-12-state-and-control-reference>)
- [Decision Desk resetting deliberately](<https://present-sketch-tp94.here.now/chapters/continuity-decision-desk-resetting-deliberately>)
- [Decision Desk refusals and failures](<https://present-sketch-tp94.here.now/chapters/continuity-decision-desk-refusals-and-failures>)
- [Sharing is a separate disclosure decision](<https://present-sketch-tp94.here.now/chapters/continuity-sharing-is-a-separate-disclosure-decision>)
- [Review Desk\:&#32;edit and decide locally](<https://present-sketch-tp94.here.now/chapters/extensions-review-desk-edit-and-decide-locally>)
- [Background work and owner\-addressed delivery](<https://present-sketch-tp94.here.now/chapters/extensions-background-work-and-owner-addressed-delivery>)

## Permission belongs to an operation\,&#32;a revision\,&#32;and a lifetime

A tool can be enabled without its domain action being authorized\.&#32;A human can approve a local edit without approving publication\.&#32;A new session can reuse an extension closure without inheriting the same kind of persisted permission as another example\.

The Extension part makes these distinctions concrete\.&#32;Continuity explains why they remain important when the active conversation moves\,&#32;and Tan explains why Main’s protections cannot simply be presumed to follow another runtime\.&#32;The Permissions part adds the operator’s resolution path\:&#32;which declaration\,&#32;effective policy key\,&#32;mode\,&#32;and UI capability govern this particular call\?

The title’s operation\/revision\/lifetime discipline is a design principle\,&#32;not a claim that the generic tool\-approval dialog implements every domain revision check\.&#32;Its binary answer admits one call\;&#32;the domain must still validate its own object and authority\.

### Compare the examples without flattening their policies

| Example | State and authority lifetime | Consequence |
| --- | --- | --- |
| Seed Desk reservations | Reservations and&#32;`agentEnabled`&#32;are persisted in branch snapshots\.&#32;Revision combines session identity and the latest relevant state\-entry ID\. | Ancestor grants can be inherited\.&#32;A headless reopen can retain an existing grant\;&#32;both human grant and revoke commands require UI in this supplied stage\. |
| Review Desk | Draft state is reconstructed from branch entries\.&#32;The one\-action grant is in memory\,&#32;tied to session identity and numeric revision\. | A successful mutation consumes the grant\.&#32;Navigation\,&#32;revocation\,&#32;and shutdown invalidate authority and retire pending dialogs\. |
| Field Notes | Selection is local to the factory binding and is not persisted in the journal\. | `/new`&#32;and session switching can keep the selection\;&#32;a fresh binding starts without it\. |
| Memory and skills | Retained evidence and procedural guidance have their own storage and discovery scope\. | Retrieved prose does not grant a tool permission or establish that a procedure was executed\. |

Even the persisted\-data validation policies differ\.&#32;Seed Desk refuses malformed matching snapshots rather than skipping them\.&#32;Review Desk’s small fixture skips entries that fail its state predicate\.&#32;Preserve that distinction when studying recovery\;&#32;the examples do not define one universal corruption policy\.

### Resolve host approval without inventing a domain grant

[Approval Desk\:&#32;modes and policies](<https://present-sketch-tp94.here.now/chapters/permissions-approval-desk-modes-and-policies>)&#32;establishes a precise order\.&#32;Tool deny and effective user deny are checked before automatic mode admission\.&#32;Explicit tool allow or prompt can outrank a non\-deny user policy\.&#32;An override\-only prompt is not an unbypassable policy in yolo\.&#32;Consequently\,&#32;neither “the user record always wins” nor “yolo disables every gate” is an accurate summary\.

[Dispatch Desk](<https://present-sketch-tp94.here.now/chapters/permissions-dispatch-desk-device-and-path-gates>)&#32;adds policy identity\.&#32;A valid device policy replaces the invoking&#32;`write`&#32;fallback\;&#32;it is not an intersection with every policy in the record\.&#32;The outer transport borrows a tier\,&#32;while an applicable inner wrapper can still deny\.&#32;`xdevApproved`&#32;suppresses a particular unchanged\-input duplicate prompt\,&#32;not every nested check\.&#32;A supported input replacement can require reclassification\,&#32;and the source predicate is not a universal content\-revision guard\.

The generic&#32;[one\-call boundary](<https://present-sketch-tp94.here.now/chapters/permissions-boundary-desk-one-call-at-a-time>)&#32;offers Approve and Deny only\.&#32;Dismissal is not approval\,&#32;and an approval does not persist an allow\-for\-session setting\.&#32;Two questions at outer and inner device gates can still precede only one execution\.&#32;None of those answers creates Seed Desk’s branch grant or Review Desk’s revision\-bound one\-action authority\.

### Authority and concurrency are separate checks

Seed Desk re\-reads its revision after awaiting human confirmation\.&#32;Review Desk uses generation invalidation to prevent an old dialog answer from reviving authority\.&#32;Both respond to the same asynchronous hazard\:&#32;the context that made a decision meaningful may have changed while the UI was waiting\.

A current revision does not supply permission\,&#32;and permission does not make an old revision current\.&#32;On a stale refusal\,&#32;inspect again and reconsider the action\;&#32;do not merely substitute a newer token into an unchanged request\.

These revisions are domain\-specific\.&#32;Seed Desk’s token\,&#32;Review Desk’s counter\,&#32;a workbook page revision\,&#32;and a provider\-facing session ID are not interchangeable concurrency controls\.&#32;The generic wrapper’s supported pre\-approval input replacement and Review Desk’s pending\-dialog generation invalidation also remain different mechanisms\.

### UI\,&#32;launch\,&#32;and child construction are distinct boundaries

[When the host can ask](<https://present-sketch-tp94.here.now/chapters/permissions-boundary-desk-when-the-host-can-ask>)&#32;separates terminal presence from an installed UI adapter\.&#32;RPC can provide select requests through its transport\.&#32;ACP form support is capability\-dependent\,&#32;and a non\-no\-op adapter can still return an unavailable value for a particular operation\.&#32;Print\/no\-UI execution fails closed when this generic gate requires a prompt\.&#32;None of those statements proves physical keyboard behavior or every native component\.

[Configuration and launch precedence](<https://present-sketch-tp94.here.now/chapters/permissions-configuration-and-launch-precedence>)&#32;explains why a displayed always\-ask setting can coexist with wrapper yolo\:&#32;the execute\-time autoApprove boolean can outrank the configured mode\.&#32;This does not remove an effective explicit deny or acknowledge pending provider safety checks\.

[Subagents and inherited policies](<https://present-sketch-tp94.here.now/chapters/permissions-subagents-and-inherited-policies>)&#32;traces both Task construction and&#32;`TanCommandController.start()`&#32;to&#32;`createSubagentSettings()`\.&#32;The helper defaults child mode to yolo while retaining per\-tool policy values\,&#32;and explicit helper overrides are applied afterward\.&#32;Initial construction\,&#32;later live runtime inputs\,&#32;and conversation ancestry are not one inheritance contract\.&#32;No universal live synchronization or safeguard clone is established\.

### Reload is not a universal revocation boundary

The source’s&#32;`AgentSession.reload()`&#32;reopens the current session through&#32;`switchSession()`\.&#32;It does not establish that every extension factory is imported and rebound\.&#32;Review Desk uses the switch path to invalidate its authority\.&#32;Field Notes deliberately retains its factory\-local selection\.

Therefore neither “reload clears everything” nor “reload preserves everything” is a sound rule\.&#32;State the intended lifetime\,&#32;implement its transitions\,&#32;and test those transitions through the actual host\.&#32;Restarting the explicit launch is the source workbook’s reliable procedure for testing extension code edits\,&#32;not a claim that every hot\-reload route is equivalent\.

### Give the agent an interface\,&#32;not implied authority

A model\-required capability needs a supported tool\,&#32;not a suggestion to invoke a human slash command\.&#32;Humans and tools should share mandatory domain rules when both can reach the same action\.&#32;A human command path is not cryptographic proof of a human keystroke\:&#32;SDK and RPC hosts can deliberately dispatch command text\.

Host approval controls execution under the host’s policy\.&#32;Domain grants control the example’s operation\.&#32;Pending provider safety acknowledgement is another gate\.&#32;Operating\-system and broker policy control a different boundary\.&#32;The supplied&#32;`isProjectTrusted()`&#32;compatibility method and trusted\-file selection do not sandbox arbitrary extension JavaScript or its imports\.

This matters especially for unattended tangents and permission\-denied file fallbacks\.&#32;Verify the actual subagent settings and installed safeguards rather than assuming Main’s experience follows it\.&#32;A fallback adapter must refuse the wrong session or destination and rely on a real broker\;&#32;returning success before the exact write has completed is not permission\-aware behavior\.&#32;That is a host\-design contract\,&#32;not a request to use a privileged broker for this workbook\.&#32;[Approval is not a sandbox](<https://present-sketch-tp94.here.now/chapters/permissions-approval-is-not-a-sandbox>)&#32;preserves the distinction between application admission\,&#32;a denied primitive\,&#32;and a verified effect\.

**Source trail\:**&#32;[Seed Desk reservations](<https://present-sketch-tp94.here.now/chapters/extensions-seed-desk-reservations-on-the-active-branch>)\,&#32;[Review Desk](<https://present-sketch-tp94.here.now/chapters/extensions-review-desk-edit-and-decide-locally>)\,&#32;[Discovery\,&#32;installation and reload](<https://present-sketch-tp94.here.now/chapters/extensions-discovery-installation-and-reload>)\,&#32;and&#32;[File fallbacks](<https://present-sketch-tp94.here.now/chapters/extensions-permission-denied-file-fallbacks>)\.&#32;Supplied examples include&#32;`examples/seed-desk/03-reservations/desk.ts`&#32;—&#32;`reconstruct`\,&#32;`changeReservation`\;&#32;`examples/review-desk/index.ts`&#32;—&#32;`stateFor`\,&#32;`invalidateAuthority`\;&#32;and&#32;`examples/package-lab/multi/index.ts`&#32;— the factory\-local&#32;`selected`&#32;variable\.&#32;New permissions anchors include&#32;`packages/coding-agent/src/tools/approval.ts`&#32;—&#32;`resolveApproval`\;&#32;`extensibility/extensions/wrapper.ts`&#32;—&#32;`ExtensionToolWrapper.execute`\;&#32;`modes/rpc/rpc-mode.ts`&#32;—&#32;`runRpcMode`\;&#32;and&#32;`task/executor.ts`&#32;—&#32;`createSubagentSettings`\,&#32;with the latter abbreviated paths relative to&#32;`packages/coding-agent/src/`\.&#32;Their recorded checks remain scoped in&#32;[Permissions evidence](<https://present-sketch-tp94.here.now/chapters/permissions-evidence-and-limitations>)\.

Related chapters\:

- [Prepare a reversible lab](<https://present-sketch-tp94.here.now/chapters/extensions-prepare-a-reversible-lab>)
- [Seed Desk\:&#32;reservations on the active branch](<https://present-sketch-tp94.here.now/chapters/extensions-seed-desk-reservations-on-the-active-branch>)
- [Review Desk\:&#32;edit and decide locally](<https://present-sketch-tp94.here.now/chapters/extensions-review-desk-edit-and-decide-locally>)
- [Package Lab\:&#32;one file to an embedded host](<https://present-sketch-tp94.here.now/chapters/extensions-package-lab-one-file-to-an-embedded-host>)
- [Discovery\,&#32;installation and reload](<https://present-sketch-tp94.here.now/chapters/extensions-discovery-installation-and-reload>)
- [Tools\,&#32;interception and native delegation](<https://present-sketch-tp94.here.now/chapters/extensions-tools-interception-and-native-delegation>)
- [Permission\-denied file fallbacks](<https://present-sketch-tp94.here.now/chapters/extensions-permission-denied-file-fallbacks>)
- [Decision Desk refusals and failures](<https://present-sketch-tp94.here.now/chapters/continuity-decision-desk-refusals-and-failures>)
- [2\.&#32;Start from Main](<https://present-sketch-tp94.here.now/chapters/tan-2-start-from-main>)
- [Four places knowledge can live](<https://present-sketch-tp94.here.now/chapters/memory-four-places-knowledge-can-live>)
- [Approval Desk\:&#32;modes and policies](<https://present-sketch-tp94.here.now/chapters/permissions-approval-desk-modes-and-policies>)
- [Dispatch Desk\:&#32;device and path gates](<https://present-sketch-tp94.here.now/chapters/permissions-dispatch-desk-device-and-path-gates>)
- [Boundary Desk\:&#32;one call at a time](<https://present-sketch-tp94.here.now/chapters/permissions-boundary-desk-one-call-at-a-time>)
- [Boundary Desk\:&#32;when the host can ask](<https://present-sketch-tp94.here.now/chapters/permissions-boundary-desk-when-the-host-can-ask>)
- [Configuration and launch precedence](<https://present-sketch-tp94.here.now/chapters/permissions-configuration-and-launch-precedence>)
- [Subagents and inherited policies](<https://present-sketch-tp94.here.now/chapters/permissions-subagents-and-inherited-policies>)
- [Approval is not a sandbox](<https://present-sketch-tp94.here.now/chapters/permissions-approval-is-not-a-sandbox>)
- [Recovery without widening permission](<https://present-sketch-tp94.here.now/chapters/permissions-recovery-without-widening-permission>)

## A result\,&#32;a receipt\,&#32;and permission to share are different facts

The word done often hides a chain of independent claims\.&#32;Work may have returned text\,&#32;stored a body\,&#32;delivered it to an owner\,&#32;scheduled a turn\,&#32;satisfied an assignment\,&#32;received approval\,&#32;or disclosed data\.&#32;None of those facts automatically establishes the next\.

### Keep the claims separate

| Fact | Useful evidence | Limit |
| --- | --- | --- |
| Returned output | The actual tool result\,&#32;assistant text\,&#32;transcript\,&#32;or returned artifact\. | A preview may be incomplete\;&#32;the initial Tan job returns its last assistant text\,&#32;which may be a later follow\-up rather than the earlier report\. |
| Delivery | A receipt or an identified owner\-routed result\. | Delivery is not compliance\,&#32;a model reply\,&#32;or a completed project check\. |
| Observation | A query\,&#32;inspection\,&#32;or recovered result at a particular time\. | Observation can change delivery bookkeeping\;&#32;it is not always repeatable retrieval of the same body\. |
| Completion | A settled operation plus evidence for the requested outcome\. | A completed job can still have failed its assignment\. |
| Approval | A scoped host decision or domain grant for the intended action\. | Approval to edit or accept locally is not permission to disclose the transcript\. |
| Disclosure | Data leaves its prior trusted boundary or an access\-bearing copy is provided\. | Encryption\,&#32;redaction\,&#32;a hidden panel\,&#32;or UI cancellation does not alone make disclosure authorized or reversible\. |

### A delivered body and a scheduled wake are not the same result

The extension delivery coordinator makes this explicit\.&#32;`captureSessionTarget()`&#32;records an owner anchor\.&#32;`deliverMessage()`&#32;reports whether a body was deferred\,&#32;committed\,&#32;or already committed\,&#32;and separately reports wake state\.

A committed body with a pending wake is not missing content\.&#32;Retain the same target\,&#32;namespace\,&#32;delivery ID\,&#32;and body for a legitimate retry\.&#32;Creating a new identity to recover a missing reply can duplicate delivery\.&#32;A fork does not inherit the original owner’s delivery authority merely because it contains related conversation text\,&#32;and moving away from the anchor or across a reset boundary can make delivery inadmissible\.

Tan’s job delivery is a different interface\,&#32;but it teaches the same distinction\.&#32;Hub&#32;`jobs`&#32;or&#32;`wait`&#32;can recover a settled result and suppress duplicate automatic delivery\.&#32;Later snapshots may omit the body because it was already delivered or recovered\.&#32;Read the earlier result\,&#32;the actual returned full\-output artifact\,&#32;or the transcript\;&#32;do not infer that no output existed or construct an ordinary\-task artifact URL for a tan\.

### Presentation is not content policy

Extension result&#32;`content`&#32;is the model\-facing path\;&#32;`details`&#32;is not automatically replayed to the model\.&#32;Decision\-critical fields need an appropriate model\-visible representation\.&#32;Conversely\,&#32;`display: false`&#32;hides presentation without making custom\-message content invisible to the model\.

The same distinction appears in Memory\:&#32;recalled material enters model context\.&#32;Local storage does not prove no egress\.&#32;It also appears in export\:&#32;filters\,&#32;collapsed sections\,&#32;and a blank viewer do not remove embedded bytes\.

### Choose the copied representation before considering a recipient

A live dump uses current context and can include current system\/tool information and a temporary sidecar\.&#32;A file\-based HTML export uses saved history without reconstructing a running agent’s current prompt inventory\.&#32;Ordinary HTML can embed pre\-clear and nested history\.&#32;Default sharing builds a different snapshot and does not collect adjacent nested transcripts\;&#32;a custom TUI share handler receives ordinary HTML instead\.

Those differences are not privacy rankings\.&#32;They are reasons to inspect the exact representation selected for the purpose\.&#32;The current HTML viewer also depends on external CDN scripts\:&#32;embedded data does not make it a self\-contained offline viewer\.&#32;Raw or decoded fictional data remains inspectable when scripts are blocked\.

Redaction depends on the available obfuscator and its recognized fields and strings\.&#32;Images\,&#32;unknown values\,&#32;and some identifying metadata can remain\.&#32;Encryption protects access to the sealed representation\,&#32;while the complete fragment\-bearing link grants decryption access\.&#32;Size trimming removes evidence\,&#32;not necessarily sensitive content\.&#32;Sharing cancellation has the separate late\-completion boundary explained in&#32;[Changing focus\,&#32;stopping work\,&#32;and cancelling disclosure](<https://present-sketch-tp94.here.now/chapters/connection-stop-the-owned-operation>)\.

Review Desk’s&#32;`accepted`&#32;status is a useful final check on vocabulary\:&#32;it records a local decision and publishes nothing\.&#32;Do not promote that fact into approval to share a packet\.

**Source trail\:**&#32;[Observe jobs and read results](<https://present-sketch-tp94.here.now/chapters/tan-6-observe-jobs-and-read-results>)\,&#32;[Owner\-addressed delivery](<https://present-sketch-tp94.here.now/chapters/extensions-background-work-and-owner-addressed-delivery>)\,&#32;[Choosing a snapshot](<https://present-sketch-tp94.here.now/chapters/continuity-review-packet-choosing-a-snapshot>)\,&#32;and&#32;[Sharing](<https://present-sketch-tp94.here.now/chapters/continuity-sharing-is-a-separate-disclosure-decision>)\.&#32;Cited anchors include&#32;`AsyncJobManager.consumeJobResults`\,&#32;`ExtensionDeliveryCoordinator`\,&#32;`buildSessionData`\,&#32;`collectSubSessions`\,&#32;`buildShareSnapshot`\,&#32;and&#32;`shareSession`&#32;in their source chapters\.

Related chapters\:

- [4\.&#32;Steer and queue work](<https://present-sketch-tp94.here.now/chapters/tan-4-steer-and-queue-work>)
- [6\.&#32;Observe jobs and read results](<https://present-sketch-tp94.here.now/chapters/tan-6-observe-jobs-and-read-results>)
- [7\.&#32;Continue a finished tan](<https://present-sketch-tp94.here.now/chapters/tan-7-continue-a-finished-tan>)
- [Tools\,&#32;interception and native delegation](<https://present-sketch-tp94.here.now/chapters/extensions-tools-interception-and-native-delegation>)
- [Background work and owner\-addressed delivery](<https://present-sketch-tp94.here.now/chapters/extensions-background-work-and-owner-addressed-delivery>)
- [Review Desk\:&#32;edit and decide locally](<https://present-sketch-tp94.here.now/chapters/extensions-review-desk-edit-and-decide-locally>)
- [Privacy and costs](<https://present-sketch-tp94.here.now/chapters/memory-privacy-and-costs>)
- [Review Packet choosing a snapshot](<https://present-sketch-tp94.here.now/chapters/continuity-review-packet-choosing-a-snapshot>)
- [Review Packet local HTML export](<https://present-sketch-tp94.here.now/chapters/continuity-review-packet-local-html-export>)
- [Review Packet reading the whole packet](<https://present-sketch-tp94.here.now/chapters/continuity-review-packet-reading-the-whole-packet>)
- [Sharing is a separate disclosure decision](<https://present-sketch-tp94.here.now/chapters/continuity-sharing-is-a-separate-disclosure-decision>)

## Match each claim to the layer that was checked

This book contains implementation explanations\,&#32;completed recorded checks\,&#32;fictional expected outcomes\,&#32;and browser\-readable lesson state\.&#32;The useful question is not whether something was tested in the abstract\,&#32;but whether the named check reaches the effect being claimed\.

### Preserve the original proof boundaries

| Source | Recorded evidence retained here | Boundary that remains |
| --- | --- | --- |
| Memory | Sanitized configuration inspection and source\-grounded lifecycle and command explanations\. | No memory\-content audit\,&#32;database\-health check\,&#32;or actual provider resolution was established\. |
| Tan | Nine isolated runtime scenarios and a report of 136 passing focused tests\. | Mock provider and recording UI\;&#32;no physical keyboard proof\,&#32;real task\-quality result\,&#32;or guaranteed restart rediscovery\. |
| Extensions | 312 passing contract tests across 22 files\,&#32;1\,039 assertions\,&#32;strict checking of supplied example TypeScript\,&#32;and 44 isolated example scenarios\. | No live provider\/OAuth\,&#32;real elevated broker\,&#32;full physical\-terminal audit\,&#32;or general cross\-process coordination proof\. |
| Continuity | 38 passing checks across Return Desk\,&#32;Decision Desk\,&#32;and Review Packet\,&#32;plus a separate headless\-browser report\. | Read\-only RPC startup\,&#32;inert or synthetic seams where stated\,&#32;intercepted sharing\,&#32;and no physical TUI or real upload proof\. |

These counts belong to different historical reports\.&#32;They are not one newly executed unified suite\.&#32;The original source and evidence chapters retain the narrower qualifications\,&#32;including tests added during their own publication review\.

A successful domain transition does not prove loader wiring\.&#32;A loader scenario does not prove model tool choice\.&#32;An RPC roundtrip does not prove a physical terminal’s key decoding\.&#32;A controller test that intercepts an OS\-open request does not prove a desktop viewer opened\.&#32;The same discipline applies to a reader’s own observations\.

### The unified browser interface is a worked boundary\,&#32;not an OMP connection

The reading\-site contract names&#32;`window.ompWorkbook`\,&#32;version 1\,&#32;with tutorial\-only scope\.&#32;Its&#32;[machine\-readable contract](<https://present-sketch-tp94.here.now/browser-interface.json>)&#32;and live discovery describe the loaded interface\.&#32;They do not expose a session journal\,&#32;provider\,&#32;filesystem\,&#32;or command runner\.

| Operation | Reading use | Limit |
| --- | --- | --- |
| `discover()` | Obtain schemas\,&#32;permissions\,&#32;page\/explorer scope\,&#32;exact registered controls\,&#32;and availability\. | Do not infer a control from its visible label or from a pattern in another page\. |
| `inspect()` | Read the current document revision\,&#32;page\,&#32;explorer\,&#32;checklist\,&#32;clipboard\,&#32;navigation\,&#32;disclosures\,&#32;and controls\. | The result describes tutorial state only\. |
| `query()` | Search authored chapter titles and text across the book\. | Results are excerpts\,&#32;not proof that another chapter is visible or that OMP contains matching data\. |
| `act()` | Request a supported native click\,&#32;disclosure state\,&#32;or checkbox state using a fresh revision\. | A navigation result is a request\,&#32;not confirmed loading\;&#32;a checked lesson is self\-report\. |
| `wait()` | Observe a newer matching revision in the current document within a bounded deadline\. | It cannot follow cross\-document navigation or establish real work completion\. |
| `diagnose()` | Inspect tutorial runtime\,&#32;storage\,&#32;clipboard capability\,&#32;and control integrity\. | `connectedToOMP`&#32;is false and OMP health is not assessed\. |

Query text is bounded to 240 characters\,&#32;with a result limit from 1 to 10\.&#32;The wait deadline is 0–5000 milliseconds\,&#32;defaulting to 1500\.&#32;Wait conditions can identify a page\,&#32;story\,&#32;or stage\;&#32;a stage requires its owning story\.&#32;Obtain actual IDs through discovery rather than inventing them\.

Inspect immediately before an action and pass the opaque revision unchanged\.&#32;Focus\,&#32;scroll\,&#32;viewport\,&#32;disclosure\,&#32;clipboard\,&#32;and other native changes can invalidate it\.&#32;On a stale revision\,&#32;inspect and reassess\.&#32;Hidden\,&#32;disabled\,&#32;detached\,&#32;or closed\-disclosure targets are not permission to bypass the interface by modifying the DOM\.

After a native navigation request\,&#32;inspect the destination document\.&#32;New or restored documents issue their own revisions\,&#32;and leaving the old document invalidates pending waits there\.&#32;An unchanged idempotent action need not create a newer revision\,&#32;so a timeout is not evidence that a requested transition happened\.

### Progress and copying remain local claims

Unified checklist reads\,&#32;writes\,&#32;and reset use only&#32;`omp-workbook-checklist-v1`\.&#32;Storage can fail\;&#32;a failed reset may leave an earlier record\.&#32;Saved state is the last local observation\,&#32;without multi\-tab locking\.&#32;Visiting a chapter or selecting an example milestone does not verify or automatically complete its exercise\.

Clipboard confirmation requires the write promise to resolve before the contract’s 2500\-millisecond deadline\.&#32;Manual selection is not confirmed copying\.&#32;Denial or timeout does not prove a late browser write is impossible\,&#32;and copying never executes the snippet\.

The&#32;[Memory lab](<https://present-sketch-tp94.here.now/labs/memory>)&#32;deliberately keeps its separate version\-1&#32;`window.memoryTutorial`&#32;interface and&#32;`omp-memory-workbook:checklist:v1`&#32;storage\.&#32;Its stage selection is fictional\,&#32;not a successful retain or recall\.&#32;If a page interface is unavailable\,&#32;use the complete authored Markdown rather than inventing a network API\.

**Source trail\:**&#32;the supplied unified browser reading contract\;&#32;the Memory lab HTML and&#32;[browser\-interface chapter](<https://present-sketch-tp94.here.now/chapters/memory-browser-agent-interface>)\;&#32;[Extension verification levels](<https://present-sketch-tp94.here.now/chapters/extensions-debugging-and-verification>)\;&#32;and each part’s complete evidence chapter\.&#32;This editorial assembly adds no new browser\,&#32;OMP\,&#32;provider\,&#32;or filesystem execution result\.

Related chapters\:

- [Browser agent interface](<https://present-sketch-tp94.here.now/chapters/memory-browser-agent-interface>)
- [Your checkpoint](<https://present-sketch-tp94.here.now/chapters/memory-your-checkpoint>)
- [Sources\,&#32;method and limitations](<https://present-sketch-tp94.here.now/chapters/memory-sources-method-and-limitations>)
- [Evidence and method](<https://present-sketch-tp94.here.now/chapters/tan-evidence-and-method>)
- [Review Desk\:&#32;a native panel and portable dialogs](<https://present-sketch-tp94.here.now/chapters/extensions-review-desk-a-native-panel-and-portable-dialogs>)
- [Debugging and verification](<https://present-sketch-tp94.here.now/chapters/extensions-debugging-and-verification>)
- [Evidence and method](<https://present-sketch-tp94.here.now/chapters/extensions-evidence-and-method>)
- [Start here](<https://present-sketch-tp94.here.now/chapters/continuity-start-here>)
- [Review Packet reading the whole packet](<https://present-sketch-tp94.here.now/chapters/continuity-review-packet-reading-the-whole-packet>)
- [Evidence and limitations](<https://present-sketch-tp94.here.now/chapters/continuity-evidence-and-limitations>)

## Shared glossary

These terms describe boundaries that recur throughout the book\.&#32;They are grouped by the confusion they resolve\,&#32;not by API name\.

### State\,&#32;identity\,&#32;and scope

**Live context\,&#32;model context\,&#32;and journal\.**&#32;Live messages are held by the running agent\.&#32;Model context is prepared from that state through conversion and transformation boundaries\.&#32;The durable JSONL journal can retain older and alternative entries that are not in current model context\.&#32;None is automatically an exact captured provider request\.&#32;See&#32;[the continuity ledger](<https://present-sketch-tp94.here.now/chapters/continuity-the-continuity-ledger>)\.

**Persistent session identity and provider\-facing identity\.**&#32;`SessionManager.getSessionId()`&#32;identifies the saved conversation’s header identity\.&#32;In the described implementation\,&#32;`AgentSession.sessionId`&#32;is provider\-facing\.&#32;`/fresh`&#32;can change the latter while preserving the former\.&#32;A provider prompt\-cache key is another value\;&#32;sharing or inheriting one does not prove a cache hit or savings\.

**Entry\,&#32;leaf\,&#32;and branch\.**&#32;An entry is a journal record with its own identity and parent relationship\.&#32;The leaf selects a current path through those records\.&#32;A branch is not every entry in the file\.&#32;Seed Desk reconstructs state from&#32;`getBranch()`&#32;so a sibling snapshot is not silently treated as current\.&#32;Tree navigation and a new\-file conversational fork are different operations\.

**Reset boundary\.**&#32;A saved&#32;`reset_boundary`&#32;tells supported context rebuilds where cleared conversation context stops contributing\.&#32;It does not remove earlier journal entries\,&#32;erase a memory backend\,&#32;scrub exports\,&#32;or restore workspace files\.

**Cwd and project scope\.**&#32;Cwd is the working directory associated with an operation or session\.&#32;Launch cwd\,&#32;recorded header cwd\,&#32;and active runtime cwd can differ during fallback or failure\.&#32;Memory’s recorded per\-project bank uses resolved cwd\,&#32;not Git root\.&#32;Extension\-module discovery and skill discovery also have different traversal rules\.

**Durable memory\,&#32;bank\,&#32;store\,&#32;and injection\.**&#32;Durable memory is retained material available for later retrieval\.&#32;A bank identifies retrieval\/storage scope\;&#32;a row’s store affects what edits are supported\.&#32;Injection is the selected memory instructions and recalled text placed into current context\,&#32;not the entire database\.&#32;The recorded Mnemopi working\,&#32;episodic\,&#32;and extracted\-fact behaviors are not interchangeable\.&#32;See&#32;[the Memory command desk](<https://present-sketch-tp94.here.now/chapters/memory-the-command-desk>)\.

**Global override and resolved default\.**&#32;These describe where a configuration value came from\.&#32;A persisted global override is not proof of a global memory bank\,&#32;successful runtime initialization\,&#32;or healthy provider access\.&#32;The Memory table reports a snapshot\,&#32;not a current\-machine audit\.

**Runtime override and effective wrapper policy\.**&#32;A&#32;`Settings.override()`&#32;value is not persisted like&#32;`Settings.set()`\.&#32;The approval wrapper also reads execute\-time&#32;`autoApprove`&#32;separately from settings\.&#32;A displayed approval mode can therefore differ from the mode the wrapper uses\.&#32;Configuration inspection is not a live audit of another call or process\.&#32;See&#32;[Configuration and launch precedence](<https://present-sketch-tp94.here.now/chapters/permissions-configuration-and-launch-precedence>)\.

**Compaction and managed skill\.**&#32;Compaction makes room by summarizing active conversational context\.&#32;A managed skill is separately stored procedural guidance in a&#32;`SKILL.md`&#32;file\.&#32;Neither is a database wipe\,&#32;a guaranteed complete record\,&#32;or a deterministic script that automatically executes\.&#32;See&#32;[four places knowledge can live](<https://present-sketch-tp94.here.now/chapters/memory-four-places-knowledge-can-live>)\.

### Workers\,&#32;views\,&#32;and lifetimes

**Tan\.**&#32;An OMP tangent subagent\:&#32;a contextual\,&#32;tool\-capable child conversation that can run concurrently with Main\.&#32;It is not TanStack\,&#32;an ordinary task subagent under every task\-executor convention\,&#32;or an isolated checkout\.

**Agent ID\,&#32;job ID\,&#32;and process name\.**&#32;The agent ID addresses a conversation\/registration\.&#32;The background job ID addresses a managed run\,&#32;notably the initial Tan run\.&#32;A process name addresses a separately supervised server\,&#32;watcher\,&#32;debugger\,&#32;or similar process\.&#32;Discover their actual association before acting\;&#32;a shared display label is not a mapping\.

**Focus\.**&#32;The currently addressed chat view\.&#32;Selecting a Hub row is not yet successful focus\.&#32;Automatic return to Main can change the recipient of an unfinished draft\.&#32;Closing an overlay may reveal the prior chat rather than Main\.&#32;See&#32;[Leave and switch safely](<https://present-sketch-tp94.here.now/chapters/tan-5-leave-and-switch-safely>)\.

**Running\,&#32;idle\,&#32;parked\,&#32;aborted\,&#32;and absent\.**&#32;These are agent lifecycle states\,&#32;not background\-job outcomes\.&#32;Idle has an attached live session\;&#32;parked requires revival for new work\;&#32;aborted is terminal in the explicit\-kill case\;&#32;absent means no current registration\,&#32;not necessarily no transcript\.&#32;Check job state separately\.

**Cold revival\.**&#32;Reconstructing an eligible parked agent from retained history and saved initialization information using available host resources\.&#32;It does not restore a complete old runtime\,&#32;restart the original job\,&#32;or guarantee identical tools\,&#32;settings\,&#32;model\,&#32;or resource mappings\.

**Steering and follow\-up\.**&#32;Steering changes the direction of current work at supported agent\-loop boundaries\.&#32;A follow\-up waits until the current work would otherwise yield\.&#32;Focused Enter\,&#32;the follow\-up chord\,&#32;and peer messaging are distinct input paths\.&#32;Queued does not mean processed\,&#32;persisted as a durable ticket\,&#32;or completed\.

**Turn and run\.**&#32;A run can contain several assistant turns\,&#32;tool results\,&#32;and maintenance continuations\.&#32;Memory’s periodic retention threshold counts new USER turns\,&#32;not assistant replies or tool calls\.&#32;Auto\-learn’s eligible tool\-call threshold is a separate loop\.&#32;Keep each source’s counting unit attached to its setting\.

**Conversation fork and workspace isolation\.**&#32;A fork separates conversation identity or history paths\.&#32;It does not create a Git branch\,&#32;worktree\,&#32;repository snapshot\,&#32;or rollback mechanism\.&#32;Shared file effects are already present in the shared workspace\;&#32;there is no automatic Tan merge operation\.

**Initial child settings and live inheritance\.**&#32;`createSubagentSettings()`&#32;snapshots base values\,&#32;defaults child approval mode to yolo\,&#32;and then applies explicit helper overrides\.&#32;Both Task construction and the supplied initial Tan controller use it\.&#32;Per\-tool policy values remain relevant\.&#32;Later live runtime inheritance\,&#32;shared callbacks\,&#32;and revival are separate paths\;&#32;conversation ancestry does not clone every safeguard or prove permanent synchronization\.&#32;Provider service\-tier inheritance is not read\/write\/exec tier inheritance\.

**Module\-local\,&#32;factory\-local\,&#32;and branch\-local\.**&#32;Module state can be shared through cached imports\.&#32;Factory\-local state belongs to one binding\,&#32;which can survive transcript changes\.&#32;Branch\-local state is reconstructed from the selected journal ancestry\.&#32;The word session\-local is too imprecise unless the actual lifetime is stated\.&#32;See&#32;[Package Lab](<https://present-sketch-tp94.here.now/chapters/extensions-package-lab-one-file-to-an-embedded-host>)\.

**Reload\,&#32;rebind\,&#32;and new session\.**&#32;Reload may reopen a journal\.&#32;Rebinding calls a prepared factory against a fresh runtime\.&#32;A new session changes conversation identity but can reuse extension closures\.&#32;None should be used as shorthand for all three effects\.

### Authority\,&#32;results\,&#32;and observation

**Host approval\,&#32;domain grant\,&#32;and trust\.**&#32;Host approval gates a tool under host policy\.&#32;A domain grant authorizes a particular application action\,&#32;often with identity and revision limits\.&#32;Trust in an in\-process extension permits code execution\;&#32;it is not an operating\-system sandbox\.&#32;A grant cannot be inferred from draft text\,&#32;a recalled memory\,&#32;or an MCP notification\.

**Tool availability and presentation\.**&#32;Availability concerns whether a capability is enabled and reachable in the actual session\.&#32;`loadMode`&#32;concerns presentation of an enabled tool\,&#32;not permission for every call\.&#32;An enabled mounted device can be absent from the top\-level schema\.&#32;An allow policy does not install a missing tool\.

**Tool tier\.**&#32;The&#32;`read`\,&#32;`write`\,&#32;or&#32;`exec`&#32;declaration used by approval resolution\,&#32;possibly computed from arguments\.&#32;An omitted declaration defaults to exec\.&#32;A tier is not a sandbox\,&#32;syscall audit\,&#32;or proof of zero effects\:&#32;some declared read operations change agent\/job state\,&#32;and ordinary reads can use the network\.&#32;It is unrelated to a provider’s processing service tier\.

**Approval mode\.**&#32;The default tier comparison\:&#32;`always-ask`&#32;admits read\;&#32;`write`&#32;admits read and write\;&#32;`yolo`&#32;admits all tiers\.&#32;Higher\-precedence applicable policies remain relevant\.&#32;The schema default in the permissions snapshot is yolo\,&#32;not a claim about the reader’s settings\.&#32;Always\-ask does not mean every call prompts\.

**Approval policy and provenance\.**&#32;`allow`\,&#32;`prompt`\,&#32;and&#32;`deny`&#32;are resolver outcomes and supported explicit policies\.&#32;Tool deny and effective user deny precede automatic admission\.&#32;Explicit tool allow\/prompt can outrank non\-deny user policy\.&#32;A resolved&#32;`source`&#32;identifies tool\,&#32;user\,&#32;or mode where supplied\;&#32;it is not execution evidence\.&#32;See&#32;[Approval Desk](<https://present-sketch-tp94.here.now/chapters/permissions-approval-desk-modes-and-policies>)\.

**Policy key and invoking\-tool fallback\.**&#32;`policyKey`&#32;lets a declaration select another user\-policy identity\,&#32;such as a device reached through write\.&#32;A missing or invalid keyed policy falls back to the invoking tool’s policy\.&#32;A valid keyed policy replaces that fallback\,&#32;including a fallback deny\;&#32;the lookup is not an intersection of every entry\.&#32;Raw property presence can still matter to a later wrapper predicate even when normalization ignored its value\.

**Override\-only prompt and explicit prompt\.**&#32;`override: true`&#32;without an explicit policy requests prompting in non\-yolo resolution but is ignored in yolo\.&#32;An explicit tool prompt survives the resolver’s yolo branch\.&#32;An override combined with explicit tool allow remains allow after deny checks\.&#32;Forwarded xdev prompting has an additional wrapper predicate\,&#32;so resolver policy and actual nested prompt count are not interchangeable\.

**AutoApprove\.**&#32;The execute\-time boolean that makes the wrapper use yolo before resolution\.&#32;The supplied built\-in CLI sets it through&#32;`--auto-approve`&#32;or&#32;`--yolo`\,&#32;not a listed&#32;`-y`&#32;alias\.&#32;It can outrank a displayed explicit approval mode but does not remove effective explicit denies or acknowledge pending provider safety checks\.

**One\-call approval and declined call\.**&#32;The generic wrapper offers Approve and Deny\.&#32;Only exact Approve is positive\;&#32;dismissal\/undefined is refused\,&#32;and a throwing selector stops the call\.&#32;The answer does not persist an allow\-for\-session policy\.&#32;A declined call is different from a configured deny and does not undo earlier effects\.

**Forwarded xdev approval\.**&#32;`xdevApproved`&#32;is context used to suppress a particular unchanged\-input duplicate prompt after an outer write gate\.&#32;Explicit user policy\,&#32;surviving overrides\,&#32;provider checks\,&#32;and supported input replacement affect the inner decision\.&#32;The source tests object identity\,&#32;not a domain revision hash\.&#32;It is not an arbitrary nested\-tool grant\.&#32;See&#32;[Dispatch Desk](<https://present-sketch-tp94.here.now/chapters/permissions-dispatch-desk-device-and-path-gates>)\.

**Pending provider safety checks\.**&#32;Computer\-provider metadata can require explicit acknowledgement independently of ordinary tier admission\.&#32;The wrapper sets&#32;`providerSafetyApproved`&#32;after the required positive selection\.&#32;Synthetic metadata tests prove that local transition\,&#32;not provider delivery\,&#32;physical computer behavior\,&#32;or the entirety of provider safety policy\.

**Permission\-denied file fallback\.**&#32;A later seam for selected denied native byte writes or unlinks\,&#32;not the approval dialog and not an elevated writer by itself\.&#32;Write and delete registries are separate and process\-wide\;&#32;requests identify origin and resolved target according to the primitive\.&#32;A handler’s true result is a durability\/completion contract\,&#32;not a grant or independent proof\.&#32;Unsupported routes and host policy remain separate\.

**Revision\.**&#32;A precondition token or counter whose meaning belongs to its issuer\.&#32;Seed Desk uses session and state\-entry identity\;&#32;Review Desk uses a numeric domain counter with separate authority invalidation\;&#32;the website uses opaque document revisions\.&#32;A content revision identifies authored material\,&#32;not the fresh page state required by&#32;`act()`\.&#32;The generic tool approval does not replace these domain preconditions\.

**Returned content and structured details\.**&#32;Extension tool&#32;`content`&#32;is the normal model\-facing result\.&#32;`details`&#32;is host\/rendering metadata and is not automatically model\-visible\.&#32;A custom renderer or notification is presentation\,&#32;not a substitute for a complete queryable result\.

**Owner anchor\,&#32;delivery ID\,&#32;receipt\,&#32;and wake\.**&#32;An owner anchor records where delayed work belongs\.&#32;A stable delivery ID supports deduplicated retry\.&#32;The receipt describes body admission or commitment\,&#32;while wake describes whether another agent turn is scheduled or pending\.&#32;A committed body is not proof that the model answered it\.&#32;See&#32;[owner\-addressed delivery](<https://present-sketch-tp94.here.now/chapters/extensions-background-work-and-owner-addressed-delivery>)\.

**Read\-only observation\.**&#32;Read\-only names the intended mutation boundary\,&#32;not a universal promise of zero side effects or disclosure\.&#32;A Main request can require model use\;&#32;a Hub job inspection can consume delivery bookkeeping\;&#32;an extension’s show command can append a custom message\.&#32;Read the exact operation contract\.&#32;Do not silently equate this phrase with every operation declared read\-tier\.

**Completion\.**&#32;Settlement of an operation and correctness of an assignment are different claims\.&#32;A completed job\,&#32;accepted message\,&#32;nonthrowing domain refusal\,&#32;or success banner must be interpreted through its actual result fields and postconditions\.&#32;Approval requested\/resolved events and prompt counts likewise do not replace an execution or effect count\.

**TUI\,&#32;RPC\,&#32;and ACP\.**&#32;TUI is terminal presentation and input\.&#32;RPC and ACP provide host\/client protocol surfaces\,&#32;with adapter\-specific semantic UI\.&#32;`hasUI`&#32;does not mean every terminal component\,&#32;dialog option\,&#32;or composer method works\.&#32;Protocol proof is not physical terminal proof\.&#32;In the supplied approval runner\,&#32;`hasUI()`&#32;tests for a non\-no\-op adapter\;&#32;RPC can install a select\-capable adapter\,&#32;while ACP form support depends on negotiation\.

### Copies\,&#32;disclosure\,&#32;and evidence

**Artifact and internal resource address\.**&#32;Artifacts are stored outputs or session\-adjacent resources\,&#32;not necessarily workspace files\.&#32;`memory://`\,&#32;`history://`\,&#32;`local://`\,&#32;`artifact://`\,&#32;and&#32;`agent://`&#32;are OMP resource schemes with different handlers\,&#32;not HTTP endpoints on this site\.&#32;Follow actual returned IDs and links\;&#32;a Tan transcript does not imply an ordinary\-task result artifact exists\.&#32;`xd://`&#32;is a tool\-dispatch address\,&#32;not an operator grant\.

**Sidecar\.**&#32;An auxiliary file whose meaning depends on the operation\:&#32;a dump JSON file\,&#32;a memory database companion\,&#32;or an agent tombstone are not the same object\.&#32;Temporary or adjacent does not mean automatically deleted\,&#32;fully backed up\,&#32;or safe to disclose\.

**Dump\,&#32;export\,&#32;and share\.**&#32;A dump represents current context and can create clipboard text plus a temporary sidecar\.&#32;HTML export copies session\-manager history\,&#32;with live versus file\-based metadata differences and possible nested transcripts\.&#32;Sharing uses a default snapshot or a custom executable route and is a separate disclosure decision\.&#32;See&#32;[choosing a snapshot](<https://present-sketch-tp94.here.now/chapters/continuity-review-packet-choosing-a-snapshot>)\.

**Embedded and offline\.**&#32;Embedded data can be recovered from a file even when its viewer fails\.&#32;The described HTML viewer still depends on CDN scripts\;&#32;the recorded blocked\-script case displayed controls but no transcript\.&#32;Base64 encoding is not encryption\.

**Redaction\,&#32;encryption\,&#32;trimming\,&#32;cancellation\,&#32;and revocation\.**&#32;Redaction transforms selected outgoing data according to available recognition and field rules\.&#32;Encryption controls access to a sealed representation\.&#32;Trimming loses content to meet a size budget\.&#32;Cancellation may stop only a particular phase or UI\.&#32;Revocation would withdraw access or authority\;&#32;no generic share\-link revocation workflow is established here\.&#32;None substitutes for recipient and purpose approval\.

**Recorded observation\,&#32;source\-backed expectation\,&#32;and exercise\.**&#32;A recorded observation belongs to the named historical check\.&#32;A source\-backed expectation follows the supplied source account\.&#32;An exercise is a result to reason about or independently test\.&#32;Browser lesson ticks are a fourth thing\:&#32;self\-report\.&#32;Combining the books does not upgrade any of these into new live verification\.&#32;The permissions cases’ expected fields remain authored data\;&#32;their separate executed report supplies observations for the named cases only\.

## Next steps

The durable skill in this workbook is not command memorization\.&#32;It is the ability to say exactly what changed\,&#32;what stayed available\,&#32;who had authority\,&#32;and what evidence supports that account\.

A useful conclusion may be a verified local result\.&#32;It may also be a refusal\,&#32;an unavailable dependency\,&#32;an ambiguous identity\,&#32;or an inspection gap\.&#32;Reporting that boundary accurately is better than manufacturing success by changing settings\,&#32;supplying credentials\,&#32;guessing a target\,&#32;or widening permissions\.

### A safe workflow for the next piece of work

1. **Choose the need before the mechanism\.**&#32;Returning to history\,&#32;recalling evidence\,&#32;asking a tangent\,&#32;understanding a tool decision\,&#32;and adding a capability are different needs\.&#32;Use a reading path and its source chapters before selecting a command or API\.
2. **Establish the target and scope\.**&#32;Record the persistent session identity and file when relevant\,&#32;the active cwd\,&#32;the exact agent\/job association\,&#32;the tool and any inner device operation\,&#32;or the domain object and revision\.&#32;Do not use a title\,&#32;row number\,&#32;or success toast as the sole identity check\.
3. **Choose an owned practice surface\.**&#32;Use the Memory simulator\,&#32;printed Tan fixtures\,&#32;inert permissions cases\,&#32;or a disposable copy of the supplied examples\.&#32;Keep personal journals\,&#32;memory rows\,&#32;settings\,&#32;credentials\,&#32;and real review packets out of the workbook route\.
4. **Predict the boundary\.**&#32;Write down the state expected to change and at least one state expected not to change\.&#32;Include authority\,&#32;pending work\,&#32;and copies outside the immediate view\.&#32;For a tool decision\,&#32;name the applicable policy key and effective mode rather than relying on the displayed tool label alone\.
5. **Use one supported action or inspection\.**&#32;Follow the original caption and prerequisites\.&#32;Printing a native command is not running it\;&#32;selecting a simulator stage is not a real memory operation\;&#32;reading an approval case is not answering a live dialog\.&#32;Stop at setup\,&#32;identity\,&#32;permission\,&#32;or compatibility failures rather than silently substituting another path\.
6. **Check the specific postcondition\.**&#32;Inspect the relevant complete state\,&#32;journal\,&#32;result body\,&#32;or owned file\.&#32;Use the original chapter’s falsifiable acceptance checks\.&#32;A receipt is evidence about its own operation\,&#32;not automatic proof of project correctness\.&#32;An approval is not proof of execution\,&#32;and execution is not proof that the intended effect succeeded\.
7. **Record the result at its real verification level\.**&#32;Separate what the source predicts\,&#32;what the historical report observed\,&#32;and what you personally observed\.&#32;Mark unexecuted or blocked checks as such\.&#32;Keep complete sensitive diagnostics private\.
8. **Finish without crossing another boundary accidentally\.**&#32;Returning to Main is not cancellation\.&#32;Cancellation is not undo\.&#32;A local accepted draft is not published\.&#32;No cleanup is needed for reading the permissions cases\.&#32;Any later disposal is limited to exclusively owned exercise material no longer needed\;&#32;destructive session or memory commands are not a cleanup shortcut\.

### Make the next check falsifiable

The supplied stories provide concrete standards without requiring a new live service experiment\:

| Learning target | Check to carry forward | Claim to avoid |
| --- | --- | --- |
| Continuity | The intended journal identity and active project scope agree with the selected fictional target\;&#32;today’s workspace still has today’s label\. | Reopening yesterday restored yesterday’s files\. |
| Memory | The simulator reports fictional tutorial state\;&#32;the source correction workflow requires an exact ID and full row content before an edit\. | Selecting a stage saved or recalled a real memory\. |
| Tan control | The intended agent\,&#32;any current backing job\,&#32;and the current input recipient are separately identified\;&#32;available output is read in its actual form\. | A job label\,&#32;delivery receipt\,&#32;or automatic return establishes successful work or terminal agent removal\. |
| Permissions | Explain the exact policy source and key\,&#32;required prompt count\,&#32;and recorded execution count\;&#32;distinguish denial\,&#32;dismissal\,&#32;unavailable UI\,&#32;revised input\,&#32;and an OS error\. | No prompt proves safety\,&#32;Approve persists a blanket grant\,&#32;or yolo supplies OS authority\. |
| Extension design | The original domain checks distinguish stale revision\,&#32;missing grant\,&#32;cancellation\,&#32;and a successful mutation\;&#32;the relevant host scenario reaches the layer being claimed\. | A resolved promise\,&#32;registered tool\,&#32;or visible widget proves the full workflow\. |
| Packet review | The fictional parent\,&#32;pre\-clear material\,&#32;and nested records are inspected through an available raw\,&#32;decoded\,&#32;or explicitly permitted rendered route\. | A blank viewer\,&#32;hidden panel\,&#32;encrypted link\,&#32;or cancelled loader proves privacy\. |

For an offline extension starting point\,&#32;the original&#32;[debugging chapter](<https://present-sketch-tp94.here.now/chapters/extensions-debugging-and-verification>)&#32;contains a complete Review Desk domain\-test exercise\.&#32;Use its existing code and stated limits rather than adding a provider call merely to make the exercise feel more complete\.&#32;For continuity\,&#32;begin with&#32;[the ledger’s fixture inspection](<https://present-sketch-tp94.here.now/chapters/continuity-the-continuity-ledger>)\.&#32;For Memory\,&#32;begin in&#32;[the local fictional lab](<https://present-sketch-tp94.here.now/labs/memory>)\.&#32;For Tan\,&#32;the&#32;[already\-running chapter](<https://present-sketch-tp94.here.now/chapters/tan-a-tan-is-already-running>)&#32;is the immediate control reference\,&#32;while its fictional assignments can be studied without launching work\.

For permissions\,&#32;compare&#32;`boundary-approval-does-not-persist`&#32;in&#32;[Boundary Desk’s data](<https://present-sketch-tp94.here.now/examples/boundary-desk/cases.json>)&#32;with&#32;`dispatch-explicit-prompt-twice`&#32;in&#32;[Dispatch Desk’s data](<https://present-sketch-tp94.here.now/examples/dispatch-desk/cases.json>)\.&#32;Both recorded two prompts and one inert execution\,&#32;but for different reasons\:&#32;two separate calls in the first case\,&#32;outer and inner gates for one dispatch in the second\.&#32;If your explanation treats them as the same lifetime\,&#32;return to&#32;[One call at a time](<https://present-sketch-tp94.here.now/chapters/permissions-boundary-desk-one-call-at-a-time>)\.&#32;Use&#32;[Recovery without widening permission](<https://present-sketch-tp94.here.now/chapters/permissions-recovery-without-widening-permission>)&#32;to finish with a precise blocked outcome rather than a broader policy\.

### Keep disclosure as a separate decision

Nothing about finishing the book authorizes publishing a real transcript\,&#32;dump\,&#32;sidecar\,&#32;memory\,&#32;or complete access\-bearing share link\.&#32;The&#32;[safe disclosure checklist](<https://present-sketch-tp94.here.now/chapters/continuity-recovery-and-safe-disclosure-checklists>)&#32;applies to the actual outgoing representation\,&#32;not merely its visible page\.&#32;If inspection is incomplete\,&#32;hold the packet\.&#32;Do not run a share to discover its policy or repeat an ambiguous share to recover a URL\.

Likewise\,&#32;no additional authentication\,&#32;provider inference\,&#32;destructive session operation\,&#32;privileged\-broker exercise\,&#32;personal settings change\,&#32;or live upload is needed to complete this unified route\.&#32;Service adapters remain contracts until their real prerequisites and separately authorized verification exist\.&#32;A tool\-tier declaration is not a substitute for that authorization\.

Carry the six connections into future work\:&#32;distinguish stores\,&#32;distinguish forks\,&#32;stop the owned operation\,&#32;bind authority to a lifetime\,&#32;separate delivery from disclosure\,&#32;and match evidence to its layer\.&#32;Those habits make both operation and extension design easier to explain\,&#32;recover\,&#32;and trust\.
