From f8d6a97ecd5f5e4281d60e419f9b72cfa4d97526 Mon Sep 17 00:00:00 2001 From: Freudator86 Date: Sun, 9 Aug 2026 07:06:59 +0200 Subject: [PATCH 1/4] fix(snapshot): read captain-actionability off the hold kind, not the record kind AGENTS.md section 10 files a captain-gated thread with `tasks-axi hold --reason "" --kind captain` and says nothing about the record kind, but the captain-actionable predicate also demanded `kind == "captain"`. A hold filed exactly as instructed was therefore invisible to /decisionboard, to bin/fm-decision-inventory.sh, and to every bearings read. Measured on the live home 2026-08-09: 35 records carried `hold-kind: captain` and the surface returned 32. The three missing were a production deploy filed as kind ship, a thread carrying no record kind at all, and one blocked record. Widening the predicate to the hold kind returns the first two; `hold --kind` is a closed vocabulary, so it admits only records someone deliberately held for the captain. The blocked one is untouched on purpose: a captain hold behind an unresolved blocker still leaves decisions_open and lands in gates carrying no kind. That is a separate, separately filed gap, and the new test pins it so this change cannot quietly move it. Cross-references that claimed the record kind was what kept a record off the captain's surface are corrected to name the hold kind: AGENTS.md section 10, bin/fm-chart-kinds-lib.sh, bin/fm-sea-chart.sh, bin/fm-to-backlog.sh, .agents/skills/sea-chart, and docs/to-backlog-provenance.md. fog and out-of-course stay safe by construction because both are held as `future`. The sea chart's own reconciliation baseline stays kind-based and is documented as deliberately narrower. Regression coverage in tests/fm-bearings-snapshot.test.sh asserts the reported surface, not the predicate: verified failing before the change and passing after. --- .agents/skills/sea-chart/SKILL.md | 2 +- AGENTS.md | 2 +- bin/fm-chart-kinds-lib.sh | 10 +++++-- bin/fm-fleet-snapshot.sh | 15 +++++++++- bin/fm-sea-chart.sh | 45 ++++++++++++++++-------------- bin/fm-to-backlog.sh | 8 ++++-- docs/to-backlog-provenance.md | 3 +- tests/fm-bearings-snapshot.test.sh | 36 ++++++++++++++++++++++++ tests/fm-sea-chart.test.sh | 3 +- 9 files changed, 92 insertions(+), 32 deletions(-) diff --git a/.agents/skills/sea-chart/SKILL.md b/.agents/skills/sea-chart/SKILL.md index 59e40f92b6..b28af4030e 100644 --- a/.agents/skills/sea-chart/SKILL.md +++ b/.agents/skills/sea-chart/SKILL.md @@ -154,7 +154,7 @@ Whether a piece of work is destructive, irreversible, security-sensitive, or out The chart reads them; it never files them. They are ordinary backlog records, owned by the backlog contract in `AGENTS.md` section 10, which is where their spelling and their meaning live. Follow it exactly on the record kind rather than the hold: `hold --kind` refuses both names, and reading that refusal as "these cannot be stored" is what once left every chart's fog and boundaries permanently empty. -Neither can ever be mistaken for a captain decision, because captain-actionability requires `kind: captain` and neither kind is that - structure, not a rule in prose. +Neither can ever be mistaken for a captain decision, because captain-actionability requires `hold-kind: captain` and both are held as `future` - structure, not a rule in prose. A fog patch that becomes sharp is not promoted in place: close it, then register the real decision through `bin/fm-decision-hold.sh`, which already accepts later keys on a live or torn-down origin. A course boundary never rises at all. diff --git a/AGENTS.md b/AGENTS.md index 2030eaf40b..583627b6a8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -485,7 +485,7 @@ It tracks work items only, never agents; persistent secondmates never appear as Work routed to a secondmate is recorded in that secondmate home's own backlog, not the main backlog. When a main-side thread such as a pending captain decision or relay reminder is worth durable tracking, file it as its own work item; use `tasks-axi hold --reason "" --kind captain` for a captain-gated thread. Unresolved decisions discovered by investigations or visual reviews follow `decision-hold-lifecycle`, which owns their mandatory backlog lifecycle. -Two further kinds carry a sea chart's own material and can never be mistaken for a captain decision, because captain-actionability requires `kind: captain`: `fog` for a question an investigation could not yet make sharp, and `out-of-course` for a deliberate scope boundary. +Two further kinds carry a sea chart's own material and can never be mistaken for a captain decision, because captain-actionability requires `hold-kind: captain` and both are held as `future`: `fog` for a question an investigation could not yet make sharp, and `out-of-course` for a deliberate scope boundary. File both under the originating undertaking's id as `-fog-` and `-oos-`, so each belongs to exactly one chart. Both names go on the record kind, which is the only field the chart classifies by: file a dark patch with `tasks-axi add "" --kind fog` and a scope boundary with `tasks-axi add <id> "<title>" --kind out-of-course`, one command per record. Then `tasks-axi hold <id> --reason "<why>" --kind future` records the reason the chart prints, because `hold --kind` is a separate closed vocabulary that rejects both names. diff --git a/bin/fm-chart-kinds-lib.sh b/bin/fm-chart-kinds-lib.sh index 2668f6f67b..b087342cf5 100755 --- a/bin/fm-chart-kinds-lib.sh +++ b/bin/fm-chart-kinds-lib.sh @@ -29,9 +29,13 @@ # # WHY THESE KINDS ARE SAFE BY CONSTRUCTION # Neither can ever be mistaken for a captain decision. Captain-actionability is -# one predicate in bin/fm-fleet-snapshot.sh and it requires `kind == "captain"`, -# so a record of either kind below reads `captain_actionable: false` no matter -# what anyone writes into it. That is structure, not a rule in prose that drifts. +# one predicate in bin/fm-fleet-snapshot.sh and it requires `hold_kind == +# "captain"`, which is the field that names who is being asked. Both kinds below +# are filed with `hold --kind future`, so both read `captain_actionable: false`. +# That is structure, not a rule in prose that drifts - but it is structure in the +# HOLD kind, so a fog record filed with a captain hold would surface as a captain +# decision, which is the correct direction: a misfiled question the captain can +# see is recoverable, one he cannot is not. # # Sourced, never executed. diff --git a/bin/fm-fleet-snapshot.sh b/bin/fm-fleet-snapshot.sh index 45a70aa661..98847e0f3a 100755 --- a/bin/fm-fleet-snapshot.sh +++ b/bin/fm-fleet-snapshot.sh @@ -470,8 +470,21 @@ backlog_json() { # [<backlog-path>] - defaults to this home's $BACKLOG elif .state == "queued" then "queued" else "done" end) | .requires_child_metadata = (.current_role == "worker") + # The audience of a hold is `hold_kind`, never the record kind. A record + # kind says what the work IS - ship, scout, fog - and `tasks-axi hold + # --kind captain` is what says the captain is the one being asked, which + # is why AGENTS.md section 10 files a captain-gated thread with that one + # command and says nothing about the record kind. Requiring both + # fields made every hold filed exactly as instructed invisible: measured + # on this home 2026-08-09, 35 records carried `hold-kind: captain` and the + # surface returned 32, the missing ones being a production deploy filed as + # kind ship and a thread carrying no record kind at all. + # `hold --kind` is a closed vocabulary (captain, external, load, parked, + # future), so widening admits only records someone deliberately held for + # the captain. A blocked record is still withheld here - that is a + # separate, separately filed gap and this predicate does not change it. | .captain_actionable = - (.state == "queued" and .kind == "captain" and .hold_kind == "captain" + (.state == "queued" and .hold_kind == "captain" and .hold_reason != null and (.unresolved_blocker_ids | length) == 0) else . end) | del(.section,.order) diff --git a/bin/fm-sea-chart.sh b/bin/fm-sea-chart.sh index e03a4924e3..5217562ee3 100755 --- a/bin/fm-sea-chart.sh +++ b/bin/fm-sea-chart.sh @@ -74,13 +74,16 @@ # ruling in its group is reported there too, as `unpaired-variant`: the fold # rests on an assumption nothing verifies, so a question only an analyst raised # must stay on the page rather than fall between the two surfaces. What makes a record -# captain-gated is its KIND, never its name: `kind: captain` together with -# `hold-kind: captain` is the only shape captain-actionability can ever admit, -# and a captain record named without `-decision-` is exactly as lost when it is -# blocked. A record of a DIFFERENT kind carrying a captain hold is not this gap -# and is not recovered here: it never reaches `decisions_open` at all, so the -# decision board cannot see it either. That belongs to the predicate in -# bin/fm-fleet-snapshot.sh and is filed as `fm-snapshot-captain-shape-invisible`. +# captain-gated is its KIND, never its name: what captain-actionability admits is +# `hold-kind: captain` (bin/fm-fleet-snapshot.sh), and a captain record named +# without `-decision-` is exactly as lost when it is blocked. This chart draws its +# own reconciliation baseline narrower, from records of `kind: captain` under this +# chart, because those are the ones its sections can classify. A record of a +# DIFFERENT kind carrying a captain hold no longer needs that baseline: since +# 2026-08-09 the predicate admits it on the hold kind alone, so it arrives here +# through the inventory and is drawn like any other decision. Only a BLOCKED one +# is still lost, and that is the blocked-decision gap this header opens with, +# not a second flank. # Being per-chart is what makes the recovery possible without the fleet-wide # `decisions_blocked[]` surface that the design defers - a chart knows its own # scope, so it can ask a bounded question the fleet-wide board cannot. @@ -378,9 +381,9 @@ CHART_JSON=$(printf '%s\n%s\n%s\n' "$LIVE" "$ARCH" "$INV" 2>/dev/null | jq -n \ # Why a captain-gated record is not on the decision list of this chart. These are # different pieces of news and must not share one sentence: a blocked record is # one the fleet has lost track of, while an in-flight one is being worked right - # now. Captain-actionability (bin/fm-fleet-snapshot.sh) wants a queued record of - # kind captain, held with hold-kind captain, and nothing unresolved against it, - # so each failing clause gets its own name and its own words. + # now. Captain-actionability (bin/fm-fleet-snapshot.sh) wants a queued record + # held with hold-kind captain and nothing unresolved against it, so each failing + # clause gets its own name and its own words. # The unpaired-variant clause comes FIRST because it is the one case where the # record did reach the actionable surface, so every sentence below it - each of # which says the surface never carried it - would be false of such a record. @@ -487,17 +490,17 @@ CHART_JSON=$(printf '%s\n%s\n%s\n' "$LIVE" "$ARCH" "$INV" 2>/dev/null | jq -n \ # RECONCILIATION. Every record this chart owns that waits on the captain, # straight from the backlog - then whatever the actionable surface did not - # return. The test is the KIND, never the identifier: `kind: captain` with - # `hold-kind: captain` is the only shape captain-actionability can ever admit, - # so the record kind IS the thing while a name is only what it happens to be - # called. Keying on `-decision-` in the id would leave a blocked captain record - # named any other way not merely undercounted but invisible, every count - # reading zero - the same silent loss this chart exists against, on a third - # flank. A record of some other kind carrying a captain hold is a different - # gap: it never reaches `decisions_open` either, so the decision board cannot - # see it in the first place. That one belongs to the captain-actionable - # predicate in bin/fm-fleet-snapshot.sh and is filed as - # `fm-snapshot-captain-shape-invisible`; this chart cannot close it. + # return. The test is the KIND, never the identifier, so the record kind IS the + # thing while a name is only what it happens to be called. Keying on + # `-decision-` in the id would leave a blocked captain record named any other + # way not merely undercounted but invisible, every count reading zero - the same + # silent loss this chart exists against, on a third flank. + # This baseline stays narrower than the captain-actionable predicate, which + # admits any queued record held with `hold-kind: captain`. That is deliberate: + # a record of some other kind now reaches `decisions_open` on its own and is + # drawn from the inventory, so naming it here would only reconcile it against a + # surface that already carries it. What such a record still loses when BLOCKED + # it loses to the blocked-decision gap in the header, not to this line. | ([ $mine[] | select(open_state and .kind == "captain") ]) as $own_decision_records | ([ $own_decision_records[] | select(.id as $id | ($seen | index($id)) == null) diff --git a/bin/fm-to-backlog.sh b/bin/fm-to-backlog.sh index 8b0e4f7105..4ee9dc6fe5 100755 --- a/bin/fm-to-backlog.sh +++ b/bin/fm-to-backlog.sh @@ -33,9 +33,11 @@ # it is cheap to fix, rather than leaving orphan units nobody's chart shows. # # WHY THREE KINDS ARE REFUSED -# `captain` is the one predicate that makes a record captain-actionable -# (bin/fm-fleet-snapshot.sh), and durable captain decisions are owned by -# bin/fm-decision-hold.sh under .agents/skills/decision-hold-lifecycle. `fog` and +# `captain` is the kind durable captain decisions carry, and those are owned by +# bin/fm-decision-hold.sh under .agents/skills/decision-hold-lifecycle. This +# script never calls `tasks-axi hold`, and a hold is what +# bin/fm-fleet-snapshot.sh reads for captain-actionability, so the refusal is +# about ownership rather than the surface. `fog` and # `out-of-course` are the sea chart's own kinds, spelled by # bin/fm-chart-kinds-lib.sh and carried on `-fog-`/`-oos-` ids this script does # not compose. Slicing work must never manufacture a record of any of the three diff --git a/docs/to-backlog-provenance.md b/docs/to-backlog-provenance.md index 9352773d02..2f1a3ac064 100644 --- a/docs/to-backlog-provenance.md +++ b/docs/to-backlog-provenance.md @@ -139,7 +139,8 @@ Ours outright, added rather than adapted: A unit may never be filed as `kind: captain`, `fog`, or `out-of-course`, because those records belong to `bin/fm-decision-hold.sh` and to the sea chart's own markers. Nor may a slug compose the reserved `-decision-` marker into its id, because `bin/fm-sea-chart.sh` reads that marker positionally rather than by kind, so an ordinary unit carrying it would leave the chart's takeable work and later read as a settled captain decision - the same hole as the kind refusal, reached through the id instead of the kind. And no unit may be blocked by its own origin, an edge that could never clear because a breakdown may never close the undertaking it is a slice of. - Without the kind and id refusals this skill could manufacture captain-actionable records as a side effect of slicing work, which is the one write the read-only argument was right about. + Without the kind and id refusals this skill could manufacture records that read as captain decisions as a side effect of slicing work, which is the one write the read-only argument was right about. + Captain-actionability itself is read off the hold, not the record kind (`bin/fm-fleet-snapshot.sh`), and this script never calls `tasks-axi hold`, so the refusals guard ownership and the chart's reading rather than that surface. ## What we dropped diff --git a/tests/fm-bearings-snapshot.test.sh b/tests/fm-bearings-snapshot.test.sh index 1884495fd3..ab7b51aac6 100755 --- a/tests/fm-bearings-snapshot.test.sh +++ b/tests/fm-bearings-snapshot.test.sh @@ -943,6 +943,41 @@ test_open_decision_surfaces_end_to_end() { pass "an authoritative captain hold surfaces end-to-end" } +# AGENTS.md section 10 files a captain-gated thread with `tasks-axi hold <id> +# --reason "<reason>" --kind captain` and says nothing about the record kind, so a +# record filed exactly as instructed keeps whatever kind it already had - or none. +# The captain-actionable predicate used to demand `kind: captain` as well, which +# made every such hold invisible to this surface, to the decision board, and to +# every bearings read. Measured on the live home 2026-08-09: 35 records carried a +# captain hold and 32 reached the surface. +test_captain_hold_on_a_non_captain_kind_record_surfaces() { + local home fakebin json + home=$(make_home captain-hold-shape); write_fixture "$home" + cat >> "$home/data/backlog.md" <<'EOF' + +## Queued +- [ ] deploy-prod - Deploy the stack (repo: firstmate) (kind: ship) (hold: production action needs his live word) (hold-kind: captain) +- [ ] allships-notice - Tell the fleet an update exists (repo: firstmate) (hold: captain chose to hold it until close-out) (hold-kind: captain) +- [ ] blocked-deploy - Clear the residue rows blocked-by: ship-task (repo: firstmate) (kind: ship) (hold: production data change, captain-gated) (hold-kind: captain) +EOF + fakebin=$(make_fakebin "$home") + json=$(run "$home" "$fakebin" --json) + printf '%s' "$json" | jq -e ' + (.decisions_open | any(.[]; .id == "deploy-prod" and .verb == "captain-hold")) + and (.decisions_open | any(.[]; .id == "allships-notice" and .verb == "captain-hold")) + and (.gates | any(.[]; .id == "deploy-prod") | not) + and (.gates | any(.[]; .id == "allships-notice") | not) + ' >/dev/null || fail "a captain hold filed on a record whose own kind is not captain must reach the captain: $json" + # The neighbouring loss is left exactly as it was: a captain hold behind an + # unresolved blocker still leaves decisions_open and lands in gates carrying no + # kind. That gap is separately filed and this change must not quietly move it. + printf '%s' "$json" | jq -e ' + (.decisions_open | any(.[]; .id == "blocked-deploy") | not) + and (.gates | any(.[]; .id == "blocked-deploy" and .blocked_by == "ship-task")) + ' >/dev/null || fail "a blocked captain hold must stay withheld exactly as before: $json" + pass "a captain hold reaches the captain whatever the record kind, and a blocked one still does not" +} + test_report_pointers_surface() { local home fakebin json home=$(make_home reports); write_fixture "$home" @@ -2043,6 +2078,7 @@ test_dangling_blocker_surfaces_ready_with_integrity_warning test_secondmate_dangling_blocker_surfaces_ready_with_integrity_warning test_completed_scout_report_not_pending test_open_decision_surfaces_end_to_end +test_captain_hold_on_a_non_captain_kind_record_surfaces test_report_pointers_surface test_superseded_queued_item_dropped_by_default test_include_prs_is_the_only_fetch_path diff --git a/tests/fm-sea-chart.test.sh b/tests/fm-sea-chart.test.sh index 43a72586cb..b4506b93f5 100755 --- a/tests/fm-sea-chart.test.sh +++ b/tests/fm-sea-chart.test.sh @@ -326,7 +326,8 @@ EOF } test_fog_and_out_of_course_can_never_be_a_captain_decision() { - # Structure, not prose: captain-actionability requires kind captain. + # Structure, not prose: captain-actionability requires hold-kind captain, and + # AGENTS.md section 10 files both kinds below with a `future` hold. local home out home=$(make_home kinds) cat > "$home/data/backlog.md" <<'EOF' From bfb4c4b9a850bd1bd7acddb2637dd4a1a2fedcfc Mon Sep 17 00:00:00 2001 From: Freudator86 <tim@allesknut.de> Date: Sun, 9 Aug 2026 07:46:11 +0200 Subject: [PATCH 2/4] no-mistakes(review): correct sea-chart and to-backlog comments on blocked captain holds --- .agents/skills/to-backlog/SKILL.md | 3 ++- bin/fm-sea-chart.sh | 30 ++++++++++++++++++++---------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/.agents/skills/to-backlog/SKILL.md b/.agents/skills/to-backlog/SKILL.md index 73739dd1c8..5925f3a8b7 100644 --- a/.agents/skills/to-backlog/SKILL.md +++ b/.agents/skills/to-backlog/SKILL.md @@ -111,7 +111,8 @@ That is the decision, not a working demo. ## What this skill does not do It authors and sizes **questions and work**. -It never answers a captain decision: `bin/fm-decision-hold.sh` and `.agents/skills/decision-hold-lifecycle` own that, and this skill refuses to file a captain-actionable record so it cannot become a second owner by accident. +It never answers a captain decision: `bin/fm-decision-hold.sh` and `.agents/skills/decision-hold-lifecycle` own that, and this skill refuses the `captain` kind so it cannot become a second owner by accident. +That refusal is about ownership rather than the captain-actionable surface, which reads the hold kind: `bin/fm-to-backlog.sh` never calls `tasks-axi hold` at all, so it could not file an actionable record whatever kind it accepted. It never files fog or a course boundary either - those are the sea chart's own markers, spelled by `bin/fm-chart-kinds-lib.sh` and filed under `AGENTS.md` section 10. It never dispatches a worker, opens a branch, or writes code. diff --git a/bin/fm-sea-chart.sh b/bin/fm-sea-chart.sh index 5217562ee3..4468bce423 100755 --- a/bin/fm-sea-chart.sh +++ b/bin/fm-sea-chart.sh @@ -78,12 +78,18 @@ # `hold-kind: captain` (bin/fm-fleet-snapshot.sh), and a captain record named # without `-decision-` is exactly as lost when it is blocked. This chart draws its # own reconciliation baseline narrower, from records of `kind: captain` under this -# chart, because those are the ones its sections can classify. A record of a -# DIFFERENT kind carrying a captain hold no longer needs that baseline: since -# 2026-08-09 the predicate admits it on the hold kind alone, so it arrives here -# through the inventory and is drawn like any other decision. Only a BLOCKED one -# is still lost, and that is the blocked-decision gap this header opens with, -# not a second flank. +# chart, because those are the ones its sections can classify. An UNBLOCKED +# record of a DIFFERENT kind carrying a captain hold no longer needs that +# baseline: since 2026-08-09 the predicate admits it on the hold kind alone, so +# it arrives here through the inventory and is drawn like any other decision. +# A BLOCKED one reaches NEITHER surface: the predicate fails it on the blocker, +# and this chart's baseline fails it on the kind, so `withheld[]` never names it +# either. It falls through to `unplaced[]`, where the reason reads cause +# `blocked` and names its kind and its blocker but never says the captain is +# being asked. That class is recovered nowhere as a decision - it lies outside +# the recovery this header opens with rather than inside it. It behaved +# identically before 2026-08-09, so this is not a regression, and the narrow +# baseline is deliberate rather than an oversight. # Being per-chart is what makes the recovery possible without the fleet-wide # `decisions_blocked[]` surface that the design defers - a chart knows its own # scope, so it can ask a bounded question the fleet-wide board cannot. @@ -497,10 +503,14 @@ CHART_JSON=$(printf '%s\n%s\n%s\n' "$LIVE" "$ARCH" "$INV" 2>/dev/null | jq -n \ # silent loss this chart exists against, on a third flank. # This baseline stays narrower than the captain-actionable predicate, which # admits any queued record held with `hold-kind: captain`. That is deliberate: - # a record of some other kind now reaches `decisions_open` on its own and is - # drawn from the inventory, so naming it here would only reconcile it against a - # surface that already carries it. What such a record still loses when BLOCKED - # it loses to the blocked-decision gap in the header, not to this line. + # an UNBLOCKED record of some other kind now reaches `decisions_open` on its own + # and is drawn from the inventory, so naming it here would only reconcile it + # against a surface that already carries it. A BLOCKED one reaches neither: the + # predicate fails it on the blocker and this line fails it on the kind, so it is + # recovered nowhere as a decision and lands in `unplaced[]` under cause + # `blocked`, named by kind and blocker but never as a question put to the + # captain. It behaved identically before 2026-08-09, so that is not a loss this + # line introduced, and staying narrow here is deliberate. | ([ $mine[] | select(open_state and .kind == "captain") ]) as $own_decision_records | ([ $own_decision_records[] | select(.id as $id | ($seen | index($id)) == null) From dc894503fbbfcc4a70de43eda73830cf842026bf Mon Sep 17 00:00:00 2001 From: Freudator86 <tim@allesknut.de> Date: Sun, 9 Aug 2026 07:57:46 +0200 Subject: [PATCH 3/4] no-mistakes(document): correct decisionboard note on captain holds of other kinds --- .agents/skills/decisionboard/SKILL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.agents/skills/decisionboard/SKILL.md b/.agents/skills/decisionboard/SKILL.md index f9949066a7..8e4456967c 100644 --- a/.agents/skills/decisionboard/SKILL.md +++ b/.agents/skills/decisionboard/SKILL.md @@ -102,7 +102,7 @@ Captain-actionability is a single predicate in `bin/fm-fleet-snapshot.sh` and a Measured on a two-decision fixture, adding one `blocked-by` edge takes the reported inventory from "records: 2 decisions kept: 2" to "records: 1 decisions kept: 1", with no footnote anywhere. So this is the captain-actionable set, not the open set: say that on the board in the same breath as the unverified fold, and never present it as everything waiting on him. For one named undertaking, `.agents/skills/sea-chart` reconciles its own records against the backlog and reports each withheld one by name with its cause - fleet-wide there is no such count yet. -The same predicate separately hides a captain hold carried on a record of another kind, filed as `fm-snapshot-captain-shape-invisible`; both losses belong to the snapshot and neither can be closed from this board. +The blocker is the only shape of this loss left, and it belongs to the snapshot rather than to this board: the predicate reads the hold kind alone, so a captain hold carried on a record of any other kind does reach here. ## Every folded record stays visible From 096abb696a5d1aafa59b96cf3e773cc2d5d4dadd Mon Sep 17 00:00:00 2001 From: Freudator86 <tim@allesknut.de> Date: Sun, 9 Aug 2026 08:38:01 +0200 Subject: [PATCH 4/4] no-mistakes: apply CI fixes --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d675e83f9f..e84efc3981 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -352,8 +352,8 @@ jobs: 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 44 ] || { - echo "::error::expected 44 Bearings tests, got $bearings_count" + [ "$bearings_count" -eq 45 ] || { + echo "::error::expected 45 Bearings tests, got $bearings_count" exit 1 }