@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.
- package/CHANGELOG.md +24 -4
- package/CONTRIBUTING.md +7 -0
- package/README.md +55 -419
- package/SECURITY.md +19 -0
- package/docs/how-it-works.md +31 -0
- package/docs/reference.md +277 -0
- package/package.json +5 -11
- package/src/commands/agent-board.ts +26 -2
- package/src/commands/attach-flow.ts +4 -0
- package/src/core/double-press.mjs +6 -2
- package/src/core/slash-commands.mjs +52 -0
- package/src/index.ts +1 -1
- package/src/runtime/service.mjs +12 -0
- package/src/ui/dashboard.ts +59 -25
- package/src/ui/pty-attach.ts +8 -3
- package/IMPLEMENTATION_PLAN.md +0 -920
- package/PRD.md +0 -484
- package/PROGRESS.md +0 -142
- package/VERIFY.md +0 -115
- package/docs/BATCH_SELECTION_READ_FLOW.md +0 -277
- package/docs/EXPLORATION.md +0 -187
- package/docs/PTY_ATTACH_IMPLEMENTATION_PLAN.md +0 -581
- package/docs/RELEASE.md +0 -148
- package/docs/superpowers/plans/2026-08-15-screenlog-gc.md +0 -704
- package/docs/superpowers/plans/2026-08-16-attach-double-cursor-jiggle-retry.md +0 -499
- package/docs/superpowers/plans/2026-08-21-attach-coldstart-jiggle-rearm.md +0 -797
- package/docs/superpowers/plans/2026-08-21-dashboard-keypress-lag.md +0 -366
- package/docs/superpowers/plans/2026-08-21-issue-14-needs-input-no-auto-done.md +0 -576
- package/docs/superpowers/plans/2026-08-22-jiggle-shrink-and-hold.md +0 -337
- package/docs/superpowers/plans/2026-08-22-launch-cwd-favorites.md +0 -916
- package/docs/superpowers/plans/2026-08-22-question-tool-grouping.md +0 -211
- package/docs/superpowers/plans/2026-08-22-rename-state-labels.md +0 -182
- package/docs/superpowers/plans/2026-08-22-stable-list-order.md +0 -51
- package/docs/superpowers/plans/2026-08-27-locks-acquirelock-spin.md +0 -614
- package/docs/superpowers/plans/2026-08-27-pty-runner-test-flaky.md +0 -32
- package/docs/superpowers/plans/2026-08-29-code-refs-badges.md +0 -223
- package/docs/superpowers/plans/2026-08-30-circular-navigation.md +0 -308
- package/docs/superpowers/plans/2026-08-30-post-exit-timing-fix.md +0 -37
- package/docs/superpowers/plans/2026-08-30-pty-attach-quality-debt.md +0 -311
- package/docs/superpowers/plans/2026-08-30-readme-v2.md +0 -294
- package/docs/superpowers/plans/2026-09-01-attach-detach-gate-cursor-anchor.md +0 -284
- package/docs/superpowers/plans/2026-09-02-attach-detach-editor-state.md +0 -722
- package/docs/superpowers/plans/2026-09-03-code-refs-pr-backlink-narrow.md +0 -551
- package/docs/superpowers/plans/2026-09-03-detach-gate-glyph-fallback.md +0 -146
- package/docs/superpowers/plans/2026-09-04-evidence-outputpreview.md +0 -209
- package/docs/superpowers/plans/2026-09-04-warm-host-reclaim.md +0 -796
- package/docs/superpowers/plans/2026-09-05-issue-13-drainnextfollowup-pty-probe.md +0 -114
- package/docs/superpowers/plans/2026-09-05-issue-38-windows-wezterm-ime-cursor.md +0 -73
- package/docs/superpowers/plans/2026-09-05-issue-39-truncate-codepoint-boundary.md +0 -143
- package/docs/superpowers/plans/2026-09-05-issue-61-mention-fallback-guards.md +0 -226
- package/docs/superpowers/plans/2026-09-05-issue-63-flaky-manual-completion.md +0 -87
- package/docs/superpowers/plans/2026-09-05-issue-64-changelog-release-helper.md +0 -53
- package/docs/superpowers/plans/2026-09-05-pty-host-stacking-sock-race.md +0 -731
- package/docs/superpowers/plans/2026-09-08-attach-ctrl-left-detach.md +0 -30
- package/docs/superpowers/plans/2026-09-08-dashboard-shrink-repaint.md +0 -68
- package/docs/superpowers/plans/2026-09-08-issue-11-attach-runtime-desync-heal.md +0 -917
- package/docs/superpowers/plans/2026-09-08-legacy-stale-host-recovery.md +0 -125
- package/docs/superpowers/plans/2026-09-08-spawn-async-error-swallow.md +0 -56
- package/docs/superpowers/plans/2026-09-08-stale-model-attach-guard.md +0 -96
- package/docs/superpowers/plans/2026-09-09-claimpid-blocks-replace.md +0 -267
- package/docs/superpowers/plans/2026-09-09-harden-runner-architecture.md +0 -603
- package/docs/superpowers/plans/2026-09-09-single-writer-completion.md +0 -252
- package/docs/superpowers/plans/2026-09-10-reader-consistency.md +0 -115
- package/docs/superpowers/plans/2026-09-14-attach-cursor-dectcem-gate.md +0 -469
- package/docs/superpowers/plans/2026-09-14-attach-snapshot.md +0 -92
- package/docs/superpowers/plans/2026-09-14-host-meta-orphan-lock.md +0 -771
- package/docs/superpowers/plans/2026-09-14-issue-106-terminal-frame-cognition.md +0 -299
- package/docs/superpowers/plans/2026-09-14-issue-113-foreground-preview-race.md +0 -609
- package/docs/superpowers/plans/2026-09-14-terminal-model.md +0 -145
- package/docs/superpowers/plans/2026-09-15-coordinator-pipe-root-normalize.md +0 -224
- package/docs/superpowers/plans/2026-09-15-lease-publish-eprem-reclaim.md +0 -341
- package/docs/superpowers/plans/2026-09-18-detach-anchor-reporter-endpoint.md +0 -875
- package/docs/superpowers/plans/2026-09-20-control-lifecycle.md +0 -116
- package/docs/superpowers/plans/2026-09-20-issue-121-perf-gate-out-of-coverage.md +0 -517
- package/docs/superpowers/plans/2026-09-23-a5-mid-stream-assertion.md +0 -25
- package/docs/superpowers/plans/2026-09-23-cwd-browse-tab-drill.md +0 -393
- package/docs/superpowers/plans/2026-09-23-terminal-query-forwarding.md +0 -51
- package/docs/superpowers/plans/2026-09-23-unconditional-detach-key.md +0 -58
- package/docs/superpowers/plans/2026-09-24-control-reconcile-flake.md +0 -698
- package/docs/superpowers/plans/2026-09-24-probe-attached-clients.md +0 -605
- package/docs/superpowers/plans/2026-09-29-issue-145-holding-semantic-state.md +0 -1183
- package/docs/superpowers/plans/2026-09-29-issue-145-user-verification.md +0 -66
- package/docs/superpowers/plans/2026-09-30-board-delete-key-x-y-confirm.md +0 -649
- package/docs/superpowers/plans/2026-09-30-issue-153-runner-stop-boot-window.md +0 -618
- package/docs/superpowers/plans/2026-09-30-issue-95-test-discipline.md +0 -555
- package/docs/superpowers/plans/2026-09-30-publish-dispatch-tag-check.md +0 -259
- package/docs/superpowers/plans/2026-10-06-attach-settle-scheme-delivery.md +0 -751
- package/docs/superpowers/plans/2026-10-09-issue-167-attach-middle-paste.md +0 -509
- package/docs/superpowers/plans/2026-10-09-issue-169-fullscreen-mouse-ownership.md +0 -346
- package/docs/superpowers/plans/2026-10-10-attach-delete-in-place.md +0 -832
- package/docs/superpowers/plans/2026-10-10-issue-174-selection-mirror-paste-source.md +0 -510
- package/docs/superpowers/plans/2026-10-14-issue-170-attach-reply-guard.md +0 -771
- package/docs/superpowers/plans/2026-10-14-issue-178-accept-registration-race.md +0 -1257
- package/docs/superpowers/plans/2026-10-14-issue-184-snapshot-window-contract.md +0 -657
- package/docs/superpowers/plans/2026-10-14-issue-185-attach-wheel-alt-screen.md +0 -47
- package/docs/superpowers/plans/2026-10-14-issue-187-motionless-drag.md +0 -931
- package/docs/superpowers/plans/2026-10-14-issue-189-mixed-input-segments.md +0 -823
- package/docs/superpowers/plans/2026-10-15-issue-188-x10-mouse-release.md +0 -526
- package/docs/superpowers/plans/2026-10-16-issue-198-attach-wheel-forward-fallback.md +0 -32
- package/docs/superpowers/plans/2026-10-18-attach-wheel-replay-pseudo-scrollback.md +0 -437
- package/docs/superpowers/plans/2026-10-19-issue-213-reply-guard-csi-key.md +0 -257
- package/docs/superpowers/plans/2026-10-19-issue-214-attach-forward-alt-modifier.md +0 -202
- package/docs/superpowers/plans/2026-10-21-issue-221-ask-user-question.md +0 -198
- package/docs/superpowers/plans/2026-10-23-issue-222-attach-ready.md +0 -290
- package/docs/superpowers/plans/2026-10-23-issue-225-monotonic-foreground-state.md +0 -450
- package/docs/superpowers/plans/2026-10-23-issue-229-vim-jkl.md +0 -449
- package/docs/superpowers/plans/2026-10-23-issue-232-attach-bg-only-cells.md +0 -153
- package/docs/superpowers/plans/2026-10-23-lease-heartbeat-staleness.md +0 -537
- package/docs/superpowers/plans/2026-10-23-selected-row-highlight.md +0 -405
- package/docs/superpowers/plans/2026-10-24-issue-217-windows-pid-reuse-stopping.md +0 -367
- package/docs/superpowers/plans/2026-10-24-issue-230-warm-host-background-runs.md +0 -806
- package/docs/superpowers/plans/2026-10-24-issue-238-windows-process-identity-token.md +0 -490
- package/docs/superpowers/plans/2026-10-24-issue-241-coordinator-root-exit.md +0 -461
- package/docs/superpowers/plans/2026-10-25-issue-239-coordinator-replacement-identity.md +0 -504
- package/docs/superpowers/plans/2026-10-26-issue-245-key-hints-doc-sync.md +0 -244
- package/docs/superpowers/plans/2026-10-27-issue-172-fullscreen-selection-clear.md +0 -344
- package/docs/superpowers/plans/2026-10-27-issue-252-select-key-hints.md +0 -209
- package/docs/superpowers/plans/2026-10-28-issue-255-coordinator-endpoint-zombie.md +0 -442
- package/docs/superpowers/plans/2026-10-28-issue-257-legacy-pty-runner-endpoint-cleanup.md +0 -256
- package/docs/superpowers/plans/2026-11-03-cwd-favorites-full-pool.md +0 -266
- package/docs/superpowers/specs/2026-08-15-screenlog-gc-design.md +0 -105
- package/docs/superpowers/specs/2026-08-16-attach-double-cursor-jiggle-retry-design.md +0 -142
- package/docs/superpowers/specs/2026-08-21-attach-coldstart-jiggle-rearm-design.md +0 -110
- package/docs/superpowers/specs/2026-08-21-dashboard-keypress-lag-design.md +0 -59
- package/docs/superpowers/specs/2026-08-21-issue-14-needs-input-no-auto-done-design.md +0 -109
- package/docs/superpowers/specs/2026-08-22-cwd-favorites-design.md +0 -131
- package/docs/superpowers/specs/2026-08-22-jiggle-shrink-and-hold-design.md +0 -111
- package/docs/superpowers/specs/2026-08-22-question-tool-grouping-design.md +0 -114
- package/docs/superpowers/specs/2026-08-22-rename-state-labels-design.md +0 -80
- package/docs/superpowers/specs/2026-08-27-locks-acquirelock-spin-design.md +0 -97
- package/docs/superpowers/specs/2026-08-27-pty-runner-test-flaky-design.md +0 -35
- package/docs/superpowers/specs/2026-08-29-code-refs-badges-design.md +0 -128
- package/docs/superpowers/specs/2026-08-30-circular-navigation-design.md +0 -47
- package/docs/superpowers/specs/2026-08-30-eprm-atomicwrite-race-design.md +0 -77
- package/docs/superpowers/specs/2026-08-30-post-exit-timing-fix-design.md +0 -80
- package/docs/superpowers/specs/2026-08-30-pty-attach-quality-debt-design.md +0 -105
- package/docs/superpowers/specs/2026-08-30-readme-v2-design.md +0 -115
- package/docs/superpowers/specs/2026-09-01-attach-detach-gate-cursor-anchor-design.md +0 -78
- package/docs/superpowers/specs/2026-09-02-attach-detach-editor-state-design.md +0 -120
- package/docs/superpowers/specs/2026-09-03-code-refs-pr-backlink-narrow-design.md +0 -92
- package/docs/superpowers/specs/2026-09-03-detach-gate-glyph-fallback-design.md +0 -99
- package/docs/superpowers/specs/2026-09-04-evidence-outputpreview-design.md +0 -50
- package/docs/superpowers/specs/2026-09-04-warm-host-reclaim-design.md +0 -106
- package/docs/superpowers/specs/2026-09-05-issue-13-drainnextfollowup-pty-probe-design.md +0 -64
- package/docs/superpowers/specs/2026-09-05-issue-38-windows-wezterm-ime-design.md +0 -48
- package/docs/superpowers/specs/2026-09-05-issue-39-truncate-codepoint-boundary-design.md +0 -64
- package/docs/superpowers/specs/2026-09-05-issue-61-mention-fallback-design.md +0 -71
- package/docs/superpowers/specs/2026-09-05-issue-63-flaky-manual-completion-design.md +0 -49
- package/docs/superpowers/specs/2026-09-05-issue-64-changelog-helper-design.md +0 -76
- package/docs/superpowers/specs/2026-09-05-pty-host-stacking-sock-race-design.md +0 -510
- package/docs/superpowers/specs/2026-09-07-issue-11-attach-runtime-desync-heal-design.md +0 -130
- package/docs/superpowers/specs/2026-09-08-attach-ctrl-left-detach-design.md +0 -58
- package/docs/superpowers/specs/2026-09-08-dashboard-shrink-repaint-design.md +0 -52
- package/docs/superpowers/specs/2026-09-08-legacy-stale-host-recovery-design.md +0 -87
- package/docs/superpowers/specs/2026-09-08-spawn-async-error-swallow-design.md +0 -56
- package/docs/superpowers/specs/2026-09-08-stale-model-attach-guard-design.md +0 -79
- package/docs/superpowers/specs/2026-09-09-claimpid-blocks-replace-design.md +0 -147
- package/docs/superpowers/specs/2026-09-09-harden-runner-architecture-design.md +0 -304
- package/docs/superpowers/specs/2026-09-14-attach-cursor-dectcem-gate-design.md +0 -114
- package/docs/superpowers/specs/2026-09-14-host-meta-orphan-lock-design.md +0 -120
- package/docs/superpowers/specs/2026-09-14-issue-106-terminal-frame-cognition-design.md +0 -146
- package/docs/superpowers/specs/2026-09-14-issue-113-foreground-preview-race-design.md +0 -116
- package/docs/superpowers/specs/2026-09-15-coordinator-pipe-root-normalize-design.md +0 -84
- package/docs/superpowers/specs/2026-09-15-lease-publish-eprem-reclaim-design.md +0 -92
- package/docs/superpowers/specs/2026-09-18-detach-anchor-reporter-endpoint-design.md +0 -123
- package/docs/superpowers/specs/2026-09-20-issue-121-perf-gate-out-of-coverage-design.md +0 -204
- package/docs/superpowers/specs/2026-09-23-a5-mid-stream-assertion-design.md +0 -43
- package/docs/superpowers/specs/2026-09-23-cwd-browse-tab-drill-design.md +0 -95
- package/docs/superpowers/specs/2026-09-23-terminal-query-forwarding-design.md +0 -63
- package/docs/superpowers/specs/2026-09-23-unconditional-detach-key-design.md +0 -57
- package/docs/superpowers/specs/2026-09-24-control-reconcile-flake-design.md +0 -98
- package/docs/superpowers/specs/2026-09-24-probe-attached-clients-design.md +0 -190
- package/docs/superpowers/specs/2026-09-29-issue-145-holding-semantic-state-design.md +0 -394
- package/docs/superpowers/specs/2026-09-30-board-delete-key-x-y-confirm-design.md +0 -86
- package/docs/superpowers/specs/2026-09-30-issue-153-runner-stop-boot-window-design.md +0 -75
- package/docs/superpowers/specs/2026-09-30-issue-95-test-discipline-design.md +0 -226
- package/docs/superpowers/specs/2026-09-30-publish-dispatch-tag-check-design.md +0 -129
- package/docs/superpowers/specs/2026-10-06-attach-settle-scheme-delivery-design.md +0 -196
- package/docs/superpowers/specs/2026-10-09-issue-167-attach-middle-paste-design.md +0 -125
- package/docs/superpowers/specs/2026-10-09-issue-169-fullscreen-mouse-ownership-design.md +0 -130
- package/docs/superpowers/specs/2026-10-10-attach-delete-in-place-design.md +0 -224
- package/docs/superpowers/specs/2026-10-10-issue-174-selection-mirror-paste-source-design.md +0 -154
- package/docs/superpowers/specs/2026-10-14-issue-170-attach-reply-guard-design.md +0 -195
- package/docs/superpowers/specs/2026-10-14-issue-178-accept-registration-race-design.md +0 -287
- package/docs/superpowers/specs/2026-10-14-issue-184-snapshot-window-contract-design.md +0 -288
- package/docs/superpowers/specs/2026-10-14-issue-185-attach-wheel-alt-screen-design.md +0 -126
- package/docs/superpowers/specs/2026-10-14-issue-187-motionless-drag-design.md +0 -269
- package/docs/superpowers/specs/2026-10-14-issue-189-mixed-input-segments-design.md +0 -369
- package/docs/superpowers/specs/2026-10-15-issue-188-x10-mouse-release-design.md +0 -252
- package/docs/superpowers/specs/2026-10-16-issue-198-attach-wheel-forward-fallback-design.md +0 -67
- package/docs/superpowers/specs/2026-10-18-attach-wheel-replay-pseudo-scrollback-design.md +0 -250
- package/docs/superpowers/specs/2026-10-19-issue-213-reply-guard-csi-key-design.md +0 -165
- package/docs/superpowers/specs/2026-10-19-issue-214-attach-forward-alt-modifier-design.md +0 -131
- package/docs/superpowers/specs/2026-10-20-issue-221-ask-user-question-design.md +0 -270
- package/docs/superpowers/specs/2026-10-22-issue-225-monotonic-foreground-state-design.md +0 -225
- package/docs/superpowers/specs/2026-10-23-issue-222-attach-ready-design.md +0 -204
- package/docs/superpowers/specs/2026-10-23-issue-229-vim-jkl-design.md +0 -90
- package/docs/superpowers/specs/2026-10-23-issue-232-attach-bg-only-cells-design.md +0 -146
- package/docs/superpowers/specs/2026-10-23-lease-heartbeat-staleness-design.md +0 -271
- package/docs/superpowers/specs/2026-10-23-selected-row-highlight-design.md +0 -191
- package/docs/superpowers/specs/2026-10-24-issue-217-windows-pid-reuse-stopping-design.md +0 -334
- package/docs/superpowers/specs/2026-10-24-issue-230-warm-host-background-runs-design.md +0 -283
- package/docs/superpowers/specs/2026-10-24-issue-238-windows-process-identity-token-design.md +0 -286
- package/docs/superpowers/specs/2026-10-24-issue-241-coordinator-root-exit-design.md +0 -381
- package/docs/superpowers/specs/2026-10-25-issue-239-coordinator-replacement-identity-design.md +0 -331
- package/docs/superpowers/specs/2026-10-26-issue-245-key-hints-doc-sync-design.md +0 -72
- package/docs/superpowers/specs/2026-10-27-issue-172-fullscreen-selection-clear-design.md +0 -248
- package/docs/superpowers/specs/2026-10-27-issue-252-select-key-hints-design.md +0 -95
- package/docs/superpowers/specs/2026-10-28-issue-255-coordinator-endpoint-zombie-design.md +0 -380
- package/docs/superpowers/specs/2026-10-28-issue-257-legacy-pty-runner-endpoint-cleanup-design.md +0 -197
- package/docs/superpowers/specs/2026-11-03-cwd-favorites-full-pool-design.md +0 -106
- package/scripts/budget-audit.mjs +0 -135
- package/scripts/release.mjs +0 -446
- package/scripts/release_helper.mjs +0 -309
- package/scripts/run-perf-gate.mjs +0 -40
- 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.
|