@timurproko/a1 0.2.7-dev.1249 → 0.2.7-dev.1255

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 (524) hide show
  1. package/README.md +11 -36
  2. package/bin/cli.js +2 -17
  3. package/bin/ui.js +10 -51
  4. package/dist/app/session-shell/clipboard-diagnostics.d.ts +2 -2
  5. package/dist/app/session-shell/clipboard-image.d.ts +0 -1
  6. package/dist/app/session-shell/clipboard-image.js +1 -1
  7. package/dist/app/session-shell/dialog-coordinator.d.ts +6 -5
  8. package/dist/app/session-shell/dialog-coordinator.js +3 -4
  9. package/dist/app/session-shell/dock-chrome.d.ts +11 -6
  10. package/dist/app/session-shell/dock-chrome.js +5 -8
  11. package/dist/app/session-shell/editor-hyperlink-budget.d.ts +0 -2
  12. package/dist/app/session-shell/editor-hyperlink-budget.js +1 -1
  13. package/dist/app/session-shell/exit-notice.d.ts +3 -3
  14. package/dist/app/session-shell/exit-notice.js +10 -10
  15. package/dist/app/session-shell/extension-surface.d.ts +8 -7
  16. package/dist/app/session-shell/helper-pool.d.ts +3 -4
  17. package/dist/app/session-shell/helper-pool.js +1 -1
  18. package/dist/app/session-shell/image-preparation-client.d.ts +4 -3
  19. package/dist/app/session-shell/image-preparation.d.ts +0 -2
  20. package/dist/app/session-shell/image-preparation.js +2 -2
  21. package/dist/app/session-shell/image-source.d.ts +2 -3
  22. package/dist/app/session-shell/image-source.js +2 -2
  23. package/dist/app/session-shell/input-surface-switch.d.ts +9 -7
  24. package/dist/app/session-shell/input-surface-switch.js +1 -2
  25. package/dist/app/session-shell/login-interaction.d.ts +5 -4
  26. package/dist/app/session-shell/login-interaction.js +2 -3
  27. package/dist/app/session-shell/paste-executor.d.ts +4 -8
  28. package/dist/app/session-shell/paste-executor.js +2 -2
  29. package/dist/app/session-shell/paste-protocol.d.ts +2 -2
  30. package/dist/app/session-shell/path-chip-presentation.d.ts +0 -5
  31. package/dist/app/session-shell/path-chip-presentation.js +3 -3
  32. package/dist/app/session-shell/prompt-chips.d.ts +8 -7
  33. package/dist/app/session-shell/prompt-history-controller.d.ts +3 -3
  34. package/dist/app/session-shell/prompt-submission.d.ts +7 -6
  35. package/dist/app/session-shell/prompt-submission.js +7 -28
  36. package/dist/app/session-shell/quit-outro-effects.d.ts +3 -3
  37. package/dist/app/session-shell/quit-outro-effects.js +1 -4
  38. package/dist/app/session-shell/quit-outro.d.ts +9 -12
  39. package/dist/app/session-shell/quit-outro.js +11 -11
  40. package/dist/app/session-shell/response-copy-protocol.d.ts +3 -2
  41. package/dist/app/session-shell/response-copy-transport.d.ts +2 -14
  42. package/dist/app/session-shell/response-copy-transport.js +2 -3
  43. package/dist/app/session-shell/session-lifecycle.d.ts +3 -3
  44. package/dist/app/session-shell/session-lifecycle.js +2 -4
  45. package/dist/app/session-shell/session-shell-root.d.ts +74 -120
  46. package/dist/app/session-shell/session-shell-root.js +82 -115
  47. package/dist/app/session-shell/session-shell.d.ts +13 -16
  48. package/dist/app/session-shell/session-shell.js +149 -314
  49. package/dist/app/session-shell/session-viewport-controller.d.ts +7 -6
  50. package/dist/app/session-shell/session-viewport-controller.js +5 -8
  51. package/dist/app/session-shell/settings-bindings.d.ts +24 -2
  52. package/dist/app/session-shell/settings-bindings.js +24 -0
  53. package/dist/app/session-shell/share-operation.d.ts +3 -3
  54. package/dist/app/session-shell/share-operation.js +4 -6
  55. package/dist/app/session-shell/shell-clipboard.d.ts +59 -0
  56. package/dist/app/session-shell/shell-clipboard.js +119 -0
  57. package/dist/app/session-shell/shell-editor-factory.d.ts +8 -4
  58. package/dist/app/session-shell/shell-editor-factory.js +13 -15
  59. package/dist/app/session-shell/shell-host.d.ts +16 -17
  60. package/dist/app/session-shell/shell-host.js +20 -37
  61. package/dist/app/session-shell/shell-layout.d.ts +2 -2
  62. package/dist/app/session-shell/shell-prompt-suggestions.d.ts +26 -0
  63. package/dist/app/session-shell/shell-prompt-suggestions.js +48 -0
  64. package/dist/app/session-shell/suggestion-surface.d.ts +1 -6
  65. package/dist/app/session-shell/suggestion-surface.js +1 -1
  66. package/dist/app/session-shell/system-clipboard.d.ts +2 -14
  67. package/dist/app/session-shell/system-clipboard.js +2 -7
  68. package/dist/app/session-shell/transcript-document.d.ts +12 -10
  69. package/dist/app/session-shell/transcript-document.js +15 -41
  70. package/dist/app/session-shell/viewport-layout.d.ts +2 -2
  71. package/dist/app/session-shell/workflow-output.d.ts +5 -3
  72. package/dist/app/session-shell/workflow-output.js +13 -15
  73. package/dist/app/session-shell/workflow-router.d.ts +1 -3
  74. package/dist/app/session-shell/workflow-router.js +1 -4
  75. package/dist/cli/dispatch.d.ts +1 -11
  76. package/dist/cli/dispatch.js +5 -92
  77. package/dist/cli/package-messages.d.ts +0 -1
  78. package/dist/cli/package-messages.js +1 -1
  79. package/dist/cli/session-context.d.ts +1 -1
  80. package/dist/cli/session-context.js +5 -7
  81. package/dist/cli/uninstall.d.ts +4 -7
  82. package/dist/cli/uninstall.js +6 -35
  83. package/dist/cli/version-stats.js +4 -7
  84. package/dist/composition/owned-settings.d.ts +2 -2
  85. package/dist/composition/owned-settings.js +0 -4
  86. package/dist/composition/owned-ui.d.ts +3 -25
  87. package/dist/composition/owned-ui.js +37 -113
  88. package/dist/composition/reference-selection-options.d.ts +1 -1
  89. package/dist/composition/reference-selection-options.js +1 -1
  90. package/dist/composition/release-update-observation.d.ts +4 -4
  91. package/dist/composition/release-update-observation.js +17 -47
  92. package/dist/composition/session-backend.d.ts +0 -1
  93. package/dist/composition/session-runtime-registry.d.ts +5 -4
  94. package/dist/composition/settings-route-host.d.ts +0 -7
  95. package/dist/composition/settings-route-host.js +7 -20
  96. package/dist/composition/settings-search-input.d.ts +2 -1
  97. package/dist/composition/startup-route.d.ts +2 -3
  98. package/dist/composition/startup-route.js +2 -3
  99. package/dist/contracts/owned-ui/extension-ui.d.ts +2 -1
  100. package/dist/contracts/owned-ui/index.d.ts +2 -1
  101. package/dist/contracts/owned-ui/model.d.ts +0 -3
  102. package/dist/contracts/owned-ui/session-presenters.d.ts +3 -0
  103. package/dist/contracts/owned-ui/session-workflows.d.ts +1 -4
  104. package/dist/contracts/owned-ui/shell-components.d.ts +543 -0
  105. package/dist/contracts/presentation/component.d.ts +17 -0
  106. package/dist/contracts/presentation/component.js +8 -0
  107. package/dist/contracts/presentation/index.d.ts +6 -15
  108. package/dist/contracts/presentation/index.js +2 -0
  109. package/dist/contracts/presentation/terminal.d.ts +290 -0
  110. package/dist/contracts/presentation/theme.d.ts +46 -0
  111. package/dist/features/launch/development-launch.js +1 -2
  112. package/dist/features/launch/index.d.ts +1 -1
  113. package/dist/features/launch/intent.d.ts +1 -3
  114. package/dist/features/launch/intent.js +2 -4
  115. package/dist/features/launch/runtime-selection.d.ts +2 -2
  116. package/dist/features/launch/runtime-selection.js +2 -2
  117. package/dist/features/launch/session-context.d.ts +2 -2
  118. package/dist/features/owned-ui/project-trust-prompt.d.ts +2 -2
  119. package/dist/features/owned-ui/project-trust-prompt.js +1 -1
  120. package/dist/features/owned-ui/reference-screen-app.d.ts +0 -2
  121. package/dist/features/owned-ui/reference-screen-app.js +2 -9
  122. package/dist/features/owned-ui/release-note-state.d.ts +2 -1
  123. package/dist/features/owned-ui/settings-app.d.ts +0 -6
  124. package/dist/features/owned-ui/settings-app.js +42 -254
  125. package/dist/features/owned-ui/settings-edits.d.ts +31 -0
  126. package/dist/features/owned-ui/settings-edits.js +139 -0
  127. package/dist/features/owned-ui/settings-scroll-rail.d.ts +29 -0
  128. package/dist/features/owned-ui/settings-scroll-rail.js +79 -0
  129. package/dist/features/owned-ui/settings-search-input.d.ts +2 -1
  130. package/dist/features/prompt-history/service.d.ts +3 -2
  131. package/dist/foundation/launch-context/index.d.ts +0 -12
  132. package/dist/foundation/launch-context/index.js +0 -26
  133. package/dist/foundation/lifecycle/claim-gate.d.ts +9 -0
  134. package/dist/foundation/lifecycle/claim-gate.js +37 -0
  135. package/dist/foundation/lifecycle/index.d.ts +5 -9
  136. package/dist/foundation/lifecycle/index.js +3 -4
  137. package/dist/foundation/lifecycle/model.d.ts +2 -59
  138. package/dist/foundation/lifecycle/model.js +0 -35
  139. package/dist/foundation/lifecycle/paths.d.ts +0 -24
  140. package/dist/foundation/lifecycle/paths.js +0 -40
  141. package/dist/foundation/lifecycle/process-liveness.d.ts +8 -0
  142. package/dist/foundation/lifecycle/process-liveness.js +18 -0
  143. package/dist/foundation/lifecycle/session-claim-lock.d.ts +1 -1
  144. package/dist/foundation/lifecycle/session-claim-lock.js +2 -2
  145. package/dist/foundation/lifecycle/session-repository-context.d.ts +6 -6
  146. package/dist/foundation/lifecycle/session-repository-context.js +2 -3
  147. package/dist/foundation/release/automatic-update.d.ts +6 -17
  148. package/dist/foundation/release/automatic-update.js +23 -44
  149. package/dist/foundation/release/bootstrap.d.ts +2 -25
  150. package/dist/foundation/release/bootstrap.js +37 -348
  151. package/dist/foundation/release/dependency-certification.d.ts +1 -2
  152. package/dist/foundation/release/dependency-certification.js +1 -1
  153. package/dist/foundation/release/early-accent.d.ts +2 -2
  154. package/dist/foundation/release/early-accent.js +1 -1
  155. package/dist/foundation/release/index.d.ts +10 -17
  156. package/dist/foundation/release/index.js +7 -11
  157. package/dist/foundation/release/latest-release.d.ts +3 -19
  158. package/dist/foundation/release/latest-release.js +11 -12
  159. package/dist/foundation/release/ordinary-launch-selection.d.ts +1 -1
  160. package/dist/foundation/release/ordinary-launch-selection.js +1 -1
  161. package/dist/foundation/release/process-liveness.d.ts +2 -0
  162. package/dist/foundation/release/process-liveness.js +12 -0
  163. package/dist/foundation/release/release-gc.d.ts +13 -10
  164. package/dist/foundation/release/release-gc.js +37 -101
  165. package/dist/foundation/release/{cohort-state.d.ts → release-state.d.ts} +44 -69
  166. package/dist/foundation/release/{cohort-state.js → release-state.js} +38 -70
  167. package/dist/foundation/release/release-store.d.ts +2 -2
  168. package/dist/foundation/release/release-store.js +2 -2
  169. package/dist/foundation/release/restart-certification.js +1 -1
  170. package/dist/foundation/release/update-activation.d.ts +2 -9
  171. package/dist/foundation/release/update-activation.js +30 -45
  172. package/dist/foundation/release/update-execution-lease.d.ts +2 -3
  173. package/dist/foundation/release/update-execution-lease.js +2 -2
  174. package/dist/foundation/release/update-launch.d.ts +2 -4
  175. package/dist/foundation/release/update-launch.js +0 -7
  176. package/dist/foundation/release/update-recovery.js +1 -1
  177. package/dist/foundation/release/update-transaction.d.ts +1 -1
  178. package/dist/foundation/release/update-transaction.js +5 -2
  179. package/dist/foundation/release/update.d.ts +2 -12
  180. package/dist/foundation/release/update.js +37 -214
  181. package/dist/foundation/session-writer-lock/index.d.ts +14 -6
  182. package/dist/foundation/session-writer-lock/index.js +48 -103
  183. package/dist/foundation/startup/startup-budget.d.ts +5 -5
  184. package/dist/foundation/startup/startup-budget.js +2 -2
  185. package/dist/foundation/startup/startup-runtime.d.ts +1 -1
  186. package/dist/foundation/terminal-cleanup/exit-notice.d.ts +6 -0
  187. package/dist/foundation/terminal-cleanup/exit-notice.js +36 -0
  188. package/dist/foundation/terminal-cleanup/index.d.ts +2 -1
  189. package/dist/foundation/terminal-cleanup/index.js +2 -1
  190. package/dist/foundation/terminal-cleanup/terminal-reset.d.ts +0 -5
  191. package/dist/foundation/terminal-cleanup/terminal-reset.js +0 -5
  192. package/dist/integrations/pi/components/editor-interaction.d.ts +4 -12
  193. package/dist/integrations/pi/components/index.d.ts +15 -9
  194. package/dist/integrations/pi/components/index.js +9 -5
  195. package/dist/integrations/pi/components/lazy-selectors.d.ts +2 -10
  196. package/dist/integrations/pi/components/lazy-selectors.js +1 -1
  197. package/dist/integrations/pi/components/modal-frame.d.ts +5 -22
  198. package/dist/integrations/pi/components/modal-frame.js +3 -23
  199. package/dist/integrations/pi/components/owned-editor-ux.d.ts +4 -3
  200. package/dist/integrations/pi/components/prompt-input-port.d.ts +2 -19
  201. package/dist/integrations/pi/components/resources/builtin-themes.d.ts +2 -1
  202. package/dist/integrations/pi/components/shell-editor-autocomplete.d.ts +3 -2
  203. package/dist/integrations/pi/components/shell-editor-autocomplete.js +1 -4
  204. package/dist/integrations/pi/components/shell-footer-status.d.ts +3 -3
  205. package/dist/integrations/pi/components/shell-footer-status.js +5 -4
  206. package/dist/integrations/pi/components/shell-hotkey-sections.js +1 -1
  207. package/dist/integrations/pi/components/shell-presenters-info.d.ts +6 -8
  208. package/dist/integrations/pi/components/shell-presenters-info.js +6 -19
  209. package/dist/integrations/pi/components/shell-presenters-transcript.d.ts +1 -1
  210. package/dist/integrations/pi/components/shell-presenters-transcript.js +1 -1
  211. package/dist/integrations/pi/components/shell-session-info-reference.d.ts +2 -1
  212. package/dist/integrations/pi/components/shell-shared-facade.d.ts +33 -229
  213. package/dist/integrations/pi/components/shell-shared-facade.js +8 -1
  214. package/dist/integrations/pi/components/skills-command.d.ts +1 -9
  215. package/dist/integrations/pi/components/skills-command.js +5 -5
  216. package/dist/integrations/pi/components/submitted-prompt-adapter.d.ts +2 -5
  217. package/dist/integrations/pi/components/submitted-prompt-adapter.js +2 -16
  218. package/dist/integrations/pi/components/theme.d.ts +9 -2
  219. package/dist/integrations/pi/components/theme.js +17 -2
  220. package/dist/integrations/pi/components/thinking-selector-dialog.d.ts +2 -1
  221. package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.d.ts +2 -2
  222. package/dist/integrations/pi/components/upstream/components/session-footer.d.ts +1 -2
  223. package/dist/integrations/pi/components/upstream/components/session-footer.js +6 -8
  224. package/dist/integrations/pi/engine/adapter.d.ts +3 -6
  225. package/dist/integrations/pi/engine/adapter.js +4 -28
  226. package/dist/integrations/pi/engine/changelog.d.ts +2 -2
  227. package/dist/integrations/pi/engine/changelog.js +3 -3
  228. package/dist/integrations/pi/engine/compaction-progress.d.ts +2 -3
  229. package/dist/integrations/pi/engine/compaction-progress.js +1 -1
  230. package/dist/integrations/pi/engine/conformance.d.ts +2 -1
  231. package/dist/integrations/pi/engine/default-model-setting.d.ts +0 -2
  232. package/dist/integrations/pi/engine/default-model-setting.js +1 -2
  233. package/dist/integrations/pi/engine/diagnostics-state.d.ts +0 -1
  234. package/dist/integrations/pi/engine/diagnostics-state.js +0 -14
  235. package/dist/integrations/pi/engine/event-delivery.d.ts +2 -1
  236. package/dist/integrations/pi/engine/extension-ui-binding.d.ts +2 -1
  237. package/dist/integrations/pi/engine/host.js +2 -2
  238. package/dist/integrations/pi/engine/message-values.d.ts +0 -1
  239. package/dist/integrations/pi/engine/message-values.js +1 -1
  240. package/dist/integrations/pi/engine/pending-delivery.d.ts +0 -4
  241. package/dist/integrations/pi/engine/pending-delivery.js +3 -3
  242. package/dist/integrations/pi/engine/ports.d.ts +7 -1
  243. package/dist/integrations/pi/engine/ports.js +4 -0
  244. package/dist/integrations/pi/engine/project-trust-preflight.d.ts +2 -1
  245. package/dist/integrations/pi/engine/prompt-suggestions.d.ts +3 -3
  246. package/dist/integrations/pi/engine/prompt-suggestions.js +0 -40
  247. package/dist/integrations/pi/engine/provider-authentication.d.ts +1 -1
  248. package/dist/integrations/pi/engine/public-main-entry.d.ts +1 -5
  249. package/dist/integrations/pi/engine/public-main-entry.js +1 -1
  250. package/dist/integrations/pi/engine/repository-pr-rest.d.ts +1 -9
  251. package/dist/integrations/pi/engine/repository-pr-rest.js +2 -2
  252. package/dist/integrations/pi/engine/repository-pr.d.ts +1 -3
  253. package/dist/integrations/pi/engine/repository-pr.js +1 -1
  254. package/dist/integrations/pi/engine/resource-catalog.d.ts +2 -1
  255. package/dist/integrations/pi/engine/session-integration.d.ts +1 -1
  256. package/dist/integrations/pi/engine/session-runtime.d.ts +4 -3
  257. package/dist/integrations/pi/engine/session-runtime.js +0 -2
  258. package/dist/integrations/pi/engine/settings-bridge.d.ts +0 -2
  259. package/dist/integrations/pi/engine/settings-bridge.js +1 -1
  260. package/dist/integrations/pi/engine/settings-port.d.ts +3 -2
  261. package/dist/integrations/pi/engine/transcript-projection.d.ts +2 -1
  262. package/dist/integrations/pi/engine/workflow-contexts.d.ts +1 -1
  263. package/dist/integrations/pi/engine/workflow-runner.d.ts +1 -1
  264. package/dist/integrations/pi/engine/workflow-runner.js +1 -1
  265. package/dist/integrations/pi/engine/workflow-support.d.ts +0 -6
  266. package/dist/integrations/pi/engine/workflow-support.js +4 -18
  267. package/dist/integrations/pi/engine/workflows.d.ts +5 -5
  268. package/dist/integrations/pi/session-presenters/components.d.ts +3 -0
  269. package/dist/integrations/pi/session-presenters/components.js +75 -0
  270. package/dist/integrations/pi/session-presenters/index.d.ts +1 -0
  271. package/dist/integrations/pi/session-presenters/index.js +1 -0
  272. package/dist/integrations/pi/session-presenters/presentation.d.ts +3 -0
  273. package/dist/integrations/pi/session-presenters/presentation.js +10 -0
  274. package/dist/integrations/pi/session-presenters/presenters.d.ts +2 -1
  275. package/dist/integrations/pi/session-presenters/presenters.js +3 -1
  276. package/dist/integrations/pi/tui-runtime/adapter.d.ts +2 -2
  277. package/dist/integrations/pi/tui-runtime/adapter.js +23 -32
  278. package/dist/integrations/pi/tui-runtime/contracts.d.ts +22 -166
  279. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +5 -40
  280. package/dist/integrations/pi/tui-runtime/index.d.ts +1 -0
  281. package/dist/integrations/pi/tui-runtime/index.js +1 -0
  282. package/dist/integrations/pi/tui-runtime/input-presentation-coordinator.d.ts +4 -6
  283. package/dist/integrations/pi/tui-runtime/overlay-geometry.d.ts +2 -8
  284. package/dist/integrations/pi/tui-runtime/overlay-geometry.js +3 -9
  285. package/dist/integrations/pi/tui-runtime/runtime-factory.d.ts +3 -0
  286. package/dist/integrations/pi/tui-runtime/runtime-factory.js +11 -0
  287. package/dist/product-identity.d.ts +4 -6
  288. package/dist/product-identity.js +6 -11
  289. package/dist/product-identity.json +1 -40
  290. package/dist/runtime-payload-inventory.json +0 -3
  291. package/dist/ui/components/index.d.ts +4 -4
  292. package/dist/ui/components/index.js +2 -2
  293. package/dist/ui/components/selection-copy.d.ts +0 -1
  294. package/dist/ui/components/selection-copy.js +0 -12
  295. package/dist/ui/components/submitted-prompt.d.ts +3 -13
  296. package/dist/ui/components/submitted-prompt.js +8 -41
  297. package/dist/ui/components/theme.d.ts +4 -19
  298. package/dist/ui/components/theme.js +6 -0
  299. package/dist/ui/components/transcript-viewport.d.ts +1 -3
  300. package/dist/ui/components/transcript-viewport.js +1 -4
  301. package/dist/ui/settings/declarations.d.ts +2 -97
  302. package/dist/ui/settings/declarations.js +2 -78
  303. package/dist/ui/settings/early-accent.js +1 -1
  304. package/dist/ui/settings/index.d.ts +3 -5
  305. package/dist/ui/settings/index.js +2 -3
  306. package/dist/ui/settings/manager.d.ts +0 -4
  307. package/dist/ui/settings/manager.js +6 -62
  308. package/dist/ui/settings/resolution.d.ts +6 -18
  309. package/dist/ui/settings/resolution.js +4 -103
  310. package/dist/ui/settings/sections.d.ts +0 -4
  311. package/dist/ui/settings/sections.js +1 -1
  312. package/docs/architecture/boundaries.md +23 -40
  313. package/docs/architecture/code-documentation.md +2 -2
  314. package/docs/architecture/internal-naming.md +11 -17
  315. package/docs/architecture/project-structure.md +13 -16
  316. package/docs/architecture/resource-and-data-policy.md +16 -31
  317. package/docs/architecture/toolchain.md +14 -19
  318. package/docs/architecture/ui-reference-provenance.md +2 -2
  319. package/docs/ci-release-runbook.md +5 -5
  320. package/docs/features/launch-profiles.md +11 -17
  321. package/docs/features/prompt-history.md +10 -11
  322. package/docs/local-worktree-cleanup.md +28 -1
  323. package/docs/manual-owned-ui-checkpoint.md +0 -1
  324. package/docs/validation.md +5 -11
  325. package/docs/worktree-link-recovery.md +6 -6
  326. package/package.json +7 -20
  327. package/bin/guardian.js +0 -42
  328. package/bin/supervisor.js +0 -7
  329. package/bin/tab-launch-context.d.ts +0 -27
  330. package/bin/tab-launch-context.js +0 -94
  331. package/dist/app/session-shell/resident-tab-coordinator.d.ts +0 -38
  332. package/dist/app/session-shell/resident-tab-coordinator.js +0 -77
  333. package/dist/cli/tabs-request.d.ts +0 -7
  334. package/dist/cli/tabs.d.ts +0 -14
  335. package/dist/cli/tabs.js +0 -151
  336. package/dist/composition/resident-tab-bridge.d.ts +0 -41
  337. package/dist/composition/resident-tab-bridge.js +0 -209
  338. package/dist/composition/utility-tab.d.ts +0 -28
  339. package/dist/composition/utility-tab.js +0 -244
  340. package/dist/features/owned-ui/plus-menu-dialog.d.ts +0 -28
  341. package/dist/features/owned-ui/plus-menu-dialog.js +0 -89
  342. package/dist/features/resident-tab/auto-name.d.ts +0 -11
  343. package/dist/features/resident-tab/auto-name.js +0 -44
  344. package/dist/features/resident-tab/index.d.ts +0 -5
  345. package/dist/features/resident-tab/index.js +0 -7
  346. package/dist/features/resident-tab/shortcut-bindings-file.d.ts +0 -8
  347. package/dist/features/resident-tab/shortcut-bindings-file.js +0 -40
  348. package/dist/features/resident-tab/shortcut-declarations.d.ts +0 -13
  349. package/dist/features/resident-tab/shortcut-declarations.js +0 -26
  350. package/dist/features/resident-tab/shortcut-projection.d.ts +0 -29
  351. package/dist/features/resident-tab/shortcut-projection.js +0 -64
  352. package/dist/features/resident-tab/status-deriver.d.ts +0 -10
  353. package/dist/features/resident-tab/status-deriver.js +0 -12
  354. package/dist/foundation/launch-guardian/exit-watcher.d.ts +0 -14
  355. package/dist/foundation/launch-guardian/exit-watcher.js +0 -103
  356. package/dist/foundation/launch-guardian/index.d.ts +0 -2
  357. package/dist/foundation/launch-guardian/index.js +0 -1
  358. package/dist/foundation/launch-guardian/main.d.ts +0 -39
  359. package/dist/foundation/launch-guardian/main.js +0 -232
  360. package/dist/foundation/lifecycle/commands.d.ts +0 -40
  361. package/dist/foundation/lifecycle/launch-instance.d.ts +0 -46
  362. package/dist/foundation/lifecycle/launch-instance.js +0 -134
  363. package/dist/foundation/lifecycle/native-claim-gate.d.ts +0 -5
  364. package/dist/foundation/lifecycle/native-claim-gate.js +0 -41
  365. package/dist/foundation/lifecycle/supervisor-startup.d.ts +0 -30
  366. package/dist/foundation/lifecycle/supervisor-startup.js +0 -106
  367. package/dist/foundation/process-containment/addon-containment.d.ts +0 -20
  368. package/dist/foundation/process-containment/addon-containment.js +0 -139
  369. package/dist/foundation/process-containment/addon-process-inspector.d.ts +0 -11
  370. package/dist/foundation/process-containment/addon-process-inspector.js +0 -36
  371. package/dist/foundation/process-containment/addon.d.ts +0 -47
  372. package/dist/foundation/process-containment/addon.js +0 -57
  373. package/dist/foundation/process-containment/artifact.d.ts +0 -1
  374. package/dist/foundation/process-containment/artifact.js +0 -35
  375. package/dist/foundation/process-containment/contracts.d.ts +0 -33
  376. package/dist/foundation/process-containment/contracts.js +0 -35
  377. package/dist/foundation/process-containment/index.d.ts +0 -9
  378. package/dist/foundation/process-containment/index.js +0 -7
  379. package/dist/foundation/process-containment/linux-process-inspector.d.ts +0 -7
  380. package/dist/foundation/process-containment/linux-process-inspector.js +0 -27
  381. package/dist/foundation/process-containment/native-process-inspector.d.ts +0 -4
  382. package/dist/foundation/process-containment/native-process-inspector.js +0 -21
  383. package/dist/foundation/protocol/client.d.ts +0 -18
  384. package/dist/foundation/protocol/client.js +0 -130
  385. package/dist/foundation/protocol/index.d.ts +0 -3
  386. package/dist/foundation/protocol/index.js +0 -2
  387. package/dist/foundation/protocol/messages.d.ts +0 -90
  388. package/dist/foundation/protocol/messages.js +0 -100
  389. package/dist/foundation/release/cohort-selection.d.ts +0 -33
  390. package/dist/foundation/release/cohort-selection.js +0 -67
  391. package/dist/foundation/release/endpoint-lock.d.ts +0 -2
  392. package/dist/foundation/release/endpoint-lock.js +0 -37
  393. package/dist/foundation/release/endpoint-recovery.d.ts +0 -10
  394. package/dist/foundation/release/endpoint-recovery.js +0 -45
  395. package/dist/foundation/release/endpoints.d.ts +0 -32
  396. package/dist/foundation/release/endpoints.js +0 -152
  397. package/dist/foundation/release/prepared-update-activation.d.ts +0 -3
  398. package/dist/foundation/release/prepared-update-activation.js +0 -27
  399. package/dist/foundation/release/process-cleanup.d.ts +0 -14
  400. package/dist/foundation/release/process-cleanup.js +0 -76
  401. package/dist/foundation/release/supervisor-identity.d.ts +0 -15
  402. package/dist/foundation/release/supervisor-identity.js +0 -63
  403. package/dist/foundation/resident-tabs/boot-identity.d.ts +0 -6
  404. package/dist/foundation/resident-tabs/boot-identity.js +0 -16
  405. package/dist/foundation/resident-tabs/bounded-host-command.d.ts +0 -20
  406. package/dist/foundation/resident-tabs/bounded-host-command.js +0 -62
  407. package/dist/foundation/resident-tabs/bridge-client.d.ts +0 -73
  408. package/dist/foundation/resident-tabs/bridge-client.js +0 -327
  409. package/dist/foundation/resident-tabs/continuity.d.ts +0 -12
  410. package/dist/foundation/resident-tabs/continuity.js +0 -63
  411. package/dist/foundation/resident-tabs/doctor.d.ts +0 -28
  412. package/dist/foundation/resident-tabs/doctor.js +0 -166
  413. package/dist/foundation/resident-tabs/draft-checkpoint.d.ts +0 -27
  414. package/dist/foundation/resident-tabs/draft-checkpoint.js +0 -145
  415. package/dist/foundation/resident-tabs/ensure-terminal-host.d.ts +0 -36
  416. package/dist/foundation/resident-tabs/ensure-terminal-host.js +0 -151
  417. package/dist/foundation/resident-tabs/fault-injection.d.ts +0 -8
  418. package/dist/foundation/resident-tabs/fault-injection.js +0 -26
  419. package/dist/foundation/resident-tabs/fixture-control.d.ts +0 -7
  420. package/dist/foundation/resident-tabs/fixture-control.js +0 -35
  421. package/dist/foundation/resident-tabs/host-entry.js +0 -17
  422. package/dist/foundation/resident-tabs/index.d.ts +0 -15
  423. package/dist/foundation/resident-tabs/index.js +0 -10
  424. package/dist/foundation/resident-tabs/maintenance.d.ts +0 -39
  425. package/dist/foundation/resident-tabs/maintenance.js +0 -75
  426. package/dist/foundation/resident-tabs/progress-observation-schema.d.ts +0 -34
  427. package/dist/foundation/resident-tabs/progress-observation-schema.js +0 -1
  428. package/dist/foundation/resident-tabs/progress-observation.d.ts +0 -4
  429. package/dist/foundation/resident-tabs/progress-observation.js +0 -150
  430. package/dist/foundation/resident-tabs/prompt-journal.d.ts +0 -63
  431. package/dist/foundation/resident-tabs/prompt-journal.js +0 -213
  432. package/dist/foundation/resident-tabs/protocol.d.ts +0 -12
  433. package/dist/foundation/resident-tabs/protocol.js +0 -195
  434. package/dist/foundation/resident-tabs/recovery.d.ts +0 -26
  435. package/dist/foundation/resident-tabs/recovery.js +0 -205
  436. package/dist/foundation/resident-tabs/release-holds.d.ts +0 -28
  437. package/dist/foundation/resident-tabs/release-holds.js +0 -78
  438. package/dist/foundation/resident-tabs/routes.d.ts +0 -5
  439. package/dist/foundation/resident-tabs/routes.js +0 -5
  440. package/dist/foundation/resident-tabs/routing.d.ts +0 -11
  441. package/dist/foundation/resident-tabs/routing.js +0 -10
  442. package/dist/foundation/resident-tabs/runtime-control.d.ts +0 -38
  443. package/dist/foundation/resident-tabs/runtime-control.js +0 -74
  444. package/dist/foundation/resident-tabs/session-sync.d.ts +0 -14
  445. package/dist/foundation/resident-tabs/session-sync.js +0 -37
  446. package/dist/foundation/resident-tabs/settings.d.ts +0 -16
  447. package/dist/foundation/resident-tabs/settings.js +0 -60
  448. package/dist/foundation/resident-tabs/terminal-host-artifact.d.ts +0 -14
  449. package/dist/foundation/resident-tabs/terminal-host-artifact.js +0 -78
  450. package/dist/foundation/resident-tabs/zip-archive.d.ts +0 -9
  451. package/dist/foundation/resident-tabs/zip-archive.js +0 -56
  452. package/dist/foundation/storage/control-store.d.ts +0 -20
  453. package/dist/foundation/storage/control-store.js +0 -331
  454. package/dist/foundation/storage/index.d.ts +0 -1
  455. package/dist/foundation/storage/index.js +0 -1
  456. package/dist/foundation/supervision/index.d.ts +0 -3
  457. package/dist/foundation/supervision/index.js +0 -3
  458. package/dist/foundation/supervision/main.d.ts +0 -1
  459. package/dist/foundation/supervision/main.js +0 -86
  460. package/dist/foundation/supervision/paths.d.ts +0 -1
  461. package/dist/foundation/supervision/paths.js +0 -1
  462. package/dist/foundation/supervision/server.d.ts +0 -46
  463. package/dist/foundation/supervision/server.js +0 -611
  464. package/dist/integrations/pi/components/components.d.ts +0 -4
  465. package/dist/integrations/pi/components/components.js +0 -48
  466. package/dist/integrations/pi/components/conformance.d.ts +0 -18
  467. package/dist/integrations/pi/components/conformance.js +0 -70
  468. package/dist/integrations/pi/components/shell-components.d.ts +0 -7
  469. package/dist/integrations/pi/components/shell-components.js +0 -7
  470. package/dist/integrations/pi/components/upstream/theme/theme-controller.d.ts +0 -33
  471. package/dist/integrations/pi/components/upstream/theme/theme-controller.js +0 -114
  472. package/dist/integrations/pi/engine/offline-settings-port.d.ts +0 -23
  473. package/dist/integrations/pi/engine/offline-settings-port.js +0 -54
  474. package/dist/native/darwin-arm64/manifest.json +0 -23
  475. package/dist/native/darwin-arm64/process-guardian.node +0 -0
  476. package/dist/native/darwin-arm64/terminal-host-THIRD-PARTY-NOTICES.txt +0 -10500
  477. package/dist/native/darwin-arm64/terminal-host.manifest.json +0 -46
  478. package/dist/native/darwin-arm64/terminal-host.node +0 -0
  479. package/dist/native/darwin-x64/manifest.json +0 -23
  480. package/dist/native/darwin-x64/process-guardian.node +0 -0
  481. package/dist/native/darwin-x64/terminal-host-THIRD-PARTY-NOTICES.txt +0 -10737
  482. package/dist/native/darwin-x64/terminal-host.manifest.json +0 -46
  483. package/dist/native/darwin-x64/terminal-host.node +0 -0
  484. package/dist/native/linux-x64/manifest.json +0 -23
  485. package/dist/native/linux-x64/process-guardian.node +0 -0
  486. package/dist/native/linux-x64/terminal-host-THIRD-PARTY-NOTICES.txt +0 -11226
  487. package/dist/native/linux-x64/terminal-host.manifest.json +0 -46
  488. package/dist/native/linux-x64/terminal-host.node +0 -0
  489. package/dist/native/win32-x64/manifest.json +0 -23
  490. package/dist/native/win32-x64/process-guardian.node +0 -0
  491. package/dist/native/win32-x64/terminal-host-THIRD-PARTY-NOTICES.txt +0 -10757
  492. package/dist/native/win32-x64/terminal-host.manifest.json +0 -46
  493. package/dist/native/win32-x64/terminal-host.node +0 -0
  494. package/dist/ui/settings/migrations.d.ts +0 -19
  495. package/dist/ui/settings/migrations.js +0 -209
  496. package/dist/ui/settings/progress-palette.generated.d.ts +0 -139
  497. package/dist/ui/settings/progress-palette.generated.js +0 -139
  498. package/docs/architecture/evidence/resident-tabs/linux-x64.md +0 -37
  499. package/docs/architecture/evidence/resident-tabs/macos-arm64.md +0 -36
  500. package/docs/architecture/evidence/resident-tabs/macos-x64.md +0 -35
  501. package/docs/architecture/evidence/resident-tabs/windows-x64.md +0 -36
  502. package/docs/architecture/evidence/terminal-host-spike/manual-one-pane-terminal-host.md +0 -70
  503. package/docs/architecture/evidence/terminal-host-spike/manual-two-by-two-basic-terminal-host.md +0 -32
  504. package/docs/architecture/evidence/terminal-host-spike/milestone-baseline.json +0 -115
  505. package/docs/architecture/evidence/terminal-host-spike/owned-ui-foundation-prerequisite.json +0 -48
  506. package/docs/architecture/evidence/terminal-host-spike/terminal-host-provenance.json +0 -125
  507. package/docs/architecture/evidence/terminal-host-spike/terminal-host-task-5-1.json +0 -56
  508. package/docs/architecture/evidence/terminal-host-spike/terminal-host-task-5-3.json +0 -174
  509. package/docs/architecture/evidence/terminal-host-spike/terminal-spike-acceptance-record.json +0 -18
  510. package/docs/architecture/native-fixture-isolation.md +0 -23
  511. package/docs/architecture/process-guardian-provenance.md +0 -36
  512. package/docs/architecture/resident-tabs-requirements.md +0 -18
  513. package/docs/architecture/resident-tabs.md +0 -668
  514. package/docs/architecture/terminal-host-proof-gate.md +0 -34
  515. package/docs/architecture/terminal-host-provenance.md +0 -81
  516. package/docs/architecture/terminal-host-spike-evidence.md +0 -39
  517. package/docs/launch-recovery.md +0 -23
  518. package/docs/manual-launch-instance-acceptance.md +0 -43
  519. package/docs/manual-resident-tabs-acceptance.md +0 -51
  520. /package/dist/{cli/tabs-request.js → contracts/owned-ui/shell-components.js} +0 -0
  521. /package/dist/{foundation/release → contracts/presentation}/progress-palette.generated.d.ts +0 -0
  522. /package/dist/{foundation/release → contracts/presentation}/progress-palette.generated.js +0 -0
  523. /package/dist/{foundation/lifecycle/commands.js → contracts/presentation/terminal.js} +0 -0
  524. /package/dist/{foundation/resident-tabs/host-entry.d.ts → contracts/presentation/theme.js} +0 -0
@@ -1,668 +0,0 @@
1
- # Resident tabs — architecture and roadmap
2
-
3
- Status: implemented. Milestones 1–6 deliver resident tabs as the bare-`a1` route on Windows x64, Linux x64, macOS arm64, and macOS x64, with `a1 solo` as the direct single-agent route and `a1 tabs` for maintenance. Every release carries a verified terminal host for each of those platforms. Certification is recorded per platform from exact-package evidence (see [Release state and known gaps](#release-state-and-known-gaps)); it is evidence, not a runtime gate. Discovery or verification failure falls back to the direct path with one notice. This document is the living design for multi-agent tabs; each change cites the decisions it implements and updates this file when a decision changes. The one target requirement no milestone has delivered yet is kept in [`resident-tabs-requirements.md`](resident-tabs-requirements.md).
4
-
5
- ## Context
6
-
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
-
9
- The user set three product constraints:
10
-
11
- 1. Agents survive terminal closure, and relaunching reattaches.
12
- 2. Extensions work as natively as possible, including extensions that render their own in-process UI.
13
- 3. The same tab system must next host arbitrary interactive CLIs.
14
-
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
-
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
-
19
- Existing assets:
20
-
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 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
- - **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
-
25
- References:
26
-
27
- - **v2 prototype** (UX, and the child status bridge):
28
- - A detached daemon owns a PTY per agent and a headless emulator.
29
- - An in-child bridge reports sequenced work status, session file, and name, and enforces session leases.
30
- - Quit detaches.
31
- - Lessons: restore without a gate duplicated agents; painting background frames stalled the foreground; forwarding `Ctrl+C` to the child made `Ctrl+C+C` kill the agent; there was no needs-input state; there was no close confirmation; `ConPTY` dies with the process that created it.
32
- - **herdr** (resident architecture):
33
- - A single Rust binary with client and server roles.
34
- - The socket bind is the lock, backed by an ownership marker and an owner-only DACL.
35
- - `DETACHED_PROCESS` plus a WMI launch to escape SSH kill-on-close jobs (#2008). A macOS per-user bootstrap port (#4100).
36
- - Server-side libghostty-vt models. Reattach sends a full surface, then patches.
37
- - A one-slot render lane for slow clients.
38
- - A stable endpoint generation, so updates keep the server running.
39
- - A persisted layout, with agents resumed after a crash.
40
- - Its documented weakness is that one server crash kills every pane (#453, #276).
41
-
42
- ### Reference audit and evidence boundary (2026-10-05)
43
-
44
- The read-only review used A1 `develop` at `b42b3c9e`, PR #586 at `8fdb7361`, `E:/Backups/pi/v2`, and herdr at `e5443f07`. No prototype execution or native certification was performed. PR #586 contains planning artifacts only; its skipped draft checks are not product evidence.
45
-
46
- - v2's `agent/multiagent/daemon.ts` owns all PTYs, performs synchronous spawn/tree-kill work, and has a daemon-wide stall watchdog. `daemon-logic.ts` truncates stalled input to the newest 8192 code units. `protocol.ts`/`child-bridge.ts` also carry rendered frames, creating a second screen source. Retain UX and regression scenarios, not these mechanisms or logic hot-swapping.
47
- - v2's `startup-benchmark.md` records warm creation p50 13 ms / p95 41 ms and an outer restart of 6.255 s. Those measure different paths and are not performance promises for A1.
48
- - Current herdr's `src/platform/windows.rs`, `src/server/client_transport.rs`, and `src/server/render_stream.rs` are references for detachment, bounded transport, and surface revisions. `src/pty/actor.rs` uses threads, not one process per PTY. Unix-only descriptor handoff and Unix-gated detach/multi-client tests do not certify Windows survival. `src/persist/writer.rs` synchronizes recovery copies, while ordinary session saving in `src/persist/io.rs` still uses unsynchronized write-and-rename.
49
- - A1's existing prompt history is asynchronous and can skip writes; it is not the durable-before-dispatch journal. The native host is a fixed 2×2 proof, not resident infrastructure. Its historical acceptance record still has pending technical/physical verdicts and must not be relabelled as accepted.
50
- - Before implementation, reconcile current `develop`, including #588's terminal-query/CI ownership and #667's surviving-owner terminal restoration. Update architecture/proof documentation during approved implementation to distinguish this single-pane resident certification from future split certification, preserving historical evidence unchanged.
51
- - herdr's Unix paths (socket bind as lock, Unix detach, the macOS per-user bootstrap port fix in #4100) are references for the macOS and Linux roles, not evidence for them.
52
-
53
- ### Milestone 2 pinned launch contract (2026-10-11)
54
-
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
- - 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
- - Tab children consume and erase `A1_TAB_ID`, `A1_TAB_TOKEN`, and `A1_TAB_ENDPOINT` before loading A1, Pi, extensions, or tools.
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
- - 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.
61
- - Until milestone 4 adds quarantine and history recovery, an unreadable registry fails closed and is left untouched.
62
- - Milestone 2 multiplexes authenticated attach, holder, bridge, and probe roles over the owner endpoint and sends a replaceable full current surface through each client's single-slot render lane. Dedicated holder endpoints and patch deltas remain multi-tab optimizations rather than claims of this one-tab milestone.
63
- - The native server, holder, attach, bridge reconnect, child containment, and role integration fixture are implemented. Milestone 2 records complete Windows x64 terminal-close and OpenSSH-loss evidence; macOS arm64 and Linux x64 physical terminal/SSH evidence remains uncertified and is an explicit milestone-6 gate rather than an inferred milestone-2 result.
64
-
65
- ### Milestone 3 failure-isolation contract (2026-10-14)
66
-
67
- - The native `core/server.rs`, `core/holder.rs`, and `core/attach/` state machines are deterministic and I/O-free; the production holder uses the holder core for bounded input admission and ordered writer effects, the production server uses the server core for heartbeat supervision, and the attach client steps the attach core for every interaction mode (popup, rename, drag), shortcut, deferred input, and server answer while `roles/attach.rs` keeps the connection, outer terminal, encoders, and presentation. Blocking identity/termination work runs through the bounded worker shell with deadlines and stale-incarnation filtering.
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. 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
- - 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
- - 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
- - 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
- - 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
- - 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
-
76
- ### Milestone 4 crash-recovery contract (2026-10-12)
77
-
78
- - The server persists restart intent and the three-in-ten-minute budget before instructing the surviving holder to respawn its child. The holder verifies prior-writer release, preserves its PTY ownership boundary, rotates incarnation credentials, and resumes the recorded session with 1/5/30-second backoff. User-requested restart is explicit and does not consume the crash budget.
79
- - Child-event-loop heartbeat silence is counted only while server, bridge, holder, and child supervision remains continuously healthy. Transport loss resets the observation window. Progress stalls are advisory and never trigger automatic termination. Milestone 5 retires the `tabsUnresponsiveRestartSeconds` and `tabsStallNoticeSeconds` controls and keeps their 120/300-second behavior internal.
80
- - Tab-mode submission is durably journaled before dispatch, correlated with the session entry, synchronized after settlement, and retired idempotently. Recovery reconciles identity and exact text without collapsing duplicate prompts or automatically replaying work. Periodic draft checkpoints expose a degraded state when the one-second healthy-storage envelope cannot be met.
81
- - Registry replacement retries transient failures, quarantines unreadable state, recovers only from the newest valid bounded history, and fails closed when no valid generation remains. A changed boot or OS-session identity archives the prior tab set instead of restoring it; journals younger than seven days can be adopted when their sessions are explicitly reopened.
82
- - Last-screen and fixture-only stderr files are private bounded recovery artifacts. Structured server, holder, attach, and child crash records use allowlisted fields, rotate without startup truncation, and remain physically separate from recovery content.
83
- - Milestone 4 added the minimal lifecycle states, failed-tab banner, and retry/fresh/close handling needed to expose recovery. Milestone 5 adds the rich strip, shortcuts, mouse interactions, naming, prewarm, no-idle-suspension contract, and two-client UX. `a1 tabs doctor`, packaging, physical terminal/SSH-loss acceptance, and complete certification remain milestone 6.
84
-
85
- ### Relation to the in-process prep refactors (2026-10-09/10)
86
-
87
- PRs #729–#736 prepared the owned UI for several sessions in one process. This design runs one A1 process per tab, so it does not depend on the shared helper pools (#733), the engine host's multi-session factory (#734), the terminal-host/presenter split (#735), or the per-agent claim dimension (#736). It does use the lifecycle ordering from #732 (bounded quit, clean disposal), which tab detach and restart rely on, and the unique required session ids from #734. Each tab process must get its own launch runtime id so worktree claims (#736) never collide.
88
-
89
- ## Goals / Non-Goals
90
-
91
- **Goals:**
92
-
93
- - Several independent A1 agents in bare `a1` tabs on Windows x64, Linux x64, and macOS arm64, each running the full A1 UI with its extensions. The user can create, switch, rename, reorder, and close them, or run `a1 solo` for direct single-agent mode.
94
- - Tabs keep running through terminal closure, SSH drops, attach-client crashes, and resident-server crashes. Relaunching from any terminal reattaches with exact screen state. A crashed tab restarts into its session without replaying an interrupted prompt.
95
- - One tab's failure never affects another tab, the server, or the client. Package activation retains every immutable release still used by resident processes.
96
- - Status comes from structured engine events, not screen text.
97
- - Terminal bytes, input, and rendering stay native. Node never relays them.
98
- - Security and recovery remain verifiable after server loss: derived credentials re-admit holders, an OS writer lease prevents split-brain registry writers, and every command or mutation is epoch-fenced.
99
- - The tab model is kind-neutral, so arbitrary CLI tabs are a follow-up without redesign.
100
- - The work is testable hermetically, including kill, crash, stale-server, controller-transfer, and reattach interleavings.
101
-
102
- **Non-Goals:**
103
-
104
- - Restoring tabs after reboot or logout. Resident processes end with the OS session; the sessions stay resumable through the normal session picker (Decision 9).
105
- - Platforms beyond the three above, automatic resident server handoff/recycling, arbitrary CLI tabs, split panes/layouts, and v2-style per-folder workspaces or sidebar.
106
- - Network or remote attach, and multi-user sharing.
107
- - Adding resident behavior or presentation to `a1 pi`. Its only shared change is the session-writer guard described in Decision 8; it must not initialize a resident host or bridge.
108
- - Automatic repository/worktree allocation, conflicting-edit prevention, task delegation, inter-agent messaging/results, shared context, or cost/approval orchestration. Independent sessions may still write the same working directory; tabs are not repository isolation.
109
- - Automatically replaying an interrupted turn.
110
- - Inline terminal image protocols (kitty graphics, sixel). Children see a host terminal identity without image support, Pi's text fallback applies, and the first version does not claim image-protocol extension parity.
111
-
112
- ## Decisions
113
-
114
- ### 1. Tabs are terminal sessions running the complete A1 UI
115
-
116
- Each Agent tab runs `node bin/ui.js --tab` under a pseudoterminal. That child is today's owned UI with its in-process Pi runtime, extensions, and custom extension components, so there is no engine split, no remote adapter, and no extension rendering bridge. Settings and Changelog remain terminal sessions but use the slim utility composition recorded in Decision 11 because they host no Agent. Agent tab mode adds only resident integration concerns:
117
-
118
- - it connects the tab bridge (Decision 7);
119
- - quit routes request a client detach instead of exiting (Decision 11);
120
- - the first-run intro and quit outro are suppressed, because the attach client owns the outer surface.
121
-
122
- Alternatives considered:
123
-
124
- - **Structured workers with a remote engine adapter.** Rejected by user direction:
125
- - an extension's in-process custom UI can never cross a process boundary;
126
- - it doubles the architecture when CLI tabs arrive;
127
- - it requires splitting the entire shell/engine contract.
128
- - **Node host with `node-pty` and `@xterm/headless`, as in v2.** Rejected:
129
- - it puts PTY and byte relay into Node, which governance forbids;
130
- - it duplicates a terminal core that already exists natively;
131
- - v2 recorded stalls from exactly this design.
132
-
133
- ### 2. One native addon, three Node-hosted roles, plus the A1 child
134
-
135
- ```text
136
- terminal (detached, per OS user × profile)
137
- ┌──────────────────────────┐ ┌─────────────────────────────────────┐
138
- │ a1 → guardian → │ │ node host-entry server │
139
- │ node host-entry attach │◀───▶│ registry (atomic JSON, fsync) │
140
- │ tab strip + active tab │ pipe│ topology, client fan-out, restore │
141
- │ outer terminal I/O │ └───┬──────────────┬──────────────┬───┘
142
- └──────────────────────────┘ │pipe │pipe │pipe
143
- (launch instance, dies with â–¼ â–¼ â–¼
144
- the terminal: harmless) holder: tab 1 holder: tab 2 holder: tab N
145
- PTY/ConPTY PTY/ConPTY PTY/ConPTY
146
- ghostty model ghostty model ghostty model
147
- │ │
148
- node ui.js --tab node ui.js --tab …
149
- (full A1 UI + Pi + extensions)
150
- └── tab bridge ──▶ server (status, session, name, detach)
151
- ```
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
-
155
- - **attach** is the foreground `a1` surface. It runs inside the bare-A1 launch instance and dies with the terminal, which is harmless.
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.
157
- - **server** is resident. It owns:
158
- - the registry and topology revisions;
159
- - the list of holders and client sessions;
160
- - fan-out of the active tab's surface to each client;
161
- - tab-bridge termination, and restore and restart policy.
162
- It runs no A1 or Pi code and holds no PTY. That keeps it small and hardens it against the herdr single-point-of-failure problem. Generation 1 forwards replaceable full retained surfaces; patch deltas remain a later optimization.
163
- - **holder** is resident, one per tab. It owns:
164
- - exactly one PTY (ConPTY on Windows) and its child process tree;
165
- - one libghostty-vt retained model with bounded scrollback;
166
- - input encoding and the per-tab damage state.
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.
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.
169
-
170
- ### Windows completion-driven transport and wake diagnosis
171
-
172
- Windows server and client endpoints use overlapped named-pipe I/O. Each operation owns a stable `OVERLAPPED` and non-inherited completion event; read and write workers share handle custody but never serialize a pending read against a write. A worker waits for completion, connection shutdown, or an absolute admission deadline, not `PeekNamedPipe` plus a short sleep. The four-second first-envelope deadline covers fragmented headers and bodies. Cancellation targets one operation and drains its kernel completion before releasing its buffer/event; split-worker guards wake the other direction when the connection owner exits. Owner ACLs, first-instance protection, peer checks, frame limits, control ordering, and latest-surface bounds are unchanged. Unix socket behavior is unchanged.
173
-
174
- Transport correctness does not rely on `timeBeginPeriod`. The existing per-role timer request remains for non-I/O timers, including ConPTY render-pass settling and animation/maintenance deadlines; removing it requires separate scheduling evidence. A detected attach gap still requests one complete cached repaint, never a process restart or prompt replay.
175
-
176
- The existing opt-in progress route (`A1_RESIDENT_PROGRESS_OBSERVATION_DIR` plus `A1_RESIDENT_PROGRESS_RUN_ID`) adds `pipe-frame-read`, `pipe-write-complete`, `outer-write-complete`, `wake-gap`, and `wake-repaint-requested` stages. Connection-level records may omit tab identity. `durationUs` is local monotonic completion/assembly time; `idleWaitUs` separately describes waiting for the next frame and is not delivery latency. Native records include `monotonicUs`, `clockEpoch`, and optional `clockDiscontinuity`. Consumers exclude negative, epoch-crossing, and clock-discontinuity-crossing derived samples, while retaining valid local completion durations. The queue remains bounded and asynchronous, with loss accounting and no terminal content or credentials. Outer-write completion is not proof of physical pixels.
177
-
178
- For recurring post-wake stalls, compare an existing resident client, a new client on the same resident host, and direct `a1 solo` in the same outer terminal. Use matched holder/attach revisions and local I/O durations to distinguish child progress, transport delay, and outer writes. If solo and other terminal applications also stall, do not assign the failure to resident pipes without further evidence. Simulated-gap fixtures do not certify physical sleep/wake recovery; do not automate workstation suspend. Updates retain old resident cohorts. A new client aggregates verified source routes while new execution uses its selected immutable release. Existing roles keep their original code and lifecycle; installing an update never recycles running tabs.
179
-
180
- ### 3. Host scope, endpoints, writer authority, and credentials
181
-
182
- - There is **one server per OS user, canonical A1 profile root and immutable release identity**. Different releases coexist without migration; the supervisor remains forbidden from retaining runtime processes. A domain-separated hash of canonical profile and fixed originating release/addon identity selects the flat `<data>/tabs/<scope>` directory, credential files, registry, epochs and `server-start-<scope>.lock`.
183
- - Windows endpoint: `\\.\pipe\a1-tabs-<scope[0:20]>`.
184
- - macOS and Linux endpoint: a Unix domain socket `a1-tabs-<same scope>.sock` inside an owner-only (`0700`) per-user runtime directory (`$XDG_RUNTIME_DIR` when set, otherwise a private directory under the profile's runtime dir), kept short enough for the platform's socket path limit.
185
- - Generation 1 admits holders over the authenticated owner endpoint. Dedicated `…-h-<tabId>` endpoints remain an optional later optimization.
186
- - Hermetic override: `A1_TABS_ENDPOINT`, together with the `A1_*_DIR` overrides.
187
- - **The endpoint is discovery; the writer lease is authority.** A starting server probes with a real handshake. If a live server answers, it exits as `already-running` and joins the winner. A failed probe does not authorize blind endpoint reclaim: the starter reads the owner marker, verifies pid and native start identity, requests termination, and waits for the owner-only OS-exclusive registry-writer lease. If the owner cannot be verified or the lease cannot be acquired, startup fails closed instead of creating split brain.
188
- - The ownership marker `{serverId, pid, startIdentity, bootNonce, generation, build}` is written atomically under the writer lease. Every registry mutation rechecks the held lease and current epoch before commit. A server removes an endpoint or marker only while both still identify it.
189
- - **Access and authentication:**
190
- - The endpoint, token files, secret file, registry, and writer lease are owner-only: Windows ACLs on the named pipe and files, `0700` directories and `0600` files on macOS and Linux, plus a peer-credential check (`SO_PEERCRED` / `getpeereid`) on every Unix socket connection.
191
- - Clients present a random 32-byte client token from an owner-only file.
192
- - One random owner-only profile/release secret persists outside its registry. Per-tab/per-incarnation holder and bridge credentials are derived from that secret and the non-secret tab/incarnation identity, so a replacement server can authenticate surviving processes without storing tab credentials.
193
- - Holders and bridges also present pid and native start identity. Nothing unverifiable is adopted, signalled, terminated, or allowed to mutate state.
194
-
195
- ### 4. Detached start
196
-
197
- - **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.
198
- - **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.
199
- - **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.
200
- - **Windows:**
201
- - 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.
202
- - 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).
203
- - Milestone 1 must pin the concrete authorized recovery transport and handle-inheritance rules and prove them with containment fixtures before milestone 2 relies on survival.
204
- - **Every platform** records the observed detachment mode and any failed verification reason.
205
- - **macOS and Linux:**
206
- - 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.
207
- - 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.
208
- - 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.
209
- - Milestone 1 proves these with fixtures on each platform before any milestone relies on survival.
210
- - **Holders** are spawned by the server through the same platform detach routine.
211
- - **Unverified survival** uses the direct single-agent fallback with one notice, not a purported resident tab that remains contained. Reject or clean up only the exact verified incomplete resident start; never terminate an unverifiable process. Existing resident records and session leases remain intact.
212
-
213
- ### 5. Protocols
214
-
215
- The channels carry bounded generation-1 frames: a u32 little-endian length followed by one MessagePack named-map envelope with stable `type`, optional `requestId`, and typed `body` fields. There is a 2 MiB cap per frame, 1 MiB per input-bearing message, bounded nesting and collection lengths, and a 4 s handshake timeout. Length and structural bounds are checked before corresponding allocation or recursion.
216
-
217
- - **Handshake:** `hello{role, generation, build, features[], token}` → `welcome{…, limits}`.
218
- - `generation` is the stable compatibility number. Within a generation, named maps change only additively: unknown fields are ignored, new fields are optional with defaults, enums carry an explicit `unknown` fallback, and a missing method disables only that action.
219
- - A mismatched generation yields a typed `incompatible-generation` outcome.
220
- - A pinned canonical encoder emits exact-byte fixtures. A manifest sorted by fixture name records each SHA-256 and one digest over ordered names and bytes; later additions regenerate it rather than merging digest text by hand.
221
- - **Client ↔ server:**
222
- - **Topology.** `tabs.subscribe` returns a snapshot with a revision, followed by changes. Mutations are `tab.create{kind:"a1", cwd, env, session?}`, `tab.rename`, `tab.reorder{expectedRevision}`, `tab.close`, `tab.retry`, `tab.fresh`, and `tabs.stopAll`. A stale revision is rejected atomically.
223
- - **Surface.** `tab.view{tabId, size}` returns a full retained surface (cells, attributes, hyperlinks, cursor, modes), then patches against its revision. `tab.unview` ends it. Only the tab a client is viewing streams surfaces to that client (v2 lesson).
224
- - **Controller.** `tab.claimInput{tabId, clientId, expectedControllerRevision}` transfers one controller lease through a fenced input-admission boundary. The sans-IO state moves through active, frozen, draining, awaiting-boundary, then active for the new generation. After every accepted old item enters the shared PTY writer, the holder writes a versioned DCS marker carrying the target generation and a 128-bit nonce through that same queue. When a pseudoterminal reserves raw DCS input syntax, a fixed printable envelope carries the exact marker bytes and the child parser reconstructs them only after earlier queue bytes arrive. Tab-mode input handling consumes it and acknowledges `{generation, nonce}` over the authenticated bridge only after earlier bytes have been admitted by the child parser; a sideband acknowledgement without that PTY ordering is insufficient. Milestone 1 freezes the marker and proves the state machine plus a native fixture; milestone 5 wires the production tab-mode parser and bridge. Requests bind an immutable origin controller generation at command admission, not execution; stale or ambiguous requests are rejected, never relabelled. If the bridge or proven boundary is unavailable, terminal input remains usable with client-scoped child commands disabled; attach-local double-`Ctrl+C` remains available.
225
- - **Input.** `tab.input{tabId, controllerRevision, bytes|keyEvent|mouseEvent|paste|focus}` is accepted only from the current controller and encoded by the holder with libghostty-vt against the child's current modes.
226
- - **Resize.** `tab.resize{tabId, controllerRevision, cols, rows}`. The PTY size follows the current input controller; read-only viewers cannot resize the child.
227
- - **Attention.** Status and attention events arrive for all tabs, even unviewed ones.
228
- - **Server ↔ holder:** `holder.ready{tabId, token, pid, startIdentity, childPid, childStartIdentity, release}`, then surface patches, `holder.exit{code, signal}`, and heartbeats. The server forwards input and resizes.
229
- - **Tab bridge (A1 child ↔ server):**
230
- - The child receives `A1_TAB_ID`, `A1_TAB_TOKEN`, and the endpoint through the fixed startup contract. The child consumes credentials before loading extensions or spawning tools and removes them from inherited environments; holders cannot sanitize grandchildren by themselves. Reconnect credentials remain in child-private memory and are never restored to `process.env`.
231
- - The child sends `bridge.status{seq, state}` derived from engine events: `agent_start`, `turn_start`, tool execution, settled idle, `stopReason`, pending extension UI or trust requests, and compaction.
232
- - It also sends `bridge.session{file, name}`, `bridge.request{action, clientId, controllerRevision}`, and `bridge.prompt{interrupted?}` metadata. Client-scoped requests are honored only when their controller generation is current. Prompt text and terminal input are never sent through the bridge.
233
- - The server sends `bridge.visibility{visible}`, so a hidden A1 throttles animation, and `bridge.rename`, so a strip rename sets the Pi session name.
234
- - The bridge is advisory. A missing or broken bridge degrades that tab to process-level status and never breaks the tab.
235
- - **Backpressure:**
236
- - Each client connection has a reliable, bounded control lane and a **single-slot render lane**. A newer full current surface replaces an unsent one, so a client that drains never depends on a dropped intermediate revision. Structured surfaces are immutable `Arc` values while routed; encoding happens outside the routing mutex, decoded structured state is capped at 8 MiB, and a superseded holder socket cannot publish into a newer incarnation.
237
- - The server never blocks a holder, another client, or registry writes on a slow client. Attach and holder hot loops yield after bounded frame, byte, and two-millisecond work quanta; completed outer writes, rather than offers, advance the presentation baseline.
238
- - Holders parse their PTY continuously, whether or not the tab is viewed, so a PTY is never throttled and a child never blocks on output.
239
-
240
- ### 6. Rendering, input, and outer-terminal fidelity
241
-
242
- - **Composition.** The attach client composes row 0 (the strip) and rows `1..N` (the viewed tab's surface). The tab PTY is sized `cols × (rows − 1)`. Ghostty remains the sole child-terminal parser. Its stream parser reports synchronized-output begin/end/reset boundaries to the holder, which keeps mutable parser state separate from an immutable committed surface and never publishes an open transaction. A non-renewable 250 ms deadline publishes one typed recovery commit for a malformed transaction. On Windows, ConPTY re-renders the child's cells on its own schedule and relays the DEC 2026 markers when it parses them, so a render pass can land between a frame's markers and the rest of its cells can arrive after the end marker; the holder therefore treats a relayed end marker as "frame parsed", keeps the state private until the following render pass has arrived and settled for 2 ms, with a non-renewable 16 ms fallback for the oldest unpublished pass. New end markers and continuous settling fragments cannot postpone that bound; consuming a complete or recovery publication clears it for the next update. The fallback never authorizes ordinary publication inside an open transaction. A begin marker that arrives while that pass is still pending does not publish the torn state. Unix PTYs relay bytes verbatim, so their markers still delimit frames exactly. The outer composer owns the only synchronized envelope and falls back to a full repaint only when identity, geometry generation, dimensions, or the structured base changes. Decoded child grids move into shared immutable attach/outer-offer/completed-baseline custody, so chrome-only work neither deep-clones nor revalidates an unchanged body. Overlay restoration and new grids still take the damage/validation path, and resize fading uses copy-on-write. Each attach round admits one queued input before its bounded server-render round; neither input nor rendering can starve the other. Attach and holder wait on coalesced wake notifications only after a round receives no work, so an already-ready backlog never waits for another producer pulse; all per-lane round limits and quiescent idle waits remain intact.
243
- - **Outer modes.** The client negotiates the outer terminal's modes: raw mode, alternate screen, bracketed paste, focus events, SGR mouse, kitty keyboard protocol where available, and synchronized output.
244
- - Child-requested modes are tracked in the holder's model. Input is re-encoded per child mode by libghostty-vt, which the proof already does.
245
- - Mouse rows are offset by the strip. Row 0 belongs to the client.
246
- - When the child has not requested mouse reporting, selection is host-owned and scoped to the pane, as in the proof.
247
- - **Passthrough and intercepts:**
248
- - OSC 52 clipboard writes, OSC 8 hyperlinks as cell attributes, OSC 0/2 titles as tab metadata, and cursor shape pass through. A bell (BEL) from the child is never forwarded to the outer terminal; icons are the only attention signal (Decision 7).
249
- - Queries the child sends, such as DA and cursor position, are answered by the holder's model, never by the outer terminal.
250
- - 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.
251
- - **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.
252
-
253
- ### 6a. Solo interaction parity (2026-10-17)
254
-
255
- 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.
256
-
257
- - **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.
258
- - **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.
259
- - **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.
260
- - **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.
261
- - **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.
262
- - **Committed presentation.** A requested view commits only with a topology that lists it and a complete target surface. Retained rows, palette/default colors, modes, and cursor are captured atomically; equal captures do not invent a revision. Surfaces of the committed view apply in revision order, and revisions rise across holder incarnations. A retained re-offer is marked stale from the request start and parks for at most 120 ms while the fresh frame arrives. One compositor writes strip, body, overlay, modes, and the viewport-clipped final cursor inside a single synchronized update. It erases suffix cells when a complete replacement row becomes shorter and restores the newest underlying surface when an overlay leaves. Clock, hover, and title-animation damage repaints only the strip and restores the child's cursor.
263
- - **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 exact-artifact harnesses. `verify-interaction-parity.mjs` compares the bytes the same child observes directly and through the built host. `verify-resident-parallel-progress.mjs` configures the real engine against a bounded loopback streaming provider and crosses all six first-response orders with all three initial views while every turn remains unfinished behind a completion barrier. At matching 100×29 child geometry, the same real-A1 prompt also runs through isolated direct-solo and resident profiles; the native Ghostty oracle compares a content-free digest of the intermediate fixture cells and their resolved styles together with normalized cursor and mode state. It advances only at parser-reported synchronized-output boundaries, rejects unbalanced transactions or disappearing intermediate cells, and excludes only the resident strip row rather than normalizing terminal semantics. Opt-in per-process JSONL observations identify engine delivery, completed child writes, PTY ingestion, distinct holder commits, attach receipt, and completed outer paint by run/tab/incarnation/process-instance/sequence without recording terminal or conversation content; queue loss, clock faults, duplicates, stale order, and missing stages fail the report. The child observer batches check-phase appends with one directory initialization while retaining its pending-byte and loss limits. Auxiliary PTY-input completion and child input/semantic/composition/write phases narrow input-side delays without recording key or terminal content. Healthy viewed windows require at least forty matched live commit-to-first-paint samples per tab and enforce p95 at most 33 ms; activation/stale re-offers, later chrome paints of the same revision, and deliberately stopped or paused-sink windows cannot masquerade as live delivery samples. An additional real-A1 fixture compares forty synthetic typing, Models navigation, and transcript wheel responses per path at equal child geometry. Timing requires the shipping Rust optimization profile with all fixture controls retained; debug unit/role/probe coverage and feature-free shipping-artifact checks remain separate. Both paths retain matching complete 60-marker transcript baselines before sampling. Untimed provider setup acknowledges a complete literal editor echo before sending a separate Enter; the measured inputs keep their ordinary 30 ms cadence. Its incremental native Ghostty oracle matches completed semantic states, permits coalesced intermediate states, and rejects missing responses, one-second stalls, or resident p95 beyond direct solo plus the existing 16+33 ms input/presentation envelope. Direct target hashes come from the final semantic state in each ordinary inter-input interval, while timing uses its first observed occurrence, not a later settling delay or an early torn ConPTY hash. A fixed fixture role entry pins the exact addon for all detached roles, including Windows WMI launches that do not inherit the invoking shell's development override; production launch behavior is unchanged. Named pressure variants keep all turns unfinished while exercising hidden progress, repeated switching, navigation input, rename overlays, tab-local resize, a bounded 35 ms attach-receipt delay, and a 900 ms paused outer sink; every tab must retain partial animation/progress before settlement and then present its final output. A detached/reconnect variant releases all three unfinished turns after terminating the authenticated attach and exact self-reported server identity; child writes, PTY ingestion, and holder commits must continue before a replacement server and attach present every retained final output. Separate isolated exact-artifact control markers stop one target tab at engine delivery, physical child rendering, holder commit, or outer paint; each control requires its upstream stages and the other tabs to advance, then removes the stop and requires final output from all three tabs. The outer-paint control waits for a payload-free `fixture-stop-ack` from the sole writer before starting its scenario; that acknowledgement follows all earlier writes, and same-writer sequence ordering excludes only pre-acknowledgement paints from the zero-progress check. Any later paint still fails. A fifth control fragments a real owned-shell synchronized write into at most 64 physical writes without changing its bytes, records the DEC private mode 2026 transaction classification, and requires ordinary complete holder commits without deadline recovery before final output. This characterizes the built PTY/ConPTY coherence path; a passing fragmented run does not by itself assign the historical stall to that path without a failing live reproduction. The marker path is absent in ordinary sessions and is scrubbed before extensions or tools inherit the child environment, while observation callbacks remain passive and cannot select a control. CI runs the vendored Ghostty VT tests and both exact-artifact harnesses on Windows x64, Linux x64, and macOS arm64. Each platform's verdict stands alone.
264
-
265
- ### 7. Status and attention come from the tab bridge
266
-
267
- Tab status is an A1-owned state machine fed by `bridge.status`:
268
-
269
- | Status | Source | Glyph |
270
- |---|---|---|
271
- | `starting` / `restoring` | holder spawned, bridge not ready | dim progress frames |
272
- | `idle` | settled idle | none |
273
- | `working` | agent or turn start, tool execution, compaction | shared progress frames |
274
- | `needs-input` | an extension UI, trust, or permission request is pending | yellow `?` (warning) |
275
- | `done` | the last turn finished successfully | green `✓` success |
276
- | `aborted` | the last turn was aborted | red `✗` error |
277
- | `error` | the last turn ended with an error | red `✗` error |
278
- | `crashed` / `restarting` / `failed` | holder or child exit, or a failed tab lifecycle (never a failed turn) | `✗` error plus a banner |
279
-
280
- - A `done`, `aborted`, or `error` marker stays on the chip, including after any client views the tab, until that tab's next turn starts.
281
- - A tab with a broken bridge shows only process-level states.
282
- - Future CLI tabs will derive status from process state, and may use BEL or OSC 9/777 from the program as an attention input.
283
- - The glyph set is the same in every client. A working spinner uses the resolved A1 accent reported by that specific tab.
284
- - **Icons are the only attention signal (user decision).** There is no bell, sound, terminal notification, or OS notification. The status icon is the signal: accent spinner = working, `✓` = finished, red `✗` = failed, yellow `?` = needs you.
285
-
286
- ### 8. Durable state
287
-
288
- - **Pi's session JSONL**, written by the child, is the authoritative transcript.
289
- - **The registry** is `<dataDir>/tabs/<profile-token>/registry.json`:
290
- - It is written only while the server holds the OS-exclusive writer lease and current epoch: to a temporary file, then write-capable `fsync(file)`, rename, and `fsync(dir)`, on every mutation before acknowledgement.
291
- - Up to 20 rotated generations are kept in `registry-history/`, at most one per 15 minutes.
292
- - An unparseable file is quarantined as `registry.corrupt-<ts>.json`, and the last good history generation is loaded. A notice names both. If existing state has no valid recoverable generation, fail recovery closed with preserved files rather than initialize an empty registry.
293
- - JSON with explicit synchronization is chosen over sqlite to keep the native server dependency-light. Milestone 1 pins the write-handle, atomic replacement, and directory/metadata synchronization sequence and supported local filesystems: on Windows a write-capable flush plus write-through replacement; on Linux `fsync` of file and parent directory; on macOS `F_FULLFSYNC` of the file plus the strongest declared supported parent-directory synchronization. A failed or unsupported required barrier is reported rather than ignored. Kill-at-step tests establish process-crash old-or-new content and instrumented API ordering; they do not stand in for milestone-6 physical power-loss evidence.
294
- - A durable mutation is acknowledged only after its commit barrier. On failed persistence, reject new durable mutations; retain live observed process state in memory without claiming it is committed or rebuilding it from older disk state. Retry bounded writes; never kill a live tab solely because persistence failed.
295
- - **Per-tab fields:**
296
- - identity: `tabId`, `kind`, `createdAt`
297
- - naming: `displayName`, `nameSource` (`default|auto|user`)
298
- - placement: `order`, `cwd`
299
- - session: `sessionFile`, `sessionDir`
300
- - lifecycle: `desired` (`running|stopped`), `lifecycle`, `attentionSeq`, `seenSeq` (volatile status is rebuilt from the bridge and is never persisted)
301
- - holder: `holderPid`, `holderStartIdentity`, `childPid`, `childStartIdentity`, `incarnation`, `release`
302
- - restarts: `restarts`, `restartWindowStart`, `lastExit`
303
- - `registryRevision`, `epoch`, and `bootId`
304
- - **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.
305
- - **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.
306
- - **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.
307
- - **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.
308
-
309
- ### 9. Failure and recovery
310
-
311
- | Failure | Detection | Outcome |
312
- |---|---|---|
313
- | Tab child exits or crashes | holder sees the child exit | If the exit was not requested: `crashed`. The holder respawns `ui.js --tab --session <file>` with backoff 1 s, 5 s, 30 s. After 3 restarts in 10 min: `failed`, with a banner `✗ <reason> — [r] retry · [f] start fresh · [alt+w] close`. An interrupted prompt (reported via the bridge, or detected as a trailing user entry) is offered back into the editor and never resent. Child stderr, if separately available, is a private content-bearing recovery artifact, never an ordinary diagnostic log. |
314
- | Holder crash | pipe EOF plus verified process identity/exit | Pseudoterminal loss is not proof that every descendant exited. Verify the old writer/tree is gone and acquire its writer lease before resuming in a new holder; otherwise keep the tab blocked without duplicate writers. |
315
- | Holder hang | holder control heartbeat every 5 s, 3 missed over a healthy supervision path, then one heartbeat of grace for the holder-local watchdog record | Terminate only the verified holder/tree within bounded deadlines, then follow the lease-gated recovery path. |
316
- | A1 child hang | missing child event-loop heartbeat over a healthy bridge path | Record `unresponsive` at 30 s without changing the chip or painting a notice; restart at `tabsUnresponsiveRestartSeconds` (default 120), preserving journal/screen. A server/bridge transport outage alone is not proof of a child hang. |
317
- | Server crash | clients and holders see the pipe EOF | Holders keep running. A client or holder starts a replacement, which acquires the released writer lease, increments the epoch, derives expected credentials, and re-admits identity-verified holders. A live but unresponsive recorded owner is terminated only after native identity verification; an unverifiable owner blocks replacement. After 3 starts in 60 s, clients show `✗ tab host stopped — [r] restart · [q] quit`. |
318
- | Attach client crash or terminal close | the server sees the pipe EOF | Nothing happens to tabs. Pending requests stay `needs-input`. |
319
- | Reboot or logout | the platform boot identity differs, or no verified holders exist for a new OS session | Tabs are not restored in this version. The server moves the previous tab set to registry history, and bare `a1` starts with one new tab. Every previous session stays resumable through the normal session picker or `a1 --session`. A pending journaled prompt is offered when its session is next opened in a tab, for up to seven days. Restoring tabs after reboot is a follow-up change. |
320
- | Missing cwd or session on crash restart | stat or open fails | The tab is `failed` with the reason and path. It is never moved elsewhere, and the file is never overwritten. A reserved first-turn identity with a journal and no previously created transcript follows journal recovery instead. |
321
- | Registry corrupt | parse or validation fails | The file is quarantined, the last good generation is loaded, and a notice is shown. |
322
- | PID reuse | start-identity mismatch | The process is never adopted or killed, and the record is treated as having a dead holder. |
323
- | Disk full | write fails | The mutation is rejected with its reason, and running tabs are unaffected. |
324
- | libghostty or parser panic in a holder | the Rust panic hook | The holder logs, exits, and follows the holder-crash path. Other tabs are unaffected; the per-tab processes exist for exactly this (the herdr #453 lesson). |
325
-
326
- ### 10. Updates and version skew
327
-
328
- - **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.
329
- - **Exact release admission.** Ordinary attach clients, holders and bridges present their originating release identity before capability negotiation. Matching protocol generation or display version does not permit implicit cross-release attachment. The explicit `SourceRoute` adapter selects and verifies one source namespace, native owner and epoch before authenticating client control to that source; it never adopts foreign holders or rewrites execution custody. Native clients also check the welcome identity; bridge identity is consumed from holder-private launch context before extensions load.
330
- - **Independent activation.** A newer client starts or reuses its own execution source for new tabs, then reconnects the same profile workspace through compatible explicit source routes. Readiness and active-reference publication require repeated verified source-contract inspection; incompatibility keeps an eligible verified prior client selected. Existing resident and solo processes remain untouched. Source recovery and child launches retain the originating fixed Node, entry and addon; the active-release pointer never retargets them. No automatic migration, execution handoff or idle recycling is performed.
331
- - **Retention.** Read-only bounded collection scans every flat release namespace and legacy profile namespace. It keeps server, holder and child releases while identities verify and retains all candidates on unreadable, linked, oversized or unverifiable state. Package activation never overwrites loaded artifacts.
332
- - **Maintenance and legacy state.** Maintenance resolves the invoking package's immutable materialization without activation or host startup, uses only that release's verified entry, and never retries another recorded cohort. List, stop, status and doctor remain release-local, including unresponsive-host termination. Legacy unversioned state is neither migrated nor adopted; it is read only for conservative retention. Shared configuration and session file locks remain profile-neutral.
333
-
334
- #### Stable runtime control and profile workspace
335
-
336
- The `resident-control-v1` descriptor is available through authenticated native `runtime.describe` maintenance. It binds the mandatory attach/list/view/input/resize/detach/controller-boundary semantics to an exact source release, profile scope, epoch and boot/login identity. It explicitly forbids automatic input retry after uncertain delivery. Native MessagePack fixtures and the TypeScript consumer pin the same bytes; unknown optional metadata is not retained. This descriptor alone is not proof that every live Agent can be controlled and is not an activation verdict.
337
-
338
- `SourceRoute` verifies canonical profile/source scope, bounded owner-private records, native owner/start identity, boot/login, credentials and the authenticated epoch. Inspection starts no source and loads no source code. The explicit attach adapter permits client control through the source's existing controller barriers, without acquiring holder or session-writer custody. Generic same-release refusal and release-local maintenance remain unchanged. Supported published generation-1 predecessors use their proven mandatory features and neutral private launch contract; sources advertising `resident-control-v1` must satisfy its exact binding and non-replay semantics. Unsupported declared versions fail closed.
339
-
340
- `WorkspaceStore` lives at `<data>/resident-workspaces/<canonical-profile-token>/`. Its kernel writer lease, independent monotonic epoch counter, atomic primary/previous generations and bounded corruption preservation prevent stale writers and empty reinitialization over known state. It stores source-qualified logical tab identities, names, order, selection and custody metadata, not credentials, environment, prompts, drafts or terminal bytes. Only a positively authenticated complete source snapshot may remove that source's entries; discovery absence cannot remove another source. A positively ended boot/login archives bounded topology history rather than restoring Agents.
341
-
342
- Ordinary resident launch uses the native workspace proxy. Its connection, routing, view-admission and persistence modules aggregate source lists/statuses and retained surfaces; new Agent and singleton creation uses the selected release. Input, paste, pointer/focus, resize, dialogs and explicit lifecycle requests go to the authenticated live owner. Drafts, attachments, queued work, session configuration and interaction state stay in the original child, not a client-side reconstruction. Selection commits durably before the exact source/tab/view generation can release bounded queued input. Superseded or failed views reject that input. Reconnection restores observation/resize only and repeats view admission; it never retries input or lifecycle mutations. Source status inspection and durable index writes run in workers, not the live control loop.
343
-
344
- Coordinator/client loss retains source execution and uses the originating source's existing recovery path. Current and previous workspace generations retain source artifacts until positive closure; missing artifacts are not closure. Release/dependency retention also covers recent private recovery custody using bounded metadata only, without reading, exporting or deleting draft/journal contents. Unknown custody retains candidates. Explicit host shutdown remains execution-only rather than being blocked by recovery files.
345
-
346
- Optional authenticated client-build metadata is advisory, not execution identity or controller authority. Modern retained children acknowledge installed update notices already satisfied by that connected client while preserving copy feedback and Notify/No behavior. An immutable predecessor unable to acknowledge that metadata gets a client-owned notice explaining that retained Agent UI notices do not require restarting Agents. Existing engine code remains its original version.
347
-
348
- Real published-predecessor tests are included in exact-package CI on Windows x64, Linux x64 and macOS arm64. The focused Windows run exercises live model/tool work, queued input, retained draft/selection, hard client death and originating-coordinator recovery with unchanged Agent/holder/tool identities and no replay or deferred idle restart. Local evidence is not certification of unrun platforms or of the exact packed CI candidate.
349
-
350
- ### 11. Foreground UX
351
-
352
- - **Strip.** Row 0, drawn by the attach client, reproduces the approved v2 default appearance:
353
- - 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;
354
- - the empty state keeps the hoverable `+` control and the clock beside the landing, with no cursor;
355
- - chips are at most 20 columns, clipped with `…` by grapheme width, with no separators or close glyph;
356
- - a `…` overflow menu holds tabs that do not fit, and `+` is always reserved;
357
- - these presentation choices are fixed, not settings, while each working spinner uses that tab's reported A1 accent.
358
- - **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.
359
-
360
- | Key | Action |
361
- |---|---|
362
- | `Alt+A` | new tab |
363
- | `Alt+W` | close |
364
- | `F2` | rename |
365
- | `Alt+,` | open the global Settings tab, or close it while it is viewed |
366
- | `Alt+/` | open the command palette, or close it while it is open |
367
- | unbound (`tabs.changelog`) | open the Changelog tab |
368
- | `Alt+1`…`Alt+9`, `Alt+0` | jump to a tab |
369
- | unbound (`tabs.next` / `tabs.previous`) | next / previous, wrapping |
370
- | unbound (`tabs.moveRight` / `tabs.moveLeft`) | move the tab right / left |
371
- | `Ctrl+C` `Ctrl+C` | detach (leave `a1`; tabs keep running) |
372
-
373
- - `Alt+[` and `Alt+]` are avoided because `ESC [` is the CSI introducer in legacy encodings.
374
- - `Alt+←` and `Alt+→` are avoided because A1 uses them for word motion.
375
- - **`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.
376
- - **Mouse on row 0:**
377
- - click activates a tab;
378
- - right-click opens `Rename`/`Close` (`Close` only for Settings and Changelog);
379
- - right-click on `+` opens a one-row `Configure` menu that opens the `Plus Menu` dialog over the dimmed active tab, without switching tabs;
380
- - dragging reorders, with a `│` drop marker, after 250 ms or once the pointer moves;
381
- - `+` follows the plus menu: with only Agent enabled it adds an Agent at once; with other kinds it opens a persistent dark v2 menu of the enabled kinds, `+ Agent` first and a separator before the `Settings` and `Changelog` toggles, each `✓` while its tab is open; with none the strip hides `+`. Hover and click remain active through the opening release.
382
- - **Settings.** The strip owns one profile-global `Settings` tab. The grouped launcher creates or activates it; `Alt+,` does the same from another tab and closes it while it is viewed. Esc does not close the Settings tab; closing the 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.
383
- - **Commands inside A1 tabs:** a tab's name and its session's name stay in sync. a strip rename or an automatic name sets the session name through the bridge, and a session name set otherwise publishes the session view at once, so the tab takes it as a user name; and a resumed session's name becomes the tab name. An automatic name never replaces a user name. Resident Agents also omit and refuse `/changelog`, which opens as its own tab, and `/name`, since renaming the tab (`F2`) renames the session; direct A1 keeps both. Milestone 5 adds no tab-management slash commands: `/new-tab`, `/close`, `/tabs`, `/quit-all`, and `/restart-tab` are not exposed.
384
- - **Naming:**
385
- - Default names are `agent` or `agent N`.
386
- - 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.
387
- - A user rename sets `nameSource=user`, and auto-naming never overrides it.
388
- - 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.
389
- - 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.
390
- - **Plus menu.** The A1 setting `tabBarPlusMenu`, shown as `Plus menu` in the `Tab Bar` Settings section, holds the kinds `+` offers, Agent and every utility kind, as comma-joined keys in that order and defaults to `agent`. The `Plus Menu` dialog lists every kind with `●` when enabled and `○` when not under `Choose what the + button in the tab bar can add.`, with `Space` to toggle, Up and Down to move, typing to filter, and `Esc` to close. `+`'s `Configure` opens it in the attach client as a modal over the dimmed active tab or landing, built on the command palette's list and renderer, with a reversed white search caret like owned inputs. The Settings `configure` entry opens the same modal: the Settings child sends a `configure-plus` bridge request as the tab's controlling client, which the server forwards to that client as `plus.configure`; only without a bridge does Settings open its own equivalent dialog. Each toggle is written at once: the attach client takes the `settings.json.lock` directory lock the TypeScript writer takes through `proper-lockfile`, rereads the document, replaces only `a1.tabBarPlusMenu` (removed at the default), and renames a temporary file over it. A refused write restores the stored menu. The attach client reads the setting live, and with no kind enabled the strip hides `+`. Because a modal's overlay saves and restores the cursor while faint is active, the strip starts every frame from reset attributes, so no chip stays faint after a modal closes.
391
- - **Command palette.** `Alt+/` (`tabs.commands`) opens a centered box over the whole frame in the owned list style: an accent `Commands` title, a `> ` search row, `Agent`, `Settings`, and `Changelog` with their effective shortcuts right-aligned, and a key hint. Typing or pasting filters it, Up and Down move, `Enter` runs the selected entry through its shortcut action, and `Esc` or `Alt+/` closes it; another tab shortcut closes it and runs. While it is open no key, paste, or pointer event reaches the strip or a child, the cursor is hidden, and the composer redraws the strip and body with faint re-asserted after every SGR beneath the bright overlay.
392
- - **Changelog.** The changelog opens as a singleton `changelog-` tab titled `Changelog`, from the palette or the unbound `tabs.changelog` action. The tab registry accepts the `agent`, `settings`, and `changelog` kinds. Its provider registration makes it non-renameable, Close-only, sessionless, prewarmed, and hidden: the attach client starts it with `tab.prewarm-changelog` once Settings is ready, and again after a Changelog tab closes, so opening it shows the running tab at once. Like a hidden Settings tab, a hidden Changelog never takes the view when the viewed tab closes, so closing the last Agent shows the landing. Its screen is the tab: it shows no `Esc close` hint, and neither `Esc` nor one `Ctrl+C` closes it.
393
- - **Tab-bar provider.** Agent, Settings, and Changelog are registered once in the I/O-free `TabKind` provider shared by the attach client and server. Each registration declares its label, shortcut action, multiplicity, create-or-toggle activation, prewarm policy, renameability, Agent-session ownership, child route, and launcher marker. Shortcuts, the command palette, `+`, popup capabilities, registry validation, session handling, and route delivery consume that provider instead of rebuilding Agent or utility tables. Multi-instance Agent activation creates a tab; singleton-toggle activation creates or reveals its one tab, selects it in place when already shown, and hides it while viewed according to the common toggle rule. Singleton visibility and prewarm state are arrays in provider order, so adding another singleton does not add a concrete view field.
394
- - The tab bar also owns one kind-neutral ordering API: every newly created tab and every hidden tab newly joining the strip appends after all live tabs; selecting an already shown tab retains its slot; drag order remains authoritative; and stopped registry records never affect the visible insertion destination. Cold holder creation, standby promotion, and singleton reveal use these same operations, so `Agent`, then `Settings`, then another `Agent` stays in that order.
395
- - The workspace proxy reserves creation positions before source IDs arrive, restores caller request identities, and prevents old prewarm responses from taking an explicit open. It publishes final topology before requesting the first selected source surface, including when a prewarmed utility ID already exists in an older client position. Durable acknowledgements update custody without replacing newer user order. Hidden foreign singleton records never produce duplicate Settings/Changelog chips or consume published reorder indexes, and unchanged list replies do not invent new topology revisions.
396
- - Normal lifecycle reconciliation is quiet: known-closed identities absorb duplicate Close, stale references refresh topology rather than become input errors, and superseded or closed views cancel without banners. Source-side repeated Close does not repeat stop or persistence. Storage uses one coalesced latest-intent slot plus its in-flight operation, retains every affected source, and waits for other clients' writer leases only off the UI loop. Passive source recovery retains observation/resize intent but never queued input. See the delivery's tab-notification audit for producer coverage.
397
- - Utility toggles hide and reuse their process even when the final visible utility leaves the strip; the landing replaces it. Explicit Close still removes the instance. Source/tab/generation fencing and one complete synchronized presentation commit exclude stale bodies and intermediate selection jumps. The full-coverage host landing repaints in place rather than clearing the strip between utility transitions.
398
- - `bin/ui.js` resolves the utility route from the consumed tab id before loading the declared Agent startup graph. A utility id imports the slim composition instead, so no Pi engine host, Agent session, extensions, prompt history, suggestions, or draft recovery are loaded or started; Agent tabs, the standby, `a1 solo`, and `a1 pi` keep the full graph.
399
- - The slim process still owns one fullscreen `OwnedUiTerminalHost`, the background canvas, theme and accent singletons, Pi keybindings, controller-boundary input, and the tab bridge. A minimal presenter opens only the pinned route. It publishes effective tab shortcuts, requests the attach client's Plus Menu through `configure-plus`, requests tab close when the screen closes, detaches the controlling client on the interrupt exit, and ends on `bridge.stop`.
400
- - Settings builds its Agent section from Pi's persisted `SettingsManager` after the ordinary project-trust decision, with persist-only bindings because each Agent process owns live effects. Its per-model thinking choices come from an offline `ModelRuntime` over the profile's `auth.json` and `models.json`. Changelog reads the packaged release-note catalog directly and never takes or acknowledges an Agent startup "What's New" claim.
401
- - **Reveal.** Opening a tab draws it at once: a created Agent and a revealed Settings or Changelog tab both keep the current content, another tab or the landing, until their first nonblank drawn frame commits, a retained blank surface included. Blank child revisions remain parked for any duration rather than exposing an empty body or child cursor; explicit failure and recovery outcomes still replace the startup presentation. Every changed-size resize forwards geometry immediately and repaints the retained strip and child surface directly at the new dimensions; no resize curtain or fade remains. Inactive terminal-tab resizes also repaint without an overlay, and focus return refreshes the complete frame. A starting chip shows no spinner. One tab opens at a time: new-Agent input while a tab the client asked for is starting or not yet shown starts nothing.
402
- - **Rename selection.** The inline rename draws a selection on the prompt editor's selection background (RGB `38,79,120`, palette `24`) and keeps the chip's text color, restoring the chip's background after it; only the caret is a reversed cell.
403
- - **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 `+` and the clock beside the landing.
404
- - **Landing.** While the attach client has no tab to show, the attach role renders the a1 landing as the frame's body. The launch cwd sits where an Agent tab's footer shows it, flush left on the second-to-last body row with the same `~` formatting and `dim` color, so the folder does not move when a tab opens or closes; `v<version>`, read once from the release root's `package.json`, sits below it and the three-row half-block `a1` wordmark in the profile's accent above it (omitted in Apple Terminal). The accent colors are a native table pinned by `landing_accents.json`, which a TypeScript test checks against `derivePiAccentProjection`. The body is painted with the canvas `backgroundStyle` gives Agent tabs. The landing holds no input: tab shortcuts, the launcher, strip mouse, and double-`Ctrl+C` behave as with tabs, and no cursor shows. A hidden Settings tab is prewarmed and kept running beside the landing, as beside Agents, so Settings opens without a start; it closes when the client detaches. Plain workspace launch treats a roster holding only utilities as empty and shows the landing without creating an Agent; new execution requires an explicit Agent or session-open action. An Agent record whose child exited and that no holder owns is not a live Agent for plain launch: remain on the landing rather than replacing its execution. Explicit creation uses a fresh identity and never adopts a stopped tab's directory, draft or recovery screen. A retained live workspace is selected from the complete authenticated roster before admission; its selected source/tab/generation is explicitly observed before the first nonempty frame, so no landing is painted while those Agents reconnect. On the client, a first registry list with nothing to show counts as the strip shown empty, so the landing with `+` and the clock paints at once instead of an empty screen, while a listed starting tab keeps its first-draw gate. Adding a tab from the landing keeps it until the tab's first drawn frame replaces it at once; the landing has no animation of its own.
405
- - **Detach.**
406
- - `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.
407
- - 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.
408
- - **Launch and reattach:**
409
- - Bare `a1` ensures the server and attaches.
410
- - With zero tabs, it creates one A1 tab in the launch cwd; a registry holding only the hidden Settings tab counts as zero.
411
- - 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.
412
- - `a1 --session X` opens X in a new tab, or focuses the tab holding it.
413
- - A new tab's cwd is the client's launch cwd.
414
- - 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.
415
- - **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`.
416
- - **Background attention.** Only the chip icon changes. There is no bell, sound, or notification, and no setting for one.
417
-
418
- ### 12. Limits and resources
419
-
420
- Internal resources remain bounded without imposing a user-visible tab-count quota.
421
-
422
- - 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.
423
- - Unresponsive restart timing is internal fixed behavior at 120 seconds; there is no progress-stall notice; concurrent starts use internal default 2/hard cap 8 and prewarm uses internal count/hard cap 1.
424
- - Every open tab keeps its holder and A1 process until explicit close. There is no idle-suspension setting, timer, lifecycle, or glyph.
425
- - Scrollback is capped at 10 MiB per holder, and the replay after a server restart is the retained model itself.
426
- - 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.
427
- - 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.
428
- - 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.
429
- - `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.
430
-
431
- #### Hard bounds (milestone 6 audit)
432
-
433
- Every queue, buffer, file, and collection the resident roles keep has a declared cap. Each row names where the cap is enforced and the test that pushes past it.
434
-
435
- | Bound | Cap | Enforced in | Test |
436
- |---|---|---|---|
437
- | Protocol frame | 2 MiB, nesting 64, 65,536 collection items | `native/terminal-host/src/protocol.rs`, `src/foundation/resident-tabs/protocol.ts` | `refuses_oversized_header_before_payload`; `refuses a frame header past 2 MiB before buffering its payload`, `refuses nesting past 64 levels`, `refuses a collection past 65,536 items` |
438
- | Structured complete surface | 512 × 256 geometry, 65,536 cells, 4,096 styles, 4,096 links, and 8 MiB decoded state | `protocol.rs` `StructuredSurface::validate` | `structured_surfaces_fail_closed_on_dimensions_indexes_and_control_injection` |
439
- | Input per message and holder input queue | 1 MiB | `protocol.rs`, `core/holder.rs`, `roles/holder.rs` | `over_capacity_paste_delivers_no_prefix` |
440
- | Holder PTY ingress and work round | 64 × 16 KiB lossless chunks; at most 256 KiB or 2 ms before yielding | `roles/holder.rs` | `verify-resident-parallel-progress.mjs` concurrent and stopped-stage cases |
441
- | Per-client control lane | 1,024 frames and 4 MiB | `roles/server/mod.rs` | `control_lane_is_bounded_by_frames_and_bytes` |
442
- | Per-client render slot | one pending full surface; a newer one replaces it | `roles/server/mod.rs` `Peer::offer_surface` | `stalled_client_gets_one_full_current_surface` |
443
- | Concurrent endpoint connections | 128 | `roles/server/mod.rs` `admit_connection` | `connections_beyond_the_cap_are_refused_and_released_slots_are_reused` |
444
- | Attached viewing clients | 16 | `roles/server/mod.rs` `client_slot_available` | `attached_clients_are_capped` |
445
- | Concurrent holder starts | default 2, hard 8 | `core/server.rs` | `limits_starts_and_drops_stale_completion` |
446
- | Stopped tab records | 16, oldest dropped first | `roles/server/mod.rs` `prune_stopped_records` | `stopped_records_are_bounded_oldest_first_and_live_tabs_are_kept` |
447
- | Prior tab-set archives | bounded history | `roles/registry.rs` | `prior_tab_sets_are_bounded` |
448
- | Abandoned blocking workers | 8 | `shell/workers.rs` | `fails_fast_after_eight_abandoned_workers` |
449
- | Diagnostic logs | 5 MiB × 3 generations per role | `roles/diagnostics.rs` | `logs_rotate_at_their_cap_and_keep_a_bounded_number_of_generations` |
450
- | 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` |
451
- | 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` |
452
- | 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` |
453
- | Session reconciliation read | 64 MiB | `src/foundation/resident-tabs/recovery.ts` | `refuses to scan a session past the 64 MiB reconciliation cap` |
454
- | 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` |
455
- | 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` |
456
- | Attach server ingress | reader queue 64 frames/4 MiB; execution queue 8 frames/4 MiB; at most 64 KiB or 2 ms before yielding | `roles/attach.rs` | `verify-resident-parallel-progress.mjs` slow-receipt and switching cases |
457
- | Attach input | 1,024 parsed events; semantic input is emitted in at most 1 MiB chunks and target-bound while a view/create is pending | `roles/attach.rs` | `deferred_input_is_atomic_bounded_and_bound_to_the_requested_tab` |
458
- | Outer presentation writer | one replaceable complete desired state, 8 control/effect frames, and 4 MiB effect bytes | `roles/attach.rs` `OuterWriter` | `presentation_is_one_ordered_synchronized_write`; `verify-resident-parallel-progress.mjs` paused-sink case |
459
- | Doctor bundle | 25 MiB, oldest log generations dropped first | `src/foundation/resident-tabs/doctor.ts` | `drops the oldest log generations first to stay within its cap` |
460
-
461
- ### 13. Packaging and certification
462
-
463
- - `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.
464
- - 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.
465
- - 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.
466
-
467
- ### 14. Security
468
-
469
- - Endpoints, the registry-writer lease, profile secret, client token, registry, journals, and recovery snapshots are owner-only.
470
- - Tab creation takes a cwd and an environment map. The child argv is fixed by A1, and clients never supply command lines.
471
- - Holder and bridge credentials are derived per tab and process incarnation from the profile secret, are stripped from grandchild environments, and are never written to the registry or logs.
472
- - The registry and structured logs contain no credentials, environment values, prompt text, or terminal content. Journals, retained screens, and optional raw stderr are explicitly sensitive recovery data with owner-only access, bounds, expiry, and no automatic diagnostic export.
473
- - Everything runs as the invoking user.
474
-
475
- ### 15. Rollback
476
-
477
- - `a1 solo` starts the direct single-agent A1 UI without starting the server; the registry is preserved. The shared session-writer guard remains active, so solo or fallback cannot write into a live tab's session.
478
- - If the server cannot start, bare A1 falls back to the direct single-agent launch with one notice, instead of failing.
479
-
480
- ### 16. Reliability engineering
481
-
482
- The v2 daemon was unreliable for structural reasons, and a forensic pass over its source established them. This design treats reliability as architecture rather than as patches, and proves it with release-gating tests. The full normative contract is the `resident-tab-reliability` section of [`resident-tabs-requirements.md`](resident-tabs-requirements.md).
483
-
484
- **Principles:**
485
-
486
- 1. **Failure domains are processes.** The client, the server, one holder per tab, and one A1 process per tab are separate processes. Nothing shares an event loop across tabs.
487
- 2. **Crash-only.** Every resident process may be killed at any instruction, and its startup path is its recovery path. Internal invariant violations fail fast: that process crashes with a record, instead of being swallowed. Because the failure domains are small, failing fast is cheap.
488
- 3. **The control loop never blocks.** Each Rust role keeps its state machine I/O-free, a "sans-IO core". A thin shell runs blocking work (spawn, pseudoterminal creation, kill, identity inspection, fsync) on workers with deadlines. A missed deadline becomes a typed event, never a stall.
489
- 4. **Level-triggered reconciliation.** Desired state (lifecycle, holder presence, size) is continuously reconciled against observed state. Every process incarnation carries an ID, and stale events are dropped.
490
- 5. **Fencing.** The server must hold the OS-exclusive registry-writer lease; each start increments the durable epoch under that lease, each mutation verifies both lease and epoch, and holders and bridges obey only the highest epoch they have seen. A timed-out endpoint alone never authorizes a second writer.
491
- 6. **Explicit durability per data class.** There is no "best effort". Each class of data has a stated guarantee, and each guarantee has a test.
492
- 7. **Evidence survives failure.** Logs rotate but are never truncated at startup. Each crash produces its own record.
493
- 8. **Prove it in stages.** Deterministic simulation, crash-point injection, fuzzing, bounded CI chaos on each platform, and exact-package physical evidence per platform gate certification. A separately authorized isolated-worker 24-hour soak remains required platform evidence; evidence is never inferred across platforms. Every named crash point exists only in the `fault-injection` test build, and `A1_TABS_FAULT_CRASH_MARK` names the directory where a kill-at-point test reads the breadcrumb a crashed role leaves.
494
-
495
- **How each v2 failure is closed:**
496
-
497
- | v2 failure (forensic finding) | Root cause | Countermeasure here |
498
- |---|---|---|
499
- | A ConPTY spawn blocked forever, the hung daemon kept the pipe, and every window lost its agents | Synchronous `pty.spawn` on the shared loop | The PTY is created only inside that tab's holder, and the server kills that holder after a deadline and retries. Other tabs and the server are unaffected. |
500
- | The watchdog SIGKILLed the daemon after a 60 s stall, and every agent died | Agents were children of the daemon | Holders and children survive a server death. The watchdog kills only the stalled process. |
501
- | Synchronous `taskkill` loops blocked the daemon for 30–60 s | Blocking kills on the event loop | Kills run asynchronously and concurrently, with deadlines. The loop keeps answering heartbeats. |
502
- | Blocked ConPTY writes stalled the loop, and keystrokes or pastes were silently dropped ("newest wins") | No flow control | A dedicated writer per holder with a bounded queue. Reserve capacity for a complete paste before delivery; reject an over-capacity paste visibly without delivering a prefix. After admission, preserve byte order and report delivery uncertainty if a child or process fails; never silently truncate or retry accepted bytes. |
503
- | Terminal queries (DA/DSR/CPR) were answered by the busy shared emulator, so children stalled | One emulator loop for all agents | Each holder answers its own tab's queries from its own model. |
504
- | Repaints arrived in fragments ("ghost frames"), patched with timing heuristics and two screen sources | Emulation reconciled with bridge surfaces by timers | One source of truth, the holder model. Frames are published at synchronized-output boundaries, with a bounded coalescing window; where ConPTY relays those boundaries ahead of the cells it re-renders, publication waits for the render pass instead of the marker. |
505
- | A late exit event poisoned the replacement child | Events were not tied to an incarnation | Incarnation IDs on every event. Stale events are discarded. |
506
- | Resizes were lost in transit | Edge-triggered resize | Size is desired state, reconciled until observed. |
507
- | Restore spawned duplicate agents | No restore gate | A single start gate for crash restarts, at most one start in flight per tab, and conditional revisions. The duplicate-start invariant is checked in simulation. |
508
- | A fresh agent whose session file was never reported could not be recovered | Session identity was learned after spawn | Session identity is committed to the registry before the tab accepts input. The prompt journal covers Pi's no-file-until-first-reply behavior. |
509
- | A corrupt state file was treated as a fresh boot and wiped every record | Parse failure fell through to an empty state | Quarantine, then load the last good history generation, with a notice. The server never starts empty over existing data. |
510
- | A failed persist plus a hot-swap re-read old state from disk and killed live agents as orphans | Rebuilding from disk over newer memory; the persist result was ignored | Memory stays authoritative on write failure: retry with backoff and report degraded health. There is no hot-swap, and processes are never killed because a registry read came back short. |
511
- | fsync was probably a no-op on Windows (file opened read-only), and the directory was never synced | Wrong handle access | Files are synced with write access and the directory is synced too. A test asserts the platform flush calls. |
512
- | Two processes appended to one session JSONL | Leases lived only in daemon memory | A canonical session-writer lock shared by every A1-owned launch mode, held by the writer process itself so the kernel releases it only when the writer is gone. |
513
- | Boot-identity rounding misclassified a crash as a reboot | `now − uptime` rounded to minutes | Each platform reads an exact native boot identity (Windows boot sequence/`LastBootUpTime`, Linux `/proc/sys/kernel/random/boot_id`, macOS `kern.boottime`) and always verifies process identity by pid plus native start time. |
514
- | A persist storm: a synchronous pretty-printed JSON write on every status change | Status was persisted with lifecycle | Only lifecycle and identity mutations are persisted. Status is volatile and rebuilt from the bridge. Writes run off-loop. |
515
- | Hot-swapping the logic bundle caused generation bugs, lost deferred callbacks, and memory growth; kernel updates never applied | In-process code replacement, needed because restarts killed agents | No logic hot-swap. Crash recovery replaces a server while retaining its compatible cohort. Automatic upgrade handoff and release recycling remain deferred; idle suspension is not a cohort upgrade. |
516
- | Crash evidence was deleted: the log was removed at boot, crash logs truncated on respawn, a 256-line queue dropped lines | Log handling | Size-rotated generations that are never truncated at boot. A distinct crash record per incident with a backtrace. `a1 tabs doctor` produces a redacted bundle. |
517
- | Crash text was wiped by the alternate-screen clear | The child cleared the screen before printing | The holder freezes the last screen as a read-only snapshot and writes a recovery file. Separately available stderr goes only to private bounded recovery storage, never the doctor bundle. |
518
- | Concurrent starts hit the `auth.json` lock ("No models available"), and `taskkill /F` left stale locks | Start storms and forced kills | Starts are capped and spaced. Graceful stop comes before force, so Pi releases its locks. A ten-tab concurrent start test asserts models are available. |
519
- | A mid-edit extension reload crashed children | Children loaded a tree that was being edited | This is handled like any other crash: the bounded restart budget, the prompt journal, and the last-screen snapshot. The restart is visible and never silent. |
520
- | The v1 daemon churned: slow replies were read as an outdated build | Timing used as a version signal | The version is carried by the explicit handshake generation, never inferred from timing. |
521
-
522
- **Agents that stop making progress.** Liveness and progress are separate signals:
523
-
524
- - Holder heartbeat: a Rust main-loop tick every 5 s seen by the server; three missed ticks over an otherwise healthy supervision path plus one heartbeat of grace trigger verified holder recovery. The grace keeps external termination behind the holder's 15-second process-local watchdog so its own crash evidence wins despite scheduler jitter.
525
- - A1 heartbeat: the child's Node event loop, seen through the bridge. After 30 s the server records `unresponsive` in the registry and counters, without changing the presented status, chip glyph, or colour and without a notice row or restart key; after `tabsUnresponsiveRestartSeconds` (default 120) the tab restarts from its session with its journal and last screen preserved. A missing server or bridge transport degrades status, not proof of a child event-loop stall; reacquire liveness evidence before destructive recovery.
526
- - Agent progress: not supervised. A working tab without model or tool events keeps its working status indefinitely, gains no transcript notice, and is never auto-killed, because a legitimate tool can run for a long time.
527
-
528
- **Data-loss envelope.** Distinguish process termination, power loss, and loss of both holder and child:
529
-
530
- - Committed session entries: never lost (Pi appends synchronously per entry).
531
- - Settled turns: survive power loss (fsync at settle).
532
- - Submitted prompts: never lost (journal fsynced before dispatch).
533
- - Drafts: at most one second of typing lost while periodic checkpoints and storage remain healthy; expose a degraded bound on checkpoint failure or stall.
534
- - A streaming reply at the moment of a tab crash: readable from the last-screen snapshot and recovery file, but not resumable as a model turn. That is Pi's semantics.
535
- - The only unrecoverable case is a streaming reply when the holder *and* its child die together, for example a machine crash, because it was never persisted.
536
-
537
- **Verification program.** It runs incrementally across milestones 1–6 and gates the complete resident slice:
538
-
539
- - Deterministic simulation and property tests of the sans-IO server core over death, loss, reordering, delay, stale epochs, controller transfer, churn, and mutation. Invariants include no lost committed mutation, one registry writer, one live incarnation per tab/session, attributable client-scoped requests, and convergence to running or failed.
540
- - Failure points at every persistence and IPC step, with kill-at-each-point crash tests.
541
- - `cargo-fuzz` targets for the protocol decoder and surface patch encoder.
542
- - Bounded CI chaos on each platform with high-rate fake agents, random process death, resize/attach churn, controller transfer, blocked writes, pseudoterminal creation hang, rename denial, and containment escape attempts.
543
- - Exact-package manual or isolated-worker physical acceptance on each platform. Certification must additionally supply an authorized 24-hour soak on that platform with zero lost journaled prompts/committed entries, zero orphans/duplicates, bounded memory/handles, reattach p95 under 300 ms, tab restart under 3 s, and server recovery under 2 s.
544
-
545
- **Local native iteration.** Zig never runs on a developer workstation, but native tests can, by linking the terminal library a CI lane built:
546
-
547
- 1. `node scripts/development/fetch-terminal-vt.mjs [--branch <name>]` downloads this platform's `terminal-vt-<platform>-<architecture>` artifact from the branch's newest run that published one (default: the current branch) into `.artifacts/terminal-vt/`. If none exists, dispatch `gh workflow run ci.yml --ref <branch>`; native lanes keep it for 14 days.
548
- 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.
549
- 3. Run one test: `cargo test --manifest-path native/terminal-host/Cargo.toml --features resident-fixtures --test role_integration <name>`.
550
-
551
- 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.
552
-
553
- 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.
554
-
555
- **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.
556
-
557
- ## Risks / Trade-offs
558
-
559
- - **[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.
560
- - **[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.
561
- - **[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.
562
- - **[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.
563
- - **[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.
564
- - **[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.
565
- - **[No image protocol in tabs at first]** → Pi's text fallback applies and the gap is documented. Forwarding is a follow-up.
566
- - **[Stale or duplicate processes after races]** → An OS-exclusive writer lease, current-epoch commits, one start gate, conditional registry revisions, derived credentials, and adoption only after identity verification.
567
- - **[Two clients type into one tab or a delayed slash command detaches the wrong terminal]** → One controller lease, a proven cross-channel input-admission barrier, immutable command-origin generations, and fail-closed ambiguous requests. A sideband acknowledgement alone is not sufficient.
568
- - **[Physical close, logout, and reboot cannot be tested hermetically]** → Owner-tree kills stand in for terminal close. Exact-artifact manual or isolated-worker records are required, and nothing is automated on an active workstation.
569
-
570
- ## Milestone 1 implementation record
571
-
572
- The contract package keeps the existing proof binary and adds one shared Rust library plus a feature-gated Node-API 9 lock artifact. Process identity uses native process-start data together with Windows boot/logon LUIDs, Linux boot ID/session ID, and macOS boot time/audit session. Endpoints use a first-instance owner-only named pipe with client-token comparison on Windows and protected Unix sockets with per-accept peer credentials on Linux/macOS. Fixed resident roles use detached breakaway creation with a fixed WMI fallback on Windows and double-fork plus `setsid` on Unix; no public executable/argv launch input was added.
573
-
574
- Writer locks converge on Windows file IDs or Unix device/inode identities while retaining a canonical location gate through new-file bind. Durable replacement rejects detected remote filesystems, uses write-capable flush and write-through replacement on Windows, file/parent `fsync` on Linux, and `F_FULLFSYNC` plus parent synchronization on macOS; macOS records the explicit directory capability fallback when that call rejects a directory descriptor. Hosted abort-at-step and call-order fixtures prove only complete old-or-new process-crash behavior, not physical power-loss survival.
575
-
576
- Generation 1 is the bounded little-endian named-map MessagePack contract in this document. Its exact fixture manifest, decoder fuzz target, controller simulator, and nonce-bearing same-PTY-queue fixture are package artifacts. The only implementation-boundary deviation is intentional: the addon, roles, and primitives remain test artifacts with no product route or release packaging in milestone 1.
577
-
578
- ## Roadmap
579
-
580
- Each milestone is its own OpenSpec change and pull request. Milestones 1–4 merged behind `residentTabs: false`; milestone 5 removes the toggle, makes tabs the bare-`a1` route, and adds `a1 solo` as the direct path. A milestone starts only after the previous one merges, moves its requirements from [`resident-tabs-requirements.md`](resident-tabs-requirements.md) into its own spec delta, and covers Windows x64, Linux x64, and macOS arm64 together unless its change says otherwise.
581
-
582
- | # | Change | Deliverable and exit evidence |
583
- |---|---|---|
584
- | 1 | `resident-tabs-contracts` (PR #586) | Native platform primitives on Windows x64, Linux x64, and macOS arm64: fixed-role detached start and containment checks, process/boot/OS-session identity, owner-only endpoints, the writer-bound lock through Rust and a test-only Node-API artifact, and durable atomic replacement with bounded evidence claims. Freeze named-map MessagePack generation 1 with fixtures/fuzzing, prove controller generations in the sans-IO core, and exercise the nonce-bearing child-observed PTY marker with a native fixture. No product wiring or release packaging. |
585
- | 2 | `resident-tabs-persistent-tab` (PR #746) | The three native roles with one full A1 text UI, minimal authenticated registry and bridge, bounded I/O, explicit opt-in and direct fallback. Terminal and client closure, detached output, retained reattach, input, and surviving-owner terminal restoration. |
586
- | 3 | `resident-tabs-failure-isolation` (PR #747) | Two tabs, server replacement with unchanged holder/child identities, isolated holder/child failure and hang, slow client and blocked writer isolation, exclusive registry epochs, the session-writer lock wired into every A1 launch mode, and no duplicate starts. |
587
- | 4 | `resident-tabs-crash-recovery` (PR #748) | Crash restart into the session, durable-before-dispatch journaling, periodic drafts during continuous typing, correlation/retirement crash points, missing first-turn versus missing existing session, corruption/rename/disk failures, last-screen recovery, and sensitive-recovery/diagnostic separation. After reboot, previous sessions stay resumable, with no tab restore. |
588
- | 5 | `resident-tabs-tab-ux` (PR #749) | Approved v2-style strip with always-visible close buttons, per-agent accent status and needs-input, mouse, assignable shortcuts with unbound navigation/reorder actions, rename/reorder/close, local detach/resume, automatic two-terminal controller transfer, and extension text fidelity. Then animated auto-naming, `tabsMax`, and one internal prewarmed tab; open tabs are never idle-suspended. |
589
- | 6 | `resident-tabs-certification` (PR #750) | Packaging and release retention on every platform, maintenance commands, bounded resources, deterministic/property/crash/fuzz/chaos evidence and exact-package physical acceptance per platform, including SSH loss, forced attach death, conflict-safe direct rollback, and performance measured separately for cold launch, warm tab creation, reattach, and recovery. |
590
-
591
- A failed milestone keeps its pull request open until fixed and preserves safe direct fallback. Each milestone 2–6 PR is a draft that holds its full plan (proposal, design, tasks, spec deltas) and starts implementation only after the previous milestone merges.
592
-
593
- ### Release state and known gaps
594
-
595
- Per-platform state is recorded in `docs/architecture/evidence/resident-tabs/` using [the acceptance checklist](../manual-resident-tabs-acceptance.md). A platform is `CI-verified` until every physical item has a recorded pass on a published candidate, then `certified`.
596
-
597
- | Platform | State | Automated evidence | Physical record |
598
- |---|---|---|---|
599
- | Windows x64 | CI-verified | terminal-host and resident-chaos lanes; publication build | [windows-x64](evidence/resident-tabs/windows-x64.md) |
600
- | Linux x64 | CI-verified | terminal-host and resident-chaos lanes, bounded fuzzing; publication build | [linux-x64](evidence/resident-tabs/linux-x64.md) |
601
- | macOS arm64 | CI-verified | terminal-host and resident-chaos lanes; publication build | [macos-arm64](evidence/resident-tabs/macos-arm64.md) |
602
- | macOS x64 | CI-verified (build only) | publication build and identity check | [macos-x64](evidence/resident-tabs/macos-x64.md) |
603
-
604
- Known gaps:
605
-
606
- - Physical acceptance is pending on every platform; the user runs the macOS checks after milestone 6.
607
- - The 24-hour soak per platform has not run and remains a separate authorization.
608
- - Intel macOS has no automated behavior suite of its own: modular validation outcomes are keyed by platform without architecture, so the pull-request suites run on macOS arm64 only.
609
- - Reboot and logout restore, CLI tabs, split layouts, remote attach, worktree isolation, agent coordination, automatic cohort handoff, and image protocols are follow-ups.
610
- - Raw child stderr is not captured separately on any platform; milestone 4 provides only a tested private sink.
611
- - Arbitrary child VT output is not fuzzed, because the fuzz crate cannot link the Zig-built `libghostty-vt`; the probe and terminal-fidelity steps cover that path.
612
- - Downgrading to a release without the session-writer guard while tabs run is unsupported; stop the host first.
613
- - A maintenance stop of an unresponsive host uses an ordinary replacement server that is told to stop every tab at once, rather than a dedicated maintenance-only server mode.
614
- - Revisioned, atomic topology mutations remain a target requirement in [`resident-tabs-requirements.md`](resident-tabs-requirements.md).
615
- ### Follow-ups outside this roadmap
616
-
617
- - **Reboot and logout restore:** reopen the previous tab set after a new OS session.
618
- - **Work isolation:** explicit task-to-repository/worktree ownership, dirty-tree policy, concurrent-edit conflicts, and review/integration ownership. A tab's cwd is not an isolation guarantee.
619
- - **Coordination:** task identities, delegation, prompt/result exchange, cancellation, permission and cost limits, and parent/child lifecycle. Herdr's agent start/prompt/read/wait API is a reference, not functionality included here.
620
- - **Presentation and lifecycle:** generic CLI tabs, split layouts, remote attachment, and automatic cohort handoff.
621
-
622
- Each follow-up needs its own approved change.
623
-
624
- ## Recorded User Decisions
625
-
626
- The user made these decisions on 2026-09-24:
627
-
628
- - Every tab is a terminal session running the full A1 UI, so extensions stay native and CLI tabs can follow. This replaces the structured-worker design.
629
- - There is one tab set per profile.
630
- - LLM auto-naming is on by default.
631
- - Icons are the only attention signal, with no bell, sound, or notification: spinner = working, `✓` = finished, red `✗` = failed, yellow `?` = needs the user.
632
- - `Ctrl+C` twice leaves `a1` from any tab while tabs keep running. There is no separate detach key.
633
- - Reliability is a first-class, release-gating requirement (Decision 16).
634
-
635
- On 2026-10-24 the user refined the tab bar:
636
-
637
- - `+` adds an Agent by default; what it offers, Agent, Settings, and Changelog, is configurable from a right-click `Configure` modal over the active tab and from `Tab Bar` › `Plus menu` in Settings, where the `Tabs` section is renamed `Tab Bar`. With nothing enabled, `+` is hidden.
638
- - `Alt+/` opens a keyboard-only command palette over a dimmed frame: `Agent`, `Settings`, and `Changelog`.
639
- - The changelog opens at once as its own prewarmed singleton tab that `Esc` does not close, and leaves resident Agents' commands.
640
- - Opening a tab, a new Agent or a revealed Settings or Changelog tab, draws it at once when its first nonblank frame is drawn, with no fade and no starting spinner; the current content, landing included, stays across blank revisions until then, while explicit failure and recovery remain visible. Quick snaps, prolonged drags, and inactive terminal-tab resizes all repaint directly without a curtain or fade. The landing's wipe animation is removed.
641
- - The tab rename selection matches the editor's selection style, and a tab's name and its session's name stay in sync.
642
-
643
- On 2026-10-16 the `ci-critical-path` change replaced the Intel runner for the release builds:
644
-
645
- - 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.
646
-
647
- On 2026-10-14 the user resolved the milestone-6 `darwin-x64` question:
648
-
649
- - 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.
650
-
651
- On 2026-10-13 the user refined milestone 5:
652
-
653
- - Match the v2 default strip appearance, add an always-visible `×`, and color each working spinner with that tab's resolved A1 accent.
654
- - Keep `Alt+A`, `Alt+W`, `F2`, and numbered jumps; leave next/previous and move actions unbound but assignable.
655
- - Add no tab-management slash commands; retain existing `/name`, `/quit`, and empty-editor `Ctrl+D` semantics.
656
- - Automatically transfer control to the active terminal through the causal barrier without an `Enter` takeover or view-only banner.
657
- - Remove the tab enable/disable setting, always show `tabsMax` and `tabsAutoName` under Tabs during resident launches, and add `a1 solo` for direct single-agent launches with no tab strip or Tabs settings section. Keep unresponsive-restart/stall timing, concurrent starts, and one prewarm internal.
658
- - Keep every open tab process running until explicit close; remove idle suspension and its `◌` glyph.
659
- - Use `1 agent still running run a1 to return.` / `N agents still running run a1 to return.` after local detach.
660
-
661
- On 2026-10-10 the user confirmed this resident design over in-process tabs and set the first version's scope:
662
-
663
- - Deliver as six milestone pull requests; milestones 1–4 merge behind `residentTabs: false`, while the later milestone-5 decision above removes that setting and makes resident tabs the bare-A1 route.
664
- - Target Windows x64, Linux x64, and macOS arm64 in the first version; every additional target requires its own approved plan and evidence.
665
- - Keep crash recovery (restart into the session, prompt journal, drafts); drop reboot and logout restore to a follow-up.
666
- - Keep auto-naming, one prewarmed standby, and two attached clients.
667
- - Hosting only: worktree isolation and agent coordination are separate follow-ups.
668
- - Retire the earlier `evolve-bare-a1-into-multi-agent-workspace` plan (#745); split layouts are a follow-up idea.