Skip to content

feat(bearings): color-code board statuses and add keep/now/remove triage choices - #49

Closed
doitdigital0495 wants to merge 3 commits into
mainfrom
fm/bearings-board-keep-remove
Closed

doitdigital0495 wants to merge 3 commits into
mainfrom
fm/bearings-board-keep-remove

Conversation

@doitdigital0495

Copy link
Copy Markdown
Owner

Intent

The bearings Lavish board needs to read at a glance and let the captain triage the whole board in one sitting: statuses must be color-coded so waiting-on-you, working, waiting, completed and unconfirmed look different at a glance on desktop and at 390px (wording stays visible, color is a second signal); every request row, an optional knowledge section, and every Projects and People item must carry a three-choice keep-on-backlog / do-now / remove-completely selection that flows through the board's existing Lavish answer path as one versioned choice per marked row; the payload contract extends with optional fields only, the board script header documents exactly how firstmate reads such an answer, and the result ships as one PR with sample-build plus 390px and desktop screenshot proof.

What Changed

  • The bearings board template now colors request, project and person statuses using an explicit map: waiting on you, working, waiting, completed and unconfirmed each get their own color. Unknown wording stays neutral, and the status words are always visible. At phone width, request table cells stack so words no longer wrap mid-word.
  • Each routable row now gets a single three-choice radio group: keep on backlog (backlog), do now (now) or remove completely (remove). This covers request rows, the new optional knowledge section, projects (through a new optional id) and crew. Each section's send bar queues every marked row as its own fm-bearings-answer.v1 choice, keyed triage.<request|knowledge|project|crew>.<id>, through the existing window.lavish.queuePrompt answer path. A choice that is changed after queueing can be sent again.
  • bin/fm-bearings-board.sh validates the optional knowledge[] entries and the optional project id: unique slug ids whose full triage key stays within 128 characters. Its header now documents how firstmate reads a triage answer and applies it. The bearings SKILL.md describes the new optional fields. The render harness and the board tests cover the status colors, the triage choices and the new validation.

🤖 Generated with Claude Code

Risk Assessment

✅ Low: The change adds optional payload fields with matching validation and only touches UI rendering. It reuses the existing Lavish answer path under a namespaced key that cannot resolve a captain-held task. The one real defect, a choice changed after sending is not re-sent, is UX-level and easy to fix.

Testing

Built the sample payload with the real build script. It produced a live, armed Lavish session, which I drove in headless Chrome. Marking a request Do now and sending it queued now. Switching the same row to Remove re-enabled the send bar, and the second send replaced the queued prompt with remove. The captured result that firstmate reads lists exactly four choices (request remove, knowledge remove, project backlog, crew remove), and the stale now is not among them. Screenshots at 390px show request rows stacked, with badges such as UNCONFIRMED and labels such as 'Remove completely' each on one line. At 1440px the three-column table is kept, with distinct status colors. The targeted render and build test scripts both passed. Setup note: the throwaway FM_HOME needed umask 077 because the process-event state root must be a private directory.

  • Live validation: ✅ go - 6 of 6 scenarios driven live against the product
Scenario Result Live Evidence
Captain sees the five statuses (waiting on you, working, waiting, completed, unconfirmed) in distinct colors with words visible on desktop 1440px ✅ pass live r2-desktop-1440-requests.png, r2-desktop-1440-initial.png
At 390px status badges and keep/now/remove labels read without mid-word breaks (request rows stack) ✅ pass live r2-mobile-390-full.png, r2-mobile-390-requests.png; JS scan found no single-word label wrapping
Every request row, each knowledge entry, and each project and person shows a three-choice group, and unknown status wording stays neutral ✅ pass live r2-mobile-390-full.png and the a11y snapshot showed a radio group on all 6 requests, 2 knowledge entries, 2 projects and 2 crew
Adversarial: captain sends Do now, then switches the same row to Remove; the bar re-enables and re-sending replaces the queued answer ✅ pass live r2-requeue-after-change.txt (single queued prompt, selection remove); r2-firstmate-read.txt contains no 'Do now'
Choices flow through the existing Lavish answer path as one fm-bearings-answer.v1 choice per marked row, with triage.<kind>.<id> questions, and firstmate reads them from the captured result ✅ pass live r2-desktop-1440-queued.png, r2-requeue-after-change.txt, r2-firstmate-read.txt (4 of 4 choice annotations)
An untouched row sends nothing and the send bar stays disabled with 'no choice marked' ✅ pass live The a11y snapshot showed the knowledge, project and request bars disabled until a row was marked, and disabled again after sending
Evidence: Sample build transcript (round 2)

Source: Sample build transcript (round 2)

board: /tmp/fm-lab-triage2.DJsTeY/.lavish/bearings-board.html
session:
  file: /tmp/fm-lab-triage2.DJsTeY/.lavish/bearings-board.html
  url: "https://desktop-agentic.tail1dbb2d.ts.net:8387/session/4c223ece1f8a1084"
  status: opened
session: live
served: /tmp/fm-lab-triage2.DJsTeY/.lavish/bearings-board.html
bound: lavish-4c223ece1f8a1084
armed: lavish-4c223ece1f8a1084
listening: live
![Desktop 1440 Lavish session initial](https://github.com/user-attachments/assets/29c02114-1a2a-47d0-887e-39c7e247dab4) - Evidence: [Desktop 1440 Lavish session initial](https://github.com/doitdigital0495/firstmate/blob/19d0af5ea37d6bd00e93f8ef3bdb4ff5a5d14005/.no-mistakes/evidence/fm/bearings-board-keep-remove/r2-desktop-1440-initial.png) ![Desktop 1440 Lavish session with queued triage choices](https://github.com/user-attachments/assets/fb5a9c7b-e905-4c09-8143-9c4d3357971b) - Evidence: [Desktop 1440 Lavish session with queued triage choices](https://github.com/doitdigital0495/firstmate/blob/19d0af5ea37d6bd00e93f8ef3bdb4ff5a5d14005/.no-mistakes/evidence/fm/bearings-board-keep-remove/r2-desktop-1440-queued.png) ![Desktop 1440 requests table](https://github.com/user-attachments/assets/5d458713-47b3-460d-915c-8aefd1690725) - Evidence: [Desktop 1440 requests table](https://github.com/doitdigital0495/firstmate/blob/19d0af5ea37d6bd00e93f8ef3bdb4ff5a5d14005/.no-mistakes/evidence/fm/bearings-board-keep-remove/r2-desktop-1440-requests.png) ![390px full board](https://github.com/user-attachments/assets/e6e02e21-1be4-49c1-9786-f90e53e6cee2) - Evidence: [390px full board](https://github.com/doitdigital0495/firstmate/blob/19d0af5ea37d6bd00e93f8ef3bdb4ff5a5d14005/.no-mistakes/evidence/fm/bearings-board-keep-remove/r2-mobile-390-full.png) ![390px requests section (stacked, no mid-word wraps)](https://github.com/user-attachments/assets/d63cc9c7-50c8-41b1-9612-17299818d060) - Evidence: [390px requests section (stacked, no mid-word wraps)](https://github.com/doitdigital0495/firstmate/blob/19d0af5ea37d6bd00e93f8ef3bdb4ff5a5d14005/.no-mistakes/evidence/fm/bearings-board-keep-remove/r2-mobile-390-requests.png)
Evidence: Queue after changing a sent choice (now -> remove)

Source: Queue after changing a sent choice (now -> remove)

        uid=g12:1_151 button "Send my choices" disableable disabled
        uid=g12:1_152 status atomic live="polite" relevant="additions text"
        uid=g12:1_153 StaticText "Updated 2026-10-02T10:00Z"
  uid=g12:1_154 complementary
    uid=g12:1_155 heading "Conversation" level="2"
    uid=g12:10_3 StaticText "QUEUED"
    uid=g12:10_4 button "Remove queued prompt"
    uid=g12:10_5 StaticText "<choice>"
    uid=g12:10_6 StaticText "“Approve DNS cutover -> Remove completely”"
    uid=g12:10_7 StaticText "Keep, do or remove - Approve DNS cutover: Remove completely

Context data:
{
  "schema": "fm-bearings-answer.v1",
  "question": "triage.request.req-waiting-you",
  "selection": "remove",
  "note": ""
}"
    uid=g12:1_157 textbox "Write a message for the agent..." multiline
    uid=g12:1_158 button "Attach images"
    uid=g12:1_159 status atomic live="polite" relevant="additions text"
    uid=g12:1_160 button "Send & End"
    uid=g12:1_161 button "Send to Agent"
help[5]:
  Run `chrome-devtools-axi fill @g12:1_157 "text"` to fill the "Write a message for the agent..." field
  Run `chrome-devtools-axi click @g12:1_3` to click the "Annotate" button
  Run `chrome-devtools-axi click @g12:1_124` to click the "Review the proposed changes" link
  Run `chrome-devtools-axi scroll down` to scroll down
  Use `chrome-devtools-axi eval <expr>` for JS expressions. For multi-statement code, pass a function: `eval "() => { ...; return result }"`
Evidence: Firstmate read of captured board result

Source: Firstmate read of captured board result

SESSION-ENDING MESSAGE: (none)

declared_items: 4
presented_items: 4
malformed_items: 0
complete: yes
lifecycle: feedback
session_ended: (unset)
annotation_count: 4
session_ending_message_count: 0

ANNOTATIONS
ANNOTATION 1 of 4
element_uid: 1
element_selector: tbody#bb-requests > tr:nth-of-type(1) > td:nth-of-type(2) > div
tag: choice
text:
| Approve DNS cutover -> Remove completely
ANNOTATION 2 of 4
element_uid: 2
element_selector: div#bb-knowledge > div:nth-of-type(2) > div:nth-of-type(2)
tag: choice
text:
| Old staging host -> Remove completely
ANNOTATION 3 of 4
element_uid: 3
element_selector: div#bb-projects > article:nth-of-type(1) > div:nth-of-type(1) > div
tag: choice
text:
| Invoices -> Leave on backlog
ANNOTATION 4 of 4
element_uid: 4
element_selector: div#bb-projects > article:nth-of-type(2) > div:nth-of-type(2) > div > div
tag: choice
text:
| Sam -> Remove completely
END ANNOTATIONS
END LAVISH RESULT (4 of 4)
- Outcome: 🔧 1 issue found → auto-fixed ✅ across 2 runs (10m13s)

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

⚠️ **Review** - 1 info
  • ⚠️ .agents/skills/bearings/assets/board-template.html:667 - After a section's choices are sent, each group gets is-queued, but its radios stay enabled, and pendingGroups() filters out queued groups for good. Repro: the captain marks a request 'Do now' and clicks Send, which queues now. They then switch the same row to 'Remove completely'. The row now shows Remove with a 'sent' chip, the count reads 'no choice marked', and the send button is disabled, so the answer firstmate reads is still now. The page shows a different choice from the one that was sent, and nothing signals the mismatch. The Captain's Call option cards (line 912-928) allow re-answering. Fix: on change, remove is-queued from that group (and from the bar) so it can be re-sent under the same queueKey. The alternative is to disable a group's inputs once it is queued. The same logic serves all three bars (requests, knowledge, projects/people) through armTriageBar at lines 1112-1116.
  • ℹ️ bin/fm-bearings-board.sh:217 - Knowledge and project ids are validated against the triage key shape (triage_id), but request ids (line 217) and crew ids (line 233) are only nonempty_string. A request whose id falls outside [A-Za-z0-9._-] or is longer than 113 characters (triage.request. + id > 128, e.g. a long legacy &lt;origin&gt;-decision-&lt;key&gt; id) builds fine but renders with no keep/now/remove group (template triageKey, line 154). The header documents this as intended ('a row whose id cannot route simply renders without choices'), so it is not a defect. It is still a quiet gap against the intent's 'every request row'. In practice task ids are short slugs.

🔧 Fix applied.
1 info still open:

  • ℹ️ bin/fm-bearings-board.sh:217 - Knowledge and project ids are validated against the triage key shape (triage_id), but request ids (line 217) and crew ids (line 233) are only nonempty_string. A request whose id falls outside [A-Za-z0-9._-] or is longer than 113 characters (triage.request. + id > 128, e.g. a long legacy &lt;origin&gt;-decision-&lt;key&gt; id) builds fine but renders with no keep/now/remove group (template triageKey, line 154). The header documents this as intended ('a row whose id cannot route simply renders without choices'), so it is not a defect. It is still a quiet gap against the intent's 'every request row'. In practice task ids are short slugs.
🔧 **Test** - 1 issue found → auto-fixed ✅
  • ⚠️ .agents/skills/bearings/assets/board-template.html - At 390px the requests table's narrow columns break labels in the middle of a word: the 'UNCONFIRMED' status badge wraps as 'UNCONFIRME / D' and 'Remove completely' wraps as 'Remove completel / y' on every request row (see mobile-390-full.png). The wording stays visible, but the break is visible and looks off on the phone layout the intent calls for. A likely fix is to stop forced word breaking in those cells (e.g. overflow-wrap: normal / white-space: nowrap on badges and choice labels) or to stack the choice column below the row at narrow widths.
  • Live validation: ✅ go - 8 of 9 scenarios driven live against the product
Scenario Result Live Evidence
Captain opens the built sample board on desktop and sees Waiting on you, Working, Waiting for another task, Completed and Unconfirmed as distinct colored badges with the wording visible, and unknown w… ✅ pass live desktop-1440-full.png; computed colors: waiting-on-you red-on-dark-red, working green-on-dark-green, waiting dark-on-amber, completed dark-on-green, unconfirmed/unknown grey
Captain opens the board at 390px: status colors and every keep/now/remove group are visible, with no horizontal overflow ✅ pass live mobile-390-full.png; scrollWidth == 390
Every request row, every knowledge entry, and every project and person shows a Leave on backlog / Do now / Remove completely group ✅ pass live Snapshot showed 12 radio groups (6 requests, 2 knowledge, 2 projects, 2 crew); screenshots
Captain marks rows in each section and clicks Send my choices: each marked row queues as one fm-bearings-answer.v1 choice through the Lavish answer path, and unmarked rows send nothing ✅ pass live queued-triage-prompts.txt shows 5 prompts with questions triage.request/knowledge/project/crew.<id> and selections backlog/remove
Adversarial: captain sends 'Do now' on a row and then changes it to 'Remove completely'; the row can be re-sent and the queue holds the new answer, not the stale one ✅ pass live After the change the SENT chip cleared and the bar showed '1 CHOICE MARKED TO SEND'; after re-send the queue held only triage.request.req-waiting-you=remove
Firstmate reads the captured answer: fm-procevent-lavish.sh read lists each '<title> -> <label>' and answers emits one keyed triage.* row per marked row ✅ pass live firstmate-read.txt (5 of 5, complete: yes); keyed-answers.txt
Keyed-answer intake (fm-captain-hold.sh answers) skips triage.* keys as the header documents ⏸️ untested no fm-captain-hold.sh answers needs the tasks-axi backlog backend; driving it from the lab risked reading or touching the real fleet backlog. To run it, provide a lab-isolated tasks-axi backend (e.g. thr…
Adversarial: payload with an invalid, duplicate or over-long knowledge/project triage id is rejected by build ✅ pass live validator-rejections.txt (4 rejections)
Older payload without knowledge[] or project ids still validates (contract extends with optional fields only) ✅ pass live optional-fields-omitted.txt: validation passed and the build reached the session step (Lavish stubbed to stop there)
  • bin/fm-bearings-board.sh build sample-payload.json in a lab FM_HOME (/tmp/fm-lab-triage.*): real lavish-axi session opened, bound, armed, listening live
  • Chrome (isolated chrome-devtools-axi session fm-lab-triage) on the Lavish session: clicked choices in the requests, knowledge and projects/people sections and clicked each section's Send my choices
  • Re-send scenario: marked 'Do now' on a request, sent it, switched the row to 'Remove completely', checked that the bar re-enabled, and sent again
  • Clicked Lavish 'Send to Agent'; the armed listener captured the result into the lab procevent-inbox
  • bin/fm-procevent-lavish.sh read &lt;result&gt; and bin/fm-procevent-lavish.sh answers &lt;result&gt; on the captured result
  • Full-page screenshots of the artifact at 390x3728 and 1440x2279; scrollWidth 390 at 390px (no horizontal overflow); read the computed badge colors per status
  • Validator rejections: knowledge id with spaces, duplicate knowledge id, project id 'x/y', knowledge id over the 128-char key limit
  • Payload without knowledge[] and without project ids passes validation (stubbed lavish-axi)
  • Teardown: fm-procevent-lavish.sh retire, lavish-axi end, lab dir removed, Chrome session stopped

🔧 Fix applied.
✅ Re-checked - no issues remain.

  • Live validation: ✅ go - 6 of 6 scenarios driven live against the product
Scenario Result Live Evidence
Captain sees the five statuses (waiting on you, working, waiting, completed, unconfirmed) in distinct colors with words visible on desktop 1440px ✅ pass live r2-desktop-1440-requests.png, r2-desktop-1440-initial.png
At 390px status badges and keep/now/remove labels read without mid-word breaks (request rows stack) ✅ pass live r2-mobile-390-full.png, r2-mobile-390-requests.png; JS scan found no single-word label wrapping
Every request row, each knowledge entry, and each project and person shows a three-choice group, and unknown status wording stays neutral ✅ pass live r2-mobile-390-full.png and the a11y snapshot showed a radio group on all 6 requests, 2 knowledge entries, 2 projects and 2 crew
Adversarial: captain sends Do now, then switches the same row to Remove; the bar re-enables and re-sending replaces the queued answer ✅ pass live r2-requeue-after-change.txt (single queued prompt, selection remove); r2-firstmate-read.txt contains no 'Do now'
Choices flow through the existing Lavish answer path as one fm-bearings-answer.v1 choice per marked row, with triage.<kind>.<id> questions, and firstmate reads them from the captured result ✅ pass live r2-desktop-1440-queued.png, r2-requeue-after-change.txt, r2-firstmate-read.txt (4 of 4 choice annotations)
An untouched row sends nothing and the send bar stays disabled with 'no choice marked' ✅ pass live The a11y snapshot showed the knowledge, project and request bars disabled until a row was marked, and disabled again after sending
  • FM_HOME=&lt;tmp&gt; bin/fm-bearings-board.sh build sample-payload.json (live Lavish session, bound+armed+listening)
  • chrome-devtools-axi (isolated session nm-test-r2): mark 'Approve DNS cutover' Do now -> Send -> switch to Remove completely -> verify bar re-enabled ('1 choice marked to send') -> Send -> queue holds single remove prompt
  • chrome-devtools-axi: mark knowledge 'Old staging host' remove, project 'Invoices' backlog, crew 'Sam' remove, send each bar, then 'Send to Agent'
  • bin/fm-procevent-lavish.sh read &lt;captured .result&gt;: firstmate's read of the captured answer
  • file:// render of built board at 390x3760 full page + 390 requests viewport + 1440 requests viewport screenshots; JS check for single-word labels wrapping (none)
  • bash tests/fm-bearings-board-render.test.sh
  • bash tests/fm-bearings-board.test.sh
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

…oices

Status badges now read at a glance: request, project and person statuses
map from their wording to danger/warn/online/info/done/neutral tones,
unknown wording stays neutral, and the words always stay visible so
color is a second signal, never the only one.

Every requests row, optional knowledge entry, project and person whose
routing id can carry a keyed answer gets one three-radio group (leave on
backlog / do now / remove completely). Each section's send bar queues
exactly one fm-bearings-answer.v1 choice annotation per marked row over
the board's existing single answer path, keyed
triage.<request|knowledge|project|crew>.<routing id>; rows whose ids
cannot route render without choices. The keyed-answer intake keeps
skipping non-task keys, so firstmate applies the choices from the
captured result read.

The payload contract gains only optional fields: knowledge[] entries and
optional project ids, both slug-gated so full triage keys stay within
the 128-character keyed-answer limit. The script header documents how
firstmate reads a triage answer; SKILL.md points composers at the new
optional fields.

Verified: 19 board build tests, 24 render tests, full procevent and
captain-hold suites, shellcheck/actionlint, and an in-browser pass at
390px and desktop width (distinct tone colors, no horizontal overflow,
queue path exercised in the real DOM).
@doitdigital0495

Copy link
Copy Markdown
Owner Author

Closed unmerged: the bearings board upgrade is being removed (see the revert PR).

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