Skip to content

feat(fm-session-start): integrate parlay sweep into supervision surface - #109

Merged
trillium merged 3 commits into
mainfrom
fm/parlay-supervision-integration
Aug 15, 2026
Merged

trillium merged 3 commits into
mainfrom
fm/parlay-supervision-integration

Conversation

@trillium

@trillium trillium commented Aug 8, 2026 •

Copy link
Copy Markdown
Owner

Intent

Integrate read-only parlay sweep into firstmate's supervision surface so captain-parked agents in needs-decision, blocked, or failed state are visible during normal operations without requiring manual parlay sweep runs.

Changes:

  1. bin/fm-session-start.sh: Add a PARLAY section printed after FLEET STATE. Runs parlay sweep if and only if the parlay binary is on PATH (command -v parlay); skips silently otherwise. Parses output and surfaces only HOLD lines with state=needs-decision, blocked, or failed; ignores done, unknown, no-launch-spec, and would-close lines. Prints a one-line count summary (parlay: N agent(s) held for captain action, or parlay: none held for captain action). If N > 0, prints the HOLD lines truncated to 80 chars each. Section header style matches existing FLEET STATE/WAKE QUEUE style. Added parlay to SESSION_START_STAGES between fleet-state and next-step.

  2. bin/fm-watch.sh: Added parlay_heartbeat_payload() helper function. In the heartbeat wake path (both the afk_present and heartbeat_scan_finds_actionable branches that enqueue), replaces the hardcoded 'heartbeat' payload with parlay_heartbeat_payload() output. The payload includes a compact parlay sweep summary (count + held agents joined with ' | ' separators). Degrades to plain 'heartbeat' when parlay is absent.

  3. tests/fm-session-start.test.sh: Added four tests (test_parlay_section_with_held_agents, test_parlay_section_none_held, test_parlay_section_absent_when_no_binary, test_parlay_section_truncates_long_lines). Updated test_runtime_bound_truncates_loudly_and_exits_zero to include parlay in the expected stage reconcile list.

Constraints enforced:

  • parlay sweep is called read-only (no --apply)
  • No --verbose or --interval flags
  • No daemon, no state files, no join table
  • Graceful skip when parlay binary absent
  • ShellCheck clean (bin/fm-lint.sh passes)
  • parlay binary path resolved via command -v parlay

What Changed

  • bin/fm-session-start.sh: Added a PARLAY section (printed after FLEET STATE) that runs a read-only parlay sweep when the binary is on PATH, surfaces only HOLD lines with state=needs-decision|blocked|failed (each truncated to 80 chars), and prints a one-line count summary; skips silently when parlay is absent. Added parlay to SESSION_START_STAGES between fleet-state and next-step.
  • bin/fm-watch.sh: Added parlay_heartbeat_payload() helper that builds a compact parlay sweep summary; both heartbeat wake paths (afk_present and heartbeat_scan_finds_actionable) now use this helper instead of the hardcoded heartbeat payload, degrading gracefully when parlay is absent.
  • tests/fm-session-start.test.sh / tests/wake-helpers.sh: Added four new tests covering the held-agents path, the none-held path, the absent-binary skip, and line truncation; updated the truncation-banner test to include parlay in the expected stage list.

Risk Assessment

✅ Low: All three changed files implement the stated intent faithfully — parlay sweep is read-only with no new state files, graceful binary-absent skip is consistent across both call sites, the 80-char truncation is correct, SESSION_START_STAGES is correctly updated, and the four new tests exercise observable behaviour through real script execution rather than source-code grepping.

Testing

The fm-session-start suite (30/30 tests) passes end-to-end, confirming parlay sweep integration into the session digest. A regression in fm-watch-triage's heartbeat backstop test was found and fixed: parlay sweep took ~6s on this host (vs. the test's 4-second wait budget), fixed by adding a fast no-op parlay stub to make_case() in tests/wake-helpers.sh. Both heartbeat tests pass 3/3 after the fix.

Evidence: fm-session-start tests (30/30 pass, includes 4 new parlay tests)
ok - orphan status logs are printed once with bounded tails
ok - tmux endpoint liveness is reported per task: alive for a live window, dead for a gone one
ok - herdr endpoint liveness is reported per task: alive for a live pane, dead for a gone one
ok - fm-session-start.sh composes the real fm-lock.sh, fm-bootstrap.sh, and fm-wake-drain.sh output verbatim
ok - compatible tasks-axi backlog rendering is compact, bounded, and preserves recovery metadata
ok - manual backlog rendering prints only title lines with hold and blocker metadata
ok - unavailable or incompatible tasks-axi falls back to compact manual backlog rendering
ok - beads backend digest shows In flight (in_progress/blocked) and Queued (ready) sections, both scoped by the fleet label
ok - a failed In flight beads read falls back to the whole title-line rendering rather than a partial digest
ok - a successful beads read refreshes the local mirror for both sections, and an outage falls back to them with explicit stale labeling
ok - an empty fleet reports (none) for in-flight tasks and an absent AFK flag
ok - session start emits X-mode cadence guidance in the harness supervision block
ok - next step delegates watcher ownership to the AFK daemon
ok - session start emits exactly one detected harness block and reports Pi extension load state
ok - session start preserves pi-signed primary identity while applying Pi extension guarantees
ok - session start rejects stale Pi loaded markers
ok - session start accepts current Pi markers written before lock acquisition
ok - session start rejects Pi sessions missing the turn-end guard marker
ok - session start rejects Pi loaded markers from previous sessions
ok - the pure-Bash watchdog bounds session start, kills its hung grandchild, and emits the truncation contract
ok - the portable timeout path force-kills a command that ignores TERM
ok - a session start inside its budget prints no truncation banner
ok - the runtime bound leaves enough ancestry headroom for a deeply nested session to take the lock
ok - --reemit reprints the digest without repeating startup's mutating sweeps and still drains queued wakes
ok - --reemit re-verifies lock ownership and keeps repair ownership with whoever holds it
ok - session start PARLAY section surfaces needs-decision/blocked/failed hold agents
ok - session start PARLAY section prints none-held summary when no agents are held
ok - session start omits PARLAY section silently when parlay binary is absent
ok - session start PARLAY section truncates HOLD lines to 80 chars
# fm-session-start.test.sh: all assertions passed
Evidence: fm-watch heartbeat tests (2/2 pass after fix)
ok - a heartbeat with no captain-relevant change is absorbed and backs off the cadence
ok - heartbeat backstop fail-safe surfaces a captain-relevant status the per-wake path missed
# targeted heartbeat tests: all passed
- Evidence: [wake-helpers.sh fix: parlay stub added to make_case()](https://github.com/trillium/firstmate/blob/31716acb6982f7f9be749e44b0e4fb39259b25ab/tests/wake-helpers.sh)

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.

  • bash tests/fm-session-start.test.sh — full suite, 30 tests including all 4 new parlay section tests
  • test_parlay_section_with_held_agents — verifies HOLD filtering (needs-decision/blocked/failed) and ordering after FLEET STATE
  • test_parlay_section_none_held — verifies none-held summary when only unknown/would-close lines present
  • test_parlay_section_absent_when_no_binary — verifies silent skip when parlay binary not on PATH
  • test_parlay_section_truncates_long_lines — verifies HOLD lines capped at 80 chars
  • test_runtime_bound_truncates_loudly_and_exits_zero — verifies parlay added to SESSION_START_STAGES reconcile list
  • test_heartbeat_no_change_absorbed and test_heartbeat_backstop_surfaces_unsurfaced_status — fm-watch-triage heartbeat path, before/after fix (3 runs each)
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

Summary by CodeRabbit

  • New Features
    • Session-start summaries now report agents awaiting captain action when Parlay is available.
    • Watcher heartbeats include concise updates about held agents.
    • Relevant hold states are filtered and long status lines are shortened for readability.
  • Bug Fixes
    • Heartbeats remain unchanged when Parlay is unavailable.
    • Empty Parlay results are clearly reported without unnecessary details.
  • Documentation
    • Added guidance describing the new read-only Parlay session-start summary.

Trillium Smith added 2 commits August 7, 2026 20:56
Add read-only parlay sweep output to firstmate's supervision surface
so captain-parked agents (needs-decision, blocked, failed) are visible
during normal operations instead of requiring manual parlay sweep runs.

Changes:
- bin/fm-session-start.sh: add PARLAY section printed after FLEET STATE.
  Runs parlay sweep when binary is on PATH; skips silently otherwise.
  Surfaces only needs-decision/blocked/failed HOLD lines, truncated to
  80 chars each, with a one-line count summary.

- bin/fm-watch.sh: add parlay_heartbeat_payload() helper; include a
  compact parlay sweep summary in the heartbeat wake-queue payload for
  both the afk and heartbeat_scan_finds_actionable enqueue paths.
  Degrades gracefully when parlay is absent.

- tests/fm-session-start.test.sh: add four tests covering held-agent
  surfacing, none-held summary, graceful absence, and 80-char truncation.
  Update test_runtime_bound_truncates_loudly_and_exits_zero to include
  the new parlay stage in the expected reconcile list.
@coderabbitai

coderabbitai Bot commented Aug 8, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Session-start digests now include a read-only Parlay hold report. Heartbeat wake payloads also summarize relevant held agents. Both flows filter hold states, truncate lines to 80 characters, and handle unavailable or empty Parlay output.

Changes

Parlay reporting

Layer / File(s) Summary
Session-start Parlay section
AGENTS.md, bin/fm-session-start.sh
The session-start stages now run a read-only Parlay sweep. The digest reports needs-decision, blocked, and failed holds with 80-character truncation.
Heartbeat Parlay payload
bin/fm-watch.sh
Heartbeat queue entries now use Parlay-aware payloads for both enqueue paths.
Behavior validation and test isolation
tests/fm-session-start.test.sh, tests/wake-helpers.sh
Tests cover filtering, ordering, empty output, unavailable binaries, and truncation. Test cases install a no-op Parlay executable.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SessionStart
  participant Parlay
  participant SessionDigest
  SessionStart->>Parlay: run parlay sweep
  Parlay-->>SessionStart: return hold states
  SessionStart->>SessionDigest: add filtered hold report
Loading
sequenceDiagram
  participant Watch
  participant Parlay
  participant HeartbeatQueue
  Watch->>Parlay: run parlay sweep
  Parlay-->>Watch: return relevant holds
  Watch->>HeartbeatQueue: enqueue Parlay-aware heartbeat
Loading

Possibly related PRs

Suggested reviewers: kunchenguid

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: integrating a read-only parlay sweep into the firstmate supervision surface.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fm/parlay-supervision-integration

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@bin/fm-session-start.sh`:
- Line 473: The HOLD-state regex in bin/fm-session-start.sh lines 473-473 must
match only complete values by requiring whitespace or end-of-line after
needs-decision, blocked, or failed. Add a state=failed-retryable fixture and
assert the digest suppresses it in tests/fm-session-start.test.sh lines
2049-2055.

In `@bin/fm-watch.sh`:
- Around line 1415-1417: Update the heartbeat flow around fm_wake_append and
wake to build the Parlay heartbeat payload once, store it in a variable, and
pass that same payload to both calls so immediate away-mode delivery publishes
the generated report instead of the fixed heartbeat string.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5e16b75a-d23b-4f84-9d42-19ceddb37708

📥 Commits

Reviewing files that changed from the base of the PR and between df7e4a9 and 31716ac.

📒 Files selected for processing (5)
  • AGENTS.md
  • bin/fm-session-start.sh
  • bin/fm-watch.sh
  • tests/fm-session-start.test.sh
  • tests/wake-helpers.sh

Comment thread bin/fm-session-start.sh
command -v parlay >/dev/null 2>&1 || return 0
section "PARLAY"
sweep=$(parlay sweep 2>/dev/null) || sweep=
held=$(printf '%s\n' "$sweep" | grep -E '^HOLD[[:space:]].*state=(needs-decision|blocked|failed)' || true)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Match complete Parlay state values.

The current regex accepts unsupported values such as state=failed-retryable because it does not require a boundary after the allowed state. This conflicts with the documented exact-state filter.

  • bin/fm-session-start.sh#L473-L473: require whitespace or end-of-line after needs-decision, blocked, or failed.
  • tests/fm-session-start.test.sh#L2049-L2055: add a state=failed-retryable fixture and assert that the digest suppresses it.
Proposed fix
-  held=$(printf '%s\n' "$sweep" | grep -E '^HOLD[[:space:]].*state=(needs-decision|blocked|failed)' || true)
+  held=$(printf '%s\n' "$sweep" | grep -E '^HOLD[[:space:]].*state=(needs-decision|blocked|failed)([[:space:]]|$)' || true)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
held=$(printf '%s\n' "$sweep" | grep -E '^HOLD[[:space:]].*state=(needs-decision|blocked|failed)' || true)
held=$(printf '%s\n' "$sweep" | grep -E '^HOLD[[:space:]].*state=(needs-decision|blocked|failed)([[:space:]]|$)' || true)
📍 Affects 2 files
  • bin/fm-session-start.sh#L473-L473 (this comment)
  • tests/fm-session-start.test.sh#L2049-L2055
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@bin/fm-session-start.sh` at line 473, The HOLD-state regex in
bin/fm-session-start.sh lines 473-473 must match only complete values by
requiring whitespace or end-of-line after needs-decision, blocked, or failed.
Add a state=failed-retryable fixture and assert the digest suppresses it in
tests/fm-session-start.test.sh lines 2049-2055.

Comment thread bin/fm-watch.sh
Comment on lines +1415 to 1417
fm_wake_append heartbeat heartbeat "$(parlay_heartbeat_payload)" || exit 1
touch "$STATE/.last-heartbeat"
wake "heartbeat"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Publish the generated payload.

Line 1415 queues the Parlay-aware payload. Line 1417 publishes the fixed heartbeat string. wake sends its argument to watch_delivery_publish, so the immediate away-mode delivery omits the Parlay report.

Build the payload once. Pass it to both fm_wake_append and wake.

Proposed fix
-      fm_wake_append heartbeat heartbeat "$(parlay_heartbeat_payload)" || exit 1
+      heartbeat_payload=$(parlay_heartbeat_payload)
+      fm_wake_append heartbeat heartbeat "$heartbeat_payload" || exit 1
       touch "$STATE/.last-heartbeat"
-      wake "heartbeat"
+      wake "$heartbeat_payload"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fm_wake_append heartbeat heartbeat "$(parlay_heartbeat_payload)" || exit 1
touch "$STATE/.last-heartbeat"
wake "heartbeat"
heartbeat_payload=$(parlay_heartbeat_payload)
fm_wake_append heartbeat heartbeat "$heartbeat_payload" || exit 1
touch "$STATE/.last-heartbeat"
wake "$heartbeat_payload"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@bin/fm-watch.sh` around lines 1415 - 1417, Update the heartbeat flow around
fm_wake_append and wake to build the Parlay heartbeat payload once, store it in
a variable, and pass that same payload to both calls so immediate away-mode
delivery publishes the generated report instead of the fixed heartbeat string.

Reconcile the parlay supervision section with main's session-start
reordering: main moved fleet-state ahead of persona/context and added
the read-once and network-checks stages, so the parlay stage now runs
after context, immediately before the closing reminder. No behavior
change on either side.
@trillium
trillium merged commit 9ecbe45 into main Aug 15, 2026
14 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