Skip to content

ci: pin the Pi package and force regular TUI mode in the Calm Pi E2E - #50

Merged
TastyTom13 merged 1 commit into
mainfrom
fm/firstmate-calm-pi-1-0-ci-fix
Oct 2, 2026
Merged

TastyTom13 merged 1 commit into
mainfrom
fm/firstmate-calm-pi-1-0-ci-fix

Conversation

@TastyTom13

@TastyTom13 TastyTom13 commented Oct 2, 2026 •

Copy link
Copy Markdown
Owner

Intent

CI on this repository is red for every new PR since today: the job 'Behavior portable serial 6' fails in tests/fm-calm-pi-extension.test.sh at line 4261 with 'not ok - calm mode was not off by default (missing: CALM_E2E_OUTPUT)'. Evidence: PR 49 (#49, run 37011610115, job 110862193628) changes only bin/fm-context-budget.sh, its test and docs, and failed identically on a rerun; the CI step 'Install the Pi package for the Pi extension tests' installed @earendil-works/pi-coding-agent@1.0.0 today, while main's last green run (36854596778, one day earlier) ran on the previous Pi release (the fork's Calm work targets Pi 0.99, see commit dbefebef 'match Pi 0.99 stock rendering'). A rerun of main's run was started to confirm it fails the same way. The firstmate repo is the fleet's own code, so this blocks every firstmate PR.

What Changed

  • .github/workflows/ci.yml now installs @earendil-works/pi-coding-agent at the exact version in a new workflow-level FM_CI_PI_VERSION (1.0.0) instead of the latest release. Both install steps then run pi --version, print the result, and fail if it differs from the pin.
  • tests/fm-calm-pi-extension.test.sh writes "tuiMode":"regular" into the E2E's Pi settings.json. Pi 1.0.0 made fullscreen the default TUI mode, and fullscreen leaves no scrollback for the capture to read, so the "calm mode off by default" check failed with missing: CALM_E2E_OUTPUT. Regular mode matches how bin/fm-spawn.sh launches Pi.
  • tests/fm-ci-workflow.test.sh adds test_pi_package_install_is_pinned_and_logged. It extracts every CI Pi install step with its workflow, job, and step env, then runs each one against a fake npm and pi. It checks that the pin is an exact version, that all steps use the same pin, that the step prints the version, and that the step fails on a version mismatch. The .agents/skills/harness-adapters/references/harness/pi.md note on the tuiMode default is updated to say fullscreen is the default from Pi 1.0.0 and earlier releases default to regular.

Risk Assessment

✅ Low: The change is small and CI-only: it pins Pi to the 1.0.0 that broke CI, sets tuiMode to regular in the one failing E2E fixture, and adds a behavioural test that runs the real install steps against a fake npm and pi; I could not run it against Pi 1.0.0 here.

Testing

I reproduced the CI failure with Pi 1.0.0 on the old test file, then showed the fixed test file passes in full on the same Pi, with the interactive terminal test running and not skipped. The workflow guard test passes, but it is a static check of ci.yml, so the pin was not driven in a real GitHub Actions run. The temp Pi and tmux were removed and the worktree is clean.

  • Live validation: ✅ go - 2 of 3 scenarios driven live against the product
Scenario Result Live Evidence
On Pi 1.0.0 with the fix, the Calm interactive terminal test passes and calm mode is off by default with the restored tool output visible ✅ pass live calm-pi-extension-fixed-pi1.0.0-with-tmux.log (exit 0, no skips)
Without the fix, on Pi 1.0.0, the same test fails with the CI error (proves the fix is what cures it) ✅ pass live calm-pi-extension-BASE-pi1.0.0-with-tmux.log (not ok - calm mode was not off by default (missing: 'CALM_E2E_OUTPUT'))
The CI workflow pins Pi 1.0.0 in both install steps and fails on a version mismatch ⏸️ untested no The prior payload only recorded a static check of ci.yml (tests/fm-ci-workflow.test.sh). It did not establish a live run in GitHub Actions, so no live pass is supported. To drive it live, push the bra…
Evidence: Fixed test, Pi 1.0.0, tmux present: all pass

Source: Fixed test, Pi 1.0.0, tmux present: all pass

ok - Pi calm resolves its persistent home independently of Pi's launch directory
ok - Pi calm compatibility evidence never rejects a Pi version for being newer than 0.82.0, and still fails closed on a missing or malformed version
ok - a missing collapsed-thinking presentation API degrades only that Calm adapter with a clear skip reason, while the rest of Calm still registers
ok - missing Pi presentation class exports reach the independent adapter degradation path
ok - Calm hides queued Firstmate rows only on a session that can keep them, keeps hidden ones out of the editor on Escape, delivers them once in order, and leaves unsupported sessions and Calm off stock
ok - Calm registers none of its 7 built-in tool wrappers at load while config/calm is off, and all 7 synchronously at load while config/calm is on
ok - Calm's first same-session /calm activation claims every uncontested built-in, leaves a foreign bash tool fully intact and callable, warns prominently and logs the contested name, and only rows constructed before that activation - the documented bound - fail to retroactively collapse
ok - Pi calm centralizes transcript visibility, preserves execution/export data, keeps Pi's stock working row visible while no run is active, and persists its choice across session starts
ok - Pi calm on collapses mid-turn assistant working notes to zero height while Calm off keeps them, leaves streaming, truncated-final, and genuine final replies untouched, never mutates the messages, ignores every /calm argument, and restores a legacy persisted max as ordinary Calm on
ok - Pi operational follow-up E2E processes exact user-role notifications once while Calm hides current and adjacent rows, Calm off and absent render them, and restart preserves semantics
ok - Pi 1.0.0 with Calm on hides and retains queued Firstmate input through Escape, delivers it once, and leaves Calm off stock
ok - Pi Calm native /skill:ahoy geometry keeps every collapsed thinking and tool block at zero height while preserving expansion, history, restart, and Calm-off rendering
ok - Pi Calm working ship keeps its centered two-row asymmetric Unicode boat inside a deterministic long-wave trough, paints all water standard blue and the whole boat standard yellow with balanced resets, keeps ANSI-stripped width exact, reverses cleanly at both edges and every width, clamps visible and hidden resizes, falls back deterministically when narrow, freezes and resumes across settle/start without hidden-time jumps or duplicate timers, resets only on a fresh session, and leaves Calm-off visibility untouched
ok - the rendered-export-DOM guard renders in one pass, retries a bounded number of Chrome start-up failures, and reports the Chrome binary, Chrome version, Pi version, exit status, and Chrome diagnostic when every attempt fails
ok - Pi calm native E2E replaces the stock working row with a moving, resize-clamped working ship that freezes and resumes across two working periods in one Pi session, clears on abort, keeps captain turns visible, hides exact operational user rows without changing persistence, restores stock rendering Calm-off, survives restart, and preserves export plus Ctrl+O behavior
bash tests/fm-calm-pi-extension.test.sh  13.11s user 8.36s system 28% cpu 1:16.05 total
Evidence: Base test, Pi 1.0.0: reproduces CI failure

Source: Base test, Pi 1.0.0: reproduces CI failure

ok - Pi calm resolves its persistent home independently of Pi's launch directory
ok - Pi calm compatibility evidence never rejects a Pi version for being newer than 0.82.0, and still fails closed on a missing or malformed version
ok - a missing collapsed-thinking presentation API degrades only that Calm adapter with a clear skip reason, while the rest of Calm still registers
ok - missing Pi presentation class exports reach the independent adapter degradation path
ok - Calm hides queued Firstmate rows only on a session that can keep them, keeps hidden ones out of the editor on Escape, delivers them once in order, and leaves unsupported sessions and Calm off stock
ok - Calm registers none of its 7 built-in tool wrappers at load while config/calm is off, and all 7 synchronously at load while config/calm is on
ok - Calm's first same-session /calm activation claims every uncontested built-in, leaves a foreign bash tool fully intact and callable, warns prominently and logs the contested name, and only rows constructed before that activation - the documented bound - fail to retroactively collapse
ok - Pi calm centralizes transcript visibility, preserves execution/export data, keeps Pi's stock working row visible while no run is active, and persists its choice across session starts
ok - Pi calm on collapses mid-turn assistant working notes to zero height while Calm off keeps them, leaves streaming, truncated-final, and genuine final replies untouched, never mutates the messages, ignores every /calm argument, and restores a legacy persisted max as ordinary Calm on
ok - Pi operational follow-up E2E processes exact user-role notifications once while Calm hides current and adjacent rows, Calm off and absent render them, and restart preserves semantics
ok - Pi 1.0.0 with Calm on hides and retains queued Firstmate input through Escape, delivers it once, and leaves Calm off stock
ok - Pi Calm native /skill:ahoy geometry keeps every collapsed thinking and tool block at zero height while preserving expansion, history, restart, and Calm-off rendering
ok - Pi Calm working ship keeps its centered two-row asymmetric Unicode boat inside a deterministic long-wave trough, paints all water standard blue and the whole boat standard yellow with balanced resets, keeps ANSI-stripped width exact, reverses cleanly at both edges and every width, clamps visible and hidden resizes, falls back deterministically when narrow, freezes and resumes across settle/start without hidden-time jumps or duplicate timers, resets only on a fresh session, and leaves Calm-off visibility untouched
ok - the rendered-export-DOM guard renders in one pass, retries a bounded number of Chrome start-up failures, and reports the Chrome binary, Chrome version, Pi version, exit status, and Chrome diagnostic when every attempt fails
not ok - calm mode was not off by default (missing: 'CALM_E2E_OUTPUT')
--- output ---
 Thinking...


 fm_watch_arm_pi
 watcher: started Pi extension arm child 1



 FIRSTMATE WATCHER WAKE: signal: /tmp/probe.status

 Run bin/fm-wake-drain.sh first and handle the queued wake. Watcher continuity is extension-owned.



 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.

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

────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
/private/var/folders/08/gk0k1cz96snd794rnghc_98r0000gn/T/fm-calm-pi-extension.YhaIvg/e2e-project (main)
↑7 ↓4 0.1%/4.1k (auto)                                                                                                                                                       delayed
Evidence: CI workflow guard test

Source: CI workflow guard test

ok - CI matrices cover every executable serial lane and canonical lint root exactly once
ok - a newer push to one PR supersedes that PR's in-flight CI
ok - distinct PRs get distinct concurrency groups
ok - every main push keeps its own group and is never cancelled
ok - every ci.yml job carries a finite timeout
ok - every ci.yml job belongs to one of the three timeout tiers
ok - fast tier jobs share one 5 minute tripwire
ok - normal tier jobs share one 30 minute budget
ok - Herdr keeps a 20 minute step tripwire under a 75 minute job backstop
ok - every CI Pi install (2) pins Pi 1.0.0 exactly, prints it, and fails on a version mismatch
Evidence: First fixed run without tmux (interactive E2E skipped, so not counted as evidence)

Source: First fixed run without tmux (interactive E2E skipped, so not counted as evidence)

ok - Pi calm resolves its persistent home independently of Pi's launch directory
ok - Pi calm compatibility evidence never rejects a Pi version for being newer than 0.82.0, and still fails closed on a missing or malformed version
ok - a missing collapsed-thinking presentation API degrades only that Calm adapter with a clear skip reason, while the rest of Calm still registers
ok - missing Pi presentation class exports reach the independent adapter degradation path
ok - Calm hides queued Firstmate rows only on a session that can keep them, keeps hidden ones out of the editor on Escape, delivers them once in order, and leaves unsupported sessions and Calm off stock
ok - Calm registers none of its 7 built-in tool wrappers at load while config/calm is off, and all 7 synchronously at load while config/calm is on
ok - Calm's first same-session /calm activation claims every uncontested built-in, leaves a foreign bash tool fully intact and callable, warns prominently and logs the contested name, and only rows constructed before that activation - the documented bound - fail to retroactively collapse
ok - Pi calm centralizes transcript visibility, preserves execution/export data, keeps Pi's stock working row visible while no run is active, and persists its choice across session starts
ok - Pi calm on collapses mid-turn assistant working notes to zero height while Calm off keeps them, leaves streaming, truncated-final, and genuine final replies untouched, never mutates the messages, ignores every /calm argument, and restores a legacy persisted max as ordinary Calm on
skip: pi or tmux not found for Pi operational follow-up E2E
skip: pi or tmux not found for Pi Calm queued-row Escape E2E
skip: pi or tmux not found for Pi Calm hidden-block geometry E2E
ok - Pi Calm working ship keeps its centered two-row asymmetric Unicode boat inside a deterministic long-wave trough, paints all water standard blue and the whole boat standard yellow with balanced resets, keeps ANSI-stripped width exact, reverses cleanly at both edges and every width, clamps visible and hidden resizes, falls back deterministically when narrow, freezes and resumes across settle/start without hidden-time jumps or duplicate timers, resets only on a fresh session, and leaves Calm-off visibility untouched
ok - the rendered-export-DOM guard renders in one pass, retries a bounded number of Chrome start-up failures, and reports the Chrome binary, Chrome version, Pi version, exit status, and Chrome diagnostic when every attempt fails
skip: pi or tmux not found for Pi calm interactive E2E
bash tests/fm-calm-pi-extension.test.sh  7.65s user 2.70s system 44% cpu 23.125 total

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

✅ **Review** - passed

✅ No issues found.

✅ **Test** - passed

✅ No issues found.

  • Live validation: ✅ go - 2 of 3 scenarios driven live against the product
Scenario Result Live Evidence
On Pi 1.0.0 with the fix, the Calm interactive terminal test passes and calm mode is off by default with the restored tool output visible ✅ pass live calm-pi-extension-fixed-pi1.0.0-with-tmux.log (exit 0, no skips)
Without the fix, on Pi 1.0.0, the same test fails with the CI error (proves the fix is what cures it) ✅ pass live calm-pi-extension-BASE-pi1.0.0-with-tmux.log (not ok - calm mode was not off by default (missing: 'CALM_E2E_OUTPUT'))
The CI workflow pins Pi 1.0.0 in both install steps and fails on a version mismatch ⏸️ untested no The prior payload only recorded a static check of ci.yml (tests/fm-ci-workflow.test.sh). It did not establish a live run in GitHub Actions, so no live pass is supported. To drive it live, push the bra…
  • Installed @earendil-works/pi-coding-agent@1.0.0 into a temp folder and built tmux 3.5a there (neither was on this machine)
  • bash tests/fm-calm-pi-extension.test.sh on the target commit with Pi 1.0.0 and tmux: all tests pass, none skipped, including the interactive E2E
  • Same test file taken from the base commit 3acc296, run with Pi 1.0.0 and tmux: fails with 'not ok - calm mode was not off by default (missing: CALM_E2E_OUTPUT)'
  • bash tests/fm-ci-workflow.test.sh: passes, including the new check that both CI Pi installs are pinned to 1.0.0, print the version and fail on a mismatch (static check of ci.yml only)
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

Built by: claude/opus at high

Pi 1.0.0 made fullscreen the default TUI mode. Fullscreen keeps no terminal
scrollback, so the Calm interactive E2E's tmux captures lost the restored
transcript rows and failed with "calm mode was not off by default". The
fixture now sets tuiMode to regular, which matches how bin/fm-spawn.sh
launches Pi and is already the default on Pi 0.99.

CI installed the unpinned latest Pi, so the release reddened every PR the day
it shipped. Both Pi install steps now install one pinned FM_CI_PI_VERSION,
print the pinned and installed versions, and fail on a mismatch, with a
guard in tests/fm-ci-workflow.test.sh. The Pi adapter note now records the
new tuiMode default.
@TastyTom13
TastyTom13 merged commit 2107cef into main Oct 2, 2026
20 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