feat: add read-only supervision command - #76
JTInventory wants to merge 1 commit into
Conversation
|
Maintainer update from JTInventory, 2026-06-25:\n\nThis PR is still wanted. It is the read-only fm-supervise foundation for turning Firstmate/Radar state into a concise decision model. Downstream workflow-structure work is waiting on either merge or sidecar adoption of this command.\n\nRecommended handling:\n- Keep the read-only boundary intact: no teardown, merge, restart, backlog mutation, treehouse mutation, or service changes.\n- If GitHub reports the branch stale/unknown, I can refresh it against current main after the active branch-metadata hardening PR completes.\n- Future Radar work should consume fm-supervise --json instead of duplicating decision rules.\n- This PR should stay separate from PR #68; #68 is about optional no-mistakes gate mode, while this is the supervision decision model.\n\nOwner can review the behavior model now; code refresh can be handled by us if needed before merge. |
a2bd2db to
0af5470
Compare
0af5470 to
746cae0
Compare
6e9d89a to
746cae0
Compare
|
Closing because this work is maintained in the JTInventory fork. We are not expecting upstream review or merge for this branch. |
kunchenguid#758) Verb-aware captain-relevance + head-bound no-mistakes run attribution so a nonterminal working: line never triggers false escalation or false suppression, and a historical run on a reused/rewritten branch is not mis-attributed. - fm-classify-lib.sh: new status_is_terminal_verb; status_is_captain_relevant is verb-aware (working/resolved/captain-held/paused never match free-text prose like "working: rebased onto merged kunchenguid#76"); bare legacy free-text lines still match. - fm-supervise-daemon.sh: nonterminal progress verbs take the transient-stale path (never terminal); seen-status dedupe no longer clears possible-wedge aging for them (classify_stale + handle_wake). - fm-crew-state.sh: nm_run_head_matches_worktree / nm_coarse_head_matches_worktree bind attribution to code identity (equal SHA or worktree HEAD ancestor of run head = match; diverged/rewritten = reject; empty head = fail-safe reject). - fm-brief.sh: ship-crew brief states a mid-task working: line is nonterminal. - fm-watch-arm.sh: cycle_begin before report_attached at the two reorder sites that EXIST in our fork (lock-replaced re-attach + healthy-attach). GRAFTED ADDITIVELY: the third upstream site is inside the wait_for_healthy_successor block our kunchenguid#693 divergence removed (omp extension owns successor continuity); CONFIRM_MAX slow-start guard untouched. - docs/skills/AGENTS wording updated to match. Hand-reconciled from upstream ea3ac2e (watch-arm conflict kept ours).
…ocking_on (CFVC-06) The worker-to-supervisor boundary's control facts were prose. bin/fm-classify-lib.sh classified wakes by grepping a crew's own sentence against `PR ready|checks green|ready in branch|merged`, so "working: rebased onto merged #76" escalated as a terminal event and had to be defended by a rule excluding nonterminal verbs - a guard around a guess. Each event is now a typed `fm-status-event.v1` envelope carrying verb, key, phase, repeatable evidence references and one bounded summary. New bin/fm-status-event-lib.sh owns the format; bin/fm-classify-lib.sh remains the single owner of what a verb MEANS and reads that field instead of a regex. The default classification path runs no regex at all, so the free-text arm and the collision class it created are gone rather than caught after the fact. What a task is waiting on is derived, never declared. status_event_blocking_on combines the verb, the keyed open-decision fold and bin/fm-crew-state.sh's typed verdict, so proof that a run is advancing overrules a stale blocked claim, and a decision left open earlier is not masked by a later unrelated event. A worker cannot write the field: the envelope's field set is closed, and an event carrying blocking_on= is refused whole and surfaced as malformed rather than silently stripped and half believed. Compatibility is the three shared parsers. Every consumer in bin/ already reads status lines through status_line_verb, status_line_note and the decision-key parser, so teaching those the envelope taught the fleet at once and a log part way through the migration reads correctly. Prose remains the human note. bin/fm-brief.sh teaches the typed form to every reporting scaffold, including the rule that blocking_on is derived. bin/fm-fleet-snapshot.sh projects the envelope and the derived field, reusing the crew read it already paid for. bin/fm-wake-lib.sh renders a typed event to its prose projection before the drain annotation's byte budget, so evidence references cannot truncate away the summary. Retired: FM_CLASSIFY_CAPTAIN_RE_DEFAULT's free-text arm, and the nonterminal verb rule as a defence against it. That rule survives as declared policy and still applies on the opt-in FM_CAPTAIN_RE path, where prose matching is what the home asked for. A bare verbless line such as "merged" is no longer captain-relevant, and tests/fm-daemon.test.sh records that change of behavior. Tests assert the retirement with a negative control that requires each collision line to match the retired arm as a bare regex before requiring it not to classify; that a typed event classifies with the default pattern blanked, so no regex fallback remains; that a worker-written blocking_on is refused with its own reason and cannot reach the answer even through summary prose; that the derived answer changes with crew state while the status line is held constant; that both derivation helpers are total over the declared crew-verdict vocabulary; and that every reporting scaffold teaches the typed form.
…ocking_on (CFVC-06) (#71) * feat(bin): carry the status boundary as a typed event with derived blocking_on (CFVC-06) The worker-to-supervisor boundary's control facts were prose. bin/fm-classify-lib.sh classified wakes by grepping a crew's own sentence against `PR ready|checks green|ready in branch|merged`, so "working: rebased onto merged #76" escalated as a terminal event and had to be defended by a rule excluding nonterminal verbs - a guard around a guess. Each event is now a typed `fm-status-event.v1` envelope carrying verb, key, phase, repeatable evidence references and one bounded summary. New bin/fm-status-event-lib.sh owns the format; bin/fm-classify-lib.sh remains the single owner of what a verb MEANS and reads that field instead of a regex. The default classification path runs no regex at all, so the free-text arm and the collision class it created are gone rather than caught after the fact. What a task is waiting on is derived, never declared. status_event_blocking_on combines the verb, the keyed open-decision fold and bin/fm-crew-state.sh's typed verdict, so proof that a run is advancing overrules a stale blocked claim, and a decision left open earlier is not masked by a later unrelated event. A worker cannot write the field: the envelope's field set is closed, and an event carrying blocking_on= is refused whole and surfaced as malformed rather than silently stripped and half believed. Compatibility is the three shared parsers. Every consumer in bin/ already reads status lines through status_line_verb, status_line_note and the decision-key parser, so teaching those the envelope taught the fleet at once and a log part way through the migration reads correctly. Prose remains the human note. bin/fm-brief.sh teaches the typed form to every reporting scaffold, including the rule that blocking_on is derived. bin/fm-fleet-snapshot.sh projects the envelope and the derived field, reusing the crew read it already paid for. bin/fm-wake-lib.sh renders a typed event to its prose projection before the drain annotation's byte budget, so evidence references cannot truncate away the summary. Retired: FM_CLASSIFY_CAPTAIN_RE_DEFAULT's free-text arm, and the nonterminal verb rule as a defence against it. That rule survives as declared policy and still applies on the opt-in FM_CAPTAIN_RE path, where prose matching is what the home asked for. A bare verbless line such as "merged" is no longer captain-relevant, and tests/fm-daemon.test.sh records that change of behavior. Tests assert the retirement with a negative control that requires each collision line to match the retired arm as a bare regex before requiring it not to classify; that a typed event classifies with the default pattern blanked, so no regex fallback remains; that a worker-written blocking_on is refused with its own reason and cannot reach the answer even through summary prose; that the derived answer changes with crew state while the status line is held constant; that both derivation helpers are total over the declared crew-verdict vocabulary; and that every reporting scaffold teaches the typed form. * no-mistakes(review): copy status-event lib into fixtures, project envelope key, add conformance test * no-mistakes(document): align classification docs and comments with typed status events * fix(tests): carry the status-event library into every sandbox bin fixture bin/fm-wake-lib.sh and bin/fm-classify-lib.sh source the sibling bin/fm-status-event-lib.sh eagerly, so every fixture that builds a synthetic bin/ from a hand-maintained manifest must carry that sibling too. Three did not, and under `set -eu` the missing source aborts the script under test: - tests/fm-gotmp.test.sh symlinks the libraries into a fake FM_ROOT (two sites). - tests/fm-backend.test.sh copies OLD_BIN_UNCHANGED_SIBLINGS into the synthetic pre-refactor tree, whose own header already records that a sourced sibling must be a real reachable file there or the source aborts. - tests/fm-remote-backlog-handoff.test.sh copies a fixed list into a fake remote root. The eager source is deliberately left hard rather than made conditional. The classifier's dependency is a correctness contract: a silently absent parser would let a sandbox read a typed event as prose, which is the failure this increment removes. Softening it to keep a manifest short would trade a loud fixture break for a quiet wrong answer. Red before and green after on the same tree: each of the three suites failed with the missing-sibling abort and now passes (3, 28 and 10 assertions).
…ocking_on (CFVC-06) (#71) * feat(bin): carry the status boundary as a typed event with derived blocking_on (CFVC-06) The worker-to-supervisor boundary's control facts were prose. bin/fm-classify-lib.sh classified wakes by grepping a crew's own sentence against `PR ready|checks green|ready in branch|merged`, so "working: rebased onto merged #76" escalated as a terminal event and had to be defended by a rule excluding nonterminal verbs - a guard around a guess. Each event is now a typed `fm-status-event.v1` envelope carrying verb, key, phase, repeatable evidence references and one bounded summary. New bin/fm-status-event-lib.sh owns the format; bin/fm-classify-lib.sh remains the single owner of what a verb MEANS and reads that field instead of a regex. The default classification path runs no regex at all, so the free-text arm and the collision class it created are gone rather than caught after the fact. What a task is waiting on is derived, never declared. status_event_blocking_on combines the verb, the keyed open-decision fold and bin/fm-crew-state.sh's typed verdict, so proof that a run is advancing overrules a stale blocked claim, and a decision left open earlier is not masked by a later unrelated event. A worker cannot write the field: the envelope's field set is closed, and an event carrying blocking_on= is refused whole and surfaced as malformed rather than silently stripped and half believed. Compatibility is the three shared parsers. Every consumer in bin/ already reads status lines through status_line_verb, status_line_note and the decision-key parser, so teaching those the envelope taught the fleet at once and a log part way through the migration reads correctly. Prose remains the human note. bin/fm-brief.sh teaches the typed form to every reporting scaffold, including the rule that blocking_on is derived. bin/fm-fleet-snapshot.sh projects the envelope and the derived field, reusing the crew read it already paid for. bin/fm-wake-lib.sh renders a typed event to its prose projection before the drain annotation's byte budget, so evidence references cannot truncate away the summary. Retired: FM_CLASSIFY_CAPTAIN_RE_DEFAULT's free-text arm, and the nonterminal verb rule as a defence against it. That rule survives as declared policy and still applies on the opt-in FM_CAPTAIN_RE path, where prose matching is what the home asked for. A bare verbless line such as "merged" is no longer captain-relevant, and tests/fm-daemon.test.sh records that change of behavior. Tests assert the retirement with a negative control that requires each collision line to match the retired arm as a bare regex before requiring it not to classify; that a typed event classifies with the default pattern blanked, so no regex fallback remains; that a worker-written blocking_on is refused with its own reason and cannot reach the answer even through summary prose; that the derived answer changes with crew state while the status line is held constant; that both derivation helpers are total over the declared crew-verdict vocabulary; and that every reporting scaffold teaches the typed form. * no-mistakes(review): copy status-event lib into fixtures, project envelope key, add conformance test * no-mistakes(document): align classification docs and comments with typed status events * fix(tests): carry the status-event library into every sandbox bin fixture bin/fm-wake-lib.sh and bin/fm-classify-lib.sh source the sibling bin/fm-status-event-lib.sh eagerly, so every fixture that builds a synthetic bin/ from a hand-maintained manifest must carry that sibling too. Three did not, and under `set -eu` the missing source aborts the script under test: - tests/fm-gotmp.test.sh symlinks the libraries into a fake FM_ROOT (two sites). - tests/fm-backend.test.sh copies OLD_BIN_UNCHANGED_SIBLINGS into the synthetic pre-refactor tree, whose own header already records that a sourced sibling must be a real reachable file there or the source aborts. - tests/fm-remote-backlog-handoff.test.sh copies a fixed list into a fake remote root. The eager source is deliberately left hard rather than made conditional. The classifier's dependency is a correctness contract: a silently absent parser would let a sandbox read a typed event as prose, which is the failure this increment removes. Softening it to keep a manifest short would trade a loud fixture break for a quiet wrong answer. Red before and green after on the same tree: each of the three suites failed with the missing-sibling abort and now passes (3, 28 and 10 assertions).
Summary
bin/fm-supervise.shandbin/fm-supervision-model.shfor read-only Firstmate supervision.firstmate.supervision.v1JSON model for future Radar consumption.fm-supervise --json.Read-only boundaries
The command reads existing state/status/meta, tmux liveness, treehouse status, git worktree/status data, and GitHub through
gh-axi api GET. It does not mutate state, backlog, tmux, git, treehouse, services, teardown, merge, or GitHub.PR #68 compatibility
Firstmate PR #68 is still open. This PR overlaps it only in
AGENTS.mdandREADME.md; there is no material overlap in the new supervision scripts or tests.Validation
bash -n bin/fm-supervise.sh bin/fm-supervision-model.sh tests/fm-supervision-model.test.shbash tests/fm-supervision-model.test.shfor test_script in tests/*.test.sh; do "$test_script"; donegit diff --checkbin/fm-supervise.sh --json --no-default-reminders | python3 -m json.toolno-mistakes axi run --yes --intent ...passed review, test, document, and lint; push failed only becauseoriginis not writable from this token, so this branch was pushed tofork.ce-test-browserwas skipped because this is a CLI/shell change with no browser-bearing surface.