fix: prevent silent keyed decision closure failures - #1880
Open
mremond wants to merge 5 commits into
Open
Conversation
A keyed decision closure was classified in silence whenever its status line drifted off the documented "<verb> [key=<slug>]: <note>" grammar, because both parsers read the whole raw prefix before the colon. Two forms failed, both silently, on a safety surface: - An extra token before the key made the verb unreadable, so "resolved corr=<id> [key=k]: ..." parsed its verb as "resolved corr=<id>" and never closed k. This is the exact shape fm-brief.sh tells a secondmate to write for a correlated reply, and the shape fm-secondmate-report.sh emits as "<verb> [corr=<id>]:". - A key token written after the colon was invisible, so the OPENING line was filed under "default" while a later, perfectly written closure closed a key nothing had opened. "default" then stayed open indefinitely, and the fallback printed the key token inside the note text, visually confirming a match that did not exist. An alert that cries wolf on every wake stops being read, which is the failure this fold exists to prevent. Introduce one grammar owner, _fm_status_parse, and have status_line_verb, status_line_note, _fm_decision_key, the decision fold, and the activity fold all read its verdict instead of re-deriving the line's shape. It classifies each line as strict, misplaced-key, malformed, or freeform, and never guesses: - A verb followed only by structured "<name>=<value>" or "[<name>=<value>]" tokens is the grammar, whatever the tokens are. - A key token after the colon is honored rather than filed under "default", but only when it LEADS the note; a key mentioned inside prose stays prose. - Prose that merely begins with a lifecycle verb is REFUSED, never applied. Taking the first word alone would fix both forms above while letting "resolved the conflict by hand: ..." close a key it never claimed to close, and a wrongly closed decision disappears with no review at all. Two conflicting key tokens are refused for the same reason. - "done [key=x]:" still does not close x: a terminal line never cancels an open captain decision. Recover the signal that was previously lost entirely. The fold now reports every refused line, every honored misplaced key, and every closure for a key that was not open on an opt-in side channel, and fm-wake-drain.sh prints them as a STATUS LINE ANOMALIES section beside OPEN DECISIONS from the same single pass. An unmatched close is the direct, same-day symptom of an opening line misfiled under another key. Callers that only want the open set wire no sink and are unaffected. status_is_captain_relevant's free-text fallback is unchanged: lines the grammar refuses keep their previous whole-prefix verb read, verified by differential over prose, legacy bare lines, and FM_CAPTAIN_RE overrides. Token-carrying lines do change, and must: "blocked [corr=<id>]: ...", which fm-secondmate-report.sh emits for a secondmate escalation, was not captain-relevant at all before this, "done [corr=<id>]" was equally invisible, and "paused [corr=<id>]" did not register as a declared wait. Also correct the three places in the brief scaffold that told a worker to append "resolved:" and then add the key, which is how the misplaced token gets written in the first place.
mremond
force-pushed
the
fm/status-key-closure-swallowed
branch
from
August 7, 2026 08:59
0b5a339 to
deec05c
Compare
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.
A malformed keyed status line was classified silently, so decisions could stay open forever — or close when they should not.
The bug
bin/fm-classify-lib.shderives both the verb and the key from everything before the first colon, rather than from the documented grammar<verb> [key=<slug>]: <note>. Two shapes fail, in opposite directions:The second is the nastier one: the closing line is well-formed, so re-reading your own work reveals nothing — the defect sits in a line written earlier by someone else. And the fold prints the key token from inside the note, visually confirming a match that does not exist.
Both were observed on real tasks hours apart. Nine decisions were affected.
Why it matters more than the noise: the author believes the decision is closed while the fold keeps shouting. An alert that cries wolf on every wake gets skimmed — which is exactly when the one genuinely open decision goes unnoticed. That is the failure this fold exists to prevent.
What changed
bin/fm-brief.shtaught the failing shape in three generated briefs (“appendresolved:… then add the key”). Corrected — the token is now shown in its required position.Deliberately unchanged
done [key=x]:still does not close a key. A terminal line must never cancel an open decision.working [key=p]:→resolved [key=p]:) is not an anomaly: the unmatched-close check consults both the decision fold and the activity fold before reporting.status_is_captain_relevant()shares this parser; its free-text fallback is unchanged, and covered.The obvious fix — take the first word — was rejected: prose beginning
resolved the conflict by hand:would then close a key it never claimed. A wrongly closed decision disappears with no review, which is worse than the noise.Verification
Both directions are tested: a legitimate token-bearing closure must close; prose beginning with a verb word must not. Plus post-colon keys, unmatched closures, the
donenon-regression, and cursor replay bounded to snapshotted bytes.Suites:
fm-wake-drain-open-decisions,fm-wake-drain-open-decisions-cursor,fm-watch-triage,fm-brief. Drain output captured twice over crafted incident logs to confirm each anomaly is reported exactly once.Pipeline
Updates from git push no-mistakes
intent ✅ · rebase ✅ · review ✅ (3 findings, auto-fixed) · test ✅ · document ✅ · lint ✅ · push ✅