@huseynsnmz/pi-board 0.15.3 → 0.17.0

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 (216) hide show
  1. package/CHANGELOG.md +24 -4
  2. package/CONTRIBUTING.md +7 -0
  3. package/README.md +55 -419
  4. package/SECURITY.md +19 -0
  5. package/docs/how-it-works.md +31 -0
  6. package/docs/reference.md +277 -0
  7. package/package.json +5 -11
  8. package/src/commands/agent-board.ts +26 -2
  9. package/src/commands/attach-flow.ts +4 -0
  10. package/src/core/double-press.mjs +6 -2
  11. package/src/core/slash-commands.mjs +52 -0
  12. package/src/index.ts +1 -1
  13. package/src/runtime/service.mjs +12 -0
  14. package/src/ui/dashboard.ts +59 -25
  15. package/src/ui/pty-attach.ts +8 -3
  16. package/IMPLEMENTATION_PLAN.md +0 -920
  17. package/PRD.md +0 -484
  18. package/PROGRESS.md +0 -142
  19. package/VERIFY.md +0 -115
  20. package/docs/BATCH_SELECTION_READ_FLOW.md +0 -277
  21. package/docs/EXPLORATION.md +0 -187
  22. package/docs/PTY_ATTACH_IMPLEMENTATION_PLAN.md +0 -581
  23. package/docs/RELEASE.md +0 -148
  24. package/docs/superpowers/plans/2026-08-15-screenlog-gc.md +0 -704
  25. package/docs/superpowers/plans/2026-08-16-attach-double-cursor-jiggle-retry.md +0 -499
  26. package/docs/superpowers/plans/2026-08-21-attach-coldstart-jiggle-rearm.md +0 -797
  27. package/docs/superpowers/plans/2026-08-21-dashboard-keypress-lag.md +0 -366
  28. package/docs/superpowers/plans/2026-08-21-issue-14-needs-input-no-auto-done.md +0 -576
  29. package/docs/superpowers/plans/2026-08-22-jiggle-shrink-and-hold.md +0 -337
  30. package/docs/superpowers/plans/2026-08-22-launch-cwd-favorites.md +0 -916
  31. package/docs/superpowers/plans/2026-08-22-question-tool-grouping.md +0 -211
  32. package/docs/superpowers/plans/2026-08-22-rename-state-labels.md +0 -182
  33. package/docs/superpowers/plans/2026-08-22-stable-list-order.md +0 -51
  34. package/docs/superpowers/plans/2026-08-27-locks-acquirelock-spin.md +0 -614
  35. package/docs/superpowers/plans/2026-08-27-pty-runner-test-flaky.md +0 -32
  36. package/docs/superpowers/plans/2026-08-29-code-refs-badges.md +0 -223
  37. package/docs/superpowers/plans/2026-08-30-circular-navigation.md +0 -308
  38. package/docs/superpowers/plans/2026-08-30-post-exit-timing-fix.md +0 -37
  39. package/docs/superpowers/plans/2026-08-30-pty-attach-quality-debt.md +0 -311
  40. package/docs/superpowers/plans/2026-08-30-readme-v2.md +0 -294
  41. package/docs/superpowers/plans/2026-09-01-attach-detach-gate-cursor-anchor.md +0 -284
  42. package/docs/superpowers/plans/2026-09-02-attach-detach-editor-state.md +0 -722
  43. package/docs/superpowers/plans/2026-09-03-code-refs-pr-backlink-narrow.md +0 -551
  44. package/docs/superpowers/plans/2026-09-03-detach-gate-glyph-fallback.md +0 -146
  45. package/docs/superpowers/plans/2026-09-04-evidence-outputpreview.md +0 -209
  46. package/docs/superpowers/plans/2026-09-04-warm-host-reclaim.md +0 -796
  47. package/docs/superpowers/plans/2026-09-05-issue-13-drainnextfollowup-pty-probe.md +0 -114
  48. package/docs/superpowers/plans/2026-09-05-issue-38-windows-wezterm-ime-cursor.md +0 -73
  49. package/docs/superpowers/plans/2026-09-05-issue-39-truncate-codepoint-boundary.md +0 -143
  50. package/docs/superpowers/plans/2026-09-05-issue-61-mention-fallback-guards.md +0 -226
  51. package/docs/superpowers/plans/2026-09-05-issue-63-flaky-manual-completion.md +0 -87
  52. package/docs/superpowers/plans/2026-09-05-issue-64-changelog-release-helper.md +0 -53
  53. package/docs/superpowers/plans/2026-09-05-pty-host-stacking-sock-race.md +0 -731
  54. package/docs/superpowers/plans/2026-09-08-attach-ctrl-left-detach.md +0 -30
  55. package/docs/superpowers/plans/2026-09-08-dashboard-shrink-repaint.md +0 -68
  56. package/docs/superpowers/plans/2026-09-08-issue-11-attach-runtime-desync-heal.md +0 -917
  57. package/docs/superpowers/plans/2026-09-08-legacy-stale-host-recovery.md +0 -125
  58. package/docs/superpowers/plans/2026-09-08-spawn-async-error-swallow.md +0 -56
  59. package/docs/superpowers/plans/2026-09-08-stale-model-attach-guard.md +0 -96
  60. package/docs/superpowers/plans/2026-09-09-claimpid-blocks-replace.md +0 -267
  61. package/docs/superpowers/plans/2026-09-09-harden-runner-architecture.md +0 -603
  62. package/docs/superpowers/plans/2026-09-09-single-writer-completion.md +0 -252
  63. package/docs/superpowers/plans/2026-09-10-reader-consistency.md +0 -115
  64. package/docs/superpowers/plans/2026-09-14-attach-cursor-dectcem-gate.md +0 -469
  65. package/docs/superpowers/plans/2026-09-14-attach-snapshot.md +0 -92
  66. package/docs/superpowers/plans/2026-09-14-host-meta-orphan-lock.md +0 -771
  67. package/docs/superpowers/plans/2026-09-14-issue-106-terminal-frame-cognition.md +0 -299
  68. package/docs/superpowers/plans/2026-09-14-issue-113-foreground-preview-race.md +0 -609
  69. package/docs/superpowers/plans/2026-09-14-terminal-model.md +0 -145
  70. package/docs/superpowers/plans/2026-09-15-coordinator-pipe-root-normalize.md +0 -224
  71. package/docs/superpowers/plans/2026-09-15-lease-publish-eprem-reclaim.md +0 -341
  72. package/docs/superpowers/plans/2026-09-18-detach-anchor-reporter-endpoint.md +0 -875
  73. package/docs/superpowers/plans/2026-09-20-control-lifecycle.md +0 -116
  74. package/docs/superpowers/plans/2026-09-20-issue-121-perf-gate-out-of-coverage.md +0 -517
  75. package/docs/superpowers/plans/2026-09-23-a5-mid-stream-assertion.md +0 -25
  76. package/docs/superpowers/plans/2026-09-23-cwd-browse-tab-drill.md +0 -393
  77. package/docs/superpowers/plans/2026-09-23-terminal-query-forwarding.md +0 -51
  78. package/docs/superpowers/plans/2026-09-23-unconditional-detach-key.md +0 -58
  79. package/docs/superpowers/plans/2026-09-24-control-reconcile-flake.md +0 -698
  80. package/docs/superpowers/plans/2026-09-24-probe-attached-clients.md +0 -605
  81. package/docs/superpowers/plans/2026-09-29-issue-145-holding-semantic-state.md +0 -1183
  82. package/docs/superpowers/plans/2026-09-29-issue-145-user-verification.md +0 -66
  83. package/docs/superpowers/plans/2026-09-30-board-delete-key-x-y-confirm.md +0 -649
  84. package/docs/superpowers/plans/2026-09-30-issue-153-runner-stop-boot-window.md +0 -618
  85. package/docs/superpowers/plans/2026-09-30-issue-95-test-discipline.md +0 -555
  86. package/docs/superpowers/plans/2026-09-30-publish-dispatch-tag-check.md +0 -259
  87. package/docs/superpowers/plans/2026-10-06-attach-settle-scheme-delivery.md +0 -751
  88. package/docs/superpowers/plans/2026-10-09-issue-167-attach-middle-paste.md +0 -509
  89. package/docs/superpowers/plans/2026-10-09-issue-169-fullscreen-mouse-ownership.md +0 -346
  90. package/docs/superpowers/plans/2026-10-10-attach-delete-in-place.md +0 -832
  91. package/docs/superpowers/plans/2026-10-10-issue-174-selection-mirror-paste-source.md +0 -510
  92. package/docs/superpowers/plans/2026-10-14-issue-170-attach-reply-guard.md +0 -771
  93. package/docs/superpowers/plans/2026-10-14-issue-178-accept-registration-race.md +0 -1257
  94. package/docs/superpowers/plans/2026-10-14-issue-184-snapshot-window-contract.md +0 -657
  95. package/docs/superpowers/plans/2026-10-14-issue-185-attach-wheel-alt-screen.md +0 -47
  96. package/docs/superpowers/plans/2026-10-14-issue-187-motionless-drag.md +0 -931
  97. package/docs/superpowers/plans/2026-10-14-issue-189-mixed-input-segments.md +0 -823
  98. package/docs/superpowers/plans/2026-10-15-issue-188-x10-mouse-release.md +0 -526
  99. package/docs/superpowers/plans/2026-10-16-issue-198-attach-wheel-forward-fallback.md +0 -32
  100. package/docs/superpowers/plans/2026-10-18-attach-wheel-replay-pseudo-scrollback.md +0 -437
  101. package/docs/superpowers/plans/2026-10-19-issue-213-reply-guard-csi-key.md +0 -257
  102. package/docs/superpowers/plans/2026-10-19-issue-214-attach-forward-alt-modifier.md +0 -202
  103. package/docs/superpowers/plans/2026-10-21-issue-221-ask-user-question.md +0 -198
  104. package/docs/superpowers/plans/2026-10-23-issue-222-attach-ready.md +0 -290
  105. package/docs/superpowers/plans/2026-10-23-issue-225-monotonic-foreground-state.md +0 -450
  106. package/docs/superpowers/plans/2026-10-23-issue-229-vim-jkl.md +0 -449
  107. package/docs/superpowers/plans/2026-10-23-issue-232-attach-bg-only-cells.md +0 -153
  108. package/docs/superpowers/plans/2026-10-23-lease-heartbeat-staleness.md +0 -537
  109. package/docs/superpowers/plans/2026-10-23-selected-row-highlight.md +0 -405
  110. package/docs/superpowers/plans/2026-10-24-issue-217-windows-pid-reuse-stopping.md +0 -367
  111. package/docs/superpowers/plans/2026-10-24-issue-230-warm-host-background-runs.md +0 -806
  112. package/docs/superpowers/plans/2026-10-24-issue-238-windows-process-identity-token.md +0 -490
  113. package/docs/superpowers/plans/2026-10-24-issue-241-coordinator-root-exit.md +0 -461
  114. package/docs/superpowers/plans/2026-10-25-issue-239-coordinator-replacement-identity.md +0 -504
  115. package/docs/superpowers/plans/2026-10-26-issue-245-key-hints-doc-sync.md +0 -244
  116. package/docs/superpowers/plans/2026-10-27-issue-172-fullscreen-selection-clear.md +0 -344
  117. package/docs/superpowers/plans/2026-10-27-issue-252-select-key-hints.md +0 -209
  118. package/docs/superpowers/plans/2026-10-28-issue-255-coordinator-endpoint-zombie.md +0 -442
  119. package/docs/superpowers/plans/2026-10-28-issue-257-legacy-pty-runner-endpoint-cleanup.md +0 -256
  120. package/docs/superpowers/plans/2026-11-03-cwd-favorites-full-pool.md +0 -266
  121. package/docs/superpowers/specs/2026-08-15-screenlog-gc-design.md +0 -105
  122. package/docs/superpowers/specs/2026-08-16-attach-double-cursor-jiggle-retry-design.md +0 -142
  123. package/docs/superpowers/specs/2026-08-21-attach-coldstart-jiggle-rearm-design.md +0 -110
  124. package/docs/superpowers/specs/2026-08-21-dashboard-keypress-lag-design.md +0 -59
  125. package/docs/superpowers/specs/2026-08-21-issue-14-needs-input-no-auto-done-design.md +0 -109
  126. package/docs/superpowers/specs/2026-08-22-cwd-favorites-design.md +0 -131
  127. package/docs/superpowers/specs/2026-08-22-jiggle-shrink-and-hold-design.md +0 -111
  128. package/docs/superpowers/specs/2026-08-22-question-tool-grouping-design.md +0 -114
  129. package/docs/superpowers/specs/2026-08-22-rename-state-labels-design.md +0 -80
  130. package/docs/superpowers/specs/2026-08-27-locks-acquirelock-spin-design.md +0 -97
  131. package/docs/superpowers/specs/2026-08-27-pty-runner-test-flaky-design.md +0 -35
  132. package/docs/superpowers/specs/2026-08-29-code-refs-badges-design.md +0 -128
  133. package/docs/superpowers/specs/2026-08-30-circular-navigation-design.md +0 -47
  134. package/docs/superpowers/specs/2026-08-30-eprm-atomicwrite-race-design.md +0 -77
  135. package/docs/superpowers/specs/2026-08-30-post-exit-timing-fix-design.md +0 -80
  136. package/docs/superpowers/specs/2026-08-30-pty-attach-quality-debt-design.md +0 -105
  137. package/docs/superpowers/specs/2026-08-30-readme-v2-design.md +0 -115
  138. package/docs/superpowers/specs/2026-09-01-attach-detach-gate-cursor-anchor-design.md +0 -78
  139. package/docs/superpowers/specs/2026-09-02-attach-detach-editor-state-design.md +0 -120
  140. package/docs/superpowers/specs/2026-09-03-code-refs-pr-backlink-narrow-design.md +0 -92
  141. package/docs/superpowers/specs/2026-09-03-detach-gate-glyph-fallback-design.md +0 -99
  142. package/docs/superpowers/specs/2026-09-04-evidence-outputpreview-design.md +0 -50
  143. package/docs/superpowers/specs/2026-09-04-warm-host-reclaim-design.md +0 -106
  144. package/docs/superpowers/specs/2026-09-05-issue-13-drainnextfollowup-pty-probe-design.md +0 -64
  145. package/docs/superpowers/specs/2026-09-05-issue-38-windows-wezterm-ime-design.md +0 -48
  146. package/docs/superpowers/specs/2026-09-05-issue-39-truncate-codepoint-boundary-design.md +0 -64
  147. package/docs/superpowers/specs/2026-09-05-issue-61-mention-fallback-design.md +0 -71
  148. package/docs/superpowers/specs/2026-09-05-issue-63-flaky-manual-completion-design.md +0 -49
  149. package/docs/superpowers/specs/2026-09-05-issue-64-changelog-helper-design.md +0 -76
  150. package/docs/superpowers/specs/2026-09-05-pty-host-stacking-sock-race-design.md +0 -510
  151. package/docs/superpowers/specs/2026-09-07-issue-11-attach-runtime-desync-heal-design.md +0 -130
  152. package/docs/superpowers/specs/2026-09-08-attach-ctrl-left-detach-design.md +0 -58
  153. package/docs/superpowers/specs/2026-09-08-dashboard-shrink-repaint-design.md +0 -52
  154. package/docs/superpowers/specs/2026-09-08-legacy-stale-host-recovery-design.md +0 -87
  155. package/docs/superpowers/specs/2026-09-08-spawn-async-error-swallow-design.md +0 -56
  156. package/docs/superpowers/specs/2026-09-08-stale-model-attach-guard-design.md +0 -79
  157. package/docs/superpowers/specs/2026-09-09-claimpid-blocks-replace-design.md +0 -147
  158. package/docs/superpowers/specs/2026-09-09-harden-runner-architecture-design.md +0 -304
  159. package/docs/superpowers/specs/2026-09-14-attach-cursor-dectcem-gate-design.md +0 -114
  160. package/docs/superpowers/specs/2026-09-14-host-meta-orphan-lock-design.md +0 -120
  161. package/docs/superpowers/specs/2026-09-14-issue-106-terminal-frame-cognition-design.md +0 -146
  162. package/docs/superpowers/specs/2026-09-14-issue-113-foreground-preview-race-design.md +0 -116
  163. package/docs/superpowers/specs/2026-09-15-coordinator-pipe-root-normalize-design.md +0 -84
  164. package/docs/superpowers/specs/2026-09-15-lease-publish-eprem-reclaim-design.md +0 -92
  165. package/docs/superpowers/specs/2026-09-18-detach-anchor-reporter-endpoint-design.md +0 -123
  166. package/docs/superpowers/specs/2026-09-20-issue-121-perf-gate-out-of-coverage-design.md +0 -204
  167. package/docs/superpowers/specs/2026-09-23-a5-mid-stream-assertion-design.md +0 -43
  168. package/docs/superpowers/specs/2026-09-23-cwd-browse-tab-drill-design.md +0 -95
  169. package/docs/superpowers/specs/2026-09-23-terminal-query-forwarding-design.md +0 -63
  170. package/docs/superpowers/specs/2026-09-23-unconditional-detach-key-design.md +0 -57
  171. package/docs/superpowers/specs/2026-09-24-control-reconcile-flake-design.md +0 -98
  172. package/docs/superpowers/specs/2026-09-24-probe-attached-clients-design.md +0 -190
  173. package/docs/superpowers/specs/2026-09-29-issue-145-holding-semantic-state-design.md +0 -394
  174. package/docs/superpowers/specs/2026-09-30-board-delete-key-x-y-confirm-design.md +0 -86
  175. package/docs/superpowers/specs/2026-09-30-issue-153-runner-stop-boot-window-design.md +0 -75
  176. package/docs/superpowers/specs/2026-09-30-issue-95-test-discipline-design.md +0 -226
  177. package/docs/superpowers/specs/2026-09-30-publish-dispatch-tag-check-design.md +0 -129
  178. package/docs/superpowers/specs/2026-10-06-attach-settle-scheme-delivery-design.md +0 -196
  179. package/docs/superpowers/specs/2026-10-09-issue-167-attach-middle-paste-design.md +0 -125
  180. package/docs/superpowers/specs/2026-10-09-issue-169-fullscreen-mouse-ownership-design.md +0 -130
  181. package/docs/superpowers/specs/2026-10-10-attach-delete-in-place-design.md +0 -224
  182. package/docs/superpowers/specs/2026-10-10-issue-174-selection-mirror-paste-source-design.md +0 -154
  183. package/docs/superpowers/specs/2026-10-14-issue-170-attach-reply-guard-design.md +0 -195
  184. package/docs/superpowers/specs/2026-10-14-issue-178-accept-registration-race-design.md +0 -287
  185. package/docs/superpowers/specs/2026-10-14-issue-184-snapshot-window-contract-design.md +0 -288
  186. package/docs/superpowers/specs/2026-10-14-issue-185-attach-wheel-alt-screen-design.md +0 -126
  187. package/docs/superpowers/specs/2026-10-14-issue-187-motionless-drag-design.md +0 -269
  188. package/docs/superpowers/specs/2026-10-14-issue-189-mixed-input-segments-design.md +0 -369
  189. package/docs/superpowers/specs/2026-10-15-issue-188-x10-mouse-release-design.md +0 -252
  190. package/docs/superpowers/specs/2026-10-16-issue-198-attach-wheel-forward-fallback-design.md +0 -67
  191. package/docs/superpowers/specs/2026-10-18-attach-wheel-replay-pseudo-scrollback-design.md +0 -250
  192. package/docs/superpowers/specs/2026-10-19-issue-213-reply-guard-csi-key-design.md +0 -165
  193. package/docs/superpowers/specs/2026-10-19-issue-214-attach-forward-alt-modifier-design.md +0 -131
  194. package/docs/superpowers/specs/2026-10-20-issue-221-ask-user-question-design.md +0 -270
  195. package/docs/superpowers/specs/2026-10-22-issue-225-monotonic-foreground-state-design.md +0 -225
  196. package/docs/superpowers/specs/2026-10-23-issue-222-attach-ready-design.md +0 -204
  197. package/docs/superpowers/specs/2026-10-23-issue-229-vim-jkl-design.md +0 -90
  198. package/docs/superpowers/specs/2026-10-23-issue-232-attach-bg-only-cells-design.md +0 -146
  199. package/docs/superpowers/specs/2026-10-23-lease-heartbeat-staleness-design.md +0 -271
  200. package/docs/superpowers/specs/2026-10-23-selected-row-highlight-design.md +0 -191
  201. package/docs/superpowers/specs/2026-10-24-issue-217-windows-pid-reuse-stopping-design.md +0 -334
  202. package/docs/superpowers/specs/2026-10-24-issue-230-warm-host-background-runs-design.md +0 -283
  203. package/docs/superpowers/specs/2026-10-24-issue-238-windows-process-identity-token-design.md +0 -286
  204. package/docs/superpowers/specs/2026-10-24-issue-241-coordinator-root-exit-design.md +0 -381
  205. package/docs/superpowers/specs/2026-10-25-issue-239-coordinator-replacement-identity-design.md +0 -331
  206. package/docs/superpowers/specs/2026-10-26-issue-245-key-hints-doc-sync-design.md +0 -72
  207. package/docs/superpowers/specs/2026-10-27-issue-172-fullscreen-selection-clear-design.md +0 -248
  208. package/docs/superpowers/specs/2026-10-27-issue-252-select-key-hints-design.md +0 -95
  209. package/docs/superpowers/specs/2026-10-28-issue-255-coordinator-endpoint-zombie-design.md +0 -380
  210. package/docs/superpowers/specs/2026-10-28-issue-257-legacy-pty-runner-endpoint-cleanup-design.md +0 -197
  211. package/docs/superpowers/specs/2026-11-03-cwd-favorites-full-pool-design.md +0 -106
  212. package/scripts/budget-audit.mjs +0 -135
  213. package/scripts/release.mjs +0 -446
  214. package/scripts/release_helper.mjs +0 -309
  215. package/scripts/run-perf-gate.mjs +0 -40
  216. package/scripts/run-tests.mjs +0 -375
@@ -1,437 +0,0 @@
1
- # attach wheel replay pseudo-scrollback — Implementation Plan
2
-
3
- > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4
-
5
- **Goal:** Stop the attach wheel from spending its notches on scrollback that the snapshot hydration itself manufactured, so the first notch reaches the hosted session.
6
-
7
- **Architecture:** Add a `historyFloor` baseline to `PtyAttachComponent`: everything below it is "pseudo history" created by hydrating a snapshot frame into a shorter local terminal. The floor is computed from the hydrated `baseY` minus the runner-authoritative scrollback line count, which the runner now carries in `snapshot_end.scrollbackLines`. All decision logic lands in pure functions in `src/core/pty-scroll.mjs`; the component only wires state.
8
-
9
- **Tech Stack:** TypeScript (component), ESM `.mjs` core modules, `@xterm/headless`, `node:test` + `assert/strict`, `@earendil-works/pi-tui` (`TuiAltScreen`).
10
-
11
- **Spec:** `docs/superpowers/specs/2026-10-18-attach-wheel-replay-pseudo-scrollback-design.md`
12
-
13
- ## Global Constraints
14
-
15
- - Tabs for indentation; English comments and commit messages (repo AGENTS.md).
16
- - No new dependencies.
17
- - `npm run verify` must pass (typecheck + perf gate + tests + coverage thresholds lines 85 / funcs 80 / branches 70 + pack dry-run).
18
- - Tests use `node:test` with `assert/strict`.
19
- - Snapshots/attaches must never touch the host repository's fixtures — scenario work goes under a temp dir.
20
- - The legacy screen-log replay path (`replayScreenLog`) must keep `historyFloor === 0`; its behaviour is unchanged.
21
-
22
- ## Review Focus
23
-
24
- - **Old runner: `snapshot_end` without `scrollbackLines`** — hydration must still complete, `historyFloor` must be a finite number (never `NaN`), and the wheel must forward instead of silently scrolling pseudo history.
25
- - **Resize between hydrate and assertion** — `historyFloor` must reflect the `baseY` observed inside the `term.write` callback, not a value frozen before the write.
26
- - **`snap.empty` / `snap.resnapshot`** — after `term.reset()` the floor must return to 0, or a host restart leaves the first N lines unscrollable.
27
- - **A snapshot that really has history (`scrollbackLines > 0`)** — that history must still scroll locally; the fix must not blanket-disable local scrolling.
28
- - **Disconnected attach** — the wheel must still return `undefined` (hand the event back to pi) rather than returning `{handled:true}` on a forward that `send()` drops.
29
-
30
- ---
31
-
32
- ### Task 1: Pure scroll-floor helpers in `pty-scroll.mjs`
33
-
34
- **Files:**
35
- - Modify: `src/core/pty-scroll.mjs` (`scrollViewportTop` at ~L359; append two new exports near it)
36
- - Test: `test/pty-scroll.test.mjs`
37
-
38
- **Interfaces:**
39
- - Produces:
40
- - `historyFloorAfterHydrate(baseYAfter: number, scrollbackLines: number|undefined) -> number`
41
- - `scrollableHistoryLines(baseY: number, historyFloor: number) -> number`
42
- - `scrollViewportTop(viewportTop: number|null, bottom: number, linesUp: number, floor?: number) -> { viewportTop: number|null, changed: boolean }`
43
-
44
- - [ ] **Step 1: Write the failing tests**
45
-
46
- Append to `test/pty-scroll.test.mjs` (add the two new names to the existing import list):
47
-
48
- ```js
49
- test("historyFloorAfterHydrate keeps only scrollback that the runner vouched for", () => {
50
- assert.equal(historyFloorAfterHydrate(27, 0), 27);
51
- assert.equal(historyFloorAfterHydrate(127, 100), 27);
52
- assert.equal(historyFloorAfterHydrate(5, 10), 0);
53
- assert.equal(historyFloorAfterHydrate(0, 0), 0);
54
- });
55
-
56
- test("historyFloorAfterHydrate never returns NaN for unvalidated input", () => {
57
- assert.equal(historyFloorAfterHydrate(undefined, 0), 0);
58
- assert.equal(historyFloorAfterHydrate(27, undefined), 27);
59
- assert.equal(historyFloorAfterHydrate(Number.NaN, Number.NaN), 0);
60
- assert.equal(historyFloorAfterHydrate(27, Number.POSITIVE_INFINITY), 27);
61
- });
62
-
63
- test("scrollableHistoryLines reports only the rows above the floor", () => {
64
- assert.equal(scrollableHistoryLines(27, 27), 0);
65
- assert.equal(scrollableHistoryLines(127, 27), 100);
66
- assert.equal(scrollableHistoryLines(0, 0), 0);
67
- assert.equal(scrollableHistoryLines(5, 9), 0);
68
- });
69
-
70
- test("scrollViewportTop honours the floor and matches the legacy behaviour at floor 0", () => {
71
- // floor === bottom: nothing to scroll back to
72
- assert.deepEqual(scrollViewportTop(null, 27, 1, 27), { viewportTop: null, changed: false });
73
- assert.deepEqual(scrollViewportTop(27, 27, 1, 27), { viewportTop: null, changed: false });
74
- // floor 0 must reproduce the pre-change contract exactly
75
- assert.deepEqual(scrollViewportTop(null, 27, 1, 0), { viewportTop: 26, changed: true });
76
- assert.deepEqual(scrollViewportTop(27, 27, 1), { viewportTop: 26, changed: true });
77
- assert.deepEqual(scrollViewportTop(0, 27, 1, 0), { viewportTop: 0, changed: false });
78
- // floor clamps a downward scroll the same way bottom does
79
- assert.deepEqual(scrollViewportTop(5, 27, -5, 10), { viewportTop: 15, changed: true });
80
- });
81
- ```
82
-
83
- - [ ] **Step 2: Run the tests to verify they fail**
84
-
85
- Run: `node --test test/pty-scroll.test.mjs`
86
- Expected: FAIL — `historyFloorAfterHydrate is not defined` / `scrollableHistoryLines is not defined`.
87
-
88
- - [ ] **Step 3: Implement the three functions**
89
-
90
- In `src/core/pty-scroll.mjs`, replace `scrollViewportTop` with the floor-aware version (default `floor = 0` keeps every existing call site byte-for-byte identical):
91
-
92
- ```js
93
- export function scrollViewportTop(viewportTop, bottom, linesUp, floor = 0) {
94
- const safeBottom = Math.max(0, Math.floor(bottom));
95
- const safeFloor = clampInt(floor, 0, safeBottom);
96
- const current = viewportTop == null ? safeBottom : clampInt(viewportTop, safeFloor, safeBottom);
97
- const next = clampInt(current - linesUp, safeFloor, safeBottom);
98
- return { viewportTop: next >= safeBottom ? null : next, changed: next !== current };
99
- }
100
- ```
101
-
102
- Add, with JSDoc explaining "hydrating a frame taller than the local terminal scrolls the top rows into scrollback; only `scrollbackLines` of them are history":
103
-
104
- ```js
105
- export function historyFloorAfterHydrate(baseYAfter, scrollbackLines) {
106
- const safeBase = Number.isFinite(baseYAfter) ? Math.max(0, Math.floor(baseYAfter)) : 0;
107
- const safeScrollback = Number.isFinite(scrollbackLines) ? Math.max(0, Math.floor(scrollbackLines)) : 0;
108
- return Math.min(safeBase, Math.max(0, safeBase - safeScrollback));
109
- }
110
-
111
- export function scrollableHistoryLines(baseY, historyFloor) {
112
- const safeBase = Number.isFinite(baseY) ? Math.max(0, Math.floor(baseY)) : 0;
113
- const safeFloor = Number.isFinite(historyFloor) ? Math.max(0, Math.floor(historyFloor)) : 0;
114
- return Math.max(0, safeBase - Math.min(safeBase, safeFloor));
115
- }
116
- ```
117
-
118
- - [ ] **Step 4: Run the tests to verify they pass**
119
-
120
- Run: `node --test test/pty-scroll.test.mjs`
121
- Expected: PASS, 0 failures.
122
-
123
- - [ ] **Step 5: Commit**
124
-
125
- ```bash
126
- git add src/core/pty-scroll.mjs test/pty-scroll.test.mjs
127
- git commit -m "feat(attach): pure history-floor helpers for hydrated scrollback (issue #205)"
128
- ```
129
-
130
- ---
131
-
132
- ### Task 2: Carry `scrollbackLines` on `snapshot_end`
133
-
134
- **Files:**
135
- - Modify: `src/core/terminal-attach-protocol.mjs` (two `send({ type: "snapshot_end", … })` sites, ~L120 and ~L150)
136
- - Modify: `src/core/terminal-attach-client.mjs` (`finishCollecting` emits `snapshotReady`, ~L486; header comment ~L85)
137
- - Test: `test/terminal-attach-protocol.test.mjs`, `test/terminal-attach-client.test.mjs`
138
-
139
- **Interfaces:**
140
- - Produces: `snapshot_end.scrollbackLines: number` (runner) and `snapshotReady.scrollbackLines?: number` (client, key omitted when the runner did not send one).
141
-
142
- - [ ] **Step 1: Write the failing tests**
143
-
144
- In `test/terminal-attach-protocol.test.mjs`, add a case asserting the empty-baseline end message:
145
-
146
- ```js
147
- assert.deepEqual(lastSent(), { type: "snapshot_end", nextSeq: 1, scrollbackLines: 0 });
148
- ```
149
-
150
- and, for a model with scrollback, that the value equals `dto.scrollback.length` (reuse whichever existing fixture builds a model with fed output; assert `typeof msg.scrollbackLines === "number"` and `msg.scrollbackLines === <expected rows>`).
151
-
152
- In `test/terminal-attach-client.test.mjs`, update the existing `deepEqual` at L94-95 to include the new key **only on the case that supplies it**, and add a case proving the key is absent when the runner omits it:
153
-
154
- ```js
155
- assert.deepEqual(eventsOf(events, "snapshotReady"), [
156
- { frame: "FRAME", empty: undefined, resnapshot: undefined, nextSeq: 6 },
157
- ]);
158
- // with scrollbackLines on the wire:
159
- client.handleMessage({ ...end(6), scrollbackLines: 100 });
160
- assert.equal(eventsOf(events, "snapshotReady")[0].scrollbackLines, 100);
161
- ```
162
-
163
- Also update the payload shape comment at `terminal-attach-client.mjs` ~L85 to `{ frame?, empty?, resnapshot?, nextSeq, scrollbackLines? }`.
164
-
165
- - [ ] **Step 2: Run the tests to verify they fail**
166
-
167
- Run: `node --test test/terminal-attach-protocol.test.mjs test/terminal-attach-client.test.mjs`
168
- Expected: FAIL — `scrollbackLines` missing from the sent end message / from the emitted event.
169
-
170
- - [ ] **Step 3: Implement**
171
-
172
- Protocol, empty branch (~L120): `send({ type: "snapshot_end", nextSeq: lastSeq + 1, scrollbackLines: 0 })`.
173
- Protocol, normal branch (~L150): `send({ type: "snapshot_end", nextSeq: lastSeq + 1, scrollbackLines: dto.scrollback.length })`.
174
-
175
- Client `finishCollecting` (~L486) — omit the key rather than writing `undefined`, because the existing `deepEqual` pins the exact shape:
176
-
177
- ```js
178
- emit("snapshotReady", {
179
- frame: partial.frame ?? undefined,
180
- empty: partial.empty || undefined,
181
- resnapshot: partial.resnapshot || undefined,
182
- nextSeq,
183
- ...(typeof msg.scrollbackLines === "number" ? { scrollbackLines: msg.scrollbackLines } : {}),
184
- });
185
- ```
186
-
187
- - [ ] **Step 4: Run the tests to verify they pass**
188
-
189
- Run: `node --test test/terminal-attach-protocol.test.mjs test/terminal-attach-client.test.mjs`
190
- Expected: PASS.
191
-
192
- - [ ] **Step 5: Commit**
193
-
194
- ```bash
195
- git add src/core/terminal-attach-protocol.mjs src/core/terminal-attach-client.mjs test/terminal-attach-protocol.test.mjs test/terminal-attach-client.test.mjs
196
- git commit -m "feat(attach): carry snapshot scrollback line count on snapshot_end (issue #205)"
197
- ```
198
-
199
- ---
200
-
201
- ### Task 3: Wire `historyFloor` into `PtyAttachComponent`
202
-
203
- **Files:**
204
- - Modify: `src/ui/pty-attach.ts` (`pushOutput` ~L1682, `handleAttachEvent` snapshotReady ~L1596, `resizeIfNeeded` ~L1384, `tryScrollBy` ~L1421, `tryScrollToTop` ~L1428, `clampViewportTop` ~L1465, imports ~L16)
205
- - Test: `test/pty-attach-wheel.test.mjs` (regression only — the positive path lands in Task 4)
206
-
207
- **Interfaces:**
208
- - Consumes: `historyFloorAfterHydrate`, `scrollableHistoryLines`, `scrollViewportTop(…, floor)` from Task 1.
209
- - Produces: private `historyFloor: number`, private `scrollFloorStart(height: number): number`.
210
-
211
- - [ ] **Step 1: Write the failing test**
212
-
213
- In `test/pty-attach-wheel.test.mjs`, add a case that hydrates an authoritative frame taller than the component's rows through the public event path and asserts the floor suppresses local scrolling. Anchor it on the existing component fixture in that file:
214
-
215
- ```js
216
- test("a hydrated frame taller than the viewport does not create scrollable history", () => {
217
- // component rendered at 80x24 → rows = 22; hydrate a 40-row frame
218
- // so the write scrolls 18 rows into the local buffer.
219
- // (drive pushOutput with { authoritative: true, scrollbackLines: 0 } and await the write callback)
220
- assert.equal(component.term.buffer.active.baseY, 18);
221
- assert.equal(component.scrollFloorStart(component.bodyHeight()), component.bottomViewportTop(component.bodyHeight()));
222
- });
223
- ```
224
-
225
- - [ ] **Step 2: Run the test to verify it fails**
226
-
227
- Run: `node --test test/pty-attach-wheel.test.mjs`
228
- Expected: FAIL — `scrollFloorStart is not a function` (or the two numbers differ).
229
-
230
- - [ ] **Step 3: Implement the wiring**
231
-
232
- 1. Import the two helpers from `../core/pty-scroll.mjs` next to the existing import (L16).
233
- 2. Field: `private historyFloor = 0;` near the other viewport state (`viewportTop`).
234
- 3. `pushOutput(data, opts)` — extend the opts type with `scrollbackLines?: number` and, inside the existing `this.term.write(data, () => { … })` callback, add:
235
-
236
- ```ts
237
- if (opts.authoritative) {
238
- this.historyFloor = historyFloorAfterHydrate(this.term.buffer.active.baseY, opts.scrollbackLines);
239
- }
240
- ```
241
-
242
- 4. `handleAttachEvent("snapshotReady")`:
243
- - frame branch → `this.pushOutput(snap.frame, { authoritative: true, scrollbackLines: snap.scrollbackLines });`
244
- - extend the payload cast to `{ frame?: string; empty?: boolean; resnapshot?: boolean; scrollbackLines?: number }`
245
- - `snap.empty || snap.resnapshot` branch → after `this.term.reset();` add `this.historyFloor = 0;`
246
- 5. `resizeIfNeeded` — after `this.term.resize(this.cols, this.rows);` add `this.historyFloor = this.term.buffer.active.baseY;`.
247
- 6. New helper:
248
-
249
- ```ts
250
- private scrollFloorStart(height: number): number {
251
- const bottom = this.bottomViewportTop(height);
252
- return Math.max(0, bottom - scrollableHistoryLines(this.term.buffer.active.baseY, this.historyFloor));
253
- }
254
- ```
255
-
256
- 7. Use it as the scroll lower bound:
257
- - `tryScrollBy`: `scrollViewportTop(this.viewportTop, this.bottomViewportTop(this.bodyHeight()), linesUp, this.scrollFloorStart(this.bodyHeight()))`
258
- - `tryScrollToTop`: bail when `this.scrollFloorStart(height) <= 0 || this.viewportTop === this.scrollFloorStart(height)`
259
- - `clampViewportTop`: clamp to `[this.scrollFloorStart(height), this.bottomViewportTop(height)]`
260
- - Leave `project()`'s `start = this.viewportTop ?? this.bottomViewportTop(height)` unchanged (display alignment).
261
-
262
- - [ ] **Step 4: Run the test to verify it passes**
263
-
264
- Run: `node --test test/pty-attach-wheel.test.mjs test/pty-attach-alt-wheel.test.mjs test/pty-attach-wheel-fallback.test.mjs`
265
- Expected: PASS — the new case passes and the existing wheel suites stay green (legacy path unaffected: `historyFloor` stays 0 there).
266
-
267
- - [ ] **Step 5: Commit**
268
-
269
- ```bash
270
- git add src/ui/pty-attach.ts test/pty-attach-wheel.test.mjs
271
- git commit -m "fix(attach): ignore snapshot-hydration scrollback when scrolling the wheel (issue #205)"
272
- ```
273
-
274
- ---
275
-
276
- ### Task 4: Production-topology scenario for frame overflow
277
-
278
- **Files:**
279
- - Create: `test-support/pty-attach-frame-overflow-scenario.ts`
280
- - Create: `test/pty-attach-frame-overflow.test.mjs`
281
-
282
- **Interfaces:**
283
- - Consumes: the component from Task 3; `captureTerminalSnapshot` / `synthesizeFullRedrawFrame` from `src/core/terminal-snapshot.mjs`; `createTerminalModel` / `feedOutput` / `whenIdle` from `src/core/terminal-model.mjs`.
284
- - Produces: a JSON line on stdout with `{ attachBaseY, historyFloor, scrollableHistoryLines, firstForwardAt, firstForwardData, viewportTop, realHistoryCase, resetCase, resizeCase, missingFieldCase }`.
285
-
286
- - [ ] **Step 1: Write the failing test**
287
-
288
- Create `test/pty-attach-frame-overflow.test.mjs`:
289
-
290
- ```js
291
- test("a snapshot frame taller than the attach viewport forwards the first wheel notch", () => {
292
- const out = execFileSync(process.execPath, ["--experimental-transform-types", SCENARIO], { encoding: "utf8", timeout: 90_000 });
293
- const parsed = JSON.parse(out.trim().split("\n").filter(Boolean).pop());
294
- assert.equal(parsed.error, null, `scenario error: ${parsed.error}`);
295
- assert.equal(parsed.attachBaseY, 27, "topology pin: a 65-row frame in a 38-row terminal scrolls 27 rows");
296
- assert.equal(parsed.historyFloor, 27);
297
- assert.equal(parsed.scrollableHistoryLines, 0);
298
- assert.equal(parsed.firstForwardAt, 1, "the first notch must reach the child");
299
- assert.equal(parsed.firstForwardData, "\x1b[<64;4;3M");
300
- assert.equal(parsed.viewportTop, null, "follow-bottom is preserved");
301
- assert.equal(parsed.realHistoryCase.scrollable, 100, "runner-vouched history still scrolls locally");
302
- assert.equal(parsed.realHistoryCase.firstForwardAt, 101);
303
- assert.equal(parsed.resetCase.historyFloor, 0, "empty/resnapshot resets the floor");
304
- assert.equal(parsed.resizeCase.historyFloor, parsed.resizeCase.baseY, "resize re-bases the floor on the new baseY");
305
- assert.equal(parsed.resizeCase.scrollableHistoryLines, 0);
306
- assert.equal(parsed.missingFieldCase.historyFloor, parsed.missingFieldCase.attachBaseY, "a runner without scrollbackLines disables local history instead of NaN");
307
- assert.equal(parsed.missingFieldCase.firstForwardAt, 1);
308
- });
309
- ```
310
-
311
- - [ ] **Step 2: Run the test to verify it fails**
312
-
313
- Run: `node --test test/pty-attach-frame-overflow.test.mjs`
314
- Expected: FAIL — scenario file missing.
315
-
316
- - [ ] **Step 3: Implement the scenario**
317
-
318
- `test-support/pty-attach-frame-overflow-scenario.ts` must, in one process:
319
-
320
- 1. Build a **runner-side** `createTerminalModel({ cols: 300, rows: 65, scrollback: 2000 })` and `feedOutput` a synthetic 65-row alternate/normal screen (no dependency on a live session). `await whenIdle(model)`.
321
- 2. `const dto = await captureTerminalSnapshot(model)`; `const frame = await synthesizeFullRedrawFrame(dto)`; require `dto.scrollback.length === 0`.
322
- 3. Serve a real named pipe (`net.createServer`) that answers `subscribe_terminal` with `snapshot_begin` / `snapshot_frame` / `snapshot_end` carrying `scrollbackLines: dto.scrollback.length`. **Ignore any `resize` control message** — the frame must stay 65 rows (spec §6 "帧投递接缝", option 1).
323
- 4. Build a fake TUI (`mode: "fullscreen"`, `terminal: { rows: 40, cols: 300 }`) and a real `PtyAttachComponent` with that pipe; `await` until `component.attachMode === "protocol"` **and** `component.receivedOutput`.
324
- 5. `await new Promise((resolve) => component.term.write("", resolve))` to drain the async parse before asserting.
325
- 6. Record `attachBaseY` (`component.term.buffer.active.baseY`), `historyFloor`, `scrollableHistoryLines` (call the pure helper with those two values).
326
- 7. Drive `component.handleMouse({ type: "wheel", wheelDelta: -1, x: 3, y: 3, screenX: 3, screenY: 3, width: 80, height: 22, shift: false, alt: false, ctrl: false })` and capture the forwarded `{type:"input"}` via the `Socket.prototype.write` interception pattern from `test-support/pty-attach-wheel-fallback-scenario.ts:113-119`.
327
- 8. Repeat the same flow with a model that first `feedOutput`s 100 lines of plain text (so `dto.scrollback.length === 100`), driving up to 120 notches and recording the first forwarded index into `realHistoryCase.firstForwardAt`.
328
- 9. Deliver one `snapshotReady` with `{ empty: true }` (call the component's attach-event handler or send a `snapshot_begin{empty}`+`snapshot_end` pair through the fake runner) and record `resetCase.historyFloor`.
329
- 10. **Resize case (A6)**: after the initial hydrate, call `component.render(120)` (a narrower width forces `resizeIfNeeded` → `term.resize`), drain the write queue, and record `resizeCase = { baseY, historyFloor, scrollableHistoryLines }`.
330
- 11. **Missing-field case (A7)**: repeat the hydrate with a fake runner that omits `scrollbackLines` from `snapshot_end`; record `missingFieldCase = { attachBaseY, historyFloor, firstForwardAt }`.
331
- 12. Print exactly one JSON line and exit 0; catch errors into `{ error }` and exit 0 so the test reports them.
332
-
333
- - [ ] **Step 4: Run the test to verify it passes**
334
-
335
- Run: `node --test test/pty-attach-frame-overflow.test.mjs`
336
- Expected: PASS.
337
-
338
- - [ ] **Step 5: Commit**
339
-
340
- ```bash
341
- git add test-support/pty-attach-frame-overflow-scenario.ts test/pty-attach-frame-overflow.test.mjs
342
- git commit -m "test(attach): pin the snapshot-hydration wheel topology (issue #205)"
343
- ```
344
-
345
- ---
346
-
347
- ### Task 5: Fullscreen dispatch smoke through the real `TuiAltScreen`
348
-
349
- **Files:**
350
- - Modify: `test-support/pty-attach-frame-overflow-scenario.ts` (new `tuiDispatch` section)
351
- - Modify: `test/pty-attach-frame-overflow.test.mjs` (new assertions)
352
-
353
- **Interfaces:**
354
- - Consumes: everything from Task 4.
355
- - Produces: `tuiDispatch: { handleMouseCalls: number, firstForwardAt: number }` in the scenario JSON.
356
-
357
- - [ ] **Step 1: Write the failing test**
358
-
359
- ```js
360
- assert.ok(parsed.tuiDispatch.handleMouseCalls >= 1, "the wheel must reach the overlay component");
361
- assert.equal(parsed.tuiDispatch.firstForwardAt, 1);
362
- ```
363
-
364
- - [ ] **Step 2: Run to verify it fails**
365
-
366
- Run: `node --test test/pty-attach-frame-overflow.test.mjs`
367
- Expected: FAIL — `parsed.tuiDispatch` undefined.
368
-
369
- - [ ] **Step 3: Implement**
370
-
371
- In the scenario, build `const tui = new TuiAltScreen(stubTerminal, false, undefined, { mouse: true, wheelScrollLines: 1 })`, `tui.showOverlay(component, { anchor: "top-left", width: "100%", maxHeight: "100%", margin: 0 })`, populate layouts with `tui.render(300)` + `tui.compositeOverlays(tui.render(300), 300, 40)`, then feed `"\x1b[<64;10;10M"` and read `(tui as any).renderedOverlayLayouts` / the component's `handleMouse` call count. The stub terminal needs `columns`, `rows`, `kittyProtocolActive`, `start`/`stop`/`drainInput` no-ops, `write`/`moveBy`/`hideCursor`/`showCursor`/`clearLine`/`clearFromCursor`/`clearScreen`/`setTitle`/`setProgress`/`setProgramStatus` no-ops.
372
-
373
- - [ ] **Step 4: Run to verify it passes**
374
-
375
- Run: `node --test test/pty-attach-frame-overflow.test.mjs`
376
- Expected: PASS.
377
-
378
- - [ ] **Step 5: Commit**
379
-
380
- ```bash
381
- git add test-support/pty-attach-frame-overflow-scenario.ts test/pty-attach-frame-overflow.test.mjs
382
- git commit -m "test(attach): drive the wheel through the real fullscreen dispatcher (issue #205)"
383
- ```
384
-
385
- ---
386
-
387
- ### Task 6: Docs + full verification gate
388
-
389
- **Files:**
390
- - Modify: `README.md` (attach paragraph, ~L201: document that the wheel forwards when the local scrollback is hydration residue)
391
- - Modify: `CHANGELOG.md` — **not by hand**; skip unless `npm run verify` reports a missing PR after merge (repo AGENTS.md).
392
-
393
- **Interfaces:**
394
- - Consumes: all previous tasks.
395
-
396
- - [ ] **Step 1: Update the README attach paragraph**
397
-
398
- Append one sentence after the existing wheel description: the attach viewport only scrolls scrollback the hosted session actually produced; a snapshot hydration that is taller than the local window is not scrollable, and the wheel is forwarded to the session instead.
399
-
400
- - [ ] **Step 2: Run the full gate**
401
-
402
- Run: `npm run verify`
403
- Expected: PASS — typecheck, perf gate, tests, coverage thresholds, pack dry-run.
404
-
405
- - [ ] **Step 3: Commit**
406
-
407
- ```bash
408
- git add README.md
409
- git commit -m "docs(attach): explain wheel forwarding when hydrated scrollback is synthetic (issue #205)"
410
- ```
411
-
412
- ---
413
-
414
- ### Task 7: Post-implementation manual verification (U1)
415
-
416
- **Files:** none (verification only)
417
-
418
- **Interfaces:**
419
- - Consumes: the merged branch loaded by a real pi process.
420
-
421
- - [ ] **Step 1: Merge + refresh the pi git package**
422
-
423
- After the PR merges: `cd C:/Users/27499/proj/opensource/pi-agent-board && git pull`, then `pi update` (or restart pi so it re-clones), then confirm
424
- `git -C ~/.pi/agent/git/github.com/zhuxixi/pi-agent-board log --oneline -1` contains the merge commit.
425
-
426
- - [ ] **Step 2: Restart pi completely**
427
-
428
- The running process holds module caches; a hot reload does not pick up `src/` changes.
429
-
430
- - [ ] **Step 3: Attach a long session and roll the wheel once**
431
-
432
- pi fullscreen → `/agent-board` → attach a session whose `screen.log` exceeds 60 KB → one wheel notch up.
433
-
434
- - [ ] **Step 4: Record the result**
435
-
436
- PASS: the first notch shows the previous line of the child's transcript; no editor-row fragments; PageUp still behaves as before.
437
- FAIL: record the observed baseY/historyFloor in the issue and reopen.