@xingular/harness 1.2.0 → 1.3.1-unstable.1

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 (186) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/README.md +26 -17
  3. package/builtin/skills/plugin-authoring/SKILL.md +34 -0
  4. package/dist/cli.js +7362 -6239
  5. package/dist/sdk/agents/builtin.js +3 -3
  6. package/dist/sdk/agents/planResearch.d.ts +23 -1
  7. package/dist/sdk/agents/planResearch.js +71 -8
  8. package/dist/sdk/agents/profiles.d.ts +16 -4
  9. package/dist/sdk/agents/profiles.js +75 -12
  10. package/dist/sdk/annotations/bridge.d.ts +2 -0
  11. package/dist/sdk/annotations/bridge.js +4 -0
  12. package/dist/sdk/annotations/capture.d.ts +44 -2
  13. package/dist/sdk/annotations/capture.js +104 -3
  14. package/dist/sdk/annotations/delivery.d.ts +24 -17
  15. package/dist/sdk/annotations/delivery.js +82 -49
  16. package/dist/sdk/annotations/inspect.js +7 -6
  17. package/dist/sdk/annotations/pageScript.js +4 -4
  18. package/dist/sdk/annotations/part.d.ts +0 -2
  19. package/dist/sdk/annotations/part.js +4 -33
  20. package/dist/sdk/annotations/queue.d.ts +2 -2
  21. package/dist/sdk/annotations/queue.js +12 -7
  22. package/dist/sdk/annotations/serverMethods.js +11 -7
  23. package/dist/sdk/annotations/store.d.ts +13 -21
  24. package/dist/sdk/annotations/store.js +47 -37
  25. package/dist/sdk/capabilities/runSnapshot.js +10 -4
  26. package/dist/sdk/commands/list.js +2 -0
  27. package/dist/sdk/commands/registry.d.ts +11 -3
  28. package/dist/sdk/commands/registry.js +3 -2
  29. package/dist/sdk/core/links.d.ts +51 -0
  30. package/dist/sdk/core/links.js +155 -0
  31. package/dist/sdk/core/shell.d.ts +20 -0
  32. package/dist/sdk/core/shell.js +73 -0
  33. package/dist/sdk/core/workspaceBashTool.d.ts +17 -1
  34. package/dist/sdk/core/workspaceBashTool.js +16 -2
  35. package/dist/sdk/core/workspaceSandbox.d.ts +10 -0
  36. package/dist/sdk/core/workspaceSandbox.js +52 -12
  37. package/dist/sdk/core/workspaceWriteOperations.d.ts +11 -0
  38. package/dist/sdk/core/workspaceWriteOperations.js +26 -8
  39. package/dist/sdk/doctor/checks.js +71 -12
  40. package/dist/sdk/harness.d.ts +5 -0
  41. package/dist/sdk/harness.js +11 -6
  42. package/dist/sdk/hub/hub.js +5 -3
  43. package/dist/sdk/index.d.ts +3 -2
  44. package/dist/sdk/index.js +2 -1
  45. package/dist/sdk/plugins/builtin.js +14 -9
  46. package/dist/sdk/plugins/builtinRegistry.d.ts +3 -1
  47. package/dist/sdk/plugins/builtinRegistry.js +9 -3
  48. package/dist/sdk/plugins/discover.d.ts +7 -1
  49. package/dist/sdk/plugins/discover.js +41 -2
  50. package/dist/sdk/plugins/identity.js +1 -1
  51. package/dist/sdk/plugins/install.d.ts +6 -2
  52. package/dist/sdk/plugins/install.js +29 -5
  53. package/dist/sdk/plugins/inventory.js +3 -3
  54. package/dist/sdk/plugins/manifest.d.ts +2 -0
  55. package/dist/sdk/plugins/manifest.js +21 -4
  56. package/dist/sdk/plugins/plugins.d.ts +2 -0
  57. package/dist/sdk/plugins/plugins.js +1 -0
  58. package/dist/sdk/plugins/proposals.d.ts +17 -0
  59. package/dist/sdk/plugins/proposals.js +147 -0
  60. package/dist/sdk/plugins/types.d.ts +1 -1
  61. package/dist/sdk/plugins/types.js +1 -1
  62. package/dist/sdk/prompt/compile.d.ts +13 -4
  63. package/dist/sdk/prompt/compile.js +46 -20
  64. package/dist/sdk/prompt/standard.d.ts +1 -1
  65. package/dist/sdk/prompt/standard.js +1 -1
  66. package/dist/sdk/server/methods.js +14 -2
  67. package/dist/sdk/sessions/compactionCheckpoint.d.ts +3 -3
  68. package/dist/sdk/sessions/compactionCheckpoint.js +12 -12
  69. package/dist/sdk/sessions/continuation.d.ts +4 -2
  70. package/dist/sdk/sessions/continuation.js +7 -6
  71. package/dist/sdk/sessions/contracts.d.ts +22 -4
  72. package/dist/sdk/sessions/contracts.js +12 -7
  73. package/dist/sdk/sessions/planMode.js +2 -2
  74. package/dist/sdk/sessions/runStore.js +3 -1
  75. package/dist/sdk/sessions/runner.js +2 -2
  76. package/dist/sdk/sessions/sessionState.d.ts +13 -0
  77. package/dist/sdk/sessions/sessionState.js +31 -0
  78. package/dist/sdk/sessions/sessions.d.ts +7 -0
  79. package/dist/sdk/sessions/sessions.js +73 -15
  80. package/dist/sdk/sessions/subagentService.d.ts +15 -0
  81. package/dist/sdk/sessions/subagentService.js +195 -55
  82. package/dist/sdk/sessions/subagents.d.ts +10 -1
  83. package/dist/sdk/sessions/subagents.js +4 -0
  84. package/dist/sdk/sessions/workSlice.d.ts +8 -5
  85. package/dist/sdk/sessions/workSlice.js +77 -45
  86. package/dist/sdk/terminal/controller.d.ts +20 -0
  87. package/dist/sdk/terminal/controller.js +43 -18
  88. package/dist/sdk/terminal/opentui/ansi.d.ts +25 -0
  89. package/dist/sdk/terminal/opentui/ansi.js +192 -0
  90. package/dist/sdk/terminal/opentui/model.d.ts +163 -0
  91. package/dist/sdk/terminal/opentui/model.js +482 -0
  92. package/dist/sdk/terminal/opentui/subagentsWidget.d.ts +3 -3
  93. package/dist/sdk/terminal/opentui/subagentsWidget.js +7 -5
  94. package/dist/sdk/terminal/opentui/theme.d.ts +2 -0
  95. package/dist/sdk/terminal/opentui/theme.js +2 -0
  96. package/dist/sdk/terminal/piExtension.js +98 -15
  97. package/dist/sdk/terminal/subagentMonitor.d.ts +17 -4
  98. package/dist/sdk/terminal/subagentMonitor.js +29 -8
  99. package/dist/sdk/terminal/terminals.d.ts +16 -0
  100. package/dist/sdk/terminal/terminals.js +130 -26
  101. package/dist/sdk/terminal/trajectory.d.ts +6 -1
  102. package/dist/sdk/terminal/trajectory.js +8 -0
  103. package/dist/sdk/terminal/tui/annotateCommand.d.ts +5 -1
  104. package/dist/sdk/terminal/tui/annotateCommand.js +21 -7
  105. package/dist/sdk/terminal/tui/annotateSend.d.ts +30 -0
  106. package/dist/sdk/terminal/tui/annotateSend.js +40 -0
  107. package/dist/sdk/terminal/tui/chrome.d.ts +4 -0
  108. package/dist/sdk/terminal/tui/chrome.js +13 -0
  109. package/dist/sdk/terminal/tui/commandPages.d.ts +1 -1
  110. package/dist/sdk/terminal/tui/commandPages.js +16 -11
  111. package/dist/sdk/terminal/tui/commands.d.ts +4 -0
  112. package/dist/sdk/terminal/tui/commands.js +12 -2
  113. package/dist/sdk/terminal/tui/dialogs.js +1 -0
  114. package/dist/sdk/terminal/tui/pageOverlay.d.ts +3 -0
  115. package/dist/sdk/terminal/tui/pageOverlay.js +25 -7
  116. package/dist/sdk/terminal/tui/pages/agentsPage.js +5 -2
  117. package/dist/sdk/terminal/tui/pages/annotatePage.d.ts +70 -16
  118. package/dist/sdk/terminal/tui/pages/annotatePage.js +349 -115
  119. package/dist/sdk/terminal/tui/pages/browserPage.js +5 -4
  120. package/dist/sdk/terminal/tui/pages/hubPage.d.ts +14 -1
  121. package/dist/sdk/terminal/tui/pages/hubPage.js +192 -28
  122. package/dist/sdk/terminal/tui/pages/pluginsPage.d.ts +14 -1
  123. package/dist/sdk/terminal/tui/pages/pluginsPage.js +295 -25
  124. package/dist/sdk/terminal/tui/pages/subagentsPage.js +8 -5
  125. package/dist/sdk/terminal/tui/pages/usagePage.d.ts +4 -2
  126. package/dist/sdk/terminal/tui/pages/usagePage.js +39 -12
  127. package/dist/sdk/terminal/tui/pluginCommands.d.ts +1 -1
  128. package/dist/sdk/terminal/tui/pluginCommands.js +13 -3
  129. package/dist/sdk/tools/defaultSkills.d.ts +4 -0
  130. package/dist/sdk/tools/defaultSkills.js +20 -0
  131. package/dist/sdk/tools/definitions.d.ts +3 -1
  132. package/dist/sdk/tools/definitions.js +17 -1
  133. package/dist/sdk/tools/dispatch.js +12 -0
  134. package/dist/sdk/tools/harnessTools.js +1 -1
  135. package/dist/sdk/tools/sessionName.d.ts +9 -0
  136. package/dist/sdk/tools/sessionName.js +58 -11
  137. package/dist/sdk/tools/spawnSubagent.js +9 -7
  138. package/dist/sdk/tools/todo.d.ts +15 -0
  139. package/dist/sdk/tools/todo.js +64 -0
  140. package/dist/sdk/world/world.d.ts +0 -2
  141. package/dist/terminal-opentui.js +3331 -1584
  142. package/dist/terminal-pi.js +421 -133
  143. package/docs/README.md +3 -0
  144. package/docs/architecture/00-overview.md +1 -1
  145. package/docs/architecture/01-extension-points.md +1 -1
  146. package/docs/architecture/02-sessions-and-runs.md +73 -36
  147. package/docs/architecture/03-variants.md +1 -3
  148. package/docs/architecture/04-prompt.md +24 -13
  149. package/docs/architecture/06-plugins-and-capabilities.md +46 -10
  150. package/docs/architecture/07-tools-and-gate.md +49 -8
  151. package/docs/architecture/09-accounts-and-connectors.md +8 -2
  152. package/docs/architecture/10-server-protocol.md +68 -16
  153. package/docs/architecture/11-terminal.md +94 -45
  154. package/docs/architecture/12-storage.md +1 -0
  155. package/docs/architecture/13-world-tools.md +8 -6
  156. package/docs/architecture/14-surfaces-and-parity.md +4 -1
  157. package/docs/architecture/README.md +20 -18
  158. package/docs/architecture/proposals/PROP-0006-run-checklist.md +2 -0
  159. package/docs/architecture/proposals/PROP-0018-annotation-queue-and-app-targets.md +6 -2
  160. package/docs/architecture/proposals/PROP-0041-bounded-work-slices.md +32 -21
  161. package/docs/architecture/proposals/PROP-0044-work-slice-defaults.md +93 -0
  162. package/docs/architecture/proposals/PROP-0045-rename-checklist-to-todo.md +132 -0
  163. package/docs/architecture/proposals/PROP-0046-progress-aware-stall-guards.md +82 -0
  164. package/docs/architecture/proposals/PROP-0047-plugins-hub-separation.md +122 -0
  165. package/docs/architecture/proposals/PROP-0048-simple-annotate.md +85 -0
  166. package/docs/architecture/proposals/README.md +11 -6
  167. package/docs/decisions/ADR-0039-progress-aware-stall-guards.md +39 -0
  168. package/docs/decisions/ADR-0040-architecture-change-workflow.md +31 -0
  169. package/docs/decisions/ADR-0041-plugins-hub-separation.md +41 -0
  170. package/docs/decisions/ADR-0042-windows-shell-without-sandbox.md +40 -0
  171. package/docs/decisions/ADR-0043-parents-continue-stopped-subagents.md +49 -0
  172. package/docs/decisions/ADR-0044-annotations-pending-or-sent.md +24 -0
  173. package/docs/decisions/ADR-0045-default-plugin-authoring-skill.md +9 -0
  174. package/docs/decisions/ADR-0046-hash-bound-plugin-proposals.md +20 -0
  175. package/docs/decisions/ADR-0047-links-open-with-the-os-handler.md +47 -0
  176. package/docs/doctor.md +14 -1
  177. package/docs/parity.md +16 -13
  178. package/docs/releases/1.3.0.md +21 -0
  179. package/docs/releases/1.3.1-unstable.1.md +8 -0
  180. package/docs/releases/README.md +9 -6
  181. package/docs/terminal-tui.md +127 -65
  182. package/package.json +1 -1
  183. package/dist/sdk/terminal/tui/pages/notesPage.d.ts +0 -75
  184. package/dist/sdk/terminal/tui/pages/notesPage.js +0 -201
  185. package/dist/sdk/tools/checklist.d.ts +0 -9
  186. package/dist/sdk/tools/checklist.js +0 -55
@@ -59,8 +59,9 @@ mechanism below is **run execution**, which the harness owns.
59
59
 
60
60
  A foundation policy, on by default for every run (interactive, automatic, plan, probe, subagent):
61
61
 
62
- - `workSlice.enabled` true; `workSlice.maxToolCalls` 160, `workSlice.maxMinutes` 40,
63
- `workSlice.maxNewTokens` 320 000. `enabled: false`, or `XINGULAR_WORK_SLICE=off` at launch,
62
+ - `workSlice.enabled` true; `workSlice.maxToolCalls` 320, `workSlice.maxMinutes` 60,
63
+ `workSlice.maxNewTokens` 0, so no token cap by default (the values were raised by PROP-0044).
64
+ `enabled: false`, or `XINGULAR_WORK_SLICE=off` at launch,
64
65
  removes the guard from a run: no budget, no stall notes, no continuation. Any
65
66
  limit reached, or `0` to disable that one, ends the slice. A token cap counts new input, cache
66
67
  writes and output; counting the cached prefix made one large read end a reply before any work
@@ -120,20 +121,29 @@ A foundation extension (`src/sessions/workSlice.ts`), registered where the compa
120
121
  - Signatures are `toolName + canonicalised args`, hashed, so every argument participates and a
121
122
  changed argument (a checklist whose steps moved on, a different path) is a different call. Detect,
122
123
  per reply:
123
- - the same signature 3 times (`checkpoint_write`, `session_name`, `wait_subagents` and
124
- `subagents.*` polling exempt);
125
- - the same Bash command failing twice;
126
- - 25 tool calls with no `file.changed` and no passing check;
127
- - one file edited 4 times with no intervening passing check.
128
- - The counts and the nudge start fresh in each reply, because each reply is one slice: progress a
129
- later reply reports cannot make an earlier reply's call look repeated. Reading a fixed handful of
130
- field names instead of all arguments once made every `checklist_write` look identical, which is
131
- how a reply that reported its progress was told it was repeating work.
132
- - First hit: block that call once and inject a compiler-rendered, bounded evidence note (the earlier
133
- timestamps, exit codes and paths). This is the Manus "keep the failure visible" rule.
134
- - Second hit for the same signature, or any hit after one nudge in the same slice: force a
135
- checkpoint, end the slice with `blocked`, and report the evidence instead of continuing. Never a
136
- silent kill, never an unbounded retry.
124
+ - the same signature 3 times (`checkpoint_write` and `session_name` are handoff calls when a
125
+ checkpoint is available; `todo_write`, `wait_subagents` and `subagents.*` polling are exempt);
126
+ - the same Bash check failing twice;
127
+ - four successful native `edit`/`write` results on one path without an intervening passing check;
128
+ the next edit attempt is refused, and a further trigger ends the slice;
129
+ - context-compaction thrash at the third compaction.
130
+ - Only a `tool_result` with `isError === false` and a path counts as a successful edit or write. A
131
+ failed or blocked attempt consumes no edit-churn allowance. A successful edit/write clears failed
132
+ check counts, exact-repeat counts for Bash check calls, and the pending nudge so that a fixed check
133
+ may run against changed code. It leaves per-file edit counts and exact-repeat counts for other
134
+ calls intact. A passing check clears edit churn and the reply's repetition state.
135
+ - There is no general 25-call file-change/check no-progress trigger. The work-slice call/time budget
136
+ and the targeted repeated-call, repeated-failing-check, edit-churn and compaction-thrash protections
137
+ remain. `todo_write` is excluded from repeated-call detection and does not reset loop state, but it
138
+ remains subject to the tool-call budget. Counts and the nudge start fresh in each reply, because
139
+ each reply is one slice.
140
+ - With `checkpoint_write`, the first targeted trigger is refused with a bounded nudge; repeating the
141
+ trigger ends the reply as `stalled` and the runner derives a checkpoint when the model wrote none.
142
+ A plan-research child keeps its frozen read-only capability set even though it lacks
143
+ `checkpoint_write`: it receives the same configured work-slice bound, is told to stop and return a
144
+ concise partial research report on its first budget or stall refusal, and a further tool call ends
145
+ the reply. The runner records an evidence-derived checkpoint; the child is not auto-continued and
146
+ receives no additional tool or connector permission.
137
147
 
138
148
  ### 5. Command budget
139
149
 
@@ -194,7 +204,7 @@ A foundation extension (`src/sessions/workSlice.ts`), registered where the compa
194
204
  ## Consequences
195
205
 
196
206
  - More runs per long task, each with its own record and pins. Continuation already supports this, so
197
- session and thread views group them; a person sees a checkpoint every ≤ 40 minutes instead of
207
+ session and thread views group them; a person sees a checkpoint every ≤ 60 minutes instead of
198
208
  nothing.
199
209
  - Two new tools/events and two new settings, which the App must present for parity. The compiled tool
200
210
  manifest gains one entry, which changes the provider-cache prefix once (INV-PRM-8: measure with real
@@ -230,10 +240,11 @@ Implemented in this tree:
230
240
 
231
241
  - `checkpoint_write` (`src/tools/checkpoint.ts`) with its plugin descriptor, frozen capability entry,
232
242
  prompt group and terminal socket bridge; the run records `checkpoint`.
233
- - The slice guard (`src/sessions/workSlice.ts`): budget refusal, checkpoint allowance (including the
234
- first call past the limit), termination,
235
- repeated-call, repeated-failure, edit-churn and no-progress notes, compaction-thrash guard, and
236
- per-reply reset. Wired for structured and terminal replies.
243
+ - The slice guard (`src/sessions/workSlice.ts`): budget refusal, checkpoint-aware handoff, targeted
244
+ repeated-call and repeated-failure notes, success-only edit churn with progress-aware check retry
245
+ resets, compaction-thrash guard, and per-reply reset. The broad file/check no-progress trigger is
246
+ removed; plan-research children without `checkpoint_write` use the derived-checkpoint handoff and
247
+ the same configured bounds without gaining tools. Wired for structured and terminal replies.
237
248
  - The runner's `heartbeat`, derived fallback checkpoint, `slice_complete`/`stalled` terminal reasons
238
249
  and the third `finish` argument.
239
250
  - Auto-continue with `maxSlices` 8, a fruitless-slice stop and a blocked stop, off for automatic,
@@ -0,0 +1,93 @@
1
+ ---
2
+ id: PROP-0044
3
+ title: Raise the work-slice defaults to 320 tool calls and 60 minutes, with no token cap
4
+ status: Accepted
5
+ created: 2026-10-26
6
+ updated: 2026-10-26
7
+ affects: [INV-RUN-13, 02-sessions-and-runs.md, 10-server-protocol.md, 11-terminal.md, PROP-0041]
8
+ ---
9
+
10
+ # PROP-0044: Raise the work-slice defaults to 320 tool calls and 60 minutes, with no token cap
11
+
12
+ ## Problem
13
+
14
+ `DEFAULT_SLICE_POLICY` (`src/sessions/workSlice.ts`) and `INV-RUN-13` set 160 tool calls, 40 minutes
15
+ and 320 000 new tokens, whichever comes first. Two of those defaults are wrong for ordinary work.
16
+
17
+ - **The token cap is the earliest limit, and it is not the bound it claims to be.** Its own comment
18
+ called 320 000 "a safety net, not the bound", yet it counts new input, cache writes and output, so a
19
+ few large reads in a big context cross it before the work is done. The "Confirm plan model support"
20
+ thread counted 76 807 new tokens after four requests, and `workSlice.test.ts` still carried a case
21
+ asserting a 320k stop. A cap that fires on reading is a false stop, and the stall rules — not the
22
+ token count — are what detect an unbounded reply.
23
+ - **40 minutes is tighter than the field and tighter than the evidence.** The measured failure in
24
+ STUDY-0003 was a reply that ran 114 minutes with no text and 448 calls, not one that ran 40 minutes.
25
+ OpenHands allows 500 agent steps per run by default; Claude's Agent SDK has no default `maxTurns`;
26
+ Cursor documents no tool-call limit; SWE-agent's caps are benchmark stops ($3, 1800 s) that
27
+ auto-submit and exit. A checkpoint every 40 minutes turns one healthy task into a series of runs
28
+ without a measured benefit.
29
+
30
+ The rule stays: a reply is bounded and hands off. What changes is where the boundary sits.
31
+
32
+ ## Affected rules
33
+
34
+ - **INV-RUN-13** says: "The foundation defaults are 160 tool calls, 40 minutes and 320 000 new tokens,
35
+ whichever comes first." The invariant's requirement — a bounded reply, a handoff at the limit and a
36
+ continuation — is unchanged; its stated numbers and the default token cap change.
37
+ - **PROP-0041** §1 states the same numbers and §Consequences says "a checkpoint every ≤ 40 minutes";
38
+ both are corrected to point here.
39
+
40
+ ## Proposal
41
+
42
+ `DEFAULT_SLICE_POLICY` becomes:
43
+
44
+ | Setting | Was | Now |
45
+ |---|---|---|
46
+ | `maxToolCalls` | 160 | **320** |
47
+ | `maxMinutes` | 40 | **60** |
48
+ | `maxNewTokens` | 320 000 | **0** (no cap) |
49
+ | `maxSlices` | 8 | 8 (unchanged) |
50
+ | `continue` | true | true (unchanged) |
51
+
52
+ - Calls and the clock are the bound, whichever comes first: about 80 minutes of ordinary work at 15 s a
53
+ call, with 60 minutes as the ceiling.
54
+ - A token cap stays available to whoever wants a cost ceiling: `workSlice.maxNewTokens` per run, still
55
+ counted on new input, cache writes and output and never on the cached prefix.
56
+ - Nothing else changes: the refusal and `checkpoint_write`, the exemption for a handoff call, the
57
+ stall rules and their per-reply reset, the terminal's behaviour, and automatic continuation.
58
+
59
+ ## Alternatives
60
+
61
+ - **Do nothing.** Rejected: the token cap was the first limit a large read crossed, and its stop is
62
+ visible to the person as an arbitrary end of a useful reply.
63
+ - **Keep a 320 000 safety net and raise only the calls and the clock.** Rejected: the same false stop
64
+ stays, and two limits that are never reached in ordinary work are enough.
65
+ - **Turn the guard off by default.** Rejected for now: the stall rules are the evidence-backed part
66
+ (repeated identical calls, repeated failures, edit churn, compaction thrash), and the budget itself
67
+ is what bounds a reply. Only its size changes.
68
+ - **Bound cost in dollars instead of tokens.** Not adopted here: the harness records cost per request,
69
+ but a dollar ceiling belongs with the account's usage limits (`usage/limits`, PROP-0042), not with a
70
+ reply's work slice.
71
+
72
+ ## Consequences
73
+
74
+ - A healthy task reaches its first checkpoint after roughly 80 minutes of ordinary work instead of 40,
75
+ and no token count ends a reply on its own. The longest a task runs unattended is still eight slices,
76
+ so the slice cap now allows up to about eight hours instead of about five.
77
+ - With no token cap, only calls and time bound an expensive reply; a person who wants a token ceiling
78
+ sets one, and the heartbeat still reports `steps` against `maxToolCalls`.
79
+ - No stored-data, protocol or event change: an absent `workSlice` simply resolves to the new defaults,
80
+ and a run already recorded keeps the policy it ran under.
81
+
82
+ ## Migration
83
+
84
+ None. Undo by restoring the three values in `DEFAULT_SLICE_POLICY`, or by passing `workSlice` on
85
+ `sessions.run`. A continuation inherits the policy as it always did.
86
+
87
+ ## Checks
88
+
89
+ - `npx vitest run src/sessions/workSlice.test.ts src/sessions/sessions.test.ts` (the defaults
90
+ assertion and the limit-selection test change with the values; the token-cap test keeps an explicit
91
+ override)
92
+ - `npm run typecheck`, `npm test`, `npm run prove`
93
+ - App: `npm run typecheck`, `npm test`, `npm run check:layers`, `cd src-tauri && cargo test`
@@ -0,0 +1,132 @@
1
+ ---
2
+ id: PROP-0045
3
+ title: Rename the run checklist to todo and add the /todo command
4
+ status: Accepted; implemented
5
+ created: 2026-10-26
6
+ updated: 2026-10-26
7
+ affects: [02-sessions-and-runs.md, 04-prompt.md, 06-plugins-and-capabilities.md, 07-tools-and-gate.md, 10-server-protocol.md, 11-terminal.md, 14-surfaces-and-parity.md, parity.md, terminal-tui.md, README.md, PROP-0006]
8
+ ---
9
+
10
+ # PROP-0045: Rename the run checklist to todo and add the /todo command
11
+
12
+ ## Problem
13
+
14
+ One run concept is named "checklist": the `checklist_write` tool, its `checklist.updated` event,
15
+ `ChecklistItem`, `checklistOf`, the `todo` list's `runs.checklist` method, the `checklist` field of
16
+ `runs.continuation`, the `checklist` plugin, and the terminal's checklist block and `checklist`
17
+ telemetry. Every agent framework a person meets elsewhere calls the same idea a todo list, and
18
+ Claude Code's `/todos` made the word familiar: `todo`, `todo_write`, `/todo`. PROP-0006 chose
19
+ "checklist" explicitly to avoid an application concept (`PROP-0006-run-checklist.md:28`), but
20
+ "todo" is generic vocabulary, not a product name, and the current name makes the tool harder to
21
+ recognise for both the model and the person.
22
+
23
+ The list is also only visible where the model last wrote it: the terminal transcript and (nowhere) in
24
+ the App. A person who scrolled past a write, resumed a session, or is watching a long reply has no
25
+ way to ask for the current list. The terminal already has a slash-command surface for this kind of
26
+ read (`/help`, `/subagents`, `/trajectory`); there is no `/todo`.
27
+
28
+ ## Affected rules
29
+
30
+ - **02-sessions-and-runs.md, Data** defines `checklist.updated` and `checklistOf(events)`.
31
+ - **04-prompt.md** defines the Progress paragraph that names `checklist_write`.
32
+ - **07-tools-and-gate.md** defines `checklistTool` and its event.
33
+ - **10-server-protocol.md** defines `runs.checklist`.
34
+ - **11-terminal.md** defines the checklist block, the telemetry call and the terminal's tool.
35
+ - **14-surfaces-and-parity.md** and **parity.md** (PAR-001) define the terminal and App surfaces.
36
+ - No invariant's rule changes: the tool remains read-only and plan-safe, the event remains a
37
+ whole-list replacement, and the list remains one bounded run event. Only the names and one added
38
+ read command change.
39
+
40
+ ## Proposal
41
+
42
+ ### 1. The concept is named todo
43
+
44
+ | Was | Becomes |
45
+ |---|---|
46
+ | tool `checklist_write` | `todo_write` |
47
+ | event `checklist.updated` | `todo.updated` |
48
+ | `ChecklistItem`, `checklistItemSchema`, `MAX_CHECKLIST_ITEMS` | `TodoItem`, `todoItemSchema`, `MAX_TODO_ITEMS` |
49
+ | `checklistOf`, `describeChecklist`, `checklistTool` | `todoOf`, `describeTodo`, `todoTool` |
50
+ | plugin id `checklist`, title "Checklist" | plugin id `todo`, title "Todo" |
51
+ | method `runs.checklist { runId }` | `runs.todo { runId }` |
52
+ | `Continuation.checklist` | `Continuation.todo` |
53
+ | terminal telemetry `checklist` | `terminal.todo` |
54
+ | transcript kind `checklist`, header "Checklist" | kind `todo`, header "Todo" |
55
+ | `TerminalSubagentActivity.checklist` | `.todo` |
56
+
57
+ The tool keeps its behaviour: the model sends the whole list, the latest event replaces every
58
+ earlier one, at most 50 items, `pending`, `in_progress` and `completed`, read-only for the gate and
59
+ allowed in plan mode. The prompt's Progress paragraph asks for a todo list in the same places.
60
+
61
+ ### 2. `/todo` shows the current list
62
+
63
+ `/todo` is a core command (`src/commands/registry.ts`), `group: 'Inspect'`, `kind: 'session'`,
64
+ `methods: ['runs.todo']`, with `/checklist` as a compatibility alias for one release.
65
+
66
+ - It reads the latest non-side-question run of the current native session and returns that run's
67
+ latest todo. It makes no model request, asks for no approval, and is available in plan mode.
68
+ - The terminal appends a snapshot card to the transcript: `Todo · n of m done`, then one row per
69
+ item with the existing glyphs and colours (`✓` completed, `●` in progress, `○` pending). An empty
70
+ list is a one-line notice, not an empty card.
71
+ - The App offers the same read through `runs.todo` as a read-only picker with the same marks. It does
72
+ not render each `todo_write` in its transcript; that presentation difference is recorded in
73
+ PAR-001.
74
+
75
+ ### 3. Compatibility
76
+
77
+ Stored run events are never rewritten. For one minor release:
78
+
79
+ - `runEventTypeSchema` accepts `checklist.updated` beside `todo.updated`, both with the same payload;
80
+ `todoOf` reads both. New events are written only as `todo.updated`.
81
+ - A replayed native history whose tool call is `checklist_write` still renders the todo card.
82
+ - `runs.checklist` remains as a deprecated alias returning the same list as `runs.todo`.
83
+ - `runs.continuation` returns `todo` and keeps `checklist` as a deprecated mirror field.
84
+ - A pinned capability set containing `checklist_write` is read as `todo_write`, so an interrupted
85
+ run recorded before the rename can still continue with a todo tool.
86
+ - The exported `checklistOf` and `CHECKLIST_WRITE` names stay as deprecated aliases for embedders.
87
+
88
+ The aliases and the legacy event type can be removed in the next major release.
89
+
90
+ ## Alternatives
91
+
92
+ - **Rename only the labels the person sees.** Rejected: the tool name the model sees, the event, the
93
+ method and the docs would disagree, which is the drift the specification exists to prevent.
94
+ - **Keep both names indefinitely.** Rejected: two names for one concept in the prompt and the
95
+ protocol, and a permanent compatibility branch. One release is enough for stored data that is
96
+ never rewritten.
97
+ - **Rewrite stored events to the new type.** Rejected: `events.jsonl` is append-only evidence; a
98
+ read alias is enough and cannot corrupt a run.
99
+ - **Make `/todo` a live panel or a full-screen page.** Rejected for this change: a transcript
100
+ snapshot matches `/help`, reuses the existing block, and leaves the person's scrollback intact.
101
+ A live panel can be a later presentation change; it is not needed to show the list.
102
+ - **Do nothing.** Rejected by the owner's request and by the missing read surface.
103
+
104
+ ## Consequences
105
+
106
+ - One tool name and one prompt paragraph change, so the compiled tool manifest and the provider
107
+ cache prefix change once. The cost is measurable only with real requests (`INV-PRM-8`), and the
108
+ change does not alter any model-visible behaviour besides the name.
109
+ - Applications built on the harness keep working through the method, field and capability aliases;
110
+ they should move to the new names. The App's event schema accepts both event types, so old run logs
111
+ keep displaying.
112
+ - Runs created before the rename keep their recorded events. `todoOf` and the terminal renderer read
113
+ them without a rewrite.
114
+ - PAR-001 narrows: both surfaces can now show the current todo; the App still does not draw an inline
115
+ block for each write.
116
+
117
+ ## Migration
118
+
119
+ No data migration and no file rewrite. Old events, old pinned capability sets and old native
120
+ histories are read through the aliases above. Rolling back is reverting the commit; new
121
+ `todo.updated` events would then need a reverse alias if they were already written, so the rollback
122
+ is safe only before new runs write the event.
123
+
124
+ ## Checks
125
+
126
+ - `npm run typecheck`, `npm test`, `npm run prove`.
127
+ - New tests: the todo tool, a legacy `checklist.updated` read, a legacy pinned capability, the
128
+ `runs.todo` method and its alias, the terminal `/todo` card, and a replayed `checklist_write` call.
129
+ - `python3 scripts/verify-terminal.py --todo-only` at 80x24 and 100x30: the card, the alignment of
130
+ wrapped rows, and no model request for `/todo`.
131
+ - App: `npm run typecheck`, `npm test`, `npm run check:layers`, `npm run check:ui`,
132
+ `cd src-tauri && cargo test`.
@@ -0,0 +1,82 @@
1
+ ---
2
+ id: PROP-0046
3
+ title: Progress-aware stall guards and bounded plan research children
4
+ status: Implemented
5
+ created: 2026-10-27
6
+ updated: 2026-10-27
7
+ affects: [INV-RUN-13, 02-sessions-and-runs.md, PROP-0041, terminal-tui.md, ADR-0039]
8
+ ---
9
+
10
+ # PROP-0046: Progress-aware stall guards and bounded plan research children
11
+
12
+ ## Problem
13
+
14
+ The stall guard currently has a broad 25-call rule: it nudges after substantive calls without a file change or passing check. It is disabled for plan-mode research, but remains active for ordinary runs. A long, legitimate sequence of reads can be stopped in normal mode but not plan mode; the rule imposes a mode-specific file/check-progress boundary rather than detecting a loop.
15
+
16
+ Two targeted rules also ignore successful code changes. Failed-check counts and identical Bash-check calls can persist after an edit, so a fixed test command may be blocked even though its input code has changed. The per-file edit-churn count is incremented in `tool_call`, before the edit runs, so a denied or failed attempt can count as an edit.
17
+
18
+ Finally, plan-research children receive only the explicit read-only research tool intersection, which excludes `checkpoint_write` (`src/agents/planResearch.ts`). `Sessions` installs the work-slice guard only when that tool is in the frozen capability set (`src/sessions/sessions.ts`). These children consequently lack the call/time boundary that the work-slice proposal says applies to subagent runs. The terminal guide also says plan-mode shell commands are unavailable even though the parent may run OS-sandboxed read-only Bash (`docs/terminal-tui.md`; PROP-0043).
19
+
20
+ ## Affected rules
21
+
22
+ - **INV-RUN-13** currently says: “The file-change/check-based no-progress detector does not apply to plan-mode read-only research.” This proposal removes the detector for all runs, rather than preserving a mode-specific exception. It also clarifies how successful edits affect the remaining stall counters and how a bounded child without `checkpoint_write` hands back.
23
+ - **PROP-0041, §4 Stall guard** currently includes “25 non-bookkeeping tool calls without a file change or passing check,” counts edit attempts before execution, and describes repeated Bash failures without resetting for a changed code state. Its stall-guard description must match the new rule.
24
+ - **INV-RUN-8** remains unchanged: plan-research children keep their existing frozen, read-only capability intersection, with no connectors, questions, writes, commands or nested subagents. The child work-slice bound does not add a tool or widen that set.
25
+ - **`docs/terminal-tui.md`** must distinguish the parent's sandboxed read-only Bash from the child's no-command restriction.
26
+
27
+ ## Proposal
28
+
29
+ ### 1. Remove the broad no-progress trigger
30
+
31
+ Remove the 25-call file-change/check detector from every run. Remove its counter and plan-mode exception; do not replace it with another general “no file change” threshold. The tool-call/time work-slice budget remains.
32
+
33
+ Keep the targeted stall protections: the same exact call signature still receives one nudge at its existing threshold and ends the reply on a subsequent trigger; the same Bash check failing twice remains a trigger; per-file edit churn remains limited to four successful edits without a passing check; and the existing third-compaction thrash guard remains. Todo bookkeeping remains exempt from repeated-call detection and remains counted toward the work-slice tool-call budget.
34
+
35
+ ### 2. Tie check retries and edit churn to observed results
36
+
37
+ Use the post-execution `tool_result` for native `edit` and `write` tools. A result with `isError === false` and a path is a successful edit for this rule; a blocked or failed call does not count. After four successful edits to one path without a passing check, the next edit attempt for that path receives the existing nudge/block, and a further trigger ends the slice. A passing check continues to clear the per-file edit counts.
38
+
39
+ A successful native edit/write starts a fresh check-retry episode: clear failed-check counts, the exact-repeat count for Bash check calls, and the pending one-shot nudge so the same check can run against changed code. Exact-repeat protection remains active for unchanged code and for other calls. The successful-edit counts used by the per-file churn rule are not cleared by this reset; only a passing check clears them. Failed or denied edits reset none of these counters.
40
+
41
+ The cross-surface success signal is the tool result, not a byte-level file comparison. The guard must count only a confirmed non-error result and use its path; no filesystem watcher or new event schema is introduced.
42
+
43
+ ### 3. Bound read-only plan-research children
44
+
45
+ When work-slice policy is enabled, attach the existing guard to plan-research child runs even when their frozen capabilities omit `checkpoint_write`. Use the existing policy/defaults (320 calls and 60 minutes, no token cap); inherit an explicit parent policy override. Plan and subagent runs still do not auto-continue.
46
+
47
+ A child without `checkpoint_write` must not be told to call an unavailable tool. The first budget or stall refusal tells it to stop using tools and return a concise partial research report; if it attempts another tool, the existing termination rule applies. The runner's existing evidence-derived checkpoint records the handoff when the model did not write one. The child receives no additional tool or connector permissions. This proposal bounds each child, not the aggregate number of siblings; the existing parent-controlled fan-out is unchanged. The time limit remains checked at tool-call boundaries and is not a cancellation timer for an in-flight tool.
48
+
49
+ ### 4. Keep plan-mode action restrictions
50
+
51
+ Do not enable generic browser clicks or `mcp_call_tool` in plan mode. Existing per-call safety classifications and read-only restrictions remain unchanged. A future need for connected-app research must add server-validated read-only classification rather than admitting the generic broker call.
52
+
53
+ Clarify `docs/terminal-tui.md`: an interactive plan parent may run Bash in the OS-enforced read-only sandbox (PROP-0043); plan-research children still cannot run commands. Writes, network access and generic connected-app actions remain unavailable in plan mode.
54
+
55
+ ## Alternatives
56
+
57
+ - **Keep the 25-call trigger outside plan mode.** Rejected: it still mistakes legitimate read/research sequences for lack of progress and preserves an arbitrary mode distinction.
58
+ - **Remove all stall protections.** Rejected: exact repeated calls, repeated failing checks, edit churn, compaction thrash detection and the independent work-slice budget remain useful, targeted bounds.
59
+ - **Give plan-research children `checkpoint_write` or broader tools.** Rejected: the existing derived-checkpoint path is sufficient and preserves the frozen read-only capability intersection.
60
+ - **Add a tighter child-only budget or sibling concurrency cap.** Deferred: this change applies the established work-slice policy per child; aggregate fan-out remains a separate decision.
61
+ - **Detect byte-level file changes.** Rejected for this change: the post-execution native tool result is the consistent signal available to the shared guard across surfaces.
62
+
63
+ ## Consequences
64
+
65
+ A run may make any number of distinct reads without hitting a file/check-based no-progress stall, subject to the work-slice budget and the retained targeted loop protections. After a successful edit, the same Bash check can be retried against the changed code; unsuccessful edit attempts do not consume the four-edit churn allowance.
66
+
67
+ Plan-research children can stop at the same call/time boundary without receiving a new capability. A child that reaches the bound is recorded as an interrupted slice with the runner-derived checkpoint and can return partial findings; it is not automatically restarted. No server method, run-event schema, stored-data layout or App presentation changes are required. Existing plan-mode browser and connector restrictions remain in force.
68
+
69
+ This lasting foundation guardrail decision is recorded in [ADR-0039](../../decisions/ADR-0039-progress-aware-stall-guards.md).
70
+
71
+ ## Migration
72
+
73
+ None. Existing run records and checkpoints remain readable. The proposal changes only future run behavior; no event or capability snapshot is rewritten. A child stopped at its slice boundary uses the existing `checkpoint` event and terminal reason contracts.
74
+
75
+ ## Checks
76
+
77
+ After acceptance:
78
+
79
+ - Focused Harness tests: `npx vitest run src/sessions/workSlice.test.ts src/sessions/sessions.test.ts src/sessions/subagentService.test.ts src/sessions/planMode.test.ts`.
80
+ - Add regressions for more than 25 distinct reads in normal and plan runs; unchanged exact repeats; failures followed by a successful edit and a retry of the same Bash check; failed/blocked edits not counting; four successful edits to one path; and a plan child receiving a work-slice bound without gaining `checkpoint_write` or other tools, using a derived checkpoint and no auto-continuation.
81
+ - Run Harness `npm run typecheck`, `npm test`, `npm run prove`, and `git diff --check`.
82
+ - Run Xingular Agent `npm run typecheck`, `npm test`, `npm run check:layers`, and `cd src-tauri && cargo test`.
@@ -0,0 +1,122 @@
1
+ ---
2
+ id: PROP-0047
3
+ title: Separate local plugin management from Community Hub discovery
4
+ status: Implemented
5
+ created: 2026-10-27
6
+ updated: 2026-10-27
7
+ affects: [INV-MCP-4, INV-TUI-2, INV-TUI-4, INV-PAR-1/2, 06-plugins-and-capabilities.md, 09-accounts-and-connectors.md, 10-server-protocol.md, 11-terminal.md, 14-surfaces-and-parity.md, terminal-tui.md, parity.md]
8
+ ---
9
+
10
+ # PROP-0047: Separate local plugin management from Community Hub discovery
11
+
12
+ ## Problem
13
+
14
+ `/plugins` and `/hub` currently open the same `BrowserPage` (`src/terminal/tui/commandPages.ts`),
15
+ with different initial filters. That page combines local plugin controls with public catalog
16
+ browsing. Separate `PluginsPage` and `HubPage` implementations already exist but are not routed by
17
+ the commands. The App similarly presents local plugins and the public catalog in one Capability
18
+ Hub view.
19
+
20
+ The distinction matters operationally: direct plugin operations use `plugins.install`,
21
+ `plugins.update` and `plugins.remove`; catalog installs use exact Hub versions and create receipts
22
+ through `Hub.install`. A direct `plugins.remove` currently removes the package without updating a
23
+ Hub receipt (`src/server/methods.ts`, `src/plugins/install.ts`), whereas `Hub.uninstall` owns both
24
+ package removal and receipt cleanup (`src/hub/hub.ts`). Pi packages also have no JSON payload to
25
+ preview (`Hub.preview` rejects them), and a failed remote refresh must not make cached catalog data
26
+ look current or successful.
27
+
28
+ The requested product behavior is: `/plugins` manages local installed plugins and their package
29
+ lifecycle; `/hub` is a separate public-catalog page showing Community entries across supported
30
+ catalog types. Hub's Installed view retains receipts from every channel, labeled by channel. Opening
31
+ Hub first shows cached/bundled entries and then refreshes remotely, retaining visible origin and
32
+ stale/error status.
33
+
34
+ ## Affected rules
35
+
36
+ The current terminal-page contract says that `/plugins` and `/hub` are shortcuts into the unified
37
+ resource browser (`docs/architecture/11-terminal.md`, “Resource browser”):
38
+
39
+ > `/skills`, `/plugins`, `/connections` (`/mcp`), `/tools`, `/hub` and `/components` are shortcuts into that browser.
40
+
41
+ The terminal guide also says the resource sections share Installed/Discover and source filters, and
42
+ that `/hub` selects Discover (`docs/terminal-tui.md`, “The Harness resource browser”). This proposal
43
+ replaces those two route-specific statements; the broad `/harness` browser remains available.
44
+
45
+ Receipt handling must continue to obey **INV-MCP-4**:
46
+
47
+ > Hub installation grants no attachment, trust, credential or permission. Writes are serialized;
48
+ > existing registry files are never overwritten; uninstall removes only the exact receipt's
49
+ > resources and revokes its attachment choices.
50
+
51
+ The proposal does not weaken **INV-PLG-3**: package installation runs no code and grants no
52
+ attachment, trust, credential or permission. Nor does it change **INV-TUI-4**: terminal mutations
53
+ continue through the control-key-protected routes. **INV-PAR-1/2** continue to require the same
54
+ capabilities and acceptance on terminal and host surfaces.
55
+
56
+ ## Proposal
57
+
58
+ 1. Route the direct terminal commands to distinct pages: `/plugins` to the local plugin manager and
59
+ `/hub` to the public Hub catalog. Keep `/harness` as the broader resource browser. The pages are
60
+ distinct presentations, not new capabilities or loaders.
61
+ 2. The Plugins page manages local installed packages and their contributions: inventory, switching,
62
+ code trust, settings, direct package installation, update and removal. Public catalog discovery
63
+ is not part of this page. Ordinary local packages use the existing `plugins.*` services.
64
+ 3. A package recorded in a Hub receipt is owned for removal by Hub: a `plugins.remove` request for
65
+ that package must go through Hub's receipt-aware uninstall path so the package and its exact
66
+ receipt cannot diverge. Do not use a floating `plugins.update` for a Hub-managed package. Its
67
+ replacement must be an explicit operation against an exact catalog version and must retain
68
+ receipt ownership; if no safe replacement operation exists, require uninstall then install of the
69
+ selected exact version.
70
+ 4. The Hub page's Catalog view contains Community entries only, across catalog types supported by
71
+ the active product. This does not restore the deprecated agent-template application flow. The
72
+ Installed view contains receipts from all channels with the entry's channel visible; existing
73
+ Official receipts therefore remain reviewable and uninstallable. The underlying catalog and
74
+ `hub.list` contract remain unfiltered for other callers.
75
+ 5. On Hub entry, load cached/bundled content first, then request a remote refresh. Keep the last
76
+ known entries and show whether they are remote, cached or bundled, when they were refreshed, and
77
+ any refresh error. An explicit refresh remains available. Keep bounded payload Preview for
78
+ registry/document entries, but do not expose Preview for Pi packages; install and uninstall
79
+ remain pinned to the selected receipt/catalog version.
80
+ 6. Host surfaces use the same catalog filtering, receipt, refresh and lifecycle semantics in their
81
+ own page layouts. No host-only surface, new loader, data store or server response schema is added.
82
+
83
+ ## Alternatives
84
+
85
+ - **Keep the shared browser for both commands.** Rejected: it leaves local management and public
86
+ discovery coupled, contrary to the requested ownership and page distinction.
87
+ - **Make `/hub` Community-only but keep `/plugins` as the browser's Installed/Discover shortcut.**
88
+ Rejected: public discovery would remain inside the command meant for local plugin management.
89
+ - **Hide non-Community receipts in Installed.** Rejected: it would strand existing Official Hub
90
+ installations from the dedicated management view.
91
+ - **Do nothing.** Rejected: it retains the current refresh-status and package-lifecycle gaps.
92
+
93
+ ## Consequences
94
+
95
+ - The terminal and host frontends need separate Plugins and Hub presentations, but continue to call
96
+ the same Harness-owned services. Their layouts need not match.
97
+ - `plugins.remove` behavior changes only for a package owned by a Hub receipt: Hub remains the sole
98
+ writer that removes that receipt. Its method shape and response schema need not change.
99
+ - Community filtering is a presentation rule. Catalog metadata, receipts, and the unfiltered Hub
100
+ service remain unchanged. Existing official receipts require no migration.
101
+ - The public catalog currently has a Community skill but no Community Pi-package entry. This change
102
+ does not publish or invent a package; the Community plugin list stays empty until a publisher adds
103
+ a valid entry.
104
+
105
+ ## Migration
106
+
107
+ No data migration. Existing receipts from all channels remain in storage and appear in Installed with
108
+ their channel label. Existing plugin trust, attachment, settings and package data remain unchanged.
109
+ The command names and protected service routes remain stable; only the direct-page routing and
110
+ receipt-aware removal behavior change. `/harness` retains its broad browser.
111
+
112
+ ## Checks
113
+
114
+ - Harness route and page tests: distinct command destinations; Community-only catalog results;
115
+ all-channel Installed receipts; cache-first refresh and visible fallback status; bounded
116
+ registry/document Preview with no Pi-package Preview; and receipt-aware removal/update behavior.
117
+ - Harness: `npm run typecheck`, `npm test`, and `npm run prove` after changing the server method.
118
+ - Host/App tests: matching route, filtering, refresh and receipt behavior; then App
119
+ `npm run typecheck`, `npm test`, `npm run check:layers`, `npm run check:ui`, and
120
+ `cd src-tauri && cargo test`.
121
+ - Inspect isolated terminal and host screens at compact and wide sizes. Tests use fixtures; do not
122
+ install a real package or make a model request for verification.
@@ -0,0 +1,85 @@
1
+ ---
2
+ id: PROP-0048
3
+ title: One annotate page, two actions, and easy app attach
4
+ status: Implemented
5
+ created: 2026-10-27
6
+ updated: 2026-10-27
7
+ affects: [PROP-0018, 07-tools-and-gate.md, 10-server-protocol.md, 11-terminal.md, 13-world-tools.md, terminal-tui.md, parity.md]
8
+ ---
9
+
10
+ # PROP-0048: One annotate page, two actions, and easy app attach
11
+
12
+ ## Problem
13
+
14
+ People testing `/annotate:` found it hard to use:
15
+
16
+ - `/annotate:annotate` duplicated `/annotate:` in every command list.
17
+ - With no address, the page asked for one in a dialog that opened *behind* the full-screen page.
18
+ `PageOverlay` started its page in its constructor, before `dialogs.custom` mounted the overlay,
19
+ so any dialog the page opened as it started was stacked below it.
20
+ - While that hidden dialog waited, the page's single-action guard dropped every key (`s`, `i`)
21
+ without a word: "send queue sometimes doesn't work".
22
+ - A send held back while the agent was busy was tried once, 250 ms after the run ended. If the
23
+ terminal still reported its turn as active, the notes waited forever.
24
+ - `/annotate:notes` was a separate centred dialog with its own capture flow.
25
+ - The popover had three choices (save, ask the agent to fix, proceed now), the page had a queue, a
26
+ confirm step, an unqueue key and a "fixed" status reported by the agent's `page_notes_mark_fixed`.
27
+ - Attaching an Electron app needed its DevTools address typed by hand.
28
+
29
+ ## Design
30
+
31
+ **Delivery.** A note is either **pending** (shared with the session, unsent) or **sent**.
32
+ `NoteDelivery.state` is `pending | sent`; records written before this read `queued` as `pending`
33
+ and `fixed` as `sent`, without a migration write. Every annotation made in the inspector with a
34
+ session is shared with it at once. The popover has two buttons: **Annotate** (`save`) and
35
+ **Annotate & send** (`send`, also Cmd/Ctrl+Enter); `send` sends *all* the session's pending notes
36
+ as one message. Any other action the page reports is a save. Page-origin notes (DevTools or
37
+ bridge, untrusted) still wait for the person's confirmation; the terminal asks once for all of
38
+ them, and an `empty` send reports how many still wait.
39
+
40
+ `NoteDeliveries.runEnded` now tries a held-back send again (250 ms, 1 s, 3 s, 8 s) while the
41
+ session answers `busy`, and starts over if another run ends meanwhile.
42
+
43
+ `/annotate:submit <request>` sends the pending notes with the person's request after them
44
+ (`compose(notes, prompt)`). With no pending notes it sends nothing. A request is not kept while
45
+ the agent is busy; the notes stay pending and the person submits again. `/annotate:clear` resolves
46
+ the session's pending notes and withdraws their grant to it; they can be reopened from
47
+ `/annotate:notes`.
48
+
49
+ `page_notes_mark_fixed` is removed; the agent's page-note tools are read-only again. Only the
50
+ person resolves a note.
51
+
52
+ **Commands.** A built-in command may be `rootOnly`: it is the namespace root (`/annotate:`) and has
53
+ no named route, is not listed, and is not reachable as `/plugin:<name>`. The annotations plugin has
54
+ `/annotate: [url | --app [port] | --bridge]`, `/annotate:notes`, `/annotate:submit` and
55
+ `/annotate:clear`.
56
+
57
+ **One page.** `AnnotatePage` is one full-screen page with an **Annotate** tab and an **All
58
+ annotations** tab (`/annotate:notes` opens on the second). The Annotate tab takes the page address,
59
+ a DevTools port or an app path in an inline field, never a dialog. `a` opens a list of the apps
60
+ answering on a DevTools port on this computer, with *Start an app with a DevTools port*, *Type a
61
+ DevTools port*, *Use the page bridge* and *Annotate a web page instead*. The notes are grouped
62
+ **Unsent (n)** and **Sent**. A key pressed while the page is busy is answered with a notice.
63
+
64
+ `PageOverlay` starts its page when the host reports it mounted (`mounted()`, called by
65
+ `dialogs.custom` after `showOverlay`), or on the next turn for a host that does not. Dialogs a page
66
+ opens as it starts now stack above it, on every page.
67
+
68
+ **DevTools apps.** `AnnotationCapture.discover()` probes `127.0.0.1` on ports 9222–9230 and any port
69
+ used before, reading `/json/version` and `/json/list` with a short timeout. A Node inspector
70
+ (`node.js/…`) is skipped. `launchApp(key, path, args)` starts the program (the one inside a macOS
71
+ `.app` bundle) with `--remote-debugging-port=<free loopback port>`, waits for its DevTools port, and
72
+ attaches. The app keeps running after the capture ends. The loopback-only rule for `cdp` targets
73
+ is unchanged.
74
+
75
+ **Protocol.** Removed: `annotations.queue`, `annotations.dequeue`, `annotations.queued`. Added:
76
+ `annotations.pending`, `annotations.markPending`, `annotations.confirmAll`, `annotations.clear`,
77
+ `annotations.capture.discover`, `annotations.capture.launch`. `annotations.createAndAct` takes
78
+ `save | send`; `annotations.compose` and `annotations.send` take an optional `prompt`. Terminal
79
+ routes: `terminal.annotations.confirmAll|clear|discover|launch`; `queue` and `dequeue` are gone.
80
+
81
+ ## Consequences
82
+
83
+ The App's Annotate panel used `queue`, `fixed` and `createAndAct('queue')` and follows in its own
84
+ change. The prompt's Annotations group no longer names `page_notes_mark_fixed`, which changes the
85
+ stable prefix once.