Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion .agents/skills/bearings/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ Rules that keep the contract unambiguous:
- Recently Landed always renders the bounded current baseline, even when the same completions appeared in an earlier report.
- The four buckets are mutually exclusive, so every item is forced into exactly one: needs-your-action is Captain's Call, done is Recently Landed, self-progressing is Underway, and not-yet-started work or an action-free fleet-integrity warning is Charted Next.
- The strict boundary keeps action-free items OUT of Captain's Call: a working or validating task, a queued item blocked on another task or a date, landed work, a completed scout's report pointer, a declared `paused:` external wait, and a bare recorded PR with no merge-ready signal each belong to one of the other three sections, never Captain's Call.
- A secondmate appears Underway only for `active_child_work`; `externally_held` belongs in Charted Next, and `unknown` belongs there as an unavailable-state gate unless its reason requires the captain's action.
- A secondmate's own row appears Underway only for `active_child_work`; `externally_held` belongs in Charted Next, and `unknown` belongs there as an unavailable-state gate unless its reason requires the captain's action.
- Do not suppress separately projected decisions, landed records, or gates from a `partial-structured` home merely because that secondmate's own row is `unknown`.
- The chat follows `AGENTS.md` section 9 and carries one scannable line per item, each PR as the full `https://...` URL; detailed decisions, plans, full gate reasons, and evidence live only in the report file, which the chat links to, so the chat stays materially shorter than that file.

## Tone and content rules
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -327,16 +327,16 @@ jobs:
snapshot_output=$(/bin/bash tests/fm-fleet-snapshot-view.test.sh)
printf '%s\n' "$snapshot_output"
snapshot_count=$(printf '%s\n' "$snapshot_output" | grep -c '^ok - ')
[ "$snapshot_count" -eq 14 ] || {
echo "::error::expected 14 snapshot/fleet-view tests, got $snapshot_count"
[ "$snapshot_count" -eq 15 ] || {
echo "::error::expected 15 snapshot/fleet-view tests, got $snapshot_count"
exit 1
}

bearings_output=$(/bin/bash tests/fm-bearings-snapshot.test.sh)
printf '%s\n' "$bearings_output"
bearings_count=$(printf '%s\n' "$bearings_output" | grep -c '^ok - ')
[ "$bearings_count" -eq 39 ] || {
echo "::error::expected 39 Bearings tests, got $bearings_count"
[ "$bearings_count" -eq 42 ] || {
echo "::error::expected 42 Bearings tests, got $bearings_count"
exit 1
}

Expand Down
42 changes: 26 additions & 16 deletions bin/fm-bearings-snapshot.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@
# explicitly (the prs: line and the omitted[] surfaces) what was not requested, so an
# absence is never ambiguous.
#
# This wrapper consumes canonical status decisions plus structured captain-held
# backlog items. It never infers decisions from report or visual-review prose.
# This wrapper consumes canonical status decisions plus canonically normalized
# backlog roles, unresolved blockers, and captain actionability. It never infers
# decisions from report or visual-review prose or reimplements snapshot semantics.
#
# Main-home inventory validity comes from the canonical snapshot's main_inventory
# object (orphan structured in-flight without meta, unstructured current rows).
Expand Down Expand Up @@ -111,9 +112,10 @@ landed merges this home's Done with registered secondmate homes' Done, bounded b
with omitted[] disclosure. Default selection is balanced across deterministic home
order while preserving each home's internal newest-first order; sparse homes do
not waste capacity. --all-landed reveals the full global newest-first set.
For every registered secondmate, validated structured state from its own home is
authoritative. Parent events and bounded terminal reads are labeled fallback or
contradiction evidence and never become current work.
For every registered secondmate, readable structured facts from its own home are
authoritative, including independently trustworthy surfaces from a partial summary.
Parent events and bounded terminal reads are labeled fallback or contradiction
evidence and never become current work.
Opt-in surfaces: --fields bodies|paths|actions|endpoints, --all-in-flight,
--all-decisions, --all-secondmates, --all-landed, --all-reports, --all-queued, --all-recorded-prs,
--all-unhealthy, --all-pr-repos, --include-prs (adds candidate_prs).
Expand Down Expand Up @@ -326,6 +328,7 @@ MODEL=$(printf '%s' "$SNAP" | jq \
| (if $all_landed == 1 then $landed_sorted else ($per_home_groups | round_robin_landed($landed_n)) end) as $done
| ($done | map(.id)) as $done_ids
| ([.tasks[] | select(.kind != "secondmate") | .id]) as $live_ids
| ([.tasks[] | select(.kind != "secondmate" and .current_state.state == "working") | .id]) as $working_ids
| ($live_ids + $done_ids) as $rel_ids
| ([ .tasks[]
| select(.endpoint.exists == false or .endpoint.agent_alive == "dead")
Expand Down Expand Up @@ -366,8 +369,11 @@ MODEL=$(printf '%s' "$SNAP" | jq \
provenance:.provenance.selected,freshness:.freshness.status,
age_seconds:.freshness.age_seconds,contradiction:(.contradiction // false),
reason:(.current.reason // "-")} ]) as $secondmates_all
| ([ .tasks[] | select(.kind != "secondmate") | {
id, kind,
| ([ .tasks[]
| select(.kind != "secondmate")
| select(.backlog.current_role != "program")
| select(.backlog.current_role != "held" or .current_state.state == "working")
| {id, kind,
state: .current_state.state,
doing: ((.current_state.detail // "") as $d
| (if $d != "" then $d else (.hints.last_event_text // "") end) | trunc(90))
Expand All @@ -377,8 +383,7 @@ MODEL=$(printf '%s' "$SNAP" | jq \
| {id,kind:"secondmate",state:.bearings_state,
doing:([.active_children[] | .id + ": " + (.doing // .state)] | join("; ") | trunc(90))} ]) as $in_flight_all
| ([ .backlog.records[]
| select(.state == "queued" and .structured and .kind == "captain"
and .hold_kind == "captain" and .hold_reason != null)
| select(.structured and .captain_actionable == true)
| {id,key:.id,verb:"captain-hold",
summary:((.title + ": " + .hold_reason) | trunc(90)),owner:"(main)"} ]
+ [ (.secondmate_current.records // [])[] as $m | $m.decisions_open[]?
Expand All @@ -393,18 +398,23 @@ MODEL=$(printf '%s' "$SNAP" | jq \
owner:"(main)"}]
else [] end)
+ [ .backlog.records[]
| select(.state == "queued" and .structured)
| select((.kind == "captain" and .hold_kind == "captain" and .hold_reason != null) | not)
| . as $record
| select(.structured and
(.state == "queued" or
(.state == "in_flight" and .current_role == "held" and ($working_ids | index($record.id) | not))))
| select(.captain_actionable != true)
| select(($all_queued == 1)
or (((.body_excerpt // "") | test("SUPERSEDED|NOT REQUIRED|NOT-REQUIRED|DEFERRED"; "i")) | not))
| {id, title:(.title | trunc(60)), blocked_by:(.blocked_by // "-"),
reason:((.blocked_reason // "-") | trunc(40)),owner:"(main)"} ]
| {id, title:(.title | trunc(60)),
blocked_by:((.unresolved_blocker_ids // []) | if length > 0 then join(",") else "-" end | trunc(120)),
reason:((.hold_reason // .blocked_reason // "-") | trunc(40)),owner:"(main)"} ]
+ [ (.secondmate_current.records // [])[] as $m
| select($m.provenance.selected == "structured-home")
| $m.queued[]?
| select((.kind == "captain" and .hold_kind == "captain" and .hold_reason != null) | not)
| {id,title:(.title | trunc(60)),blocked_by:(.blocked_by // "-"),
reason:((.blocked_reason // "-") | trunc(40)),owner:$m.id} ]) as $gates_all
| select(.captain_actionable != true)
| {id,title:(.title | trunc(60)),
blocked_by:((.unresolved_blocker_ids // []) | if length > 0 then join(",") else "-" end | trunc(120)),
reason:((.hold_reason // .blocked_reason // "-") | trunc(40)),owner:$m.id} ]) as $gates_all
| ([ .scout_reports[]
| . as $r
| select(($all_reports == 1) or (($rel_ids | index($r.id)) != null))
Expand Down
Loading
Loading