Skip to content

fix(pi): restore Calm /export status and deflake follow-up e2e captures under Pi 0.84 - #5

Merged
Bloh09 merged 2 commits into
mainfrom
fm/fm-pi-084-test-fallout
Aug 10, 2026
Merged

fix(pi): restore Calm /export status and deflake follow-up e2e captures under Pi 0.84#5
Bloh09 merged 2 commits into
mainfrom
fm/fm-pi-084-test-fallout

Conversation

@Bloh09

@Bloh09 Bloh09 commented Aug 10, 2026

Copy link
Copy Markdown
Owner

Intent

Fix the two e2e test failures present on the firstmate default branch since the Pi 0.84.0 upgrade: (1) a duplicated captain answer, and (2) /export never completing when Calm is on. These are pre-existing failures on clean main (first noticed 2026-08-07), so every future branch inherits them. Make main's suite honestly green: either the code regressed against intended behavior (fix the code) or Pi 0.84.0 legitimately changed behavior the tests over-pinned (fix the tests) - decide per failure from evidence, not convenience.

Diagnostic discipline required: reproduce first on clean worktree at default-branch tip before changing anything; separate initiating trigger (what Pi 0.84.0 changed) from any masking condition and the visible symptom; check history of what tests pinned at 0.82.0 vs Pi 0.84 behavior; smallest counterfactual wins; each fixed test must fail on old broken behavior and pass on the fix.

Context: installed Pi is 0.84.x; tests last known green on 0.82.0. Calm is a Pi presentation extension owned by this repo. Do NOT expand into full Pi 0.84.0 smoke re-verification - fix exactly these two failures and any directly-entangled test infrastructure.

Findings and fix that landed: (1) /export: Pi 0.84+ showStatus replaces prior status row in place; Calm post-export setToolsExpanded toggle overwrote 'Session exported to' with 'Tool output: collapsed' even though HTML was written - fixed by dropping setToolsExpanded from export/share path while keeping stockExportRendering flag arming; unit regression fails if export/share calls setToolsExpanded. (2) captain answer: not a product duplicate - session always had one CAPTAIN_ANSWER; plain tmux capture-pane under Pi 0.84+ intermittently missed short assistant rows while join capture always saw exactly one; old assertion treated count!=1 as 'duplicate' including count 0 - fixed by joined pane captures, waiting for on-screen captain answer, and accurate exactly-one assertion.

Acceptance: both failures reproduced with root cause trigger/mask/symptom, fixes committed with regression coverage, touched-surface tests green, no-mistakes pipeline green with a PR on origin (Bloh09/firstmate fork) against main.

What Changed

  • .pi/extensions/fm-calm.ts no longer cycles setToolsExpanded after /export and /share: under Pi 0.84+ showStatus replaces the prior status row in place, so the post-export toggle overwrote "Session exported to: ..." with a tool-output status. The export pass still arms and resets the stock-rendering flag, and the unit harness now fails if the export/share path calls setToolsExpanded.
  • tests/fm-calm-pi-extension.test.sh switches tmux pane captures to capture-pane -J (joined soft-wrapped lines), since plain captures under Pi 0.84+ intermittently miss short assistant rows, and replaces the follow-up "duplicate captain answer" check with a poll that waits for the answer on screen and asserts exactly one occurrence (the old check reported count 0 as a duplicate).
  • docs/calm-mode-feasibility.md records the new invariant: only genuine presentation changes such as the /calm toggle use the tool-expansion redraw, and the export/share pass never cycles tool expansion.

Risk Assessment

✅ Low: A minimal, well-documented root-cause fix (removing the status-clobbering toggle) plus behavioral regression coverage that fails on the old code, with test hardening whose remaining plain captures are intentionally geometry-sensitive; all findings are informational.

Testing

Reproduced the pre-fix failure (new export regression fails on base-commit fm-calm.ts), then ran the three targeted tests from tests/fm-calm-pi-extension.test.sh against installed Pi 0.84.1 - all passed - and collected end-user evidence: a tmux pane transcript showing "/export" completing with Calm on ("Session exported to: ..." as the final status row, not clobbered by a tools-expanded status), pane transcripts showing exactly one captain answer in every follow-up e2e case, and a headless-Chrome screenshot of the exported HTML rendering as a complete session viewer. The fail-before counterfactual for the captain-answer test was not run because the old plain-capture miss was intermittent by nature and cannot be reproduced deterministically; the fixed test is strictly stronger (waits for the answer, rejects count 0 and count >1) and passed live.

Evidence: Counterfactual: unit regression fails on pre-fix fm-calm.ts

== Counterfactual: tests/fm-calm-pi-extension.test.sh unit regression vs PRE-FIX .pi/extensions/fm-calm.ts (base a2ba0efc) == not ok - Pi calm renderer and lifecycle contract failed: ... Error: /export calm.html export/share path must not call setToolsExpanded (would clobber export status) EXIT=1

== Counterfactual: tests/fm-calm-pi-extension.test.sh unit regression vs PRE-FIX .pi/extensions/fm-calm.ts (base a2ba0efc) ==
not ok - Pi calm renderer and lifecycle contract failed: file:///private/var/folders/pb/bf5sf79j61bc37mnkt9kwt3h0000gn/T/fm-calm-pi-extension.RLu8t0/renderer/[eval1]:521
    throw new Error(`${command} export/share path must not call setToolsExpanded (would clobber export status)`);
          ^

Error: /export calm.html export/share path must not call setToolsExpanded (would clobber export status)
    at assertStockHtmlRendering (file:///private/var/folders/pb/bf5sf79j61bc37mnkt9kwt3h0000gn/T/fm-calm-pi-extension.RLu8t0/renderer/[eval1]:521:11)
    at async file:///private/var/folders/pb/bf5sf79j61bc37mnkt9kwt3h0000gn/T/fm-calm-pi-extension.RLu8t0/renderer/[eval1]:525:1

Node.js v26.5.0
EXIT=1
Evidence: Live TUI pane: /export completes with Calm on, status row intact

Session exported to: /var/folders/pb/.../fm-calm-pi-extension.0igVir/calm-export.html (final status row above the editor; not overwritten by "Tool output: expanded/collapsed")


 pi v0.84.1                                                                                                                                                                         
 escape to interrupt                                                                                                                                                                
 ctrl+c to clear                                                                                                                                                                    
 ctrl+c twice to exit                                                                                                                                                               
 ctrl+d to exit (empty)                                                                                                                                                             
 ctrl+z to suspend                                                                                                                                                                  
 ctrl+k to delete to end                                                                                                                                                            
 shift+tab to cycle thinking level                                                                                                                                                  
 ctrl+p/shift+ctrl+p to cycle models                                                                                                                                                
 ctrl+l to select model                                                                                                                                                             
 ctrl+o to expand tools                                                                                                                                                             
 ctrl+t to expand thinking                                                                                                                                                          
 ctrl+g for external editor                                                                                                                                                         
 / for commands                                                                                                                                                                     
 ! to run bash                                                                                                                                                                      
 !! to run bash (no context)                                                                                                                                                        
 option+enter to queue follow-up                                                                                                                                                    
 option+up to edit all queued messages                                                                                                                                              
 ctrl+v to paste image (with text fallback)                                                                                                                                         
 drop files to attach                                                                                                                                                               
                                                                                                                                                                                    
 Pi can explain its own features and look up its docs. Ask it how to use or extend Pi.                                                                                              

[Extensions]                                                                                                                                                                        
  project                                                                                                                                                                           
    /var/folders/pb/bf5sf79j61bc37mnkt9kwt3h0000gn/T/fm-calm-pi-extension.0igVir/e2e-project/.pi/extensions/fm-calm-e2e-inject.ts                                                   
    /var/folders/pb/bf5sf79j61bc37mnkt9kwt3h0000gn/T/fm-calm-pi-extension.0igVir/e2e-project/.pi/extensions/fm-calm.ts                                                              
    /var/folders/pb/bf5sf79j61bc37mnkt9kwt3h0000gn/T/fm-calm-pi-extension.0igVir/e2e-project/.pi/extensions/fm-primary-pi-watch.ts                                                  
    /var/folders/pb/bf5sf79j61bc37mnkt9kwt3h0000gn/T/fm-calm-pi-extension.0igVir/e2e-project/.pi/extensions/fm-primary-turnend-guard.ts                                             

                                                                                                                                                                                    
 Show a deterministic tool example.                                                                                                                                                 
                                                                                                                                                                                    

 I will run one command.                                                                                                                                                            

                                                                                                                                                                                    
 FIRSTMATE WATCHER WAKE: can you explain this phrase?                                                                                                                               
                                                                                                                                                                                    

                                                                                                                                                                                    
 Captain quote: ⁣FIRSTMATE_OP: v1 watcher: QUOTED_CURRENT_NEAR_MISS                                                                                                                  
                                                                                                                                                                                    

                                                                                                                                                                                    
 FIRSTMATE_OP: v1 watcher: ASCII_ONLY_NEAR_MISS                                                                                                                                     
                                                                                                                                                                                    

                                                                                                                                                                                    
 Ordinary captain text before ⁣FIRSTMATE_OP: v1 watcher: EMBEDDED_CURRENT_NEAR_MISS                                                                                                  
                                                                                                                                                                                    

                                                                                                                                                                                    
 ⁣ordinary captain text after unrelated separator                                                                                                                                    
                                                                                                                                                                                    

 The deterministic tool example is complete.                                                                                                                                        

 Warning: Could not restore model anthropic/claude-sonnet-4-5. Using calm-e2e/delayed                                                                                               

 Warning: tmux extended-keys is off. Modified Enter keys may not work. Add `set -g extended-keys on` to ~/.tmux.conf and restart tmux.                                              

 Tool output: expanded                                                                                                                                                              

 Warning: CALM_TRANSIENT_DIAGNOSTIC                                                                                                                                                 

 Error: CALM_OPERATIONAL_E2E_ERROR                                                                                                                                                  

 Error: CALM_OPERATIONAL_E2E_ERROR                                                                                                                                                  

 Error: CALM_OPERATIONAL_E2E_ERROR                                                                                                                                                  

 Error: CALM_OPERATIONAL_E2E_ERROR                                                                                                                                                  

 Error: CALM_OPERATIONAL_E2E_ERROR                                                                                                                                                  

 Session exported to: /var/folders/pb/bf5sf79j61bc37mnkt9kwt3h0000gn/T//fm-calm-pi-extension.0igVir/calm-export.html                                                                

────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
                                                                                                                                                                                    
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
/var/folders/pb/bf5sf79j61bc37mnkt9kwt3h0000gn/T/fm-calm-pi-extension.0igVir/e2e-project (main)
↑7 ↓4 2.3%/4.1k (auto)                                                                                                                                             operational-error
Evidence: Live TUI pane: exactly one captain answer (Calm on case)

CAPTAIN_PROMPT_loaded_on CAPTAIN_ANSWER_loaded_on MONITOR_HANDLED_loaded_on_ONE (joined capture-pane -J; one CAPTAIN_ANSWER per case across all 8 e2e cases)

fd not found. Offline mode enabled, skipping download.

 pi v0.84.1                                                                                                                                                     
 escape interrupt · ctrl+c/ctrl+d clear/exit · / commands · ! bash · ctrl+o more                                                                                
 Press ctrl+o to show full startup help and loaded resources.                                                                                                   
                                                                                                                                                                
 Pi can explain its own features and look up its docs. Ask it how to use or extend Pi.                                                                          

[Extensions]                                                                                                                                                    
  fm-calm.ts, followup-e2e.ts                                                                                                                                   


 Warning: tmux extended-keys is off. Modified Enter keys may not work. Add `set -g extended-keys on` to ~/.tmux.conf and restart tmux.                          

                                                                                                                                                                
 CAPTAIN_PROMPT_loaded_on                                                                                                                                       
                                                                                                                                                                

 CAPTAIN_ANSWER_loaded_on                                                                                                                                       

 MONITOR_HANDLED_loaded_on_ONE                                                                                                                                  

────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
                                                                                                                                                                
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
/private/var/folders/pb/bf5sf79j61bc37mnkt9kwt3h0000gn/T/fm-calm-pi-extension.0igVir/followup-project (main)
0.8%/4.1k (auto)                                                                                                                                   deterministic
- Evidence: Rendered screenshot of Calm-on exported session HTML (local file: /var/folders/pb/bf5sf79j61bc37mnkt9kwt3h0000gn/T/no-mistakes-evidence/01KZN1J2H6169224Y4QEGP67JY/calm-export-render.png) - Evidence: Exported session HTML produced by /export with Calm on (local file: /var/folders/pb/bf5sf79j61bc37mnkt9kwt3h0000gn/T/no-mistakes-evidence/01KZN1J2H6169224Y4QEGP67JY/calm-export.html)

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

⚠️ **Review** - 3 infos
  • ℹ️ .pi/extensions/fm-calm.ts:208 - The /calm command handler retains the same setToolsExpanded double-toggle redraw that caused the export bug under Pi 0.84's in-place showStatus replacement. Here it is harmless to the fixed invariant (the handler clears its own status first and no export status exists to clobber), and the new comment in the export path documents this as deliberate, but a lingering 'Tool output: ...' status after /calm toggles is a cosmetic side effect of the same mechanism if Pi renders a status for setToolsExpanded. Out of the stated fix scope; noting for awareness only.
  • ℹ️ tests/fm-calm-pi-extension.test.sh:3404 - The e2e export test asserts 'Session exported to:' appears and the exported HTML contains tool data, but has no 'tool rows still hidden on screen' assertion between export completion and the subsequent /calm toggle. The removed toggle previously forced a live redraw after the stock-rendering window closed; the fix relies on the (documented) claim that export never re-renders the live transcript. A post-export assert_not_contains for a calm-hidden marker would pin that counterfactual directly.
  • ℹ️ tests/fm-calm-pi-extension.test.sh:1716 - The new captain-answer wait loop caps at 80 iterations (~4s) while sibling wait loops in the same test use 120-240 iterations. The loop only starts after the session file already contains the handled marker, so only render latency remains and 4s is likely enough, but aligning with the 120-iteration convention would give more headroom on slow CI machines if flake recurs.
✅ **Test** - passed

✅ No issues found.

  • Counterfactual: reverted .pi/extensions/fm-calm.ts to base a2ba0ef and ran test_rendering_and_session_lifecycle from tests/fm-calm-pi-extension.test.sh (via a temporary driver sourcing the test functions) - failed as required with "/export calm.html export/share path must not call setToolsExpanded"
  • test_rendering_and_session_lifecycle (fm-calm renderer/lifecycle unit fixture incl. the new export setToolsExpanded regression) on fixed code - passed
  • test_operational_followup_turn_e2e (live Pi 0.84.1 in tmux; captain-answer joined-capture wait and exactly-one assertion across Calm on/off/absent/exact-watcher/restart cases) - passed
  • test_interactive_terminal_e2e (live Pi 0.84.1 TUI incl. /export with Calm on, Session exported to: status assertion, export HTML content and headless-Chrome DOM checks) - passed
  • Manual evidence capture: tmux shim teed full-scrollback pane captures; verified every capture contains exactly one CAPTAIN_ANSWER per label and the export pane's final status row is Session exported to: ... (the earlier Tool output: expanded line is the e2e's own deliberate Ctrl+O toggle in scrollback)
  • Rendered the Calm-on exported HTML in headless Chrome and captured a screenshot showing the full session viewer
  • Cleanup verified: temporary driver/shim/capture dirs removed, no stray tmux servers, git status clean
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

Pi 0.84+ showStatus replaces the previous status row in place. Calm post-/export
setToolsExpanded redraw overwrote Session exported status with Tool output
collapsed, so e2e never saw export complete even though the HTML file was written.

Drop that tools-expanded toggle from the export/share input path. Arming stock
HTML rendering only needs the stockExportRendering flag; /calm still redraws when
presentation actually changes. Unit coverage fails if export/share calls
setToolsExpanded.

The duplicate captain answer failure was a misnamed assertion on count != 1:
session data kept one answer, but plain tmux capture-pane under Pi 0.84+
intermittently missed short assistant rows (join capture always saw exactly one).
Wait on joined pane captures and require exactly one on-screen captain answer.
@Bloh09
Bloh09 merged commit feef5c8 into main Aug 10, 2026
13 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.

2 participants