Skip to content

feat(cli): add read-only fleet view and Codex deep-link helpers#244

Open
e-jung wants to merge 5 commits into
kunchenguid:mainfrom
e-jung:fm/codex-visible-fleet-p9
Open

feat(cli): add read-only fleet view and Codex deep-link helpers#244
e-jung wants to merge 5 commits into
kunchenguid:mainfrom
e-jung:fm/codex-visible-fleet-p9

Conversation

@e-jung

@e-jung e-jung commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Intent

Implement the Phase 1/2 Codex GUI friendliness MVP for firstmate: make the firstmate thread usable as the captain's compact Codex GUI control/dashboard while Herdr/tmux/zellij/Orca remain the worker backends. Add a read-only fleet visibility command that summarizes in-flight tasks from primary state/backlog with task id, repo, kind, backend, current status, PR/report pointer, path, and watch/steer guidance. Add a dependency-light Codex deep-link helper for task worktrees, reports, and project directories using canonical codex://new links. Document the one-control-thread UX and update script docs/README. Add focused fake-state tests. Do not implement a new runtime backend, do not touch report-handoff, and do not change merge, teardown, or no-mistakes safety.

What Changed

  • Adds bin/fm-fleet-view.sh, a dependency-light read-only command that renders in-flight tasks from primary state/backlog with id, repo, kind, backend, current status, PR/report pointer, worktree path, and watch/steer guidance.
  • Adds bin/fm-codex-link.sh, which emits canonical codex://new deep links for task worktrees, reports (by id or absolute path), and project directories, with query-encoded prompts.
  • Documents the Codex control-thread UX in docs/codex-gui.md, indexes the new scripts in README.md/CONTRIBUTING.md/docs/scripts.md, and adds focused fake-state tests in tests/fm-fleet-view.test.sh plus a session-start test determinism fix.

Risk Assessment

✅ Low: Additive, read-only dashboard + deep-link helpers plus docs and focused fake-state tests; touches no merge/teardown/no-mistakes safety paths, mirrors the existing backlog-item parsing convention used by fm-backlog-handoff.sh, and the prior usage-range leak was correctly fixed.

Testing

Baseline full test suite passes cleanly (the earlier exit-code-1 failure is fixed by the branch's two follow-up commits). I then demonstrated the captain-facing user intent directly with a realistic fake fleet and captured real CLI transcripts: the fleet dashboard renders all required columns across tmux/herdr/orca backends, reconciles a stale needs-decision status into the live "parked" state, shows a scout report pointer, surfaces an in-flight backlog orphan while hiding queued items, and prints a clean (none) row when empty; the Codex deep-link helper emits canonical codex://new links (URL-encoded, with raw-path fallbacks) for tasks, reports, and projects and exits non-zero with clear messages for missing task/report/no-args. No actionable issues found.

Evidence: Fleet dashboard + codex-link CLI transcript (multi-backend fake fleet)

############### fm-fleet-view.sh (multi-backend fleet) ############### ID | REPO | KIND | BACKEND | STATUS | PR/REPORT | PATH | WATCH / STEER ---|---|---|---|---|---|---|--- api-fix | repos-api | ship | tmux | state: parked · source: status-log · approve the rate-limit shape | PR https://github.com/acme/api-server/pull/142 | .../repos-api | watch: bin/fm-peek.sh fm-api-fix; steer: bin/fm-send.sh fm-api-fix '<message>'; links: bin/fm-codex-link.sh task api-fix billing-migrate | repos-api | ship | herdr | state: unknown · backend target gone: herdr-sess:h-pane-billing | - | .../repos-api | watch ... steer ... links: bin/fm-codex-link.sh task billing-migrate billing-secondmate | home | secondmate | tmux | state: unknown ... | - | .../home | watch ... links ... docs-audit | repos-docs | scout | tmux | state: unknown ... | report .../data/docs-audit/report.md | .../repos-docs | watch ... links: bin/fm-codex-link.sh task docs-audit orca-exp | repos-docs | ship | orca | state: unknown ... | - | .../repos-docs | watch ... links ... orphan-cleanup | - | backlog | - | state: unknown (backlog in-flight, no metadata); stale secondmate entries need pruning | - | - | create/recover task metadata before watch/steer ############### fm-codex-link.sh task docs-audit (scout + report) ############### task: docs-audit worktree path: .../repos-docs worktree codex: codex://new?path=%2Ftmp%2F...%2Frepos-docs&prompt=Review%20firstmate%20task%20docs-audit%20... report path: .../home/data/docs-audit report codex: codex://new?path=...%2Fhome%2Fdata%2Fdocs-audit&prompt=Review%20report%20...%2Freport.md%20... ############### fm-codex-link.sh project <dir> 'Summarize the architecture' ############### project path: .../repos-api project codex: codex://new?path=...%2Frepos-api&prompt=Summarize%20the%20architecture ############### fm-codex-link.sh task does-not-exist (error path) ############### error: no metadata for task does-not-exist in .../home/state

############### fm-fleet-view.sh (multi-backend fleet) ###############
ID | REPO | KIND | BACKEND | STATUS | PR/REPORT | PATH | WATCH / STEER
---|---|---|---|---|---|---|---
api-fix | repos-api | ship | tmux | state: parked · source: status-log · approve the rate-limit shape | PR https://github.com/acme/api-server/pull/142 | /tmp/tmp.pftLHZlmL7/repos-api | watch: bin/fm-peek.sh fm-api-fix; steer: bin/fm-send.sh fm-api-fix '<message>'; links: bin/fm-codex-link.sh task api-fix
billing-migrate | repos-api | ship | herdr | state: unknown · source: none · backend target gone: herdr-sess:h-pane-billing | - | /tmp/tmp.pftLHZlmL7/repos-api | watch: bin/fm-peek.sh fm-billing-migrate; steer: bin/fm-send.sh fm-billing-migrate '<message>'; links: bin/fm-codex-link.sh task billing-migrate
billing-secondmate | home | secondmate | tmux | state: unknown · source: none · backend target gone: firstmate:fm-billing-secondmate | - | /tmp/tmp.pftLHZlmL7/home | watch: bin/fm-peek.sh fm-billing-secondmate; steer: bin/fm-send.sh fm-billing-secondmate '<message>'; links: bin/fm-codex-link.sh task billing-secondmate
docs-audit | repos-docs | scout | tmux | state: unknown · source: none · backend target gone: firstmate:fm-docs-audit | report /tmp/tmp.pftLHZlmL7/home/data/docs-audit/report.md | /tmp/tmp.pftLHZlmL7/repos-docs | watch: bin/fm-peek.sh fm-docs-audit; steer: bin/fm-send.sh fm-docs-audit '<message>'; links: bin/fm-codex-link.sh task docs-audit
orca-exp | repos-docs | ship | orca | state: unknown · source: none · backend target gone: orca-term-7 | - | /tmp/tmp.pftLHZlmL7/repos-docs | watch: bin/fm-peek.sh fm-orca-exp; steer: bin/fm-send.sh fm-orca-exp '<message>'; links: bin/fm-codex-link.sh task orca-exp
orphan-cleanup | - | backlog | - | state: unknown (backlog in-flight, no metadata); stale secondmate entries need pruning | - | - | create/recover task metadata before watch/steer

############### fm-codex-link.sh task docs-audit (scout + report) ###############
task: docs-audit
worktree path: /tmp/tmp.pftLHZlmL7/repos-docs
worktree codex: codex://new?path=%2Ftmp%2Ftmp.pftLHZlmL7%2Frepos-docs&prompt=Review%20firstmate%20task%20docs-audit%20in%20this%20worktree.%20Summarize%20status%2C%20diff%2C%20blockers%2C%20and%20next%20steps.
report path: /tmp/tmp.pftLHZlmL7/home/data/docs-audit
report codex: codex://new?path=%2Ftmp%2Ftmp.pftLHZlmL7%2Fhome%2Fdata%2Fdocs-audit&prompt=Review%20report%20%2Ftmp%2Ftmp.pftLHZlmL7%2Fhome%2Fdata%2Fdocs-audit%2Freport.md%20for%20firstmate%20task%20docs-audit.%20Summarize%20the%20conclusion%20and%20recommended%20next%20action.

############### fm-codex-link.sh task api-fix (ship worktree) ###############
task: api-fix
worktree path: /tmp/tmp.pftLHZlmL7/repos-api
worktree codex: codex://new?path=%2Ftmp%2Ftmp.pftLHZlmL7%2Frepos-api&prompt=Review%20firstmate%20task%20api-fix%20in%20this%20worktree.%20Summarize%20status%2C%20diff%2C%20blockers%2C%20and%20next%20steps.
report path: -
report codex: -

############### fm-codex-link.sh report docs-audit ###############
report path: /tmp/tmp.pftLHZlmL7/home/data/docs-audit
report codex: codex://new?path=%2Ftmp%2Ftmp.pftLHZlmL7%2Fhome%2Fdata%2Fdocs-audit&prompt=Review%20report%20%2Ftmp%2Ftmp.pftLHZlmL7%2Fhome%2Fdata%2Fdocs-audit%2Freport.md.%20Summarize%20the%20conclusion%20and%20recommended%20next%20action.

############### fm-codex-link.sh project <dir> 'Summarize the architecture' ###############
project path: /tmp/tmp.pftLHZlmL7/repos-api
project codex: codex://new?path=%2Ftmp%2Ftmp.pftLHZlmL7%2Frepos-api&prompt=Summarize%20the%20architecture

############### fm-codex-link.sh task does-not-exist (error path) ###############
error: no metadata for task does-not-exist in /tmp/tmp.pftLHZlmL7/home/state
Evidence: Empty-fleet dashboard output

ID | REPO | KIND | BACKEND | STATUS | PR/REPORT | PATH | WATCH / STEER ---|---|---|---|---|---|---|--- (none) | - | - | - | no in-flight tasks in .../state | - | - | -

ID | REPO | KIND | BACKEND | STATUS | PR/REPORT | PATH | WATCH / STEER
---|---|---|---|---|---|---|---
(none) | - | - | - | no in-flight tasks in /tmp/tmp.ueJgsvNG3t/state | - | - | -
- Evidence: [docs/codex-gui.md (new control-thread UX doc)](https://github.com/kunchenguid/firstmate/blob/fab7d7eb4e900d801d350d7d17e96ec0a0711c89/docs/codex-gui.md) - Outcome: 🔧 1 issue found → auto-fixed ✅ across 2 runs (32m46s)

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

⚠️ **Review** - 1 info
  • ℹ️ bin/fm-codex-link.sh:26 - usage() runs sed -n &#39;2,16p&#39; &#34;$0&#34;, but the comment header ends at line 12; lines 13-16 are live code (set -u, a blank line, SCRIPT_DIR=..., FM_ROOT=...), so every usage/error message prints four stray code lines after the intended help text (verified by running fm-codex-link.sh with no args). The range should be 2,12p.
  • ⚠️ bin/fm-codex-link.sh:35 - urlencode() percent-encodes every byte outside [A-Za-z0-9.~_-], including /, so an absolute path like /tmp becomes path=%2Ftmp in the emitted codex://new?path=... link (verified). This is RFC-3986-valid and the docs state 'query values encoded', but the whole value is a local directory path: if the Codex app's URL handler does not decode %2F back to / within the path query param, every task/project/report deep link resolves the wrong directory. RFC 3986 permits / unencoded in the query component, so leaving slashes literal (encoding only spaces and other unsafe bytes) is the safer interop choice. Worth confirming against the real Codex app since the feature is unverified without it.
  • ℹ️ bin/fm-fleet-view.sh:57 - status_summary spawns a full fm-crew-state.sh subprocess per in-flight task; each one can issue up to two bounded no-mistakes calls (FM_FLEET_VIEW_STATE_TIMEOUT defaults to 2s each) plus git/tmux probes. On a healthy fleet this is fast, but worst case is several seconds per row, so a large dashboard can take tens of seconds to render. Acceptable for a manual command and the timeout bounds it; noted only as a known characteristic.

🔧 Fix: fix usage() header range leak in fm-codex-link.sh
1 info still open:

  • ℹ️ bin/fm-codex-link.sh:35 - urlencode() percent-encodes '/' as %2F (so /home/user/alpha becomes path=%2Fhome%2Fuser%2Falpha). Round 1 flagged this as a possible interop risk if the Codex app does not decode %2F back to '/'. This is RFC-3986-compliant and the conservative/safe choice: any standard URL query-value parser decodes %2F->/, and encoding slashes avoids ambiguity with path separators. The one residual limitation is that the test suite only validates encoding mechanics (e.g. with%20space) and never exercises the emitted codex://new link against a real Codex app consumer, so live interop remains the author's to confirm but needs no code change here.
🔧 **Test** - 1 issue found → auto-fixed ✅
  • 🚨 tests failed with exit code 1
  • command -v tmux >/dev/null || { echo "tmux is required for e2e tests" >&2; exit 1; }; tmux -V; rc=0; for t in tests/*.test.sh; do echo "== $t =="; bash "$t" || rc=1; done; exit "$rc"

🔧 Fix: fix session-start tests to force a deterministic MISSING via lavish-axi
✅ Re-checked - no issues remain.

  • command -v tmux >/dev/null || { echo "tmux is required for e2e tests" >&2; exit 1; }; tmux -V; rc=0; for t in tests/*.test.sh; do echo "== $t =="; bash "$t" || rc=1; done; exit "$rc"
  • Baseline suite: command -v tmux &gt;/dev/null || ...; for t in tests/*.test.sh; do bash &#34;$t&#34;; done (full run, all pass)
  • bash tests/fm-fleet-view.test.sh (5/5 ok)
  • bash tests/fm-session-start.test.sh (11/11 ok, includes the lavish-axi MISSING determinism fix)
  • Manual E2E: FM_HOME=&lt;fake&gt; bin/fm-fleet-view.sh against a fleet spanning tmux/herdr/orca + scout-with-report + secondmate + backlog-orphan
  • Manual E2E: bin/fm-codex-link.sh task &lt;id&gt; for ship and scout shapes (worktree + report deep links)
  • Manual E2E: bin/fm-codex-link.sh report &lt;id&gt; and report &lt;abs-file-path&gt; (id + absolute-path resolution)
  • Manual E2E: bin/fm-codex-link.sh project &lt;dir&gt; &lt;prompt&gt; (encoded prompt deep link)
  • Manual E2E: positional shorthand bin/fm-codex-link.sh &lt;id&gt;
  • Error paths: missing task (exit 1), missing report (exit 1), no-args usage (exit 2)
  • Empty-fleet path: bin/fm-fleet-view.sh on a home with no meta/backlog prints the (none) row (exit 0)
  • Repo-hygiene: git status --porcelain clean; docs/scripts.md + README.md + docs/codex-gui.md diffs present
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

@e-jung e-jung force-pushed the fm/codex-visible-fleet-p9 branch from 614e54c to 899f295 Compare July 4, 2026 19:42
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