Skip to content

feat(bin): serve the Action Deck as a private /deck page on the bridge - #122

Merged
Amplify-Logic merged 5 commits into
mainfrom
fm/action-deck-web
Sep 11, 2026
Merged

Amplify-Logic merged 5 commits into
mainfrom
fm/action-deck-web

Conversation

@Amplify-Logic

Copy link
Copy Markdown
Owner

Intent

Build the captain's private web Action Deck as a real implementation on the existing Firstmate bridge (bin/fm-bridge-view.py + .sh), reusing the existing fm-deck.sh collector and fm-deck-render.py as the single source of the deck payload: expose the current payload as one structured JSON model (fm-deck.sh --json) and serve GET /deck (page) and GET /api/deck (model) behind the bridge's existing session cookie, Host check, CSP and security headers, plus a nav entry from the glance page. No second database, no new public service, no new UI framework, no generic shell endpoint or unconstrained command runner, no automatic FOTA sending, order booking or new privileged service. The Action Deck is for automatable actions the captain can review and fire off quickly; daily to-dos and the broad fleet backlog are separate, so held backlog decisions must not be rendered as the captain's obligations or as 'needs you' counts. The UI must clearly distinguish ready-to-run actions, staged actions needing inputs or approval, running work, and outcomes; hide completed work from the actionable default view; show honest updated-at (taken from the collector's own sample time, not browser receipt time), source, error, empty and unconfirmed states, identifiers and relevant source links. Use only real existing staged action records and never invent ready actions or executable controls; because the existing action gateway's execute is unwired and approval needs the captain secret on the desk, the page carries no Run/Approve/Merge control and says exactly what is missing. Only ordinary https URLs become clickable links; tray targets and local paths render as text, never arbitrary filesystem exposure. Compact, responsive, keyboard-accessible, mobile-friendly layout; no wall of terminal output. Terminal deck rendering stays byte-identical. Tests cover the JSON model, the route, the auth boundary, link vetting, the read-only guarantee, the 503 error path and page rendering; docs point at the new page. Accepted review decisions already carried on the branch: the deck header reports the collector sample age from read_at/server_unix rather than client time, and the duplicate setCount helper is dropped in favour of setText. No agent co-author attribution in commits. No production deployment.

What Changed

  • bin/fm-deck.sh gains a --json mode that emits the collector payload as one structured fm-deck.v1 model (needs_you / staged / under_way / outcomes) via bin/fm-deck-render.py, which also grows the JSON serializer and https-only link vetting; terminal rendering with --once stays byte-identical to base.
  • bin/fm-bridge-view.py serves GET /deck (compact, responsive, keyboard-accessible page) and GET /api/deck (model) behind the existing session cookie, Host check, nonce CSP and security headers, plus a nav entry from the glance page. The page carries no Run/Approve/Merge control and states what is missing, renders tray targets and local paths as text, hides finished work by default, reports the collector sample age from read_at/server_unix, and on a first-load 503 shows the reason inline instead of the full-screen overlay.
  • tests/fm-deck.test.sh and tests/fm-bridge-view.test.sh cover the JSON model, the route and auth boundary, link vetting, the read-only guarantee, the 503 path and page states; docs/bridge-view.md, docs/architecture.md, docs/ops-command-center.md and docs/scripts.md point at the new page. The pipeline Review left two informational notes (duplicate inline https rule in pinned_links_html, shared 20s collector timeout) that are not addressed here.

🤖 Generated with Claude Code

Risk Assessment

✅ Low: The terminal renderer refactor is output-preserving (verified branch by branch against the old code), the new routes sit behind the existing Host check, session cookie and CSP, links are vetted server-side and again client-side with only https allowed, no run/approve control exists, the child runs in the same scrubbed bounded environment as the bearings snapshot, and tests cover the JSON model, route, auth boundary, link vetting, read-only fingerprint, 503 path and page rendering as the intent requires.

Testing

Ran the deck and bridge-view suites (all passing), then stood up a real bridge with the deck fixture plus a second bridge with a failing collector and drove both through headless Chromium as the https tailnet origin, capturing the login gate, glance nav, desktop and mobile deck renders, expanded folds, keyboard focus, and the 503 state with the reason visible and no overlay; also recorded curl transcripts for the auth boundary, security headers, JSON model and 503 body, and proved the terminal pane is byte-identical between base and target with a frozen clock. Everything matched the stated intent.

  • Evidence: Deck page, desktop 1280px (authenticated, default view) (local file: /var/folders/2c/9sf1hvpn23q336b08gs1np0c0000gn/T/no-mistakes-evidence/01M290766C8B9TVTNTK4CQVE31/03-deck-desktop-1280.png)
  • Evidence: Deck page, mobile 390px (local file: /var/folders/2c/9sf1hvpn23q336b08gs1np0c0000gn/T/no-mistakes-evidence/01M290766C8B9TVTNTK4CQVE31/06-deck-mobile-390.png)
  • Evidence: Deck page with held decisions, finished and landed folds expanded (local file: /var/folders/2c/9sf1hvpn23q336b08gs1np0c0000gn/T/no-mistakes-evidence/01M290766C8B9TVTNTK4CQVE31/04-deck-desktop-folds-expanded.png)
  • Evidence: Needs You region with keyboard focus ring on an https ask link (local file: /var/folders/2c/9sf1hvpn23q336b08gs1np0c0000gn/T/no-mistakes-evidence/01M290766C8B9TVTNTK4CQVE31/05-deck-asks-keyboard-focus.png)
  • Evidence: 503 first load: header and regions say Cannot reach the desk with the collector's reason, no overlay (desktop) (local file: /var/folders/2c/9sf1hvpn23q336b08gs1np0c0000gn/T/no-mistakes-evidence/01M290766C8B9TVTNTK4CQVE31/07-deck-503-desk-unreachable-desktop.png)
  • Evidence: 503 first load, mobile (local file: /var/folders/2c/9sf1hvpn23q336b08gs1np0c0000gn/T/no-mistakes-evidence/01M290766C8B9TVTNTK4CQVE31/08-deck-503-desk-unreachable-mobile.png)
  • Evidence: Glance page with the Action Deck nav entry (local file: /var/folders/2c/9sf1hvpn23q336b08gs1np0c0000gn/T/no-mistakes-evidence/01M290766C8B9TVTNTK4CQVE31/02-glance-with-action-deck-nav.png)
  • Evidence: Unauthenticated /deck shows the login gate (local file: /var/folders/2c/9sf1hvpn23q336b08gs1np0c0000gn/T/no-mistakes-evidence/01M290766C8B9TVTNTK4CQVE31/01-deck-unauthenticated-login-gate.png)
Evidence: HTTP transcript: auth boundary, Host check, headers, /api/deck model, nav, 503 body
### GET /deck without a session (expect the login page, no deck markup)
HTTP/1.1 200 OK
Server: BaseHTTP/0.6 Python/3.13.5
Date: Fri, 11 Sep 2026 20:08:59 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 4369
Cache-Control: no-store
Content-Security-Policy: default-src 'none'; style-src 'nonce-iJlqGbL3Y3K0oqFUVXqywA'; script-src 'nonce-iJlqGbL3Y3K0oqFUVXqywA'; img-src 'self'; connect-src 'self'; form-action 'self'; base-uri 'none'; frame-ancestors 'none'
Referrer-Policy: no-referrer
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

deck regions leaked to anon page: 0
action="/login"

### GET /api/deck without a session (expect 401)
HTTP/1.1 401 Unauthorized
Server: BaseHTTP/0.6 Python/3.13.5
Date: Fri, 11 Sep 2026 20:08:59 GMT
Content-Type: application/json
Content-Length: 25
Cache-Control: no-store
Content-Security-Policy: default-src 'none'; frame-ancestors 'none'
Referrer-Policy: no-referrer
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

{"error":"unauthorized"}

### GET /api/deck with a LAN Host header (expect 403)
HTTP/1.1 403 Forbidden
Server: BaseHTTP/0.6 Python/3.13.5
Date: Fri, 11 Sep 2026 20:08:59 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 10
Cache-Control: no-store
Content-Security-Policy: default-src 'none'; style-src 'nonce-UAN-lV9l8KHwecUzpMu0PQ'; script-src 'nonce-UAN-lV9l8KHwecUzpMu0PQ'; img-src 'self'; connect-src 'self'; form-action 'self'; base-uri 'none'; frame-ancestors 'none'
Referrer-Policy: no-referrer
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

forbidden

### POST /login then GET /api/deck with the session cookie (expect 200 fm-deck.v1 model)
HTTP/1.1 200 OK
Server: BaseHTTP/0.6 Python/3.13.5
Date: Fri, 11 Sep 2026 20:09:00 GMT
Content-Type: application/json
Content-Length: 1955
Cache-Control: no-store
Content-Security-Policy: default-src 'none'; frame-ancestors 'none'
Referrer-Policy: no-referrer
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

{
  "schema": "fm-deck.v1",
  "home": "Deck",
  "read_at": 1789157339,
  "ready": [],
  "ready_note": "Running from this page is not wired yet. The desk records approvals and decisions but has no executor, so nothing here is one click from happening.",
  "staged": {
    "groups": [
      {
        "order": "fota",
        "count": 1,
        "oldest": "1031d",
        "status": "ARMED",
        "last_fire": "-",
        "cards": [
          {
            "digest_short": "aaaaaaaaaaaa",
            "action_kind": "device.firmware.push",
            "target": "device://867280068649789",
            "requester_id": "worker-1",
            "age": "1031d",
            "expiry": "expires in 26745d",
            "expired": false
          }
        ]
      }
    ],
    "quiet_orders": [],
    "missing": "Approval needs the captain secret on the desk (fm-action-gateway.sh approve), and no executor is wired for any action kind yet."
  },
  "asks": [
    {
      "ask": "review",
      "title": "Spoken updates on the glasses",
      "project": "Starship",
      "url": "https://github.com/kunchenguid/firstmate/pull/9"
    },
    {
      "ask": "review",
      "title": "Refresh the Artevo brand page",
      "project": "artevo",
      "url": "https://gitlab.example/artevo/site/-/merge_requests/3"
    }
  ],
  "decisions": [
    {
      "ask": "decide",
      "title": "Add Qwen to the fleet?",
      "project": "firstmate"
    }
  ],
  "backlog_reason": null,
  "unconfirmed": false,
  "under_way": [
    {
      "outcome": "Spoken updates on the glasses",
      "project": "Starship",
      "state": "working",
      "label": "WORKING",
      "heard_secs": 28,
      "url": "https://github.com/kunchenguid/firstmate/pull/9"
    },
    {
      "outcome": "VoiceLoop tap trigger",
      "project": "Starship",
      "state": "working",
      "label": "WORKING",
      "heard_secs": 28
    }
  ],
  "finished": [
    {
      "outcome": "Photo drops from the phone",
      "project": "Starship",
      "state": "done",
      "label": "READY",
      "heard_secs": 28
    }
  ],
  "landed": [
    {
      "closed": "2026-09-01",
      "what": "findings",
      "title": "Investigate the delta timeouts",
      "project": "firstmate",
      "ref": "data/done-scout/report.md"
    }
  ],
  "loose_ends": null,
  "counts": {
    "asks": 2,
    "decisions": 1,
    "staged": 1,
    "staged_oldest": "1031d",
    "under_way": 2,
    "finished": 1,
    "loose_ends": null
  },
  "server_unix": 1789157339
}

### GET /deck with the session cookie: response headers
HTTP/1.1 200 OK
Server: BaseHTTP/0.6 Python/3.13.5
Date: Fri, 11 Sep 2026 20:09:00 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 18197
Cache-Control: no-store
Content-Security-Policy: default-src 'none'; style-src 'nonce-bRrw5afJk7T90P5TfEC9-Q'; script-src 'nonce-bRrw5afJk7T90P5TfEC9-Q'; img-src 'self'; connect-src 'self'; form-action 'self'; base-uri 'none'; frame-ancestors 'none'
Referrer-Policy: no-referrer
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

buttons in page markup: 1 (Log out only)
<button type="submit">Log out</button>
word occurrences in page source:    2 approve
word occurrences in page source:    1 merge
word occurrences in page source:    2 rUn
word occurrences in page source:    4 run

### glance page nav entry
<nav class="page-nav" aria-label="Pages"><span aria-current="page">Fleet glance</span><a href="/deck">Action Deck</a></nav>

### broken collector bridge: GET /api/deck (expect 503 with the collector's reason)
HTTP/1.1 503 Service Unavailable
Server: BaseHTTP/0.6 Python/3.13.5
Date: Fri, 11 Sep 2026 20:09:00 GMT
Content-Type: application/json
Content-Length: 87
Cache-Control: no-store
Content-Security-Policy: default-src 'none'; frame-ancestors 'none'
Referrer-Policy: no-referrer
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

{"error": "desk unreachable", "detail": "deck collector exploded: tray log unreadable"}
Evidence: Terminal pane byte-identical between base and target

fm-deck.sh --once, same fixture home, FM_DECK_NOW=1789157400 fixture home base sha256: 0540d919...c557b8 fixture home target sha256: 0540d919...c557b8 -> cmp: byte-identical empty home base sha256: 8c3041d1...52eb2 empty home target sha256: 8c3041d1...52eb2 -> cmp: byte-identical

### Terminal deck rendering: fm-deck.sh --once at base 41e98a0 vs target HEAD, same fixture home, FM_DECK_NOW=1789157400
fixture home  base   sha256: 0540d9192009fcb2a16076fa39746e67e619fe81df85b58298f7ce6878c557b8
fixture home  target sha256: 0540d9192009fcb2a16076fa39746e67e619fe81df85b58298f7ce6878c557b8
fixture home: cmp says byte-identical
empty home    base   sha256: 8c3041d1059e671dce78d213113ef307d12b34c336af6f2cb6a211310e052eb2
empty home    target sha256: 8c3041d1059e671dce78d213113ef307d12b34c336af6f2cb6a211310e052eb2
empty home: cmp says byte-identical
line count target pane:       27

### the terminal pane as rendered by the target (unchanged from base)
────────────────────────────────────────────────────────────────────────────────────────────────────
⚓  ACTION DECK · Deck                                                           Fri 11 Sep 22:10:00
   3 need you · 1 staged (oldest 1031d) · 3 under way
────────────────────────────────────────────────────────────────────────────────────────────────────

 STAGED FOR YOUR CLICK ─────────────────────────────────────────────────────────────────────────────
  fota  —  1 waiting, oldest 1031d  ·  ARMED · last ran -
      1031d expires in 26… device.firmware.push   device://867280068649789

 NEEDS YOU ─────────────────────────────────────────────────────────────────────────────────────────
  review   Spoken updates on the glasses · Starship
           https://github.com/kunchenguid/firstmate/pull/9
  review   Refresh the Artevo brand page · artevo
           https://gitlab.example/artevo/site/-/merge_requests/3
  decide   Add Qwen to the fleet? · firstmate

 UNDER WAY ─────────────────────────────────────────────────────────────────────────────────────────
  🔵 WORKING     Spoken updates on the glasses                   Starship           heard 1m ago
  🔵 WORKING     VoiceLoop tap trigger                           Starship           heard 1m ago
  🟢 READY       Photo drops from the phone                      Starship           heard 1m ago

 JUST IN ───────────────────────────────────────────────────────────────────────────────────────────
  01 Sep  findings  Investigate the delta timeouts
          data/done-scout/report.md

────────────────────────────────────────────────────────────────────────────────────────────────────
snapshot · view only · approve fm-action-gateway.sh · decide fm-decision-surface.sh · live fm-fleet…
Evidence: Browser observations: header text, counts, buttons, links with rel/target, tab order, mobile scrollWidth, 503 overlay state

buttons on deck page: 1 ['Log out'] links: ('/', None, None), ('kunchenguid#9;, '_blank', 'noopener noreferrer'), ('https://gitlab.example/artevo/site/-/merge_requests/3&#39;, '_blank', 'noopener noreferrer') mobile scrollWidth at 390 viewport: 390 503: header 'Cannot reach the desk.', error 'deck collector exploded: tray log unreadable', overlay visible False

('unauth /deck title', 'STARSHIP', 'staged region present:', 0)
('after login url', 'https://bridge.test.example/', 'title', 'STARSHIP')
('deck header observed', 'Read just now · from the records Deck keeps')
('deck counts', '2 need you · 1 staged · 1 held decisions · 2 under way')
('buttons on deck page', 1, ['Log out'])
('links on deck page', [('/', None, None), ('https://github.com/kunchenguid/firstmate/pull/9', '_blank', 'noopener noreferrer'), ('https://gitlab.example/artevo/site/-/merge_requests/3', '_blank', 'noopener noreferrer'), ('https://github.com/kunchenguid/firstmate/pull/9', '_blank', 'noopener noreferrer')])
('decisions section hidden by default', None)
('tab order', ['BUTTON [Log out]', 'A https://bridge.test.example/ [Fleet glance]', 'A https://github.com/kunchenguid/firstmate/pull/9 [Spoken updates on the glasses]', 'A https://gitlab.example/artevo/site/-/merge_requests/3 [Refresh the Artevo brand page]', 'SUMMARY [1 held decisions - show all]', 'A https://github.com/kunchenguid/firstmate/pull/9 [Spoken updates on the glasses]', 'SUMMARY [1 finished, awaiting cleanup]', 'SUMMARY [Show recent completions]', 'BODY [Cannot reach the desk.\n\n\n  Action Deck\n ]', 'BUTTON [Log out]', 'A https://bridge.test.example/ [Fleet glance]', 'A https://github.com/kunchenguid/firstmate/pull/9 [Spoken updates on the glasses]'])
('mobile scrollWidth (390 viewport, no horizontal overflow if <=390)', 390)
('503 header', 'Cannot reach the desk.', 'error', 'deck collector exploded: tray log unreadable', 'overlay visible', False, 'overlay class', None)
- Outcome: 🔧 1 issue found → auto-fixed ✅ across 2 runs (22m13s)

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

⚠️ **Review** - 2 infos
  • ℹ️ bin/fm-bridge-view.py:2024 - pinned_links_html still carries its own inline copy of the https-only link rule (scheme == https, netloc present, no whitespace) while the new safe_https() docstring says it is 'the same rule the pinned links use'. Calling safe_https(url) here would make the two link vetting paths one function so they cannot drift (safe_https additionally caps length at 2048, which is harmless for pinned links).
  • ℹ️ bin/fm-bridge-view.py:1204 - run_deck reuses SNAPSHOT_TIMEOUT_SECONDS (20s) via _bounded_process_output. fm-deck.sh --json shells out to fm-tray.sh json (full audit-log fold), fm-order.sh list and tasks-axi list; on a large audit log a slow but healthy read becomes a 503 'action deck timed out' rather than a deck. The page copes honestly (503 path and 25s client abort are aligned), so this is a tuning note only.
🔧 **Test** - 1 issue found → auto-fixed ✅
  • ⚠️ bin/fm-bridge-view.py:2326 - When /api/deck answers 503 on first load, the deck JS sets the full-screen #stale overlay ("Cannot reach the desk.") and also writes the reason into #deck-error, but the overlay (position: fixed; inset: 0; z-index 9) covers the whole page, so the captain never sees the reason. Screenshot deck-collector-error-503-mobile.png shows only the overlay; deck-collector-error-503-underlying-page-overlay-removed.png shows the hidden reason. The glance uses the same overlay on first failure, so this matches the existing pattern, but the intent and docs/bridge-view.md say the page shows "Cannot reach the desk" and the reason. Decide whether the deck should skip the full-screen overlay on first failure (or render the reason inside it).
  • bash tests/fm-deck.test.sh (35 ok, incl. test_json_mode_emits_the_pane_as_one_model, test_json_mode_degrades_honestly_and_scrubs)
  • bash tests/fm-bridge-view.test.sh (37 ok, incl. test_deck_page_and_api, test_deck_api_reports_desk_unreachable, test_deck_page_renders_states_without_run_controls)
  • Byte-identity: captured the exact sentinel payload bin/fm-deck.sh --once pipes to the renderer over the deck test's full fixture home and an empty home, rendered it with git archive 41e98a0 bin fm-deck-render.py and the branch renderer, cmp at widths 80/100/120/160 → identical; same payload through --json gives schema fm-deck.v1 with needs_you=4 staged=3 under_way=3; base --json exits 1
  • Live bridge via curl: GET /api/deck without session → 401; GET /deck without session → login page, no deck markup; GET /deck with Host 192.168.1.20 → 403; authenticated /deck → 200 with nonce CSP, Referrer-Policy, X-Frame-Options, nosniff, single &lt;button&gt; (Log out), only /logout form action, no Run/Approve/Merge control; authenticated /api/deck → 200 JSON, Cache-Control no-store, home fingerprint unchanged; broken fm-deck.sh root → 503 {&#34;error&#34;:&#34;desk unreachable&#34;,&#34;detail&#34;:&#34;deck collector exploded: tray log unreadable&#34;}
  • Playwright (chromium) against the live bridge with Host+cookie injected via route: screenshots at 390px and 1100px, unauthenticated /deck, glance nav → click Action Deck lands on /deck, held-decisions/finished/landed/loose-ends folds opened via keyboard Enter, Tab order recorded (Log out → Fleet glance → https links → fold summaries), device:// targets and data/ report path rendered as text with zero links, all https links target=_blank rel=noopener, header "Read just now" → "Read 7 seconds ago" ticking from read_at, api read_at == server_unix, ready == []
  • Verified no Co-Authored-By lines in the two branch commits; git status clean after cleanup of temp scripts

🔧 Fix: deck first-load 503 shows reason, no overlay
✅ Re-checked - no issues remain.

  • bash tests/fm-deck.test.sh (35 ok, including the two new --json model tests)
  • bash tests/fm-bridge-view.test.sh (37 ok, including test_deck_page_and_api, test_deck_api_reports_desk_unreachable, test_deck_page_renders_states_without_run_controls)
  • curl transcript against a live bridge: anonymous GET /deck (login page, no deck markup), GET /api/deck 401, LAN Host 403, POST /login then GET /api/deck 200 fm-deck.v1 with security headers, GET /deck headers and single Log out button, glance nav entry, broken-collector GET /api/deck 503 with reason
  • Playwright headless Chromium, requests relayed to the loopback bridge as https://bridge.test.example: login through the real form, glance nav click-through to /deck, desktop 1280 and mobile 390 screenshots, folds expanded, Tab-order walk and focus ring on an https ask link, 503 desktop and mobile screenshots
  • fm-deck.sh --once at base 41e98a0 vs target HEAD on the same fixture home and an empty home with FM_DECK_NOW frozen: sha256 and cmp byte-identical
  • Two /api/deck reads 3s apart confirm read_at is the cached collector sample used for the header age
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

Add `fm-deck.sh --json`, which emits the pane's payload as one structured
`fm-deck.v1` model, and split fm-deck-render.py's section builders into
shared row/group helpers so the terminal frame and the model apply the same
reading rules. The terminal rendering is unchanged.

Add `GET /deck` and `GET /api/deck` to fm-bridge-view.py behind the existing
session cookie, Host check, CSP and security headers. The deck read runs as
the same scrubbed bounded child as the bearings snapshot, is cached, and
returns 503 with the collector's reason on failure. Live asks are the
headline; held backlog decisions, finished workers, completions and loose
ends are folded away from the actionable lists. Only ordinary https URLs
become links; tray targets and local paths render as text. The page carries
no approve, run or merge control and says why: the gateway has no executor
and approval needs the captain secret on the desk.

Tests cover the JSON model, the route, the auth boundary, link vetting, the
read-only guarantee, the 503 path and the page rendering. Docs point at the
new page from bridge-view.md, ops-command-center.md and scripts.md.
… overlay

On a first-load failure the deck raised the full-page 'Cannot reach the
desk.' overlay and wrote the collector's reason underneath it, so the
captain never saw the reason. The header and every region already say
the desk is unreachable and #deck-error carries the reason; the overlay
is now reserved for a tab whose refreshes stop after a successful load.
The rendering test drives refresh() against a fake 503 and asserts the
overlay stays off while the reason is shown.
…tranks /usr/bin

Adding python3 ahead of jq in CHILD_PATH_TOOLS let /usr/bin lead the child
PATH on Linux, where python3 lives there, so the real /usr/bin/tmux outranked
the test's fake tmux and the live observation read a paused worker as Stuck.
@Amplify-Logic
Amplify-Logic merged commit dc88efb into main Sep 11, 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.

1 participant