Skip to content

feat: add read-only supervision command - #76

Closed
JTInventory wants to merge 1 commit into
kunchenguid:mainfrom
JTInventory:fm/fm-supervise-impl-0625
Closed

JTInventory wants to merge 1 commit into
kunchenguid:mainfrom
JTInventory:fm/fm-supervise-impl-0625

Conversation

@JTInventory

Copy link
Copy Markdown

Summary

  • Adds bin/fm-supervise.sh and bin/fm-supervision-model.sh for read-only Firstmate supervision.
  • Emits concise text output plus a stable firstmate.supervision.v1 JSON model for future Radar consumption.
  • Adds focused shell tests and documents that Radar migration is deferred and should later consume 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.md and README.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.sh
  • bash tests/fm-supervision-model.test.sh
  • for test_script in tests/*.test.sh; do "$test_script"; done
  • git diff --check
  • bin/fm-supervise.sh --json --no-default-reminders | python3 -m json.tool
  • no-mistakes axi run --yes --intent ... passed review, test, document, and lint; push failed only because origin is not writable from this token, so this branch was pushed to fork.

ce-test-browser was skipped because this is a CLI/shell change with no browser-bearing surface.

@JTInventory

Copy link
Copy Markdown
Author

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.

@JTInventory
JTInventory force-pushed the fm/fm-supervise-impl-0625 branch from a2bd2db to 0af5470 Compare June 25, 2026 17:50
@JTInventory

Copy link
Copy Markdown
Author

Closing because this work is maintained in the JTInventory fork. We are not expecting upstream review or merge for this branch.

@JTInventory JTInventory closed this Jul 1, 2026
LoneExile added a commit to LoneExile/firstmate that referenced this pull request Jul 21, 2026
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).
sbracewell64 referenced this pull request in sbracewell64/firstmate Aug 10, 2026
…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.
sbracewell64 referenced this pull request in sbracewell64/firstmate Aug 10, 2026
…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).
sbracewell64 referenced this pull request in sbracewell64/firstmate Aug 10, 2026
…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).
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