Skip to content

fix(spawn): set PI_CLEAR_ON_SHRINK=1 for Firstmate-launched Pi sessions - #75

Merged
withally merged 7 commits into
mainfrom
fm/fm-pi-clear-on-shrink-p1
Aug 25, 2026
Merged

fix(spawn): set PI_CLEAR_ON_SHRINK=1 for Firstmate-launched Pi sessions#75
withally merged 7 commits into
mainfrom
fm/fm-pi-clear-on-shrink-p1

Conversation

@withally

Copy link
Copy Markdown
Owner

Intent

Fix the Pi black-regions rendering issue for Firstmate-launched Pi sessions according to the investigation report. Make every Firstmate-launched Pi and pi-signed process - crewmates, scouts, secondmates, and the documented primary launch - set PI_CLEAR_ON_SHRINK=1 beside FM_PI_HARNESS while preserving Pi's explicit terminal.clearOnShrink settings-level override. Add a unit-level launch-environment regression and an opt-in live regular-TUI regression that uses an isolated scratch Pi session with Calm on, expands and collapses a viewport-filling tool result, asserts no empty region remains above the next visible content, repeats around a real compaction rebuild, and retains existing hidden-thinking coverage. Document in docs/configuration.md and the harness-adapters Pi section that Calm-induced transcript shrink relies on PI_CLEAR_ON_SHRINK=1, why it prevents black regions, that Pi 0.84.3 does not fix the issue by itself, and the flicker and superseded-scrollback tradeoffs. Do not upgrade Pi or restart running secondmates; the setting applies on next launch. Deliver through the no-mistakes pipeline as one PR, do not merge, because the captain decides go-live.

What Changed

  • bin/fm-spawn.sh now prefixes every Pi-family launch command with PI_CLEAR_ON_SHRINK=1 alongside FM_PI_HARNESS, so crewmates, scouts, and secondmates on both pi and pi-signed fully clear the terminal when the transcript render shrinks instead of leaving stale empty regions; the README primary-launch commands set the same variable.
  • Added tests/fm-pi-clear-on-shrink-live-e2e.test.sh, an opt-in (FM_PI_CLEAR_ON_SHRINK_LIVE_E2E=1) real-Pi regular-TUI regression on an isolated scratch session with Calm on that expands and collapses a viewport-filling tool result, asserts no empty region remains above the next visible content, and repeats the check around a real /compact rebuild; registered it in bin/fm-test-run.sh's live-e2e family, and updated tests/fm-spawn-dispatch-profile.test.sh launch-shape assertions to cover the new environment variable.
  • Documented the rationale in docs/configuration.md and the harness-adapters Pi section: why Calm-induced shrink needs the setting, that an explicit terminal.clearOnShrink setting still takes precedence, that Pi 0.84.3 does not fix it alone, and the flicker/superseded-scrollback tradeoffs.

Risk Assessment

✅ Low: The functional change is a single environment-variable prefix on Firstmate's one Pi-family launch site plus two documented primary commands, verified against Pi's own settings-precedence implementation, with matching unit-level launch assertions, accurate documentation of the override and tradeoffs, and an opt-in live regression whose helpers and fixture I traced without finding a defect.

Testing

Ran the targeted unit launch-environment regression (all Pi/pi-signed launch identities carry PI_CLEAR_ON_SHRINK=1) and then drove the redesigned opt-in live regression against real Pi 0.84.2 in both directions: it fails consistently with PI_CLEAR_ON_SHRINK=0, leaving a 9-row empty region below the last rendered row after the render shrinks, and passes consistently with PI_CLEAR_ON_SHRINK=1 both before and after a real compaction rebuild, resolving the round-1 concern that the live test guarded nothing. Captured the two real tmux viewports at the shrink assertion point and rendered them side by side as a screenshot that shows the black region present pre-fix and gone post-fix; transient test copies were removed and the worktree is clean.

Evidence: Same comparison as rendered HTML

Source: Same comparison as rendered HTML

<!doctype html><meta charset=utf-8><style>
body{background:#14161a;color:#e6e6e6;font-family:-apple-system,Helvetica,sans-serif;margin:24px}
h1{font-size:18px} .row{display:flex;gap:24px} .col{flex:1}
h2{font-size:14px;font-weight:600;margin:0 0 8px}
.bad{color:#ff6b6b} .good{color:#5ddf87}
pre{background:#000;color:#d8d8d8;font:12px/1.25 "SF Mono",Menlo,monospace;padding:10px;margin:0;
border:1px solid #333;white-space:pre;overflow:hidden;min-height:calc(36*1.25*12px)}
p.cap{font-size:12px;color:#9aa0a6}
</style>
<h1>Pi regular-TUI viewport right after a transcript shrink (real Pi 0.84.2, 100&times;36 tmux pane)</h1>
<div class=row>
<div class=col><h2 class=bad>PI_CLEAR_ON_SHRINK=0 &mdash; 9 stale blank rows at the bottom</h2><pre> BEFORE_COMPACT_ROW_002
 BEFORE_COMPACT_ROW_003
 BEFORE_COMPACT_ROW_004
 BEFORE_COMPACT_ROW_005
 BEFORE_COMPACT_ROW_006
 BEFORE_COMPACT_ROW_007
 BEFORE_COMPACT_ROW_008
 BEFORE_COMPACT_ROW_009
 BEFORE_COMPACT_ROW_010
 ... (86 more lines, ctrl+o to expand)


 AFTER_TOOL_RESULT

 Tool output: collapsed


 Reply after the collapsed tool result.


 NEXT_VISIBLE_CONTENT

────────────────────────────────────────────────────────────────────────────────────────────────────

────────────────────────────────────────────────────────────────────────────────────────────────────
/private/var/folders/3n/3wfcplrn3clf44hjjkfgq8t00000gn/T/fm-pi-clear-on-shrink-live-e2e.SUy3UR/pr...
↑8.0k ↓1.6k R5.9k W8.0k CH52.8% 5.5%/100k (auto)                       (pi-shrink-e2e) deterministic








</pre>
<p class=cap>Pre-fix launch: the composer draft was cleared, the render got shorter, and the removed rows stay as an empty black region below the last drawn row.</p></div>
<div class=col><h2 class=good>PI_CLEAR_ON_SHRINK=1 &mdash; 0 stale rows</h2><pre> TRANSCRIPT_TALLER_THAN_PANE


 Run viewport_fill once, then finish.



 viewport_fill
 BEFORE_COMPACT_ROW_001
 BEFORE_COMPACT_ROW_002
 BEFORE_COMPACT_ROW_003
 BEFORE_COMPACT_ROW_004
 BEFORE_COMPACT_ROW_005
 BEFORE_COMPACT_ROW_006
 BEFORE_COMPACT_ROW_007
 BEFORE_COMPACT_ROW_008
 BEFORE_COMPACT_ROW_009
 BEFORE_COMPACT_ROW_010
 ... (86 more lines, ctrl+o to expand)


 AFTER_TOOL_RESULT

 Tool output: collapsed


 Reply after the collapsed tool result.


 NEXT_VISIBLE_CONTENT

────────────────────────────────────────────────────────────────────────────────────────────────────

────────────────────────────────────────────────────────────────────────────────────────────────────
/private/var/folders/3n/3wfcplrn3clf44hjjkfgq8t00000gn/T/fm-pi-clear-on-shrink-live-e2e.J3aLNP/pr...
↑8.0k ↓1.6k R5.9k W8.0k CH52.8% 5.5%/100k (auto)                       (pi-shrink-e2e) deterministic</pre>
<p class=cap>Firstmate-launched Pi: the shrink redraws the full viewport, so content fills down to the last terminal row.</p></div>
</div>
Evidence: Fail-before/pass-after write-up for the live regression

Source: Fail-before/pass-after write-up for the live regression

# Does the opt-in live regression fail before the fix?

Yes, after the round-2 redesign of `tests/fm-pi-clear-on-shrink-live-e2e.test.sh` (commit 7c0f004).

Probe: a copy of the shipped test was run against real Pi 0.84.2 changing only the launched value of
`PI_CLEAR_ON_SHRINK` (`1` = post-fix launch env, `0` = pre-fix launch env; Pi compares strictly to `"1"`).
The test grows the render with a wrapped composer draft over a transcript that is already taller than the
pane, clears the draft, waits for the viewport to settle, and asserts no blank region is left below the
last rendered row.

    PI_CLEAR_ON_SHRINK=0 -> not ok - pre-compaction shrink left a 9-row empty region below the last rendered row   (3/3 runs)
    PI_CLEAR_ON_SHRINK=1 -> ok - Pi 0.84.2 regular TUI leaves no stale rows when the render shrinks, before and after compaction   (4/4 runs)

Artifacts:

- `pi-clear-on-shrink-panes.png` / `.html` - side-by-side of the two real tmux viewports at the shrink
  assertion point; the pre-fix pane shows the empty black region under the footer.
- `pane-clear-on-shrink-{0,1}/pre-compaction-shrink.txt` - raw `tmux capture-pane` text behind that image.
- `live-e2e-clear-on-shrink-{0,1}.log` - the two test runs.
- `pi-clear-on-shrink-settings.png` - the launch env really flips Pi's own `terminal.clearOnShrink`, and an
  explicit `settings.json` value still overrides it.

## Superseded round-1 finding

The earlier version of this live test asserted only on blank rows *above* the next visible content after a
Ctrl+O collapse. That assertion passed identically with `PI_CLEAR_ON_SHRINK` set to `0` and `1`, so it guarded
nothing. The captures from that probe remain here as
`pane-clearonshrink-{0,1}-{pre,post}-compaction-collapse.txt` and `collapse-immediate-clearonshrink-{0,1}.txt`.
Evidence: Live test run with PI_CLEAR_ON_SHRINK=0 (pre-fix launch env)

Source: Live test run with PI_CLEAR_ON_SHRINK=0 (pre-fix launch env)

not ok - pre-compaction shrink left a 9-row empty region below the last rendered row

 BEFORE_COMPACT_ROW_002
 BEFORE_COMPACT_ROW_003
 BEFORE_COMPACT_ROW_004
 BEFORE_COMPACT_ROW_005
 BEFORE_COMPACT_ROW_006
 BEFORE_COMPACT_ROW_007
 BEFORE_COMPACT_ROW_008
 BEFORE_COMPACT_ROW_009
 BEFORE_COMPACT_ROW_010
 ... (86 more lines, ctrl+o to expand)


 AFTER_TOOL_RESULT

 Tool output: collapsed


 Reply after the collapsed tool result.


 NEXT_VISIBLE_CONTENT

────────────────────────────────────────────────────────────────────────────────────────────────────

────────────────────────────────────────────────────────────────────────────────────────────────────
/private/var/folders/3n/3wfcplrn3clf44hjjkfgq8t00000gn/T/fm-pi-clear-on-shrink-live-e2e.SUy3UR/pr...
↑8.0k ↓1.6k R5.9k W8.0k CH52.8% 5.5%/100k (auto)                       (pi-shrink-e2e) deterministic









not ok - pre-compaction shrink left a 9-row empty region below the last rendered row
Evidence: Live test run with PI_CLEAR_ON_SHRINK=1 (Firstmate launch env)

Source: Live test run with PI_CLEAR_ON_SHRINK=1 (Firstmate launch env)

ok - Pi 0.84.2 regular TUI leaves no stale rows when the render shrinks, before and after compaction

ok - Pi 0.84.2 regular TUI leaves no stale rows when the render shrinks, before and after compaction
Evidence: Raw tmux pane capture at the shrink assertion, PI_CLEAR_ON_SHRINK=0

Source: Raw tmux pane capture at the shrink assertion, PI_CLEAR_ON_SHRINK=0

 BEFORE_COMPACT_ROW_002
 BEFORE_COMPACT_ROW_003
 BEFORE_COMPACT_ROW_004
 BEFORE_COMPACT_ROW_005
 BEFORE_COMPACT_ROW_006
 BEFORE_COMPACT_ROW_007
 BEFORE_COMPACT_ROW_008
 BEFORE_COMPACT_ROW_009
 BEFORE_COMPACT_ROW_010
 ... (86 more lines, ctrl+o to expand)


 AFTER_TOOL_RESULT

 Tool output: collapsed


 Reply after the collapsed tool result.


 NEXT_VISIBLE_CONTENT

────────────────────────────────────────────────────────────────────────────────────────────────────

────────────────────────────────────────────────────────────────────────────────────────────────────
/private/var/folders/3n/3wfcplrn3clf44hjjkfgq8t00000gn/T/fm-pi-clear-on-shrink-live-e2e.SUy3UR/pr...
↑8.0k ↓1.6k R5.9k W8.0k CH52.8% 5.5%/100k (auto)                       (pi-shrink-e2e) deterministic
Evidence: Raw tmux pane capture at the shrink assertion, PI_CLEAR_ON_SHRINK=1

Source: Raw tmux pane capture at the shrink assertion, PI_CLEAR_ON_SHRINK=1

 TRANSCRIPT_TALLER_THAN_PANE


 Run viewport_fill once, then finish.



 viewport_fill
 BEFORE_COMPACT_ROW_001
 BEFORE_COMPACT_ROW_002
 BEFORE_COMPACT_ROW_003
 BEFORE_COMPACT_ROW_004
 BEFORE_COMPACT_ROW_005
 BEFORE_COMPACT_ROW_006
 BEFORE_COMPACT_ROW_007
 BEFORE_COMPACT_ROW_008
 BEFORE_COMPACT_ROW_009
 BEFORE_COMPACT_ROW_010
 ... (86 more lines, ctrl+o to expand)


 AFTER_TOOL_RESULT

 Tool output: collapsed


 Reply after the collapsed tool result.


 NEXT_VISIBLE_CONTENT

────────────────────────────────────────────────────────────────────────────────────────────────────

────────────────────────────────────────────────────────────────────────────────────────────────────
/private/var/folders/3n/3wfcplrn3clf44hjjkfgq8t00000gn/T/fm-pi-clear-on-shrink-live-e2e.J3aLNP/pr...
↑8.0k ↓1.6k R5.9k W8.0k CH52.8% 5.5%/100k (auto)                       (pi-shrink-e2e) deterministic
- Evidence: [Real Pi settings confirmation: launch env flips terminal.clearOnShrink, explicit settings.json still overrides](https://github.com/withally/firstmate/blob/ad9c5536f07caf9b65dfa58d73c46ba1482aa391/.no-mistakes/evidence/fm/fm-pi-clear-on-shrink-p1/pi-clear-on-shrink-settings.png) - Outcome: 🔧 1 issue found → auto-fixed ✅ across 2 runs (30m7s)

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

⚠️ **Review** - 2 infos
  • ⚠️ tests/fm-pi-clear-on-shrink-live-e2e.test.sh:195 - assert_no_empty_region_before asserts on the exact frame captured the instant the marker first appears (wait_for_text returns on first grep hit), with no settle for the in-progress redraw. Every sibling live Pi assertion in tests/fm-calm-pi-extension.test.sh (e.g. lines 2135-2141, 2170-2178) first spins until "Working..." clears or the transition completes before measuring geometry. Concrete sequence: Pi streams NEXT_VISIBLE_CONTENT and the status/spinner rows in separate diff-line writes; the capture can land between the marker line and the rows above it, showing a transient blank run and failing with "left a N-row empty region" on a correct build. Add the same settle loop (re-capture until the spinner row is gone, then re-capture the snapshot) before both assertions.
  • ℹ️ tests/fm-pi-clear-on-shrink-live-e2e.test.sh:69 - The awk run accumulator is never initialized, so when the marker is the first line of the captured viewport the block prints an empty string. [ &#34;$blank_run&#34; -le 3 ] then aborts with a bash "unary operator expected" error and the fallback message reads "left a -row empty region above NEXT_VISIBLE_CONTENT" - a nonsense diagnostic for what is actually the zero-blank-rows (passing) case. Add BEGIN { run = 0 }.
  • ℹ️ tests/fm-pi-clear-on-shrink-live-e2e.test.sh:178 - The live test hardcodes PI_CLEAR_ON_SHRINK=1 in its own tmux launch rather than launching through fm-spawn, so it characterizes Pi's rendering with the variable set and would pass unchanged if bin/fm-spawn.sh:1241 were reverted. The launch-environment regression is carried entirely by tests/fm-spawn-dispatch-profile.test.sh, which matches the intent's requested split, so this is coverage shape rather than a defect. If the intent is a true fail-before-fix regression, a negative arm running the same expand/collapse sequence with PI_CLEAR_ON_SHRINK=0 and asserting the empty region does appear would pin that the variable is what prevents it.

🔧 Fix: settle Pi viewport and init awk blank-run counter
1 warning still open:

  • ⚠️ tests/fm-pi-clear-on-shrink-live-e2e.test.sh:68 - settle_viewport can return during the exact in-progress redraw it was added to exclude. This fixture runs Calm on ($HOME_DIR/config/calm = on, line 118), and .pi/extensions/fm-calm.ts:145-152 calls ui.setWorkingVisible(false) while showing the animated ship, so the stock "Working..." row never appears in the pane and the ! tail -12 &#34;$SNAPSHOT&#34; | grep -Fq &#34;Working...&#34; guard (line 76) is always true. The only remaining protection is cmp -s over two frames captured 50ms apart (lines 73-77), but the Calm ship widget redraws on CALM_WORKING_SHIP_TICK_MS = 220 (.pi/extensions/lib/fm-calm-working-ship.ts:52 and 224). Concrete sequence: wait_for_text (line 214) returns on the first frame containing NEXT_VISIBLE_CONTENT while the run is still active; settle_viewport captures at t and t+50ms inside the same 220ms water phase; the frames are byte-identical, cmp succeeds, settle returns, and assert_no_empty_region_before (line 216) measures that transient frame and can fail with "left a N-row empty region above NEXT_VISIBLE_CONTENT" on a correct build. About three of four sample pairs fall inside a single tick, so the added settle removes little of the flake it targets. Fix: require stability across a window longer than one ship tick (sample more than 220ms apart, or N consecutive identical samples spanning >220ms), and gate on the Calm ship row being absent rather than on the stock "Working..." row. Same applies to the post-compaction call at line 231.

🔧 Fix: gate live-e2e settle on Calm ship across ship tick
1 warning still open:

  • ⚠️ tests/fm-spawn-dispatch-profile.test.sh:684 - Inserting PI_CLEAR_ON_SHRINK=1 between FM_PI_HARNESS=$harness and the executable makes assert_not_contains &#34;$launch&#34; &#34;FM_PI_HARNESS=$harness $harness&#34; unfalsifiable. If bin/fm-spawn.sh regressed to emitting a bare command name, the launch string would be FM_PI_HARNESS=pi PI_CLEAR_ON_SHRINK=1 pi --tui-mode regular ..., which does not contain FM_PI_HARNESS=pi pi, so this guard passes on exactly the regression it was written to catch. The paired positive assertion at line 682 still covers the absolute-path case, so this is a dead guard rather than a coverage hole, but it should be re-anchored (e.g. on PI_CLEAR_ON_SHRINK=1 $harness with a trailing space) to stay meaningful.

🔧 Fix: re-anchor bare-executable guard after clear-on-shrink assignment
3 infos still open:

  • ℹ️ tests/fm-pi-clear-on-shrink-live-e2e.test.sh:126 - The fixture writes {&#34;hideThinkingBlock&#34;:true,...} but the faux model is declared reasoning: false (line 150) and no response uses fauxThinking, so no thinking block is ever produced and the setting is inert in this test. If the intent's "retains existing hidden-thinking coverage" meant this new live test should also exercise hidden thinking, it does not: add a fauxThinking(...) block to one scripted assistant message and assert its text is absent from the pane. If it only meant "do not delete the existing hidden-thinking tests", that is satisfied (nothing was removed) and the setting is harmless noise. Flagging because resolving it changes the test's content, not just its mechanics.
  • ℹ️ tests/fm-pi-clear-on-shrink-live-e2e.test.sh:69 - SETTLE_SAMPLE_SECONDS=0.06 x SETTLE_STABLE_INTERVALS=4 = 0.24s is what makes the settle correct: it must exceed CALM_WORKING_SHIP_TICK_MS = 220 (.pi/extensions/lib/fm-calm-working-ship.ts:53) so an animating Calm ship always breaks cmp even when the tail -12 hull grep misses the widget row. That relationship is the entire reason the helper works and it holds today with only ~20ms of nominal margin, yet nothing in the file records it. A later tweak to either constant (e.g. dropping to 3 intervals, or 0.05s sampling) silently reduces the window below one tick and reinstates the exact flake this helper was added to remove. Add a comment naming CALM_WORKING_SHIP_TICK_MS and the >220ms requirement next to the two constants.
  • ℹ️ tests/fm-pi-clear-on-shrink-live-e2e.test.sh:34 - capture_viewport swallows tmux failure with || true, leaving the previous contents in $SNAPSHOT. Inside settle_viewport that turns a dead or unreachable pane into an immediate pass: the marker is still present in the stale file, no hull is present, and cmp trivially succeeds four times in a row, after which assert_no_empty_region_before measures geometry from a frame the terminal is no longer showing. In practice the wrapper's trailing sleep 20 keeps the pane alive for the assertion window, so this is a latent false-pass rather than an observed one; having capture_viewport return tmux's status and having settle_viewport reset stable=0 on a failed capture closes it.

🔧 Fix: drop inert hidden-thinking setting from live shrink test
2 infos still open:

  • ℹ️ tests/fm-pi-clear-on-shrink-live-e2e.test.sh:180 - The fixture runs with Calm on ($HOME_DIR/config/calm = on, line 125), but the shrink it exercises comes from stock Pi's tool expand/collapse, not from Calm. Calm only hides tool rows inside wrapBuiltIn() (.pi/extensions/fm-calm.ts:283 for "assistant-tool-call" and :302 for "tool-result"), which wraps Pi's seven built-in tools; viewport_fill is registered via pi.registerTool in the fixture provider and is never wrapped, so its call and result render with Pi's stock renderer and Calm's calmPresentationHides() path is never taken for them. That matches the docs' "ordinary tool-collapse transitions" half of docs/configuration.md:34 and satisfies the intent's literal "Calm on" requirement, but the Calm-specific shrink (rows collapsing to new Container(), or a /calm toggle repainting the transcript) is not covered by this test. Noting the coverage boundary; no change required for the stated acceptance criteria.
  • ℹ️ tests/fm-pi-clear-on-shrink-live-e2e.test.sh:95 - assert_no_empty_region_before resets run to 0 on every non-blank line, so it only measures the contiguous blank run immediately above the first line containing the marker. A leftover region separated from the marker by any single non-blank row - a status/spinner row, or stale BEFORE_COMPACT_ROW_* text the un-cleared shrink left behind - yields run=0 and passes. Concretely, a viewport of BEFORE_COMPACT_ROW_042 / BEFORE_COMPACT_ROW_043 / NEXT_VISIBLE_CONTENT reports 0 blank rows even though the transcript is corrupt. This matches the reported symptom (black/empty regions, which tmux capture-pane renders as blank rows), so the assertion is aimed at the right artifact; flagging only that its detection window is one adjacent run rather than the whole region above the marker.
🔧 **Test** - 1 issue found → auto-fixed ✅
  • ⚠️ tests/fm-pi-clear-on-shrink-live-e2e.test.sh:214 - The new opt-in live regression tests/fm-pi-clear-on-shrink-live-e2e.test.sh does not discriminate the bug: running an unmodified copy of it against real Pi 0.84.2 with only the launched PI_CLEAR_ON_SHRINK value changed (1 vs 0; Pi compares strictly to "1", so 0 is a genuine pre-fix launch) passes in both cases, with byte-identical tmux viewports apart from the temp path in the footer (blank_run=2 above the next visible content in all four assertions). Captures taken immediately after the Ctrl+O collapse, before the follow-up message repaints, are also identical. I additionally tried isolated scratch Pi sessions with a 96-row tool result at 100x36 and 100x60 panes and could not make a large empty region appear with clear-on-shrink off, so I could not author a fail-before/pass-after live test either. The unit-level launch-env regression IS a proper fail-before/pass-after regression, and live evidence confirms the env var really flips Pi's terminal.clearOnShrink while an explicit settings.json value still overrides it — but no artifact demonstrates that the black regions themselves are prevented. The author needs to decide whether to strengthen or re-scope the live test.
  • bash bin/fm-test-run.sh tests/fm-spawn-dispatch-profile.test.sh — all 31 cases pass on the target commit
  • Reverted only the PI_CLEAR_ON_SHRINK=1 assignment in bin/fm-spawn.sh:1241 and re-ran tests/fm-spawn-dispatch-profile.test.sh — fails with not ok - pi launch did not enable clear-on-shrink ..., then restored the fix (worktree clean)
  • FM_PI_CLEAR_ON_SHRINK_LIVE_E2E=1 bash bin/fm-test-run.sh tests/fm-pi-clear-on-shrink-live-e2e.test.sh — passes against real Pi 0.84.2
  • Ran an unmodified copy of the live e2e twice with the launched PI_CLEAR_ON_SHRINK set to 1 and 0, capturing tmux viewports at each assertion and immediately after the Ctrl+O collapse — viewports identical, both pass
  • Manual isolated Pi repro attempts (faux provider, 96-row tool result, 100x36 and 100x60 panes, expand/collapse) with clear-on-shrink on vs off — no black-region difference observed
  • Live /settings inspection in tmux for PI_CLEAR_ON_SHRINK=1, PI_CLEAR_ON_SHRINK=0, and PI_CLEAR_ON_SHRINK=1 + settings.json terminal.clearOnShrink:false — shows true / false / false
  • Captured real fm-spawn launch command lines for the pi crewmate and pi-signed persistent secondmate showing FM_PI_HARNESS=... PI_CLEAR_ON_SHRINK=1
  • bash bin/fm-test-run.sh tests/fm-calm-pi-extension.test.sh — existing Calm hidden-thinking coverage still passes

🔧 Fix: make Pi clear-on-shrink live e2e reproduce stale rows
✅ Re-checked - no issues remain.

  • bin/fm-test-run.sh tests/fm-spawn-dispatch-profile.test.sh — 25 launch-shape tests pass, including the Pi/pi-signed PI_CLEAR_ON_SHRINK=1 assertions and the re-anchored bare-executable guard
  • FM_PI_CLEAR_ON_SHRINK_LIVE_E2E=1 bin/fm-test-run.sh tests/fm-pi-clear-on-shrink-live-e2e.test.sh — passes against real Pi 0.84.2
  • FM_PI_CLEAR_ON_SHRINK_LIVE_E2E=1 bash tests/fm-pi-clear-on-shrink-live-e2e.test.sh repeated 4x with PI_CLEAR_ON_SHRINK=1 — 4/4 pass (flake check)
  • Copy of the live test with only the launched value changed to PI_CLEAR_ON_SHRINK=0 — 3/3 runs fail with pre-compaction shrink left a 9-row empty region below the last rendered row (fail-before/pass-after confirmed against real Pi)
  • Instrumented copies of both directions dumping the raw tmux capture-pane viewport at the shrink assertion point; stale-row count measured as 9 (off) vs 0 (on)
  • Rendered the two real viewports side by side to HTML and screenshotted it with headless Chrome for reviewer-visible proof of the black region
⚠️ **Document** - 1 info
  • ℹ️ docs/calm.md:52 - docs/calm.md's "Regression entry points" list does not include tests/fm-pi-clear-on-shrink-live-e2e.test.sh. I deliberately left it out: docs/configuration.md#pi-calm-preference-configcalm owns the PI_CLEAR_ON_SHRINK fact and already names the opt-in command, and calm.md already points there as owner. Adding the command to calm.md would create a second copy to keep in sync. Flagging in case the maintainers prefer calm.md to be the exhaustive Calm regression index instead.
✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

@withally
withally merged commit e2a3fe4 into main Aug 25, 2026
24 of 25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant