@timurproko/a1 0.2.7-dev.788 → 0.2.7-dev.794

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/dist/app/session-shell/session-shell-root.d.ts +2 -1
  2. package/dist/app/session-shell/session-shell-root.js +41 -15
  3. package/dist/app/session-shell/session-shell.d.ts +1 -0
  4. package/dist/app/session-shell/session-shell.js +15 -4
  5. package/dist/app/session-shell/session-viewport-controller.d.ts +2 -2
  6. package/dist/app/session-shell/session-viewport-controller.js +24 -12
  7. package/dist/cli/tabs.js +1 -1
  8. package/dist/composition/owned-ui.d.ts +2 -0
  9. package/dist/composition/owned-ui.js +42 -4
  10. package/dist/contracts/owned-ui/index.d.ts +1 -1
  11. package/dist/contracts/owned-ui/model.d.ts +3 -0
  12. package/dist/features/owned-ui/settings-app.js +9 -6
  13. package/dist/features/resident-tab/index.d.ts +3 -1
  14. package/dist/features/resident-tab/index.js +5 -1
  15. package/dist/features/resident-tab/shortcut-bindings-file.d.ts +8 -0
  16. package/dist/features/resident-tab/shortcut-bindings-file.js +40 -0
  17. package/dist/features/resident-tab/shortcut-declarations.d.ts +13 -0
  18. package/dist/features/resident-tab/{shortcuts.js → shortcut-declarations.js} +6 -17
  19. package/dist/features/resident-tab/shortcut-projection.d.ts +29 -0
  20. package/dist/features/resident-tab/shortcut-projection.js +64 -0
  21. package/dist/foundation/launch-guardian/exit-watcher.d.ts +14 -0
  22. package/dist/foundation/launch-guardian/exit-watcher.js +94 -0
  23. package/dist/foundation/launch-guardian/main.d.ts +2 -1
  24. package/dist/foundation/launch-guardian/main.js +58 -6
  25. package/dist/foundation/process-containment/{native-guardian-containment.d.ts → addon-containment.d.ts} +7 -5
  26. package/dist/foundation/process-containment/addon-containment.js +139 -0
  27. package/dist/foundation/process-containment/addon-process-inspector.d.ts +11 -0
  28. package/dist/foundation/process-containment/addon-process-inspector.js +36 -0
  29. package/dist/foundation/process-containment/addon.d.ts +42 -0
  30. package/dist/foundation/process-containment/addon.js +57 -0
  31. package/dist/foundation/process-containment/artifact.d.ts +1 -1
  32. package/dist/foundation/process-containment/artifact.js +6 -5
  33. package/dist/foundation/process-containment/contracts.d.ts +2 -0
  34. package/dist/foundation/process-containment/index.d.ts +4 -4
  35. package/dist/foundation/process-containment/index.js +3 -3
  36. package/dist/foundation/process-containment/native-process-inspector.d.ts +2 -1
  37. package/dist/foundation/process-containment/native-process-inspector.js +10 -10
  38. package/dist/foundation/release/bootstrap.js +54 -16
  39. package/dist/foundation/release/index.d.ts +1 -1
  40. package/dist/foundation/release/index.js +1 -1
  41. package/dist/foundation/release/release-store.d.ts +12 -0
  42. package/dist/foundation/release/release-store.js +124 -2
  43. package/dist/foundation/resident-tabs/bounded-host-command.d.ts +3 -1
  44. package/dist/foundation/resident-tabs/bounded-host-command.js +6 -2
  45. package/dist/foundation/resident-tabs/bridge-client.d.ts +7 -0
  46. package/dist/foundation/resident-tabs/bridge-client.js +30 -1
  47. package/dist/foundation/resident-tabs/doctor.d.ts +0 -1
  48. package/dist/foundation/resident-tabs/doctor.js +4 -4
  49. package/dist/foundation/resident-tabs/ensure-terminal-host.d.ts +7 -2
  50. package/dist/foundation/resident-tabs/ensure-terminal-host.js +15 -11
  51. package/dist/foundation/resident-tabs/host-entry.d.ts +1 -0
  52. package/dist/foundation/resident-tabs/host-entry.js +17 -0
  53. package/dist/foundation/resident-tabs/index.d.ts +2 -2
  54. package/dist/foundation/resident-tabs/index.js +1 -1
  55. package/dist/foundation/resident-tabs/maintenance.d.ts +5 -4
  56. package/dist/foundation/resident-tabs/maintenance.js +32 -17
  57. package/dist/foundation/resident-tabs/prompt-journal.d.ts +0 -2
  58. package/dist/foundation/resident-tabs/prompt-journal.js +0 -1
  59. package/dist/foundation/resident-tabs/release-holds.d.ts +3 -2
  60. package/dist/foundation/resident-tabs/release-holds.js +16 -7
  61. package/dist/foundation/resident-tabs/settings.d.ts +0 -1
  62. package/dist/foundation/resident-tabs/settings.js +1 -7
  63. package/dist/foundation/resident-tabs/terminal-host-artifact.d.ts +11 -15
  64. package/dist/foundation/resident-tabs/terminal-host-artifact.js +57 -32
  65. package/dist/foundation/session-writer-lock/index.d.ts +1 -3
  66. package/dist/foundation/session-writer-lock/index.js +8 -6
  67. package/dist/integrations/pi/components/owned-editor-ux.d.ts +2 -0
  68. package/dist/integrations/pi/components/owned-editor-ux.js +13 -0
  69. package/dist/integrations/pi/components/shell-editor-autocomplete.js +3 -1
  70. package/dist/integrations/pi/components/shell-presenters-info.d.ts +1 -0
  71. package/dist/integrations/pi/components/shell-presenters-info.js +10 -30
  72. package/dist/integrations/pi/components/shell-shared-facade.d.ts +4 -0
  73. package/dist/native/darwin-arm64/manifest.json +10 -7
  74. package/dist/native/darwin-arm64/process-guardian.node +0 -0
  75. package/dist/native/darwin-arm64/terminal-host-THIRD-PARTY-NOTICES.txt +1 -1
  76. package/dist/native/darwin-arm64/terminal-host.manifest.json +16 -14
  77. package/dist/native/darwin-arm64/terminal-host.node +0 -0
  78. package/dist/native/darwin-x64/manifest.json +10 -7
  79. package/dist/native/darwin-x64/process-guardian.node +0 -0
  80. package/dist/native/darwin-x64/terminal-host-THIRD-PARTY-NOTICES.txt +1 -1
  81. package/dist/native/darwin-x64/terminal-host.manifest.json +16 -14
  82. package/dist/native/darwin-x64/terminal-host.node +0 -0
  83. package/dist/native/linux-x64/manifest.json +10 -7
  84. package/dist/native/linux-x64/process-guardian.node +0 -0
  85. package/dist/native/linux-x64/terminal-host-THIRD-PARTY-NOTICES.txt +1 -1
  86. package/dist/native/linux-x64/terminal-host.manifest.json +16 -14
  87. package/dist/native/linux-x64/terminal-host.node +0 -0
  88. package/dist/native/win32-x64/manifest.json +10 -7
  89. package/dist/native/win32-x64/process-guardian.node +0 -0
  90. package/dist/native/win32-x64/terminal-host-THIRD-PARTY-NOTICES.txt +1 -1
  91. package/dist/native/win32-x64/terminal-host.manifest.json +16 -14
  92. package/dist/native/win32-x64/terminal-host.node +0 -0
  93. package/dist/product-identity.d.ts +1 -1
  94. package/dist/product-identity.js +1 -1
  95. package/dist/product-identity.json +3 -2
  96. package/dist/runtime-payload-inventory.json +1 -0
  97. package/dist/ui/components/index.d.ts +2 -2
  98. package/dist/ui/components/index.js +1 -1
  99. package/dist/ui/components/selection-copy.d.ts +2 -0
  100. package/dist/ui/components/selection-copy.js +13 -0
  101. package/dist/ui/components/submitted-prompt.d.ts +7 -4
  102. package/dist/ui/components/submitted-prompt.js +18 -10
  103. package/dist/ui/components/transcript-viewport.d.ts +10 -6
  104. package/dist/ui/components/transcript-viewport.js +28 -13
  105. package/dist/ui/settings/declarations.d.ts +13 -13
  106. package/dist/ui/settings/declarations.js +12 -11
  107. package/dist/ui/settings/migrations.js +7 -0
  108. package/docs/architecture/boundaries.md +10 -9
  109. package/docs/architecture/evidence/resident-tabs/macos-x64.md +1 -1
  110. package/docs/architecture/process-guardian-provenance.md +17 -11
  111. package/docs/architecture/project-structure.md +8 -0
  112. package/docs/architecture/resident-tabs.md +54 -36
  113. package/docs/architecture/terminal-host-provenance.md +6 -4
  114. package/docs/architecture/toolchain.md +6 -6
  115. package/docs/openspec-archive-automation.md +3 -3
  116. package/docs/validation.md +7 -3
  117. package/package.json +1 -1
  118. package/dist/features/resident-tab/shortcuts.d.ts +0 -10
  119. package/dist/foundation/process-containment/darwin-process-inspector.d.ts +0 -5
  120. package/dist/foundation/process-containment/darwin-process-inspector.js +0 -7
  121. package/dist/foundation/process-containment/native-guardian-containment.js +0 -152
  122. package/dist/foundation/process-containment/windows-process-inspector.d.ts +0 -19
  123. package/dist/foundation/process-containment/windows-process-inspector.js +0 -56
  124. package/dist/native/darwin-arm64/a1_session_lock.node +0 -0
  125. package/dist/native/darwin-arm64/process-guardian +0 -0
  126. package/dist/native/darwin-arm64/terminal-host +0 -0
  127. package/dist/native/darwin-x64/a1_session_lock.node +0 -0
  128. package/dist/native/darwin-x64/process-guardian +0 -0
  129. package/dist/native/darwin-x64/terminal-host +0 -0
  130. package/dist/native/linux-x64/a1_session_lock.node +0 -0
  131. package/dist/native/linux-x64/process-guardian +0 -0
  132. package/dist/native/linux-x64/terminal-host +0 -0
  133. package/dist/native/win32-x64/a1_session_lock.node +0 -0
  134. package/dist/native/win32-x64/process-guardian.exe +0 -0
  135. package/dist/native/win32-x64/terminal-host.exe +0 -0
@@ -15,7 +15,7 @@ Physical acceptance against a published candidate package is pending; the operat
15
15
 
16
16
  ## Automated evidence
17
17
 
18
- - Publication `terminal_hosts` and `guardians` jobs on `macos-15-intel`: the release terminal host and process guardian are built for Intel macOS, the host's `--version` identity is checked, and packing refuses a missing or altered artifact.
18
+ - Publication `terminal_hosts` and `guardians` jobs on `macos-15` (arm64): the release terminal host and process guardian are cross-compiled for Intel macOS with `--target x86_64-apple-darwin`, the host's `--version` identity is checked through Rosetta 2 when the runner can execute it (otherwise the manifest records a Mach-O architecture check), and packing refuses a missing or altered artifact. Before 2026-10-16 the same builds ran natively on `macos-15-intel`.
19
19
  - Known gap: the pull-request `terminal-host` and `resident-chaos` suites run on macOS arm64 only, because modular validation outcomes are keyed by platform without architecture. Intel macOS has build and identity evidence but no automated behavior suite of its own.
20
20
 
21
21
  ## Physical verdicts
@@ -1,29 +1,35 @@
1
1
  # Process guardian provenance
2
2
 
3
- A1 launch-instance containment uses a standalone native executable named `process-guardian`. The approved implementation is a separate Rust crate at `native/process-guardian`; it is not a Node-API addon and does not reuse or resume the held composed terminal-host implementation.
3
+ A1 launch-instance containment uses the Node-API 9 addon `process-guardian.node`. The approved implementation is the separate Rust crate at `native/process-guardian`; it produces no product executable. The foreground Node launch guardian loads the addon and holds the containment boundary in-process.
4
4
 
5
5
  ## Boundary
6
6
 
7
- The executable owns platform process containment and launches one immutable `a1-ui` runtime with inherited terminal handles. It does not create a PTY, read ordinary terminal input, capture or parse runtime output, render, or relay terminal bytes. Its private parent/child coordination is restricted to bounded lifecycle and identity data.
7
+ The addon owns platform process containment and launches one immutable A1 UI runtime with inherited terminal handles. It also provides native process-start inspection, terminal-mode capture and restoration, exit-notice delivery, and the bounded process-wait and process-group operations used by the exit watcher. It does not create a PTY, read ordinary terminal input, capture or parse runtime output, render, or relay terminal bytes.
8
8
 
9
- On Windows it uses `windows-sys 0.61.2` to create a Job Object, enables `JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE`, assigns the runtime before resuming it, denies silent breakaway by never enabling breakaway limits, monitors the verified Node parent process, and terminates remaining job members after root or parent exit.
9
+ On Windows it uses `windows-sys 0.61.2` to create a Job Object, enables `JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE`, and creates the runtime directly in that job through `PROC_THREAD_ATTRIBUTE_JOB_LIST`. The runtime is a job member from its first instruction; the former suspended-create, assign, and resume sequence is gone. Ordinary jobs do not enable breakaway, and closing the addon-held job handle terminates remaining members.
10
10
 
11
- On Linux and macOS it creates a dedicated POSIX process group before exec, transfers foreground-terminal ownership only when a terminal is present, monitors the verified parent, applies bounded TERM/KILL cleanup to that group, and restores the prior foreground group. Linux derives process start identity from procfs. macOS derives it from the native `proc_pidinfo(PROC_PIDTBSDINFO)` start timestamp; PID liveness alone is never treated as identity.
11
+ On Linux and macOS it uses `posix_spawn` to create a dedicated process group, transfers foreground-terminal ownership only when a terminal is present, applies bounded TERM/KILL cleanup to that group, and restores the prior foreground group. Linux derives process start identity from procfs. macOS derives it from `proc_pidinfo(PROC_PIDTBSDINFO)`; PID liveness alone is never treated as identity.
12
+
13
+ ## Exit watcher
14
+
15
+ After the contained runtime starts, the launch guardian starts `dist/foundation/launch-guardian/exit-watcher.js` in another Node runtime outside the guardian's containment. The watcher waits for the guardian, performs bounded Unix process-group cleanup when needed, and claims and delivers the armed exit notice with the captured terminal modes. On Windows the addon creates that Node process outside both the runtime job and the guardian's inherited job; on Unix it remains in the terminal session so terminal hang-up reaches it naturally. The guardian itself delivers the notice on ordinary completion, and claim-by-rename ensures only one owner can print it.
16
+
17
+ There is one accepted gap: a machine-wide kill aimed at every Node process, such as `taskkill /IM node.exe` or `pkill node`, kills the watcher together with the guardian and runtime. No exit notice is then printed and, on Unix, non-Node descendants left in the runtime process group are not signalled by the watcher. A1 does not work around that case by starting an A1-built executable. Terminal closure, `Ctrl+Break`, guardian failure, runtime termination, and a kill aimed only at the guardian remain covered.
12
18
 
13
19
  ## Artifact contract
14
20
 
15
- Release builds place the exact executable at:
21
+ Release builds place the addon at:
16
22
 
17
23
  ```text
18
- dist/native/win32-x64/process-guardian.exe
19
- dist/native/linux-x64/process-guardian
20
- dist/native/darwin-x64/process-guardian
21
- dist/native/darwin-arm64/process-guardian
24
+ dist/native/win32-x64/process-guardian.node
25
+ dist/native/linux-x64/process-guardian.node
26
+ dist/native/darwin-x64/process-guardian.node
27
+ dist/native/darwin-arm64/process-guardian.node
22
28
  ```
23
29
 
24
- The publication `guardians` job builds them on `windows-2025`, `ubuntu-24.04`, `macos-15` (arm64), and `macos-15-intel` (x64).
30
+ The publication `guardians` job builds them on `windows-2025`, `ubuntu-24.04`, and `macos-15` arm64; the macOS arm64 job also cross-compiles x64 with `--target x86_64-apple-darwin`. A native build is identified by loading the addon into Node and reading `version()`. When the producing runner cannot load the cross-compiled x64 addon, its manifest records only the observed Mach-O architecture.
25
31
 
26
- Each supported artifact must be named in the release integrity manifest with target triple, crate version, guardian protocol version, SHA-256, byte size, build provenance, and signature/attestation status. The build marks a platform supported only after its native containment integration runs on that platform. A missing, altered, incompatible, unsupported, or wrong-platform artifact is a concise containment launch error; A1 never silently falls back to PID-only cleanup.
32
+ Each `a1-process-guardian-artifact-v2` manifest records `kind: "node-addon"`, Node-API 9, target platform and architecture, crate and protocol versions, SHA-256, byte size, build provenance, and signature/attestation status. A supported artifact is verified before loading. A missing, altered, incompatible, unsupported, or wrong-platform artifact is a concise containment launch error; A1 never silently falls back to PID-only cleanup. If a certified immutable release file disappears or changes, bootstrap restores it only from a matching installed package whose source bytes reproduce the certified size and digest, then verifies the complete release again.
27
33
 
28
34
  ## Licensing
29
35
 
@@ -103,6 +103,14 @@ a1 session link-worktree <absolute-worktree>
103
103
  git -C .worktrees/<task-id> status
104
104
  ```
105
105
 
106
+ Claude Code sessions have no A1 shell-tool session, so they link with Claude Code's `EnterWorktree` tool by task name. The tracked `.claude/settings.json` wires a `WorktreeCreate` hook, `scripts/governance/claude-worktree-create.mjs`, that creates `.worktrees/<task-id>` detached at fresh `origin/develop`, or returns it when it is already registered, so Claude Code anchors the session there without an approval prompt. The agent then creates the task branch inside it:
107
+
108
+ ```sh
109
+ git -C .worktrees/<task-id> switch -c <type>/<short-description>
110
+ ```
111
+
112
+ Passing an absolute `path` to `EnterWorktree` is reserved for a worktree created by hand, from the primary checkout, before any shell command in that session; Claude Code otherwise keeps its shell tools anchored to the primary checkout and refuses their commands. The matching `WorktreeRemove` hook declines removal, so sessions leave with `ExitWorktree` `keep` and worktrees are removed only by the cleanup tool.
113
+
106
114
  Inventory is read-only and advisory because another runtime may win a later race. A successful link response confirming the exact canonical worktree is required before planning, implementation, test, or delivery-documentation edits. If inventory or linking fails, reports a live/unverifiable owner, loses a race, or confirms another path, stop task edits and report the blocker; use a separate fresh worktree rather than overriding, unlinking, or recovering that owner. A session resuming an existing delivery or switching streams atomically claims and links the exact worktree before editing it.
107
115
 
108
116
  The link acquires a cooperative live-session editing claim and switches bare A1's footer repository context and pull-request discovery. It does not change process or tool cwd, create a Git worktree lock, or register, release, or authorize cleanup. Clean unlink/disposal releases live editing authority; a crashed owner becomes reusable only after exact process death is verified. Repository commands therefore keep an explicit worktree path. The primary worktree stays on `develop` for integration and must not be used for task edits. Do not edit, adopt, move, or remove another session's worktree.
@@ -4,7 +4,7 @@ Status: implemented. Milestones 1–6 deliver resident tabs as the bare-`a1` rou
4
4
 
5
5
  ## Context
6
6
 
7
- Today bare `a1` runs as a chain: `bin/cli.js`, then a detached per-release **supervisor**, then the foreground **launch guardian**, then the native **process-guardian**, then `bin/ui.js`. That last process hosts one in-process Pi `AgentSessionRuntime` behind the owned UI. The process-guardian puts the UI in kill-on-close containment, so closing the terminal kills the agent. `launch-instance-lifecycle` requires that behavior and reserves "a separately specified explicit resident capability" for anything that must survive. This design defines that capability.
7
+ Today bare `a1` runs as a chain: `bin/cli.js`, then a detached per-release **supervisor**, then the foreground Node **launch guardian**, then its in-process native **process-guardian addon**, then `bin/ui.js`. That last Node process hosts one in-process Pi `AgentSessionRuntime` behind the owned UI. The addon puts the UI in kill-on-close containment, so closing the terminal kills the agent; no process-guardian executable sits in the chain. `launch-instance-lifecycle` requires that behavior and reserves "a separately specified explicit resident capability" for anything that must survive. This design defines that capability.
8
8
 
9
9
  The user set three product constraints:
10
10
 
@@ -14,12 +14,12 @@ The user set three product constraints:
14
14
 
15
15
  A structured, message-based agent protocol cannot meet (2), because an extension's custom UI component is code running inside the agent process. It also forces a second, unrelated system for (3). Every tab is therefore a **terminal session**: a pseudoterminal running the complete A1 owned UI in "tab mode". Native extension components retain the declared text-terminal behavior; inline image protocols are explicitly outside the first version and use the existing text fallback.
16
16
 
17
- The first version is deliberately narrower than the eventual product. It targets exactly Windows x64, Linux x64, and macOS arm64 (the targets A1 currently validates for the native process-guardian), with resident tabs on bare `a1` and argument-free `a1 solo` as the explicit direct-mode opt-out. It is delivered as six sequenced milestone changes. Restoring tabs after reboot or logout, another architecture or platform, automatic resident-cohort upgrades, arbitrary CLI tabs, and split layouts each require a later change and their own evidence.
17
+ The first version is deliberately narrower than the eventual product. It initially targeted Windows x64, Linux x64, and macOS arm64, with macOS x64 added by the later recorded decision; those are the targets A1 packages for the native addons. Resident tabs are the bare-`a1` route and argument-free `a1 solo` is the explicit direct-mode opt-out. Restoring tabs after reboot or logout, another architecture or platform, automatic resident-cohort upgrades, arbitrary CLI tabs, and split layouts each require a later change and their own evidence.
18
18
 
19
19
  Existing assets:
20
20
 
21
21
  - **`native/terminal-host`** is a working in-terminal host. It uses `libghostty-vt` pinned at `c5a21edf`, `portable-pty` 0.9.0 (ConPTY on Windows, Unix PTYs elsewhere), crossterm 0.29, and an A1-owned damage-aware frame composer. Its retained model and native encoders came from the fixed 2×2 proof; milestone 2 removed that interactive proof path and moved the model into the resident holder/attach roles. Three-platform certification remains separate from implementation.
22
- - **The supervisor, process-guardian, and launch guardian** provide the patterns for detached spawn, verified process identity (`--inspect-pid`), an endpoint probe with join-the-winner, and atomic metadata. The process-guardian already has Windows, macOS, and Linux implementations.
22
+ - **The supervisor, process-guardian addon, and launch guardian** provide the patterns for detached spawn, addon-backed process-start inspection, an endpoint probe with join-the-winner, and atomic metadata. The process-guardian addon has Windows, macOS, and Linux implementations.
23
23
  - **The 2×2 spike evidence** in [`evidence/terminal-host-spike/`](evidence/terminal-host-spike/) is historical. Its verdicts stay pending and are not evidence for this design.
24
24
 
25
25
  References:
@@ -53,8 +53,8 @@ The read-only review used A1 `develop` at `b42b3c9e`, PR #586 at `8fdb7361`, `E:
53
53
  ### Milestone 2 pinned launch contract (2026-10-11)
54
54
 
55
55
  - Bare `a1` from a terminal always requests the resident tab route; with piped or redirected standard input or output it runs the direct UI without probing the host, because the attach client needs a terminal to size and own. Tab enablement is not persisted or configurable; host discovery or verification failure uses the bounded direct single-agent fallback.
56
- - Tab settings `tabsMax` and `tabsAutoName` live in the flat `a1` object of the global agent `settings.json`; the former versioned profile document remains a read-only compatibility fallback while that object is absent.
57
- - The host artifact resolves to `dist/native/<platform>-<arch>/a1-terminal-host[.exe]`, with hermetic `A1_TERMINAL_HOST_PATH`; discovery may use `A1_TABS_ENDPOINT`.
56
+ - The `tabsAutoName` setting lives in the flat `a1` object of the global agent `settings.json`. A stored legacy `tabsMax` value is preserved as unknown compatibility data but never limits creation or appears in Settings.
57
+ - The host artifact resolves to `dist/native/<platform>-<arch>/terminal-host.node`, with hermetic `A1_TERMINAL_HOST_PATH` only when `A1_DEV_INSTANCE_ID` is present; the fixed role entry is `dist/foundation/resident-tabs/host-entry.js`, and discovery may use `A1_TABS_ENDPOINT`.
58
58
  - Tab children consume and erase `A1_TAB_ID`, `A1_TAB_TOKEN`, and `A1_TAB_ENDPOINT` before loading A1, Pi, extensions, or tools.
59
59
  - Client-scoped bridge detach binds `clientId` and `controllerRevision` captured when the command is admitted. The additive generation-1 `attachment` and `bridge.request` fixtures freeze that shape.
60
60
  - Launch fallback reasons are bounded to unsupported platform, no interactive terminal, missing artifact, failed probe, incompatible generation, unreadable registry, blocked recovery, unverified launch, and startup timeout. Fallback preserves resident records and presents exactly one direct-session notice.
@@ -66,10 +66,10 @@ The read-only review used A1 `develop` at `b42b3c9e`, PR #586 at `8fdb7361`, `E:
66
66
 
67
67
  - The native `core/server.rs` and `core/holder.rs` state machines are deterministic and I/O-free; the production holder uses the holder core for bounded input admission and ordered writer effects, and the production server uses the server core for heartbeat supervision. Blocking identity/termination work runs through the bounded worker shell with deadlines and stale-incarnation filtering.
68
68
  - Every attach, server, and holder role has an independent 15-second watchdog. Watchdog records are bounded, timestamped, owner-only metadata under the profile runtime directory; fault-only role freezes are refused by non-test release builds.
69
- - Holders read PTY output continuously, use one bounded input authority, reserve complete pastes before delivery, and emit a typed uncertain-delivery result after an admitted write failure. Server-to-client control lanes are frame/byte bounded; render lanes retain one replaceable full current surface. Holders stream surfaces only while at least one client views their tab.
69
+ - Holders read PTY output continuously, use one bounded input authority, reserve complete pastes before delivery, and emit a typed uncertain-delivery result after an admitted write failure. Each holder independently refreshes the server's latest complete retained surface even while hidden. Server-to-client control lanes are frame/byte bounded, render lanes retain one replaceable full current surface, and only the viewed tab is delivered to each attach client.
70
70
  - Server replacement is serialized by `server-start.lock` in the profile runtime directory. Recovery verifies the recorded native owner before termination, waits boundedly for the registry-writer lease, increments the durable epoch, derives holder credentials, and re-admits only exact holder/child identities and incarnations. A durable three-start/sixty-second budget stops automatic replacement; explicit `--restart` clears it.
71
71
  - Registry records now retain holder and child pid/start identities and holder incarnation. Replacement treats unverifiable records as `holder-lost`; it never starts a duplicate writer from uncertain state.
72
- - `tabsMaxConcurrentStarts` is an owned version-15 setting, default 2 and bounded 1–8. The server also enforces the existing ten-tab hard default where no earlier `tabsMax` setting exists, spaces starts by at least 250 ms, and requests graceful tab shutdown before verified forced termination.
72
+ - Concurrent starts use an internal default of 2 and hard cap of 8. The server spaces starts by at least 250 ms and requests graceful tab shutdown before verified forced termination; open-tab count has no user-visible quota.
73
73
  - All A1-owned Pi runtimes use one profile-neutral session-writer guard regardless of launch mode. A new target is reserved by canonical location, created as an empty owner-only placeholder, bound to stable file identity without a release gap, and only then exposed to Pi for session content. Runtime replacement acquires and binds the target before publishing it or releasing the prior writer; conflict paths preserve the source and may explicitly fork.
74
74
  - The generation-1 holder transport continues to use the authenticated owner endpoint and replaceable full surfaces. Dedicated holder endpoints and patch deltas remain later optimizations and are not milestone-3 claims.
75
75
 
@@ -130,13 +130,13 @@ Alternatives considered:
130
130
  - it duplicates a terminal core that already exists natively;
131
131
  - v2 recorded stalls from exactly this design.
132
132
 
133
- ### 2. One native binary, three roles, plus the A1 child
133
+ ### 2. One native addon, three Node-hosted roles, plus the A1 child
134
134
 
135
135
  ```text
136
136
  terminal (detached, per OS user × profile)
137
137
  ┌──────────────────────────┐ ┌─────────────────────────────────────┐
138
- │ a1 → guardian → │ │ a1-terminal-host server │
139
- │ a1-terminal-host attach │◀───▶│ registry (atomic JSON, fsync) │
138
+ │ a1 → guardian → │ │ node host-entry server │
139
+ │ node host-entry attach │◀───▶│ registry (atomic JSON, fsync) │
140
140
  │ tab strip + active tab │ pipe│ topology, client fan-out, restore │
141
141
  │ outer terminal I/O │ └───┬──────────────┬──────────────┬───┘
142
142
  └──────────────────────────┘ │pipe │pipe │pipe
@@ -150,6 +150,8 @@ Alternatives considered:
150
150
  └── tab bridge ──▶ server (status, session, name, detach)
151
151
  ```
152
152
 
153
+ Every attach, server, and holder role is `process.execPath` running the release's fixed `host-entry.js`; that entry loads the verified `terminal-host.node` addon and blocks in its native role dispatcher. Node carries no terminal bytes—the role remains native for its complete data path.
154
+
153
155
  - **attach** is the foreground `a1` surface. It runs inside the bare-A1 launch instance and dies with the terminal, which is harmless.
154
156
  - It owns raw mode, the alternate screen, outer terminal mode negotiation, the tab strip, composition of the active tab's retained surface, input arbitration, and shortcut interception.
155
157
  - **server** is resident. It owns:
@@ -162,7 +164,7 @@ Alternatives considered:
162
164
  - exactly one PTY (ConPTY on Windows) and its child process tree;
163
165
  - one libghostty-vt retained model with bounded scrollback;
164
166
  - input encoding and the per-tab damage state.
165
- Because ConPTY belongs to its creating process, each tab has its own creator. A server crash therefore does not destroy a tab's ConPTY; holder failure affects only that tab. Actual child/tree exit and writer-lease release still require verification, not inference from the pseudoconsole closing.
167
+ Because ConPTY belongs to its creating process, each tab has its own creator. A server crash therefore does not destroy a tab's ConPTY; holder failure affects only that tab. Actual child/tree exit and writer-lease release still require verification, not inference from the pseudoconsole closing. The 2026-10-17 Windows x64 migration measurement put an idle Node-hosted holder at approximately 30–40 MiB of resident memory, versus a few MiB for the former native-executable holder; allocator and shared-page accounting make this a range rather than a per-tab budget. The separate holder process is retained for certified crash isolation, and latency budgets were not raised to absorb Node startup.
166
168
  - **Why the server relays surfaces instead of clients connecting to holders directly.** It gives one authenticated endpoint per client, one ordering domain for focus and topology, and simpler multi-client arbitration. Measure the extra local pipe hop in the native hot-path evidence rather than assuming its latency. The server forwards encoded holder surfaces and does not re-render them.
167
169
 
168
170
  ### 3. Host scope, endpoints, writer authority, and credentials
@@ -182,9 +184,9 @@ Alternatives considered:
182
184
 
183
185
  ### 4. Detached start
184
186
 
185
- - **Server start.** The server is started from the Node pre-guardian bootstrap (`ensureTerminalHost()` beside `ensureSupervisor()`), through `a1-terminal-host server --detach`.
187
+ - **Server start.** The server is started from the Node pre-guardian bootstrap (`ensureTerminalHost()` beside `ensureSupervisor()`), through `process.execPath host-entry.js server --detach` with the verified addon path supplied to the entry.
186
188
  - **No general escape from containment.** On Windows, ordinary launch-instance jobs and holder-owned child jobs retain kill-on-close containment without `JOB_OBJECT_LIMIT_BREAKAWAY_OK` or silent breakaway; a job flag cannot allowlist an executable. On every platform, resident creation is a distinct authenticated fixed-role operation, not an arbitrary executable/argv spawn service.
187
- - **Authorized launch and recovery.** Initial creation runs before the launch guardian. A contained attach client or surviving holder invokes the native verified `server --detach` recovery path inside the terminal-host executable; there is no arbitrary-spawn IPC method. The routine accepts only a typed `server` or `holder` role and canonical profile, resolves and opens its own terminal-host artifact, constructs a fixed argument grammar, uses an allowlisted environment/cwd with handle inheritance disabled, and verifies the resulting pid/start identity and containment before success. On Windows, a contained caller uses the fixed WMI escape, while the holder's fixed recovery invocation launches directly from its already-detached resident boundary; recursively invoking WMI from a detached service can fail in non-interactive sessions. Native tests may compile one feature-gated fixture role, but production accepts no executable or free-form argv. Owner-only credentials prevent cross-user access; this is not a sandbox against arbitrary malicious code already running as the same OS user.
189
+ - **Authorized launch and recovery.** Initial creation runs before the launch guardian. A contained attach client or surviving holder invokes the addon's verified `server --detach` recovery path; there is no arbitrary-spawn IPC method. When `runRole` first enters, it records one immutable host command: the current Node executable and the release's fixed role entry. The native routine accepts only a typed `server` or `holder` role and canonical profile, opens its own addon artifact, constructs the fixed role grammar, uses an allowlisted environment/cwd with handle inheritance disabled, and starts that recorded Node-and-entry pair. It verifies the resulting pid/start identity and containment before success. On Windows, a contained caller uses the fixed WMI escape, while the holder's fixed recovery invocation launches directly from its already-detached resident boundary; recursively invoking WMI from a detached service can fail in non-interactive sessions. Native tests may compile one feature-gated fixture role, but production accepts no caller-supplied executable or free-form argv. Owner-only credentials prevent cross-user access; this is not a sandbox against arbitrary malicious code already running as the same OS user.
188
190
  - **Windows:**
189
191
  - Select valid platform creation flags for detached, console-free startup; do not assume combining `DETACHED_PROCESS` and `CREATE_NO_WINDOW` adds a guarantee. Verify resulting process identity and job containment before reporting resident readiness.
190
192
  - For a caller in a foreign kill-on-close job that denies breakaway (Windows OpenSSH, some IDE terminals), use WMI `Win32_Process.Create` with an explicit environment block and cwd, bounded startup, and post-launch verification (herdr reference).
@@ -192,7 +194,7 @@ Alternatives considered:
192
194
  - **Every platform** records the observed detachment mode and any failed verification reason.
193
195
  - **macOS and Linux:**
194
196
  - Start resident roles in a new session (`setsid`) with no controlling terminal, standard streams on `/dev/null`, and an explicit environment and cwd, so terminal close and SSH hang-up (`SIGHUP`) do not reach them. Verify the resulting session, process group, and identity before reporting readiness.
195
- - The process-guardian's kill-on-close tree tracking must not adopt resident roles, and ordinary descendants must not gain a way to leave their tree.
197
+ - The process-guardian addon's kill-on-close tree tracking must not adopt resident roles, and ordinary descendants must not gain a way to leave their tree.
196
198
  - macOS: keep the resident roles in the user's per-user bootstrap namespace, so clipboard, keychain, and other per-user services keep working after the launching terminal closes (herdr #4100 is the reference). Linux: a session manager that kills user processes at logout (systemd `KillUserProcesses`) ends tabs at logout; that is the documented logout behavior, not a failure.
197
199
  - Milestone 1 proves these with fixtures on each platform before any milestone relies on survival.
198
200
  - **Holders** are spawned by the server through the same platform detach routine.
@@ -238,6 +240,18 @@ The channels carry bounded generation-1 frames: a u32 little-endian length follo
238
240
  - Image protocols are not forwarded in the first version; the terminal identity the child sees disables them and existing text fallback applies. Extension parity is claimed only for the certified text-terminal contract.
239
241
  - **Detach.** On detach the client restores the outer terminal's keyboard and mouse modes exactly. Enhanced keyboard sequences must not leak into the parent shell (herdr CHANGELOG lesson). Preserve A1's surviving-owner restoration for a killed attach process; an in-process panic/fatal hook cannot run after forced termination. The outer bootstrap/exit-notice owner performs bounded emergency restoration without giving either guardian terminal-byte relay authority.
240
242
 
243
+ ### 6a. Solo interaction parity (2026-10-17)
244
+
245
+ Direct `a1 solo` is the behavioural oracle for every viewed resident child. Only the tab strip row and its tab actions, detach instead of exit, the hidden session name, process residency, and the text fallback for unsupported image protocols may differ.
246
+
247
+ - **Semantic input.** Attach resolves tab actions first, then names every other outer event as a bounded generation-1 `input.event`: key identity from the libghostty key table, `MOD_*` modifiers, produced text, repeat, focus, or a pointer gesture already translated to child rows. The server validates and fences it like byte input. The holder encodes it with libghostty against the child's live key, mouse, focus, and bracketed-paste modes, and falls back to the CSI u bytes direct terminals send for modified Enter, Tab, Backspace, and Escape. Double `Ctrl+C` is recognized from the key identity before any encoding; a paste or pointer activation completes another input sequence and resets the chord.
248
+ - **Compatibility.** `semantic-input` and `terminal-effects` are advertised features. A same-generation holder without semantic encoding answers `legacy-holder`; attach re-sends the refused event as bytes and keeps the byte path. That fallback is not certified against the parity contract.
249
+ - **Typed effects.** The holder registers libghostty's clipboard-write callback, which normalizes OSC 52 and OSC 1337. A write from the viewed tab crosses as one bounded `terminal.effect`; attach writes one OSC 52 for it. A hidden tab's write is dropped, never replayed into a later view. Payloads are never logged.
250
+ - **Modes and cursor.** Each surface carries the child's cursor cell and modes. Attach mirrors a cursor shape the child chose and resets it on detach or failure; a default block cursor is left to the outer terminal, as direct A1 leaves it.
251
+ - **Shortcut projection.** The owned child projects the effective A1 keybindings plus `keybindings.json` into one assignment per tab action: a key an A1 action holds never becomes a tab shortcut, and the earlier declaration keeps a duplicated key. It publishes the projection as `bridge.shortcuts`; the server relays it as `tabs.shortcuts`, and attach dispatches and labels exactly that projection. `/hotkeys` renders the same projection. A shared case file is replayed by both resolvers.
252
+ - **Committed presentation.** A requested view commits only with a topology that lists it and a complete target surface. Surfaces of the committed view apply in revision order, and revisions rise across holder incarnations. A retained re-offer is marked stale and parks for at most 120 ms while the fresh frame arrives. One compositor writes strip, body, overlay, and final cursor inside a single synchronized update, and refuses clear-only, blank, and cursor-less strip-only frames. Clock, hover, and title-animation damage repaints only the strip and restores the child's cursor.
253
+ - **Evidence.** One interaction matrix drives three layers: an owned-shell oracle that records content-free observations, role fixtures that drive real server, holder, and attach processes with a certified parity child, and an exact-artifact harness, `verify-interaction-parity.mjs`, that compares the bytes the same child observes directly and through the built host on each platform. Observations carry digests and lengths, never content. Each platform's verdict stands alone.
254
+
241
255
  ### 7. Status and attention come from the tab bridge
242
256
 
243
257
  Tab status is an A1-owned state machine fed by `bridge.status`:
@@ -277,8 +291,8 @@ Tab status is an A1-owned state machine fed by `bridge.status`:
277
291
  - restarts: `restarts`, `restartWindowStart`, `lastExit`
278
292
  - `registryRevision`, `epoch`, and `bootId`
279
293
  - **No per-tab credential is persisted in the registry.** The owner-only profile secret is stored separately and derives per-tab/per-incarnation credentials from recorded non-secret identities. The environment sent with `tab.create` is kept in holder memory only; a tab restarted after a crash reuses its holder's environment.
280
- - **Session-writer lease.** All A1-owned session writers (resident, solo/fallback, and Pi-comparison) on the supported platforms use one profile-neutral native lock primitive before opening a session for writing, independent of launch mode. It does not start a host, holder, or bridge for direct modes. The primitive is an OS lock held by the writer process itself (`LockFileEx` on Windows, an open-file-description `fcntl` lock on Linux, `flock` on macOS), so the kernel releases it only when the writer is gone. Node writers take it in-process through a Node-API 9 artifact produced by the terminal-host Cargo package from the same lock implementation; a helper process cannot hold a lock on their behalf. Existing files are opened, identified by stable volume/device plus file identity, locked through that identity, and revalidated so case, junction/symlink, and hard-link aliases converge. A new file first holds a canonical-parent/name reservation, creates/opens without writing session content, acquires and revalidates file-identity authority while the reservation remains held, and only then admits writing; losing that race writes nothing. The writer routes include startup, `--session`, in-runtime switches, fork, import, new session, and the picker rename. The registry and holder coordinate admission, but a holder-only lock is insufficient: native lease custody must remain valid for the actual writer's lifetime, including any interval after holder death. Pin and prove the writer-bound lock/handle design on each platform in milestone 1; do not infer child death from pseudoterminal loss. Replacement requires lease acquisition and verified prior-writer/tree exit; uncertainty blocks it. Session switching acquires the target before relinquishing the old writer lease, and failed switches preserve the original session. Resident selection focuses an existing tab; direct-mode conflicts fail safely or offer an explicit fork, never silently attach or overwrite. Unmodified external Pi, older nonparticipating builds, and arbitrary file writers are outside this cooperative guarantee; document that limitation rather than claiming an OS sandbox.
281
- - **Prompt journal.** `<dataDir>/tabs/<profile-token>/<tabId>/journal.jsonl` is an owner-only child-owned recovery journal, separate from asynchronous prompt history. Each submission has a stable ID and session/incarnation identity. Admission awaits a successful durable journal commit before dispatch; timeout/failure leaves the prompt in the editor with a visible failure and dispatches nothing. Correlate it with the committed session entry, and retire it only after the corresponding session data is synchronized; a settled turn's durable completion also records its submission ID so recovery never offers a completed prompt as unfinished. Recovery merges journal/session evidence by identity and never automatically resends, including when a crash occurs between dispatch, session append, sync, and journal retirement. Preserve the first-turn journal and reserved session identity if Pi has not created its file yet; distinguish that from an unexpectedly missing existing transcript.
294
+ - **Session-writer lease.** All A1-owned session writers (resident, solo/fallback, and Pi-comparison) on the supported platforms use one profile-neutral native lock primitive before opening a session for writing, independent of launch mode. It does not start a host, holder, or bridge for direct modes. The primitive is an OS lock held by the writer process itself (`LockFileEx` on Windows, an open-file-description `fcntl` lock on Linux, `flock` on macOS), so the kernel releases it only when the writer is gone. Node writers take it in-process through the same Node-API 9 `terminal-host.node` addon that exports the resident roles; there is no separate lock artifact, and a helper process cannot hold a lock on their behalf. Existing files are opened, identified by stable volume/device plus file identity, locked through that identity, and revalidated so case, junction/symlink, and hard-link aliases converge. A new file first holds a canonical-parent/name reservation, creates/opens without writing session content, acquires and revalidates file-identity authority while the reservation remains held, and only then admits writing; losing that race writes nothing. The writer routes include startup, `--session`, in-runtime switches, fork, import, new session, and the picker rename. The registry and holder coordinate admission, but a holder-only lock is insufficient: native lease custody must remain valid for the actual writer's lifetime, including any interval after holder death. Pin and prove the writer-bound lock/handle design on each platform in milestone 1; do not infer child death from pseudoterminal loss. Replacement requires lease acquisition and verified prior-writer/tree exit; uncertainty blocks it. Session switching acquires the target before relinquishing the old writer lease, and failed switches preserve the original session. Resident selection focuses an existing tab; direct-mode conflicts fail safely or offer an explicit fork, never silently attach or overwrite. Unmodified external Pi, older nonparticipating builds, and arbitrary file writers are outside this cooperative guarantee; document that limitation rather than claiming an OS sandbox.
295
+ - **Prompt journal.** `<dataDir>/tabs/<profile-token>/<tabId>/journal.jsonl` is an owner-only child-owned recovery journal, separate from asynchronous prompt history. Each submission has a stable ID and session/incarnation identity. Admission durably records bounded identity and recoverable editor text, never duplicate attachment payload bytes, before dispatch; timeout/failure leaves the prompt in the editor with a visible failure and dispatches nothing. The actual engine command retains every validated attachment. Correlate the record with the committed session entry, and retire it only after the corresponding session data is synchronized, including when command completion observes that the lifecycle already returned to ready; a settled turn's durable completion also records its submission ID so recovery never offers a completed prompt as unfinished. Recovery merges journal/session evidence by identity and never automatically resends, including when a crash occurs between dispatch, session append, sync, and journal retirement. It rehydrates image chips only from validated profile sidecars and removes unavailable tokens rather than presenting inert attachment labels. Preserve the first-turn journal and reserved session identity if Pi has not created its file yet; distinguish that from an unexpectedly missing existing transcript.
282
296
  - **Draft checkpoint.** Use a periodic dirty checkpoint with a maximum one-second dirty interval while storage is healthy, including uninterrupted typing, rather than inactivity-only debounce. Expose failed/late checkpoints as degraded recovery; never continue claiming the one-second bound during a blocked disk or event-loop stall. Drafts and submitted prompts have different guarantees.
283
297
 
284
298
  ### 9. Failure and recovery
@@ -300,7 +314,7 @@ Tab status is an A1-owned state machine fed by `bridge.status`:
300
314
 
301
315
  ### 10. Updates and version skew
302
316
 
303
- - **Binaries.** The server and holders run from immutable release directories, never from the mutable npm prefix, so no running binary is ever overwritten.
317
+ - **Artifacts and process images.** The server and holders are Node runtimes that load the addon and fixed entry from immutable release directories, never from the mutable npm prefix. Activation therefore never overwrites a loaded addon or entry, and release retention keeps every release used by a verified resident process.
304
318
  - **Same generation, newer client.** The client attaches normally, and features the server lacks are disabled per method.
305
319
  - **No automatic handoff in the first version.** A compatible resident cohort remains on its immutable release until all of its tabs stop. A newer same-generation client may attach with unsupported optional operations disabled.
306
320
  - **Retention.** Release collection keeps the server's, every holder's, and every tab child's release while native identities verify. Package activation does not terminate or overwrite them.
@@ -310,8 +324,8 @@ Tab status is an A1-owned state machine fed by `bridge.status`:
310
324
 
311
325
  - **Strip.** Row 0, drawn by the attach client, reproduces the approved v2 default appearance:
312
326
  - normal chips use RGB background `38,38,38` with foreground `170,170,170`, hover uses `48,48,48`, and the viewed chip uses `55,55,55` with white text, plus v2's palette fallback;
313
- - the empty state uses the v2 wallpaper treatment and the right edge shows its 12-hour clock;
314
- - chips are at most 20 columns, clipped with `…` by grapheme width, with no separators and an always-visible `×` close control;
327
+ - the empty state contains only the hoverable `+` control, with no clock, wallpaper, hint, or child cursor;
328
+ - chips are at most 20 columns, clipped with `…` by grapheme width, with no separators or close glyph;
315
329
  - a `…` overflow menu holds tabs that do not fit, and `+` is always reserved;
316
330
  - these presentation choices are fixed, not settings, while each working spinner uses that tab's reported A1 accent.
317
331
  - **Keys.** These are intercepted by the attach client before input encoding. They are configurable through a keybindings file and conflict-checked against A1's registry at launch.
@@ -321,6 +335,7 @@ Tab status is an A1-owned state machine fed by `bridge.status`:
321
335
  | `Alt+A` | new tab |
322
336
  | `Alt+W` | close |
323
337
  | `F2` | rename |
338
+ | `Alt+.` | create or activate the global Settings tab |
324
339
  | `Alt+1`…`Alt+9`, `Alt+0` | jump to a tab |
325
340
  | unbound (`tabs.next` / `tabs.previous`) | next / previous, wrapping |
326
341
  | unbound (`tabs.moveRight` / `tabs.moveLeft`) | move the tab right / left |
@@ -331,43 +346,43 @@ Tab status is an A1-owned state machine fed by `bridge.status`:
331
346
  - **`Ctrl+C` twice leaves, in every tab (user decision).** The client forwards the first `Ctrl+C` to the tab unchanged, so a single press still clears, copies, or interrupts. It consumes the second press within the existing clear/exit interval as the detach request and does not forward it. The tab therefore never receives a second `Ctrl+C` from the chord, which removes v2's bug where `Ctrl+C+C` killed the agent. The trade-off: a future CLI tab cannot receive two quick `Ctrl+C`s. Pressing them more slowly than the interval still reaches the program.
332
347
  - **Mouse on row 0:**
333
348
  - click activates a tab;
334
- - the always-visible `×` closes its tab;
335
- - right-click opens `Rename`/`Close`;
349
+ - right-click opens `Rename`/`Close` (`Close` only for Settings);
336
350
  - dragging reorders, with a `│` drop marker, after 250 ms or once the pointer moves;
337
- - `+` creates a tab.
351
+ - `+` opens a persistent dark v2 menu with `+ Agent`, a separator, and `Settings`; hover and click remain active through the opening release.
352
+ - **Settings.** The strip owns one profile-global `Settings` tab. The grouped launcher and `Alt+.` create or activate it; closing the screen or tab removes it. Resident Agent autocomplete omits `/settings`, and manually entering it does not open a per-Agent screen. `a1 solo` and `a1 pi` retain their existing Settings routes.
338
353
  - **Commands inside A1 tabs:** existing `/name <name>` also renames the tab. Milestone 5 adds no tab-management slash commands: `/new-tab`, `/close`, `/tabs`, `/quit-all`, and `/restart-tab` are not exposed.
339
354
  - **Naming:**
340
355
  - Default names are `agent` or `agent N`.
341
356
  - When `tabsAutoName` is on (the default, by user decision), the tab's model proposes a hyphenated lowercase name of at most 16 characters after the first settled turn. It uses a small budget and one retry, with a fallback derived from the first prompt, and it runs inside the child.
342
357
  - A user rename sets `nameSource=user`, and auto-naming never overrides it.
343
358
  - An automatic rename uses v2's title transition: 600 ms moving rainbow, typing at 45 ms per character, then a 300 ms settle. Manual renames do not animate.
344
- - Rename is inline in the chip: `Enter` commits, and `Esc` or a click elsewhere cancels. The name is trimmed, must not be empty, is at most 64 characters, and has control characters stripped.
345
- - **Close.** `Alt+W`, the always-visible `×`, and the right-click menu close a tab. When it is `working` or `needs-input`, or its bridge reports queued input, the strip asks `Stop "<name>"? Enter stop · Esc cancel`. Closing sends the child a graceful shutdown, waits a bounded time, terminates the tree, and removes the tab. Its session stays resumable. Closing the last tab leaves the strip with `+` and a hint.
359
+ - Rename is inline in the chip: `Enter` commits, `Esc` or a click elsewhere cancels, and `Ctrl+Left` / `Ctrl+Right` move by word. The name is trimmed, must not be empty, is at most 64 characters, and has control characters stripped.
360
+ - **Close.** `Alt+W` and the right-click menu close a tab directly, including while it is working. Closing removes the chip without a confirmation, stopping/read-only frame, or close glyph, then gracefully stops the child under the existing bounded termination policy. Its session stays resumable. Closing the last tab leaves only the hoverable `+` control.
346
361
  - **Detach.**
347
362
  - `Ctrl+C` twice detaches from any tab and is handled by the attach client, so it works even when the bridge is down. Inside an A1 tab, `/quit` and empty-editor `Ctrl+D` send a detach request carrying the immutable command-origin controller identity and revision; stale, ambiguous or unavailable attribution keeps the tab running and directs the user to `Ctrl+C` twice.
348
363
  - The client restores the terminal. When agents are still running, it prints `1 agent still running run a1 to return.` or `N agents still running run a1 to return.`; otherwise it prints the resume hint.
349
364
  - **Launch and reattach:**
350
365
  - Bare `a1` ensures the server and attaches.
351
366
  - With zero tabs, it creates one A1 tab in the launch cwd.
352
- - Otherwise it views the last active tab from its retained surface without byte-history replay. Measure retained first paint and first input separately from bootstrap; status for the remaining tabs arrives without subscribing to their surfaces.
367
+ - Otherwise it views the last active tab from its retained surface without byte-history replay. Every holder keeps draining its PTY and updating its retained terminal independently while hidden; selecting a working tab requests its newest complete partial-response surface immediately and then continues live delivery. Measure retained first paint and first input separately from bootstrap; status for the remaining tabs arrives without subscribing to their surfaces.
353
368
  - `a1 --session X` opens X in a new tab, or focuses the tab holding it.
354
369
  - A new tab's cwd is the client's launch cwd.
355
- - One internal prewarmed standby keeps the startup graph loaded in a hidden A1 process after a trusted tab has reported its session. Before promotion it owns no session or writer lease, has no registry/tab-strip entry, and does not count toward `tabsMax`. Creation promotes it only for an exact cwd, release, Node executable, and environment match; the server durably commits the new tab before exposing it to input. A mismatch discards it and normal creation retargets the next standby from the newly trusted tab. Standby launch or loss never changes visible tab state. It is not a setting. Measure A1 warm promotion directly; v2's tens-of-milliseconds result is reference evidence only.
370
+ - One internal prewarmed standby keeps the startup graph loaded in a hidden A1 process after a trusted tab has reported its session. Before promotion it owns no session or writer lease and has no registry/tab-strip entry. Creation promotes it only for an exact cwd, release, Node executable, and environment match; the server durably commits the new tab before exposing it to input. A mismatch discards it and normal creation retargets the next standby from the newly trusted tab. Standby launch or loss never changes visible tab state. It is not a setting. Measure A1 warm promotion directly; v2's tens-of-milliseconds result is reference evidence only.
356
371
  - **Several terminals.** Each client has its own active tab, but each tab has one explicit input controller. Focus gain automatically claims the viewed tab through Decision 5's causal input barrier; if focus reporting is unavailable, first input starts the claim and is held until acknowledgement. There is no `Enter` takeover or persistent view-only banner. Only the current controller may send input or set PTY size. Delayed `/quit` and empty-editor `Ctrl+D` preserve their submission origin; stale or unprovable attribution affects neither client and falls back to attach-local double-`Ctrl+C`.
357
372
  - **Background attention.** Only the chip icon changes. There is no bell, sound, or notification, and no setting for one.
358
373
 
359
374
  ### 12. Limits and resources
360
375
 
361
- The settings live in the owned settings screen, each with a hard cap.
376
+ Internal resources remain bounded without imposing a user-visible tab-count quota.
362
377
 
363
- - User-facing Tabs settings during resident launches are only `tabsMax` (default 10, range 1–50) and `tabsAutoName` (default on). Tab enablement is not a setting. Direct `a1 solo` and `a1 pi` launches omit the Tabs section entirely.
378
+ - The only user-facing Tabs setting during resident launches is `tabsAutoName` (default on). Legacy `tabsMax` data is preserved but ignored. Tab enablement is not a setting. Direct `a1 solo` and `a1 pi` launches omit the Tabs section entirely.
364
379
  - Unresponsive restart and stall-notice timing are internal fixed behavior at 120 and 300 seconds; concurrent starts use internal default 2/hard cap 8 and prewarm uses internal count/hard cap 1.
365
380
  - Every open tab keeps its holder and A1 process until explicit close. There is no idle-suspension setting, timer, lifecycle, or glyph.
366
381
  - Scrollback is capped at 10 MiB per holder, and the replay after a server restart is the retained model itself.
367
382
  - The server exits after 10 minutes with no attached client and no live user tab; the prewarmed standby does not keep it alive and is stopped first.
368
383
  - Structured diagnostic logs are `terminal-host-server.log` and `terminal-host-holder-<tab>.log`, rotated at 5 MiB with three kept. Use allowlisted fields/reason codes, not arbitrary exception strings, prompt text, terminal content, or credentials.
369
384
  - Raw child stderr cannot be declared sanitized: when available separately from ConPTY's combined terminal stream, store it only under the owner-only per-tab recovery area, at 5 MiB with three generations and a seven-day maximum age, alongside bounded last-screen files. Do not scrape stderr back out of terminal cells. These private artifacts may contain conversation or sensitive content and are excluded from `a1 tabs doctor`; neither their contents nor arbitrary extension errors enter structured crash records.
370
- - `a1 tabs host status` prints the server pid, build, release, generation, epoch, uptime, detachment mode, tab count by status, client count, configured and hard limits, volatile health counters since server start, and log paths.
385
+ - `a1 tabs host status` prints the server pid, build, release, generation, epoch, uptime, detachment mode, tab count by status, client count, internal resource limits, volatile health counters since server start, and log paths.
371
386
 
372
387
  #### Hard bounds (milestone 6 audit)
373
388
 
@@ -381,7 +396,6 @@ Every queue, buffer, file, and collection the resident roles keep has a declared
381
396
  | Per-client render slot | one pending full surface; a newer one replaces it | `roles/server.rs` `Peer::offer_surface` | `stalled_client_gets_one_full_current_surface` |
382
397
  | Concurrent endpoint connections | 128 | `roles/server.rs` `admit_connection` | `connections_beyond_the_cap_are_refused_and_released_slots_are_reused` |
383
398
  | Attached viewing clients | 16 | `roles/server.rs` `client_slot_available` | `attached_clients_are_capped` |
384
- | Open tabs | `tabsMax` (default 10, hard 50), including session-selection starts | `roles/server.rs` `tab.create`, `attach.start` | `ten_concurrent_tab_starts_respect_the_server_limit` |
385
399
  | Concurrent holder starts | default 2, hard 8 | `core/server.rs` | `limits_starts_and_drops_stale_completion` |
386
400
  | Stopped tab records | 16, oldest dropped first | `roles/server.rs` `prune_stopped_records` | `stopped_records_are_bounded_oldest_first_and_live_tabs_are_kept` |
387
401
  | Prior tab-set archives | bounded history | `roles/registry.rs` | `prior_tab_sets_are_bounded` |
@@ -389,7 +403,7 @@ Every queue, buffer, file, and collection the resident roles keep has a declared
389
403
  | Diagnostic logs | 5 MiB × 3 generations per role | `roles/diagnostics.rs` | `logs_rotate_at_their_cap_and_keep_a_bounded_number_of_generations` |
390
404
  | Crash records | 64 KiB each, 50 records, 30 days | `roles/diagnostics.rs`, `shell/watchdog.rs` | `crash_records_are_distinct_bounded_and_allowlisted`, `crash_record_is_bounded_and_owner_only` |
391
405
  | Last screen and private stderr sink | 2 MiB; 5 MiB × 3 generations, 7 days | `roles/recovery_artifacts.rs` | `recovery_files_are_plain_bounded_private_and_rotated`, `fixture_stderr_sink_caps_content` |
392
- | Prompt journal | 8 MiB, 2 MiB per record | `src/foundation/resident-tabs/prompt-journal.ts` | `bounds append size` |
406
+ | Prompt journal | 8 MiB, 2 MiB per bounded metadata/text record; attachment payloads excluded | `src/foundation/resident-tabs/prompt-journal.ts` | `bounds append size`; `keeps a legal multi-image payload out of bounded journal admission and sends every image` |
393
407
  | Session reconciliation read | 64 MiB | `src/foundation/resident-tabs/recovery.ts` | `refuses to scan a session past the 64 MiB reconciliation cap` |
394
408
  | Release-hold report | 32 uncertainty reasons; 1 MiB read | `roles/release_holds.rs`, `release-holds.ts`, `bounded-host-command.ts` | `uncertainty_reasons_stay_within_their_cap`; `fails a release-hold report that pushes past its byte cap without returning a prefix` |
395
409
  | Maintenance exchange | 5 s reads, 60 s stops, 4 MiB report | `roles/maintain.rs`, `maintenance.ts`, `bounded-host-command.ts` | `fails a maintenance report that pushes past its byte cap without returning a prefix`, `kills and rejects a command that outlives its deadline` |
@@ -398,7 +412,7 @@ Every queue, buffer, file, and collection the resident roles keep has a declared
398
412
 
399
413
  ### 13. Packaging and certification
400
414
 
401
- - `a1-terminal-host` is built and packaged for Windows x64, Linux x64, macOS arm64, and macOS x64 (see the 2026-10-14 decision), extending the impact-selected Windows CI owner introduced by #588 to that exact target matrix, with Zig 0.15.2 for pinned libghostty-vt. It ships in the immutable release with artifact manifest, hash verification, provenance, licenses, and notices.
415
+ - `terminal-host.node` and the fixed `dist/foundation/resident-tabs/host-entry.js` are built and packaged for Windows x64, Linux x64, macOS arm64, and macOS x64 (see the 2026-10-14 decision), extending the impact-selected Windows CI owner introduced by #588 to that exact target matrix, with Zig 0.15.2 for pinned libghostty-vt. One addon carries both every role and the session-writer lock; there is no terminal-host executable or separate lock addon. It ships in the immutable release with the v2 addon manifest, hash verification, provenance, licenses, and notices.
402
416
  - Acceptance requires exact-package hermetic suites on each platform plus manual or isolated-worker evidence per platform for terminal close, SSH/session loss, reattach, input fidelity, extension text UI, failure recovery, rollback, and render smoothness. Evidence is never inferred from one platform to another.
403
417
  - An authorized isolated-worker 24-hour soak remains required certification evidence on each platform; a miss blocks certification without silently changing the approved default or weakening fallback.
404
418
 
@@ -486,9 +500,9 @@ The v2 daemon was unreliable for structural reasons, and a forensic pass over it
486
500
  2. With `TERMINAL_HOST_LOCAL_BUILD=1` and `TERMINAL_HOST_PREBUILT_VT` naming that directory, `build.rs` verifies `terminal-vt.manifest.json` against the vendored `VERSION`, the Ghostty commit, the Cargo and Zig targets, and each file's SHA-256, then links it and skips Zig. A mismatch fails the build in one line and never falls back. Builds with `CI` set ignore the variable.
487
501
  3. Run one test: `cargo test --manifest-path native/terminal-host/Cargo.toml --features resident-fixtures --test role_integration <name>`.
488
502
 
489
- Every chaos run prints its seed and records its executed action schedule in its report; `--seed <n>` fixes the seed, and `--replay <report.json>` re-executes a recorded schedule and reports actions it could not apply as divergence. Locally, `--build` is unavailable because it runs the release packaging build, which records the Zig version; build the two binaries through the prebuilt library instead with `cargo build --release --locked --manifest-path native/terminal-host/Cargo.toml --features resident-fixtures --bin terminal-host --bin fake-tab`, then pass `--host` and `--fake-tab` from `native/terminal-host/target/release`.
503
+ Every chaos run prints its seed and records its executed action schedule in its report; `--seed <n>` fixes the seed, and `--replay <report.json>` re-executes a recorded schedule and reports actions it could not apply as divergence. With the verified prebuilt terminal library selected, `--build` builds the release addon and the feature-gated `fake-tab` fixture; otherwise pass `--host <terminal-host.node>` and `--fake-tab <fixture>` explicitly. Product builds never emit a role executable; the `role-host` and `fake-tab` binaries are test fixtures only.
490
504
 
491
- Known limit (2026-10-15): on a Windows desktop whose shells already run inside a job, a server started by a role test cannot leave that job, `verify_detached` rejects it, and tests that expect `started` fail locally while passing in CI; the release retention integration test, which runs whenever debug host binaries exist, fails the same way, and local chaos reports servers that holders did not replace. A separate change addresses this.
505
+ Known limit (2026-10-15): on a Windows desktop whose shells already run inside a job, a server started by a role test cannot leave that job, `verify_detached` rejects it, and tests that expect `started` fail locally while passing in CI; the release retention integration test, which runs whenever a debug host addon exists, fails the same way, and local chaos reports servers that holders did not replace. A separate change addresses this.
492
506
 
493
507
  **Milestone 5 preview performance budgets (p95 on each exact platform build):** cold launch to first input is the direct startup budget plus 800 ms; matching-standby creation to first input is 250 ms; reattach first paint is 300 ms and first accepted input is 400 ms; input-to-process is 16 ms; output-to-present is 33 ms. Each result or miss is recorded independently for Windows x64, macOS arm64, and Linux x64. The platform-native validation lanes run `measure-resident-tabs.mjs` against their built host: its instrumented child reports process-admission timestamps through a real resident holder, while the outer test PTY records presentation, standby promotion, and retained-surface reattach timestamps.
494
508
 
@@ -496,8 +510,8 @@ Known limit (2026-10-15): on a Windows desktop whose shells already run inside a
496
510
 
497
511
  - **[A server crash interrupts the display for everyone]** → Holders keep processes and screens alive. The server holds no VT, Pi, or extension code. It is auto-respawned by any client or holder, and reattach restores exact surfaces.
498
512
  - **[Terminal fidelity in a composed surface]** (keyboard protocols, mouse, paste, IME, Unicode width, hyperlinks) → Reuse libghostty-vt encoding and the proof's input work, and add fidelity suites on each platform. Claims cover only the certified text-terminal contract; image protocols remain an explicit gap.
499
- - **[Per-tab memory multiplies with holder and A1 processes; historical estimates are not measured A1 bounds]** → `tabsMax`, one internal prewarmed standby, and throttled hidden rendering. Every open tab keeps its process until explicit close; there is no idle suspension.
500
- - **[Detachment is platform-specific and fragile]** (Windows jobs and SSH, macOS bootstrap namespaces, Linux logout policies) → Detection and escape live in the native binary per platform. The mode is reported, never assumed, and unverifiable survival falls back instead of claiming persistence.
513
+ - **[Per-tab memory multiplies with holder and A1 processes; measured working sets are observations, not A1 bounds]** → The 2026-10-17 Windows x64 measurement shows Node hosting raising an idle holder from a few MiB to approximately 30–40 MiB resident. One internal prewarmed standby, bounded concurrent starts, and throttled hidden delivery limit avoidable multiplication. Every open tab keeps its process until explicit close; there is no idle suspension or count quota. Merging holders into the server would discard the certified per-tab crash boundary and requires a separate change.
514
+ - **[Detachment is platform-specific and fragile]** (Windows jobs and SSH, macOS bootstrap namespaces, Linux logout policies) → Detection and escape live in the native addon per platform, while the process image is Node running the fixed role entry. The mode is reported, never assumed, and unverifiable survival falls back instead of claiming persistence.
501
515
  - **[Three platforms multiply native and certification work]** → Platform code is confined to the thin I/O shell around one shared sans-IO core, and milestone 1 proves every platform primitive before later milestones build on it.
502
516
  - **[The Zig and libghostty toolchain in CI and release]** → It is pinned already. Extend the #588 Windows owner and provenance gate to a macOS and Linux build matrix.
503
517
  - **[No image protocol in tabs at first]** → Pi's text fallback applies and the gap is documented. Forwarding is a follow-up.
@@ -570,6 +584,10 @@ The user made these decisions on 2026-09-24:
570
584
  - `Ctrl+C` twice leaves `a1` from any tab while tabs keep running. There is no separate detach key.
571
585
  - Reliability is a first-class, release-gating requirement (Decision 16).
572
586
 
587
+ On 2026-10-16 the `ci-critical-path` change replaced the Intel runner for the release builds:
588
+
589
+ - The macOS x64 process guardian and terminal host are cross-compiled on the `macos-15` arm64 runner (`npm run build:terminal-host -- --target x86_64-apple-darwin`, same for the guardian) and uploaded beside the arm64 artifacts; the Intel runner took 6 to 11 minutes for builds the arm64 runner finishes in about 4, and it gated packing in every publication. The x64 executable's `--version` identity is read by executing it through Rosetta 2 when the runner can; otherwise the manifest records a Mach-O architecture check as `provenance.identityCheck: "mach-o-header"` instead of a report nobody observed. Intel macOS stays certified separately and its physical acceptance record is unchanged; no publication lane ran the x64 bytes beyond that identity check before either, because the published-pair smoke matrix has no Intel lane.
590
+
573
591
  On 2026-10-14 the user resolved the milestone-6 `darwin-x64` question:
574
592
 
575
593
  - Add an Intel macOS runner (`macos-15-intel`) for both the process guardian and the terminal host, so every release carries `darwin-x64` binaries beside `darwin-arm64`. Intel macOS is certified separately from Apple silicon: it needs its own physical acceptance record, and the chaos group runs on macOS arm64 only.
@@ -1,6 +1,6 @@
1
1
  # Terminal-host provenance
2
2
 
3
- The composed-terminal proof uses a small console-hosted stack. It runs inside the user's existing terminal and does not create a separate desktop window.
3
+ The resident terminal host uses a small console-hosted stack. Its native implementation ships as the Node-API 9 addon `terminal-host.node`; the release's fixed `dist/foundation/resident-tabs/host-entry.js` loads that addon and dispatches the server, holder, attach, probe, maintenance, and internal roles inside the hosting Node runtime. It runs inside the user's existing terminal and does not create a separate desktop window. No terminal-host executable is built or shipped, and the same addon exports the in-process session-writer lock.
4
4
 
5
5
  ## Selected source components
6
6
 
@@ -14,7 +14,7 @@ The composed-terminal proof uses a small console-hosted stack. It runs inside th
14
14
 
15
15
  ## Release notices
16
16
 
17
- `scripts/release/generate-terminal-host-notices.mjs` writes `terminal-host-THIRD-PARTY-NOTICES.txt` beside each release artifact. It walks the locked Cargo graph for the build target (normal dependencies, including the `node-addon` session lock; proc-macro crates and their subtrees never reach the binary), adds the Zig-linked components, and fails on a license expression outside MIT, Apache-2.0, BSD-2-Clause, BSD-3-Clause, ISC, Unicode-3.0, or Zlib (the `LLVM-exception` addendum is accepted), on a crate without license text, or on a pinned text whose digest changed.
17
+ `scripts/release/generate-terminal-host-notices.mjs` writes `terminal-host-THIRD-PARTY-NOTICES.txt` beside each release addon. It walks the locked Cargo graph for the build target (normal dependencies, including the Node-API glue; proc-macro crates and their subtrees never reach the addon), adds the Zig-linked components, and fails on a license expression outside MIT, Apache-2.0, BSD-2-Clause, BSD-3-Clause, ISC, Unicode-3.0, or Zlib (the `LLVM-exception` addendum is accepted), on a crate without license text, or on a pinned text whose digest changed.
18
18
 
19
19
  A buffered A1-owned frame composer is the initial presentation layer. Ratatui is an optional evaluation candidate only if the narrow composer increases rendering risk; it is not selected by default.
20
20
 
@@ -53,7 +53,7 @@ The console proof requires:
53
53
  - Rust/Cargo for the A1 terminal host;
54
54
  - Zig `0.15.x` with patch `>= 0.15.2` to build `libghostty-vt`;
55
55
  - the platform C/C++ build toolchain required by Rust and the PTY/input crates (Visual Studio 2022 Build Tools/MSVC on Windows, the runner image's Xcode Clang on macOS, GCC on Linux);
56
- - release builds on `windows-2025`, `ubuntu-24.04`, `macos-15` (arm64), and `macos-15-intel` (x64);
56
+ - release builds on `windows-2025`, `ubuntu-24.04`, and `macos-15` (arm64, which also cross-compiles the x64 artifact with `--target x86_64-apple-darwin`);
57
57
  - an existing terminal emulator for manual validation;
58
58
  - an isolated disposable worker for automated physical validation.
59
59
 
@@ -76,4 +76,6 @@ Unverified, mismatched, or unsigned/unattested artifacts are not eligible for co
76
76
 
77
77
  ## Release artifact
78
78
 
79
- `npm run build:terminal-host` (`scripts/development/build-terminal-host.mjs`) runs only on the release runners. It builds the `terminal-host` executable and the `node-addon` session-writer lock (`a1_session_lock.node`) with `cargo build --release --locked`, checks the executable's `--version` report (protocol, generation, crate version, pinned `libghostty-vt` commit, and that neither `fault-injection` nor `resident-fixtures` is compiled in), writes the notices, and records `terminal-host.manifest.json` (schema `a1-terminal-host-artifact-v1`) with the SHA-256 and size of all three files plus the target triple, Rust and Zig versions, and `Cargo.lock` digest. The publication `package` job refuses to pack unless every platform's guardian and terminal host are present and match their manifests, and A1 verifies the manifest before every execution.
79
+ `npm run build:terminal-host` (`scripts/development/build-terminal-host.mjs`) runs only on release and native validation runners. It builds the library once with `cargo build --release --locked --lib --features node-addon`, copies the cdylib as `terminal-host.node`, loads the addon in Node, and checks its `version()` report: protocol, generation, crate version, pinned `libghostty-vt` commit, and the absence of `fault-injection` and `resident-fixtures`. The macOS x64 cross build may use the recorded Mach-O architecture check when the arm64 runner cannot load the addon.
80
+
81
+ The generated `terminal-host.manifest.json` uses schema `a1-terminal-host-artifact-v2` and records `kind: "node-addon"`, Node-API 9, the addon's SHA-256 and size, the release-relative role-entry digest, the notices digest, target triple, Rust and Zig versions, and `Cargo.lock` digest. The role entry passes only the fixed role grammar and records `{ executable: process.execPath, entry, artifact: addonPath }` once in the addon; the explicit loaded artifact identity makes release-hold reporting independent of development environment propagation. Detached and WMI resident starts use the recorded Node-and-entry command and never resolve an A1-built executable. The publication `package` job refuses to pack unless every platform's process-guardian and terminal-host addons match their manifests, and A1 verifies the addon, entry, and notices before loading the host.
@@ -26,19 +26,19 @@ Physical desktop automation is absent from the active repository baseline. Any f
26
26
  The published package contains:
27
27
 
28
28
  - the sole public `a1` command;
29
- - the internal supervisor entry;
30
- - immutable release/bootstrap, lifecycle, protocol, storage, update, owned-UI, and Pi-adapter modules;
29
+ - the internal supervisor, launch-guardian, exit-watcher, and fixed terminal-host role entries;
30
+ - immutable release/bootstrap, lifecycle, protocol, storage, update, owned-UI, and Pi-adapter modules, plus `process-guardian.node` and `terminal-host.node` for every supported target with manifests and terminal-host notices;
31
31
  - current user and architecture documentation.
32
32
 
33
- It contains no PTY, terminal emulator, browser/desktop GUI, custom renderer, input translator, physical automation driver, or generated runtime data.
33
+ It contains no executable built from A1 sources, separate session-lock addon, browser/desktop GUI, physical automation driver, or generated runtime data. PTY ownership, terminal emulation, composition, and input translation exist only inside the terminal-host addon; the JavaScript product path does not relay terminal bytes.
34
34
 
35
- ## Native launch containment
35
+ ## Native addon layer
36
36
 
37
37
  `npm run doctor` reports this contract against the current machine—Node range, npm major, git, the GitHub CLI, Rust/Cargo, and whether `node_modules` matches the lockfile—and names the install command for anything missing. `npm run build` runs the same check as its first step, so an unmet prerequisite fails by name rather than inside a module resolver or Cargo. Unmet tool prerequisites block; a Node or npm version outside the declared contract is reported as an advisory without failing the build.
38
38
 
39
- Source builds require Rust/Cargo 1.85 or newer. `npm run build` compiles the standalone `native/process-guardian` crate for the host platform, places it under `dist/native/<platform>-<architecture>/`, and writes an integrity manifest. Preview and stable candidate workflows build platform artifacts on isolated Windows, Linux, macOS arm64, and macOS x64 runners and assemble them before packing. The same runners build the release terminal host and its session-writer lock with `npm run build:terminal-host`; the local `npm run build` does not, because the Zig fetch for `libghostty-vt` is quarantined by workstation antivirus on Windows (see [`terminal-host-provenance.md`](terminal-host-provenance.md)). macOS remains explicitly unsupported until its exact containment adapter is certified; the package must fail before runtime startup rather than use PID-only cleanup.
39
+ Source builds require Rust/Cargo 1.85 or newer; `rust-toolchain.toml` pins the exact version CI and publication install through `scripts/development/prepare-ci-rust.sh`, and rustup selects that version for Cargo commands inside the repository, installing it on first use. The Zig cache (`mlugg/setup-zig`, one entry per workflow and platform per run, bounded to 512 MiB) stays enabled: measured on 2026-10-16, a cold Zig build of `libghostty-vt` added about 40 to 60 seconds to a terminal-host test build (Linux 2m21s cold against 1m20s warm, Windows 2m05s against 1m26s), which the cache saves on every warm run.
40
40
 
41
- The process guardian inherits terminal handles but transports no terminal bytes. See [`process-guardian-provenance.md`](process-guardian-provenance.md).
41
+ `npm run build` compiles `native/process-guardian` as the host platform's Node-API 9 addon, places `process-guardian.node` under `dist/native/<platform>-<architecture>/`, loads it to observe its version identity, and writes the v2 integrity manifest. Preview and stable candidate workflows build the addon on isolated Windows, Linux, and macOS arm64 runners, cross-compile the macOS x64 addon on the arm64 runner with `--target x86_64-apple-darwin`, and assemble all targets before packing. The same release runners compile TypeScript and run `npm run build:terminal-host`, which builds one Node-API 9 `terminal-host.node` containing the fixed roles and the session-writer lock. Its v2 manifest binds the addon, `dist/foundation/resident-tabs/host-entry.js`, and notices. The local `npm run build` does not build that addon because the Zig fetch for `libghostty-vt` is quarantined by workstation antivirus on Windows; local native iteration uses a verified terminal library downloaded from a CI lane (see [`terminal-host-provenance.md`](terminal-host-provenance.md) and [`resident-tabs.md`](resident-tabs.md)). Cross-compiled artifacts record a Mach-O architecture check when they cannot be loaded on the producing runner. Supported native code is always loaded by Node; an absent, incompatible, or unsupported addon fails before runtime startup rather than falling back to PID-only cleanup. The process-guardian addon inherits terminal handles but transports no terminal bytes. Its separate Node-hosted exit watcher restores terminal state after guardian failure, subject to the documented node-wide-kill gap. See [`process-guardian-provenance.md`](process-guardian-provenance.md).
42
42
 
43
43
  ## Gates
44
44
 
@@ -8,7 +8,7 @@ Version-1 and version-2 deliveries and their existing comments, acceptance PRs,
8
8
 
9
9
  ## Version-3 lifecycle
10
10
 
11
- 1. **Draft plan:** create one normally named branch/PR such as `feature/...`, `fix/...`, or `chore/...`. Start the body with `## Proposal`, use it for one or two sentences of intent, and follow it with two to ten unchecked, outcome-sized task items under `## Implementation`. Each item names a delivered code outcome. Omit generic process tasks; run, verify, validate, evidence, or manual-test items; quoted phase lines; and routine validation command lists, and keep machine linkage under final collapsed `Automation`. Keep it draft and include only planning artifacts until the maintainer approves the plan and explicitly requests implementation.
11
+ 1. **Draft plan:** create one normally named branch/PR such as `feature/...`, `fix/...`, or `chore/...`. Start the body with `## Proposal`, use it for one or two sentences of intent, and follow it with one or more unchecked, outcome-sized task items under `## Implementation`, with no fixed maximum. Each item names a delivered code outcome. Omit generic process tasks; run, verify, validate, evidence, or manual-test items; quoted phase lines; and routine validation command lists, and keep machine linkage under final collapsed `Automation`. Keep it draft and include only planning artifacts until the maintainer approves the plan and explicitly requests implementation.
12
12
  2. **Same-PR implementation:** continue after explicit approval in the same worktree, branch, history, draft PR, and phase-free body. Check an Implementation item as soon as its associated code and tests are written and its detailed task bookkeeping is complete; an ordinary progress edit changes only `[ ]` to `[x]` and is not deferred until handoff. Adding, removing, reordering, or rewriting an item is a scope refinement requiring explicit approval and coherent planning updates, with affected scope left unchecked. Reconcile approved refinements before corresponding code edits.
13
13
  3. **Complete implementation:** finish the code and its tests, substantive tasks, and explicit known-gap disposition. CI runs those tests after readiness; its success is objective evidence, not acceptance, and GitHub check counts are not Implementation task progress. Manual validation is the maintainer's optional choice before integrating and never blocks a checkbox, finalization, or merge.
14
14
  4. **Plain acceptance list:** before checking the final item, keep the body phase-free and add final `## Acceptance` with one to ten concise implementation-specific behavior-and-result bullets. Do not use checkboxes, generic review/CI/approval/archive statements, angle-bracket placeholders or HTML, URLs, mentions, or automated-test inventory.
@@ -27,7 +27,7 @@ The implementation, synchronized canonical specs, conditional acceptance record,
27
27
 
28
28
  ## Draft PR body
29
29
 
30
- The first screen should separate purpose from delivery detail, not foreground CI mechanics. Distill `Proposal` into one or two sentences answering why the PR exists, then put two to ten unchecked, implementation-specific outcomes under `Implementation`. They are a coarse progress summary, not a copy of the detailed OpenSpec task ledger:
30
+ The first screen should separate purpose from delivery detail, not foreground CI mechanics. Distill `Proposal` into one or two sentences answering why the PR exists, then put one or more unchecked, implementation-specific outcomes under `Implementation`, with no fixed maximum. They are a coarse progress summary, not a copy of the detailed OpenSpec task ledger:
31
31
 
32
32
  ```markdown
33
33
  ## Proposal
@@ -159,7 +159,7 @@ If the body list changes, candidate validation reruns and compares it with the c
159
159
 
160
160
  ## Automated finalization
161
161
 
162
- The `OpenSpec finalization` workflow (`.github/workflows/openspec-finalization.yml`) runs on `pull_request_target` for `opened`, `synchronize`, `ready_for_review`, `reopened`, and `edited` events targeting `develop`, one event at a time per PR. It checks out default-branch policy, installs the pinned tooling without hooks, and runs `scripts/governance/publish-openspec-finalization.mjs --pr <n>`; the PR head enters that process only as the `openspec/` tree the pinned OpenSpec engine reads. An all-checked version-3 task-list draft with valid Acceptance and complete detailed pre-finalization inputs is re-read as one exact snapshot and marked ready; no content is changed, and incomplete, malformed, legacy, unassociated, forked, or raced drafts remain draft with a stable reason. A draft with any unchecked item is skipped quietly. A draft whose every task-list item is checked but whose promotion is refused for a body, task, evidence, or other content defect fails the run with an `OpenSpec finalization` error annotation naming the `draft-<code>` reason, so the stall shows as a red check instead of a green skip. Closed PRs are skipped. For a ready version-3 candidate the workflow reconciles the head to its finalized form:
162
+ The `OpenSpec finalization` workflow (`.github/workflows/openspec-finalization.yml`) runs on `pull_request_target` for `opened`, `synchronize`, `ready_for_review`, `converted_to_draft`, `reopened`, and `edited` events targeting `develop`, one event at a time per PR. It checks out default-branch policy, installs the pinned tooling without hooks, and runs `scripts/governance/publish-openspec-finalization.mjs --pr <n>`; the PR head enters that process only as the `openspec/` tree the pinned OpenSpec engine reads. An all-checked version-3 task-list draft with valid Acceptance and complete detailed pre-finalization inputs is re-read as one exact snapshot and marked ready; no content is changed, and incomplete, malformed, legacy, unassociated, forked, or raced drafts remain draft with a stable reason. A draft with any unchecked item is skipped quietly. A finalized PR returned to draft is promoted again when its body still has every item checked, its fence names the exact head as finalized, and the tree holds exactly that archive with no active change; otherwise it stays draft with `draft-finalization-redraft-form`. A draft whose every task-list item is checked but whose promotion is refused for a body, task, evidence, or other content defect fails the run with an `OpenSpec finalization` error annotation naming the `draft-<code>` reason, so the stall shows as a red check instead of a green skip. Closed PRs are skipped. For a ready version-3 candidate the workflow reconciles the head to its finalized form:
163
163
 
164
164
  - **Active and current:** ordinary finalization with today's UTC date; one `docs(openspec): finalize <change>` commit.
165
165
  - **Finalized, valid, and current:** nothing is pushed; the run reports `already-finalized`.
@@ -48,14 +48,18 @@ Conservative Development retains every pull-request owner but does not schedule
48
48
  node scripts/release/generate-validation-ownership-ledger.mjs --output .artifacts/validation/ownership-ledger.json
49
49
  ```
50
50
 
51
- The `terminal-host` owner is CI-only. A change under `native/terminal-host/` or to its run scripts selects Windows x64, Linux x64, and macOS arm64 **Native terminal host** jobs. Each builds the binary and Node-API lock artifact with Rust and Zig 0.15.2 for the lane's exact target and baseline CPU model, runs `npm run test:terminal-host`, and uploads the debug artifacts and frozen protocol manifest; Linux also runs bounded decoder fuzzing. Local Windows builds fail in `build.rs` unless `TERMINAL_HOST_LOCAL_BUILD=1` is set, because Zig's package fetch unpacks test data that workstation antivirus quarantines along with the installed process guardian. Native lanes also publish the terminal library they linked; a local build can link it instead of running Zig (see Local native iteration in [`architecture/resident-tabs.md`](architecture/resident-tabs.md)), which is development evidence only and never replaces selected CI. `TERMINAL_HOST_CONTRACTS_ONLY=1` is a library-only diagnostic and cannot substitute for selected CI. See [`native/terminal-host/README.md`](../native/terminal-host/README.md).
51
+ Containment-selected modular jobs build and test the debug `process-guardian.node` addon for their exact platform, export `A1_PROCESS_GUARDIAN_PATH` to that file, and run the launch-owner integration fixtures as Node processes. The tests exercise the real addon-held Windows job or Unix process group, addon-backed identity and terminal operations, and the Node exit watcher; no fixture invokes a process-guardian executable. The Windows startup lane keeps Defender real-time protection enabled while packaged startup loads the release addon.
52
+
53
+ The `terminal-host` owner is CI-only. A change under `native/terminal-host/` or to its run scripts selects, per platform (Windows x64, Linux x64, macOS arm64), one **Native terminal host** job and three **Native terminal host tests** partition jobs. The partition jobs each build the test profile and run one third of the serialized `role_integration` suite (`run-terminal-host-probe.mjs --partition <i>/3`, partitioned by sorted test name read from the suite's source); the feature-gated `role-host` binary stands in only for the fixed role grammar inside Rust integration tests and is never packaged. The build-and-probe job runs `npm run test:terminal-host` with the role suite skipped (`TERMINAL_HOST_PARTITION=none`), building `terminal-host.node` with Rust, Node-API 9, and Zig 0.15.2 for the lane's exact target and baseline CPU model. It loads the addon for its version and non-interactive probes, verifies the shared session-writer lock export, and uploads the debug addon and frozen protocol manifest; Linux also runs bounded decoder fuzzing.
54
+
55
+ The protected aggregate requires every partition of every selected platform, keyed by `partition` in the outcome authority and job envelope. Because the role suite no longer warms the resident launch paths in the build-and-probe job, its certification step runs one unmeasured `measure-resident-tabs.mjs` pass before the measured pass whose budgets are unchanged. Fidelity, latency, chaos, and certification start roles as `process.execPath dist/foundation/resident-tabs/host-entry.js` with `A1_TERMINAL_HOST_PATH` naming the addon. Windows cleanup identifies those Node-hosted residents by command line; it never kills every `node.exe` by image name. Locally, `npm run test:terminal-host` without a partition is unchanged and runs everything in one process. Local Windows builds fail in `build.rs` unless `TERMINAL_HOST_LOCAL_BUILD=1` is set, because Zig's package fetch unpacks test data that workstation antivirus quarantines. Native lanes also publish the terminal library they linked; a local build can link it instead of running Zig (see Local native iteration in [`architecture/resident-tabs.md`](architecture/resident-tabs.md)), which is development evidence only and never replaces selected CI. `TERMINAL_HOST_CONTRACTS_ONLY=1` is a library-only diagnostic and cannot substitute for selected CI. See [`native/terminal-host/README.md`](../native/terminal-host/README.md).
52
56
 
53
57
  ## Windows Full regression shards
54
58
 
55
- Standalone and PR-attached Full regression run each Windows runtime (Node 22 and Node 24) as four hosted-runner shards derived from the one canonical `full-release` plan: `core` (command gates and the bounded-parallel ordinary remainder), `resource` (the serial resource-sensitive invocation with its unchanged explicit bound), `rendering` (the serial isolated rendering invocation), and `package` (packing, the shared exact-package installation, Defender-backed first-attempt startup before package contracts, package smoke, update timing, and the exhaustive published-predecessor invocation). Each shard checks out, installs, builds, and records its own build receipt; only the package shard packs and installs the candidate. Linux, macOS, local `npm run test:full`, and publication validation keep one sequential complete plan.
59
+ Standalone and PR-attached Full regression run each Windows runtime (Node 22 and Node 24) as five hosted-runner shards derived from the one canonical `full-release` plan: `core` (command gates and the bounded-parallel ordinary remainder), `resource` (the serial resource-sensitive invocation with its unchanged explicit bound), `rendering` (the serial isolated rendering invocation), `package` (packing, its own exact-package installation, Defender-backed first-attempt startup before package contracts, package smoke, and update timing), and `predecessor` (packing, its own exact-package installation, and the exhaustive published-predecessor invocation, which took 11 to 12 minutes of the former package shard on Windows). Each shard checks out, installs, builds, and records its own build receipt; the package and predecessor shards each pack and install their own candidate, and no installed state crosses shards. Linux, macOS, local `npm run test:full`, and the sequential publication lanes keep one complete plan with one shared installation.
56
60
 
57
61
  ```
58
- node scripts/release/run-validation-tier.mjs --full-shard <core|resource|rendering|package> --result <path>
62
+ node scripts/release/run-validation-tier.mjs --full-shard <core|resource|rendering|package|predecessor> --result <path>
59
63
  ```
60
64
 
61
65
  `partitionFullRegressionPlan` assigns every canonical command and Vitest invocation to exactly one shard and rejects unknown or duplicate work; the build is the only per-shard prerequisite. Each shard result carries its shard identity, assigned work, and the canonical-plan digest. A per-runtime collector job reconstructs one ordinary `a1-validation-outcomes-v1` result only when all four current-run shards for the exact source, runtime, run, attempt, and plan passed and each reported exactly its assigned work; the reconstructed result then binds the unchanged `windows-2025-node22` or `windows-2025-node24` lane, and the required aggregate still demands exactly four lanes. The collector's summary reports each shard's time and the elapsed span across overlapping shards. Nightly triage maps a failed shard's owners to the canonical Windows lane and names the shard job, and it rejects more than one startup evidence file for a lane.