feat(bin): add captain-facing Lavish ticket board - #3515
Open
TomatoIceberg wants to merge 6 commits into
Open
Conversation
added 5 commits
September 2, 2026 17:38
The captain creates tickets by typing plain text into a stable Lavish board; bin/fm-ticket-board-consume.sh turns a captured poll result into a Backlog ticket and rebuilds the board, following the same serve-then-bind-then-arm architecture as fm-bearings-board.sh. Wires the board into the process-event-sources wake-routing contract and the new ticket-board skill so firstmate recognizes and consumes a board wake instead of leaving it generically acknowledged.
…content The ticket-board consume script duplicated fm-procevent-lavish.sh's prompts[N] parsing contract instead of reusing it, and treated a zero-row parse the same whether the captain typed nothing or the parse silently failed. Add messages/has-content subcommands to fm-procevent-lavish.sh (the wire format's one owner) and have consume delegate to them, failing loudly when content is present but nothing was captured instead of reporting captured: 0. Also verified live against a real Send & End: lavish-axi exits 0 without reopening a session the captain ended from the browser, so the board's pickup loop silently stops accepting new tickets until an agent reopens it by hand. This is a pre-existing limitation shared verbatim with fm-bearings-board.sh, so it is documented rather than fixed here.
…er skill docs Consume now dedupes tickets by result-file+row so a replayed captured result never mints a duplicate, and publishes the store before rebuilding the board so a rebuild failure never drops a captain-typed ticket. Build verifies lavish-axi actually reports an opened session and reopens it when the captain ended it from the browser, instead of trusting exit code alone. Registers the ticket-board skill in docs/documentation-audiences.json to satisfy the repo's documentation audience check.
…y truncation stays character-safe
…-id RANDOM entropy
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains. Reviews (2): Last reviewed commit: "no-mistakes: apply CI fixes" | Re-trigger Greptile |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Intent
Add a captain-facing Lavish ticket board (bin/fm-ticket-board.sh) that lets the captain create/manage tickets via a Lavish board and a receiving script (bin/fm-ticket-board-consume.sh) that reads captain-typed board content and turns it into tickets. Apply tech-lead review feedback: (1) the receiving script must reuse the existing owner script/library for parsing captured Lavish board results instead of re-implementing that parser - reuse bin/fm-procevent-lavish.sh's cmd_messages/parsing rather than duplicating it. (2) the receiving script must distinguish 'the captain typed nothing' (genuinely empty) from 'a read/parse failure' (a genuine error) - use the repo's existing error-vs-empty distinguishing machinery so a ticket the captain actually typed can never silently vanish with no error surfaced anywhere; ticket replays must also be deduped so a replayed event never duplicates a ticket. (3) verify whether rebuilding the board (reopening its Lavish session) is refused when the captain explicitly ended the session from the browser (e.g. via Send & End) - if so, handle recovery (recover ended board sessions on rebuild) so the captain's natural type-then-close-tab usage does not silently break ticket pickup; if this is a pre-existing limitation shared with the bearings board rather than unique to this PR, document that rather than treating it as this PR's bug to fully solve. Keep the shared skill-doc registration (docs/documentation-audiences.json, .agents/skills/ticket-board) accurate.
What Changed
bin/fm-ticket-board.sh, a captain-facing Lavish ticket board for creating/managing tickets, andbin/fm-ticket-board-consume.sh, which reads captain-typed board content and turns it into tickets by reusingbin/fm-procevent-lavish.sh'scmd_messages/parsing rather than duplicating a parser.bin/fm-procevent-lavish.shto support the consume flow, distinguishing a genuinely empty captain message from a read/parse failure (surfacing the latter as an error) and decoding/encoding UTF-8 so title/body truncation stays character-safe..agents/skills/ticket-board/SKILL.mdandassets/board-template.html), updated.agents/skills/process-event-sources/SKILL.mdandAGENTS.md, and added the skill todocs/documentation-audiences.json.tests/fm-ticket-board.test.sh,tests/fm-ticket-board-consume.test.sh, and a UTF-8 truncation regression test.Risk Assessment
✅ Low: This fix round is a small, well-verified pair of changes: a one-line entropy fix confirmed correct via direct bash precedence testing, and a genuine behavioral regression test (not a source-content grep) that exercises the real parsing pipeline end-to-end and correctly proves the prior UTF-8 truncation fix.
Testing
The two targeted test files (fm-ticket-board.test.sh, fm-ticket-board-consume.test.sh) pass in full, and a manual end-to-end CLI run against a stubbed lavish-axi — plus a browser screenshot of the actual rendered board — confirms all three tech-lead-review fixes work as intended: captain-typed tickets persist correctly with UTF-8-safe truncation, a replayed capture is deduped rather than creating a duplicate ticket, and
buildrecovers from a captain-ended Lavish session via an automatic--reopenretry rather than silently failing or falsely claiming success. No issues found.Evidence: Targeted test suite output
Evidence: Manual end-to-end CLI transcript
Evidence: Rendered ticket board HTML (real template + injected store from the manual run)
Source: Rendered ticket board HTML (real template + injected store from the manual run)
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
🔧 **Rebase** - 1 issue found → auto-fixed ✅
bin/fm-procevent-lavish.sh- merge conflict rebasing onto origin/main🔧 Fix applied.
✅ Re-checked - no issues remain.
🔧 **Review** - 2 issues found → auto-fixed ✅
bin/fm-procevent-lavish.sh:656- The final commit (5b8fae4) correctly fixes the byte-vs-character truncation bug in cmd_messages by decoding the result file as UTF-8 and re-encoding STDOUT as UTF-8, so substr() now operates on characters and can no longer split a multi-byte sequence - verified by inspection:open my $fh, "<:encoding(UTF-8)", $pathdecodes on read andbinmode STDOUT, ":encoding(UTF-8)"re-encodes on print, so title/body caps land on character boundaries. However, no test was added exercising non-ASCII text near the 200/4000 cap (e.g. repeated multi-byte characters straddling the cutoff), which is exactly the gap the original finding called out ("no test ... exercises non-ASCII or near-cap-length text"). The fix itself is correct but remains unverified by an automated regression test.bin/fm-ticket-board-consume.sh:60- new_id() builds the ticket id suffix with$((RANDOM % 65536))formatted as%04x, but bash's $RANDOM only ever yields 0-32767, so the modulo is a no-op and the suffix can never fall in 0x8000-0xffff - the format string implies a full 16-bit space that is actually halved. Not a functional bug since a generated collision is refused loudly (jq check followed byfail "generated a colliding ticket id"), just weaker-than-implied entropy.🔧 Fix: Add UTF-8 truncation regression test; fix ticket-id RANDOM entropy
✅ Re-checked - no issues remain.
✅ **Test** - passed
✅ No issues found.
./bin/fm-test-run.sh tests/fm-ticket-board-consume.test.sh tests/fm-ticket-board.test.sh— 20/20 assertions pass, including the multi-byte UTF-8 character-boundary truncation test, the replayed-result dedup test, and the ended-session-recovery-on-rebuild testManual CLI walkthrough:fm-ticket-board.sh init->build-> wrote a captured Lavish result with a freeform message row ->fm-ticket-board-consume.sh <result>created ticket tkt-20260902T094822-0be2 and rebuilt the boardManual CLI walkthrough: replayed the identical captured result throughfm-ticket-board-consume.shand confirmedskipped: 1 already-recorded row(s) (replayed result)with the store staying at exactly 1 ticket (no duplicate)Manual CLI walkthrough: set a stub lavish-axi to reportstatus: user-endedon the plain call, ranfm-ticket-board.sh build, and confirmed via a call log that the script retried once with--reopenand then reportedserved:successfully instead of falsely claiming success or failing loudlyOpened the actual rendered board HTML (template + injected store from the manual run) in Chrome and took a screenshot confirming the created ticket renders correctly in the Backlog column✅ **Document** - passed
✅ No issues found.
🔧 **Lint** - 1 issue found → auto-fixed ✅
🔧 Fix: No lint fixes needed; installed actionlint for sandbox PATH only
✅ Re-checked - no issues remain.
✅ **Push** - passed
✅ No issues found.