Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
1c57994
test: reproduce buried status declarations in shared readers
tiago-peixoto Sep 4, 2026
230e019
fix: share status event reads and preserve open blockers
tiago-peixoto Sep 4, 2026
a82ac54
fix: retain terminal scout and ship status declarations
tiago-peixoto Sep 5, 2026
68e4da9
no-mistakes(review): Fix status chronology, legacy completions, and r…
tiago-peixoto Sep 5, 2026
a9deb06
no-mistakes(review): Share terminal decision reconciliation across fl…
tiago-peixoto Sep 5, 2026
1cf1c27
no-mistakes(review): Unify terminal supersession across cached folds …
tiago-peixoto Sep 5, 2026
9e8d2f3
no-mistakes(review): Filter per-key status history while preserving t…
tiago-peixoto Sep 5, 2026
3a3935d
no-mistakes(test): Preserve parent lock ownership in Bash 3.2 subshells
tiago-peixoto Sep 5, 2026
69c4d38
no-mistakes(review): Anchor legacy status tokens so prose cannot hide…
tiago-peixoto Sep 5, 2026
411425c
no-mistakes(document): Document latest-event status read and kind-sco…
tiago-peixoto Sep 5, 2026
99f6991
no-mistakes(lint): Quote literal done in test for-lists for SC1010
tiago-peixoto Sep 5, 2026
0bf59a7
ci: expect 19 snapshot/fleet-view tests
tiago-peixoto Sep 5, 2026
e3b3507
no-mistakes(review): Restore multiline child outcome reporting
tiago-peixoto Sep 5, 2026
dcad113
no-mistakes(review): Select ledger terminal events through bounded sh…
tiago-peixoto Sep 6, 2026
a008835
no-mistakes(review): Report newest open decision instead of preferrin…
tiago-peixoto Sep 6, 2026
d9afee8
no-mistakes(review): Require colon before ship/scout terminal superse…
tiago-peixoto Sep 6, 2026
1fc15c3
no-mistakes(review): Gate socket-down override on latest event; drop …
tiago-peixoto Sep 6, 2026
888d9ea
no-mistakes(review): Fold only colon-bearing or keyed lines as decisi…
tiago-peixoto Sep 6, 2026
8ea86e3
no-mistakes(review): Pre-select candidate lines before per-key closin…
tiago-peixoto Sep 6, 2026
0f60b31
no-mistakes(test): Update fleet-view expectations to newest-open-deci…
tiago-peixoto Sep 6, 2026
47573e6
no-mistakes(document): Align status-read docs with fold-resolved crew…
tiago-peixoto Sep 6, 2026
a2c282e
no-mistakes(document): Correct status-reader contracts in classify-li…
tiago-peixoto Sep 6, 2026
2c510ab
no-mistakes(ci): Greptile P1 (bin/fm-crew-state.sh:729, "Stale socket…
tiago-peixoto Sep 6, 2026
39c74c5
test: fold terminal-cleanup snapshot coverage into the completed-scou…
tiago-peixoto Sep 17, 2026
64e0762
no-mistakes(document): Clarify socket-down override expiry in archite…
tiago-peixoto Sep 17, 2026
ce424ad
ci: retrigger flaky contribution check
tiago-peixoto Sep 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .agents/skills/fmx-respond/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ Treat `state/x-inbox/` as the source of truth and process **every** file you fin

1. **Gather live fleet state once.** Compose answers from what this instance genuinely knows right now:
- `data/backlog.md` "## In flight" - the work currently moving.
- `state/*.status` - the latest line of each in-flight job, for fresh phase detail.
- `state/*.status` - the latest status event of each in-flight job, for fresh phase detail.
- `data/projects.md` - the active projects, for naming what you work on in plain terms.
Translate every internal item into an outcome. Example: a backlog line `fix-login-k3 - repair OAuth redirect (repo: yourapp)` becomes "patching a sign-in redirect bug on one of the apps" - no id, no repo name unless it is already public.
2. **Drain every pending mention.** For each `state/x-inbox/*.json` file:
Expand Down
26 changes: 4 additions & 22 deletions bin/fm-captain-hold.sh
Original file line number Diff line number Diff line change
Expand Up @@ -442,23 +442,6 @@ meta_value() { # <meta> <key>
grep "^$2=" "$1" 2>/dev/null | tail -1 | cut -d= -f2- || true
}

origin_open_decisions() { # <origin-id>
local origin=$1 meta="$STATE/$1.meta" status_file="$STATE/$1.status" open kind last verb
open=$(status_open_decisions "$status_file")
[ -n "$open" ] || return 0
[ -f "$meta" ] || { printf '%s' "$open"; return 0; }
kind=$(meta_value "$meta" kind)
[ -n "$kind" ] || kind=ship
if [ "$kind" != secondmate ]; then
last=$(last_status_line "$status_file")
verb=$(status_line_verb "$last")
case "$verb" in
done|failed) return 0 ;;
esac
fi
printf '%s' "$open"
}

# A resolution record written by this script or by the retired
# fm-decision-hold.sh. Both carry the same leader-then-captain-decision shape.
body_has_resolution_record() { # <task-body>
Expand Down Expand Up @@ -1629,7 +1612,7 @@ reconcile_note() {
}

command_complete() {
local origin=${1:-} meta previous='' supplied='' keys='' entry key status_file open raw_open has_meta=0 transfer_rc resolved
local origin=${1:-} meta previous='' supplied='' keys='' entry key status_file open has_meta=0 transfer_rc resolved
local resolved_how attested_by_prefix=''
[ "$#" -ge 2 ] || { usage >&2; exit 2; }
validate_slug origin-id "$origin"
Expand Down Expand Up @@ -1673,8 +1656,7 @@ EOF
fi

status_file="$STATE/$origin.status"
raw_open=$(status_open_decisions "$status_file")
open=$(origin_open_decisions "$origin")
open=$(status_open_decisions "$status_file")
if [ -n "$open" ] && [ -z "$keys" ]; then
fail "origin $origin still has open captain decisions in its status stream; hold a captain task for what remains, or answer them, before attesting --none"
fi
Expand All @@ -1700,7 +1682,7 @@ EOF
"captain-held [key=$key]: tracked by $keys" || transfer_rc=$?
[ "$transfer_rc" -ne 2 ] || fail "cannot append the captain-held transfer for $origin/$key"
done <<EOF
$raw_open
$open
EOF
fi
fi
Expand All @@ -1726,7 +1708,7 @@ command_verify() {
$(printf '%s\n' "$keys" | tr ',' '\n')
EOF
fi
open=$(origin_open_decisions "$origin")
open=$(status_open_decisions "$STATE/$origin.status")
while IFS=$'\t' read -r key _verb _summary; do
[ -n "$key" ] || continue
fail "open captain decision $origin/$key is not transferred to the captain-held inventory; re-run complete"
Expand Down
293 changes: 214 additions & 79 deletions bin/fm-classify-lib.sh

Large diffs are not rendered by default.

38 changes: 22 additions & 16 deletions bin/fm-crew-state.sh
Original file line number Diff line number Diff line change
Expand Up @@ -72,18 +72,22 @@
# FAILED record whose daemon an explicit probe proves down reads unknown,
# never failed: an instrument failure must not read as work failure
# (nm_daemon_probe_down).
# 3. Reconcile the status log: if its last line says needs-decision/blocked but
# 3. Reconcile the status log through fm-classify-lib.sh's status_current_line:
# open decisions survive unrelated events and continuation prose cannot
# hide a declaration. Ship/scout terminal declarations supersede stale log
# decisions. If it says needs-decision/blocked but
# the run-step shows the run moved on, the log is deterministically stale and
# is flagged superseded. A genuinely parked run plus a needs-decision log
# agree, and are reported as parked. A `blocked:` line that reports a
# refused or missing daemon socket remains blocked even if an attributed
# run record is stale or terminal. Other daemon, timeout, or unreachability
# run record is stale or terminal, for as long as that blocker is still the
# log's latest event. Other daemon, timeout, or unreachability
# claims are superseded BECAUSE THE RUN IS ALIVE when the run is
# running/fixing with recent reported activity: a killed or timed-out drive
# call is not daemon death, so that claim is answered by steering the crew
# to reattach, not by escalating.
# 4. No run for this crew (pre-validation, or kind=scout): fall back to the
# recorded backend's pane busy state, then the status log's last line only
# recorded backend's pane busy state, then the resolved status declaration
# when its verb maps to a recognized run-state. Decision-only events such as
# `resolved` never become current state or detail.
# 5. Missing meta or torn-down worktree: report unknown Β· none. If no run is
Expand Down Expand Up @@ -170,11 +174,6 @@ fi

# --- status log ------------------------------------------------------------

# Last non-empty status line; fm-classify-lib.sh owns leading-verb normalization.
log_last_line() {
[ -f "$LOG" ] || return 1
grep -v '^[[:space:]]*$' "$LOG" 2>/dev/null | tail -1
}
# Map a status-log verb onto a canonical state for the fallback path. `paused` is
# the deliberate-external-wait verb (fm-classify-lib.sh's FM_CLASSIFY_PAUSED_VERB):
# a crew with no active run and an idle pane that declared a known external wait
Expand All @@ -195,7 +194,7 @@ map_log_state() { # <line>
esac
}

LOG_LINE=$(log_last_line || true)
LOG_LINE=$(status_current_line "$LOG" "$KIND")
LOG_VERB=$(status_line_verb "$LOG_LINE")

# --- remote secondmate: the true source is the remote endpoint ---------------
Expand Down Expand Up @@ -860,15 +859,22 @@ if [ "$HAVE_RUN" = 1 ]; then
#
# A refused or missing daemon socket is positive daemon-down evidence and
# outranks any attributed run record, including a terminal one left behind
# after the daemon stopped. Other blocked claims caused by a timed-out drive
# call are contradicted only when the run reports recent
# activity; the answer is then to steer the crew to reattach without touching
# the shared daemon.
# after the daemon stopped, but only while that blocker is itself the log's
# LATEST recognized event: a later event of any kind means the crew has moved
# on, and the attributed run is the better witness again. The evidence is
# therefore read off that latest event, not off the reconciled declaration -
# the two are the same line while the blocker is current, and when they differ
# the open blocker is by definition no longer the log's tip. Other blocked
# claims caused by a timed-out drive call are contradicted only when the run
# reports recent activity; the answer is then to steer the crew to reattach
# without touching the shared daemon.
case "$LOG_VERB" in
needs-decision|blocked)
LOG_LATEST=$(last_status_line "$LOG")
if [ "$LOG_VERB" = blocked ] \
&& log_reports_daemon_socket_down "$LOG_LINE"; then
emit blocked status-log "$(status_line_note "$LOG_LINE")${SEP}daemon socket down despite attributed run record"
&& [ "$(status_line_verb "$LOG_LATEST")" = blocked ] \
&& log_reports_daemon_socket_down "$LOG_LATEST"; then
emit blocked status-log "$(status_line_note "$LOG_LATEST")${SEP}daemon socket down despite attributed run record"
fi
if [ "$RUN_STATE" != parked ]; then
if [ "$RUN_STATE" = working ]; then
Expand Down Expand Up @@ -962,7 +968,7 @@ if [ "$KIND" != secondmate ]; then
esac
fi

# Fall back to the status log's last line, but ONLY when its verb maps to a real
# Fall back to the resolved status declaration, but ONLY when its verb maps to a real
# run-state. A decision-closing event - resolved: (fm-classify-lib.sh's
# FM_CLASSIFY_RESOLVE_VERB), and any future decision-only sibling - is NOT a state:
# it exists solely to CLOSE a keyed decision in the durable fold, so a trailing
Expand Down
2 changes: 1 addition & 1 deletion bin/fm-fleet-snapshot.sh
Original file line number Diff line number Diff line change
Expand Up @@ -800,7 +800,7 @@ task_json_lines() {
# never clear another concern's keyed decision. A parked/blocked state, or a
# non-authoritative status-log/none read on a still-live task, keeps the fold's
# open decision surfacing.
open_decisions_tsv=$(status_open_decisions "$status_log")
open_decisions_tsv=$(status_open_decisions "$status_log" "$kind")
if [ "$kind" != secondmate ] && \
{ { { [ "$current_source" = run-step ] || [ "$current_source" = pane ]; } \
&& [ "$current_state" != parked ] && [ "$current_state" != blocked ]; } \
Expand Down
34 changes: 19 additions & 15 deletions bin/fm-inactive-reconcile.sh
Original file line number Diff line number Diff line change
Expand Up @@ -351,20 +351,23 @@ notice_parent_report_failed() { # <record> <fingerprint> <payload>
queue_notice_once "$record" "inactive-reconcile:$fingerprint" "$payload" || true
}

# The whole terminal line a child's ledger ends in, or non-zero when the ledger
# is absent, unusable, still being appended (no trailing newline yet), or does
# not end in a done or failed line.
# The whole terminal event a child's ledger states, or non-zero when the ledger
# is absent, unusable, or states no done or failed event (1), or when that event
# is the line still being appended (2, no trailing newline yet). The event is
# selected through the shared latest-event reader, so the ledger path owns a
# terminal record whose continuation prose trails it, and an unfinished line of
# ordinary prose withholds nothing.
child_terminal_ledger_line() { # <status>
local status=$1 snapshot last marker='__FM_LEDGER_SNAPSHOT_END__'
[ -f "$status" ] && [ ! -L "$status" ] && [ -s "$status" ] || return 1
last=$(last_status_line "$status")
case "$(status_line_verb "$last")" in done|failed) ;; *) return 1 ;; esac
snapshot=$(cat "$status"; printf '%s' "$marker") || return 1
case "$snapshot" in *$'\n'"$marker") ;; *) return 1 ;; esac
snapshot=${snapshot%"$marker"}
last=$(printf '%s' "$snapshot" | grep -v '^[[:space:]]*$' | tail -1)
case "$(status_line_verb "$last")" in
done|failed) printf '%s\n' "$last" ;;
*) return 1 ;;
case "$snapshot" in
*$'\n'"$marker") ;;
"$last$marker"|*$'\n'"$last$marker") return 2 ;;
esac
printf '%s\n' "$last"
}

# Claim one already-delivered inactive fallback as the delivery of this ledger
Expand Down Expand Up @@ -405,12 +408,11 @@ report_child_ledger_locked() { # <id> <meta>
pr=$(pr_for_task "$meta" "$last")
incarnation=$(meta_incarnation "$meta")
fingerprint=$(sha256_text "$incarnation|$id|$state|ledger|$last")
previous=$(grep -v '^[[:space:]]*$' "$status" 2>/dev/null \
| tail -2 | awk 'NR == 1 { first = $0 } NR == 2 { print first }' || true)
predecessor_head=$(sha256_text "$previous")
outcome_key="child-outcome-$id-$state-${fingerprint:0:8}"
ensure_record "$fingerprint" "$id" "$incarnation" "$state" "$outcome_key" direct upstream "$pr" || return 1
[ -n "$RECORD_PENDING" ] || return 0
last_status_line "$status" previous >/dev/null
predecessor_head=$(sha256_text "$previous")
if claim_inactive_report_for_ledger "$id" "$incarnation" "$state" "$fingerprint" "$predecessor_head"; then
# The fallback line is already on the parent channel. This reported ledger
# receipt records that its richer rendering owes no second publication.
Expand Down Expand Up @@ -483,8 +485,9 @@ reconcile_direct_child_locked() { # <id> <meta> <secondmate-id-or-empty> <timeou
last=$(last_status_line "$status")
status_line_verb "$last" | grep -Fx captain-held >/dev/null 2>&1 && return 0
# A ledger that states its own outcome is the ledger-first path's to deliver.
if [ -n "$self" ] && child_terminal_ledger_line "$status" >/dev/null; then
return 0
if [ -n "$self" ]; then
child_terminal_ledger_line "$status" >/dev/null
case "$?" in 0|2) return 0 ;; esac
fi
age=$(last_activity_age "$meta" "$status" "$turn")
[ "$age" -ge "$FM_INACTIVE_RECONCILE_SECS" ] || return 0
Expand All @@ -493,7 +496,8 @@ reconcile_direct_child_locked() { # <id> <meta> <secondmate-id-or-empty> <timeou
[ "$state_rc" -ne 124 ] || return 3
last=$(last_status_line "$status")
if [ -n "$self" ]; then
case "$(status_line_verb "$last")" in done|failed) return 0 ;; esac
child_terminal_ledger_line "$status" >/dev/null
case "$?" in 0|2) return 0 ;; esac
fi
case "$state_line" in
'state: done '*) state='done' ;;
Expand Down
4 changes: 2 additions & 2 deletions bin/fm-watch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
# absorbed instead with its own long re-surface cadence,
# never as a wedge, and that recheck reason names which
# human the wait is on. Only when neither absorb class
# applies does the log's last line decide:
# applies does the log's latest recognized status event decide:
# terminal (captain-relevant) or non-terminal (no verb),
# both surfaced at once. A provably-working stale past the
# wedge threshold also surfaces, with an "escalation N"
Expand Down Expand Up @@ -2457,7 +2457,7 @@ EOF
wake "stale: $w"
fi
elif stale_is_terminal "$w" "$STATE"; then
# The log's last line is captain-relevant - but that alone is not
# The log's latest status event is captain-relevant - but that alone is not
# proof the crew is actually done: a crew's own status log gets no
# new entry once firstmate hands it to a no-mistakes validation
# (AGENTS.md's sparse status-reporting contract), so the log can
Expand Down
Loading
Loading