feat(skills): add Großreinschiff, the weekly fleet cleanup sweep - #59
Merged
Conversation
added 2 commits
August 3, 2026 14:15
Nine classes of record, instruction, branch, tool, and workspace that were true when written, are not true now, and that nothing re-measures. Every item was earned by a measured incident in the night of 2026-08-02/03; docs/grossreinschiff.md carries the evidence behind each one so a later reader can re-measure instead of trusting the checklist. The sweep reports and never deletes. Removal is a separate captain- authorized step, and inside a project it is a dispatched worker's task, never firstmate's own write. Landedness is never judged by ancestry. This fleet squashes, so ancestry calls landed work unmerged: 152 of 154 branches on coditan-bridge, and 34 of 52 merged-PR branches here. The skill carries a five-rung ladder (ancestry, patch-id, content, content-absent, empty) whose per-branch form comes from data/bridge-branch-sprawl-classify/report.md §1, with anything unsettled reported as undetermined and barred from any deletion set. Two traps are documented from measurement: git merge-tree conflicts routinely on old branches, so a caller that drops its exit status reads landed work as unlanded, and a forge-recorded merge commit proves nothing unless it is verified as an ancestor of the default branch. tests/ builds real squash fixtures for each rung rather than asserting on the prose. Cadence is a session-start item, not a new scheduler. The due check is one file read and one date comparison, so bin/fm-bootstrap.sh runs it in the detect pass that already happens once per session start; an external timer would add a per-home install step that nothing verifies, which is the no-op-instruction defect this sweep exists to find. A vessel dark on Thursday sweeps late rather than skipping the week, and the line says how late. docs/grossreinschiff.md records the rejected alternatives and the mechanism's own limits. tests/lib.sh silences the detect line suite-wide, since every suite that composes bootstrap would otherwise see it; the sweep's own suite sets it back to 0 and drives the real bootstrap.
Freudator86
force-pushed
the
fm/fleet-grossreinschiff
branch
from
August 3, 2026 14:33
fd0c025 to
ad84a19
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Intent
Build Großreinschiff: a fleet-wide cleanup sweep that runs every Thursday, shipped as a skill in firstmate's shared instruction surface so it reaches every vessel through the pin. Captain's decision 2026-08-03; first fleet-wide run Thursday 2026-08-06. Spelling is one word - Reinschiff is the seaman's term for cleaning the ship and the intensifier compounds onto it; the prose keeps the ss-ligature per the repo's German-spelling rule while the directory and script stay ASCII.
The sweep's nine-item checklist was dictated by the captain, each item earned by a measured incident in the night of 2026-08-02/03, none hypothetical: (1) merged branches nothing prunes, (2) records whose stated facts no longer hold, (3) no-op instructions that cannot fire, (4) duplicate records for one defect, (5) consolidation pointers into nothing, (6) counts including something that never existed, (7) tool currency outside the managed suite, (8) borrowed material without attribution, (9) leftover workspaces and state. The captain's numbering is preserved deliberately so a later reader can map findings back to the original decision.
The captain specified five safety properties as properties rather than preferences: it reports before it deletes and deletion is a separate authorised step; it never tests landedness by ancestry because this fleet squashes; it never touches unlanded work and a refusal is stop-and-investigate; each finding names its evidence so the next sweep re-measures instead of inheriting a belief; and it states what it did NOT cover, because a sweep implying completeness it lacks is itself the defect class it cleans.
Decisions made while building it:
Cadence - the captain asked me to decide and document whether the reminder is a scheduled wake, a session-start item, or a fleet-wide notice, and explicitly said not to build a second scheduler if one already fits. I chose a session-start item: the due check is one file read and one date comparison, so bin/fm-bootstrap.sh runs it in the detect pass that already happens once per session start. A watcher check: poll was rejected because that mechanism is per-task, registered against a task id and torn down with it, so a fleet-level sweep would need a new registration class and trust binding. An external cron/systemd timer (the pattern fm-firstmate-update-check.sh and fm-fork-sync-check.sh use) was rejected because those make network calls that earn a timer while this makes none, and a timer adds a per-home install step nothing verifies - so a home that never installed it would silently never sweep, which is checklist item 3 built into the thing meant to find item 3. A fleet notice announces but cannot make anything recur. The rejected alternatives are recorded in docs/grossreinschiff.md on purpose.
Cadence rule - due when the last recorded sweep predates the most recent Thursday 00:00 local. An absent record deliberately means never swept and therefore due, rather than hardcoding the 2026-08-06 first-run date into the script, because a hardcoded date is exactly the rotting fact checklist item 2 describes. A corrupt or unparseable record also reads as never swept, so it makes the sweep due rather than silently skipping it. A vessel dark on Thursday sweeps late rather than skipping the week. The week boundary is today's local midnight minus whole days, so DST moves it by an hour twice a year - accepted deliberately and documented, since an hour cannot make a weekly sweep fire twice or skip a week. The clock is read in one date call with six fields so tests can override it portably without needing GNU-only date -d @epoch.
Landedness - building this turned up a real correction to my own first attempt. git merge-tree --write-tree exits non-zero on a conflict, and an old branch whose files the default branch has since edited conflicts routinely; a caller that keeps the tree hash and drops the exit status reads a conflict as adds-content, which reads as not-landed, which puts landed work in a deletion set. So the skill carries a five-rung ladder (ancestry, patch-id, content, content-absent, empty) with patch-id before content, and anything unsettled reported as undetermined and barred from every deletion set. Measured on this repo 2026-08-03 over 52 merged-PR branches: 18 settled by ancestry, 33 by patch-id, 1 by content, 0 undetermined - ancestry alone would have called 34 landed branches unmerged. The per-branch form of the technique comes from data/bridge-branch-sprawl-classify/report.md and is attributed rather than presented as this skill's invention, because checklist item 8 applies to the skill itself.
Test approach - the ladder tests build real git squash fixtures rather than asserting on the skill's prose, because a written belief about how git behaves is precisely what the checklist was assembled from. One test deliberately reproduces the merge-tree conflict trap.
Test isolation - the new bootstrap detect line broke existing bootstrap silence fixtures, which the existing suite caught. Fixed by following the repo's own established pattern (FM_AXI_SUITE_DISABLE): the script honours FM_GROSSREINSCHIFF_DISABLE=1 for the detect mode only, tests/lib.sh exports it suite-wide, and the sweep's own suite sets it back to 0 and drives the real bootstrap so the mechanism is still exercised end to end. The disable is narrowed to detect because --status and --record are never called by bootstrap and can pollute nothing.
Knowledge placement follows .agents/skills/firstmate-coding-guidelines: the sweep procedure and checklist in the skill, the incident evidence and cadence rationale in docs/grossreinschiff.md, exact mechanics in the script header (which is also its --help output so the two cannot drift), and only three short additions to AGENTS.md - one state-file line, one trigger in section 6, and GROSSREINSCHIFF added to section 13's bootstrap-diagnostics prefix list. bootstrap-diagnostics owns the response to the due line, including that it is advisory in a session without the fleet lock.
One claim was corrected during the work: an early draft said all six branches in the bad measurement had landed, but only three of those six are branches of merged pull requests, so the skill now states exactly what was verified. Item 8's open question - whether anything besides the sea chart came from the Wayfinder repository - is deliberately carried forward as still open rather than closed by assumption.
Not in scope for this change, and left for after it lands: the pin bump to the vessels, and the single All-Ships notice announcing the day and what the sweep covers.
What Changed
grossreinschiffskill (.agents/skills/grossreinschiff/SKILL.md), the captain-invocable/grossreinschiffweekly cleanup sweep: a nine-item checklist over records, instructions, branches, tools, and workspaces, five stated safety properties (report before delete, never judge landedness by ancestry, never touch unlanded work, name the evidence behind each finding, state what was not covered), and a five-rung landedness ladder — ancestryA, patch-idP, emptyE, contentC, content-absentX— whereCkeys ongit merge-tree --write-treeexit status as well as its tree hash,Xrequires the branch to add at least one path, and anything unsettled is reportedundeterminedand barred from every deletion set.docs/grossreinschiff.mdcarries the incident evidence per checklist item, the on-repo ladder measurement, the rejected cadence alternatives, and attribution for the per-branch classification technique.bin/fm-grossreinschiff-due.sh(detect /--status/--record/--help, with the script header as its help text) and calls it frombin/fm-bootstrap.sh's detect pass, so the reminder rides the existing session start rather than a new scheduler. Due means the last recorded sweep predates the most recent local Thursday 00:00; an absent or unparseablestate/grossreinschiff.last-sweepreads as never swept and therefore due, and a home dark on Thursday sweeps late instead of skipping the week.FM_GROSSREINSCHIFF_DISABLE=1silences detect mode only;tests/lib.shexports it suite-wide so existing bootstrap silence fixtures still hold.GROSSREINSCHIFF:handling entry inbootstrap-diagnostics(including that it is advisory without the fleet lock, and that staleness is read from the line'slast swept:date rather than its bounded window-open count), an AGENTS.md state-file line, section 6 trigger, and section 13 prefix addition, plusREADME.mdanddocs/scripts.mdrows. Newtests/fm-grossreinschiff.test.shcovers the cadence, the disable knob, and each ladder rung against real git squash fixtures — including one that reproduces the merge-tree conflict trap — and drives the realbin/fm-bootstrap.shfor the due line end to end.Risk Assessment
✅ Low: The follow-up commit resolves all three accepted findings exactly as directed and touches no runtime behavior - the only script change is header comment text that doubles as --help output - while the ladder reorder is provably safe (E implies C for non-ancestors, so it can only relabel landed as landed-vacuous) and the X precondition moves verdicts toward undetermined, which is already barred from every deletion set.
Testing
Ran the dedicated Großreinschiff suite (24 checks, all pass), the bootstrap suite whose silence fixtures the new detect line had broken, the instruction-surface contract tests mapped to the changed AGENTS.md and skill paths, and the runner's coverage guard — all green, nothing to fix. Because passing tests alone would not show the sweep reaching a vessel, I additionally drove the real bin/fm-bootstrap.sh across a simulated week and captured the actual GROSSREINSCHIFF due line, its silence after a recorded sweep, late sweeping after a dark Thursday, the bounded window count on a three-weeks-dark home, and the corrupt-record path; and I applied the skill's documented ladder to the 9 real merged-PR branches in this checkout, where it settles all of them as landed while ancestry alone would call 2 unmerged and the exit-status-dropped content test would call 1 landed branch "adds content" — both rejected shortcuts reproduced on real data. The doc's 52-branch measurement could not be reproduced at that scale here because this gate worktree holds only 9 of those branches; the failure modes it documents were reproduced instead. The change is CLI and instruction-surface only with no rendered UI, so the reviewer-visible evidence is a terminal transcript rather than a screenshot. No transient files were left behind; the worktree is clean.
Evidence: Session-start transcript: what a vessel actually sees across a week
--- Thu 2026-08-06 09:00 (first fleet-wide run day) --- GROSSREINSCHIFF: weekly fleet cleanup sweep is due (last swept: never; this week's window has been open 0 day(s)) - load the grossreinschiff skill --- the vessel runs the sweep and records it --- recorded Grossreinschiff sweep on 2026-08-06 state file: 1786015200 2026-08-06 --- Sat 2026-08-08 11:00 (same week, already swept) --- <silent - nothing printed> --- Wed 2026-08-12 23:59 (last minute before the next window) --- <silent - nothing printed> --- Thu 2026-08-13 00:00 (next window opens) --- GROSSREINSCHIFF: weekly fleet cleanup sweep is due (last swept: 2026-08-06; this week's window has been open 0 day(s)) - load the grossreinschiff skill --- Mon 2026-08-17 08:30 (vessel was dark on Thursday - it sweeps late, not never) --- GROSSREINSCHIFF: weekly fleet cleanup sweep is due (last swept: 2026-08-06; this week's window has been open 4 day(s)) - load the grossreinschiff skill --- Thu 2026-08-27 09:00 (three weeks dark: still due, last-swept date carries the staleness) --- GROSSREINSCHIFF: weekly fleet cleanup sweep is due (last swept: 2026-08-06; this week's window has been open 0 day(s)) - load the grossreinschiff skill --- corrupt record reads as never swept --- GROSSREINSCHIFF: weekly fleet cleanup sweep is due (last swept: never; this week's window has been open 2 day(s)) - load the grossreinschiff skillEvidence: Documented ladder applied to this repo's real merged-PR branches
BRANCH PR LADDER VERDICT SETTLED BY fm/fm-context-reset-mechanism #55 landed A fm/fm-sea-chart-attribute-wayfinder #54 landed A fm/fm-transcript-path-record #53 landed A fm/fm-wake-arm-false-failed #51 landed A fm/fleet-sea-chart-build #47 landed A fm/fleet-validation-grading #43 landed A fm/fm-decisionboard-standard #41 landed A fm/fleet-lavish-tailnet-access #38 landed P fm/fm-snapshot-argv-limit-pin-source #37 landed P ladder totals: A=7 P=2 E=0 C=0 X=0 undetermined=0 ancestry alone would have called 2 of these merged-PR branches unmerged the content test with its exit status dropped would have called 1 of these landed branches 'adds content' (i.e. not landed)Evidence: Ladder demo script (rows A/P/E/C/X implemented verbatim from SKILL.md)
Evidence: Operator-facing --help output (rendered from the script header)
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
🔧 **Review** - 3 issues found → auto-fixed ✅
.agents/skills/bootstrap-diagnostics/SKILL.md:68- The handling guidance says "a large number [of window-open days] is itself worth reporting to the captain because it means this home has been dark", but that number is structurally bounded to 0-6 and can never be large. In bin/fm-grossreinschiff-due.sh:144, window_open_days = (NOW - window_open) / 86400, and window_open = midnight_today - days_back*86400 with days_back in 0..6 (line 111), so the quotient always equals days_back exactly. Worse, the escalation case it targets reads lowest: a home that last swept 2026-08-06 and starts its next session on Thursday 2026-08-27 has missed two full weeks, yet the line prints "window has been open 0 day(s)". The real staleness signal is the line's ownlast swept: <date>field, which the guidance does not mention. As written this is a no-op instruction inside the skill assembled to find no-op instructions (checklist item 3). Fix by pointing the escalation at thelast sweptdate (or by having the script also emit days-since-last-sweep) rather than at window-open days..agents/skills/grossreinschiff/SKILL.md:65- Ladder rung X ("Every path the branch adds is absent from the default branch's tree andgit log <default> -- <path>is empty") is universally quantified over the set of paths the branch adds. For a branch that adds no new paths - one that only modifies or deletes existing files, the common shape of a small fix branch - that set is empty and both conjuncts are vacuously true, so X returns the definitive verdictnot landedon zero evidence instead of falling through toundetermined. The state is concretely reachable and is exactly the one the skill documents as routine: A fails because the fleet squashes, P fails when no forge merge commit is recorded or it is not an ancestor of the default branch (a cherry-pick, a local pipeline mirror with no forge, a closed-unmerged PR), and C is inconclusive because merge-tree conflicts on an old branch whose files the default branch has since edited (the trap documented at docs/grossreinschiff.md and reproduced in tests/fm-grossreinschiff.test.sh:test_content_test_is_inconclusive_once_the_default_branch_moves_on). The failure direction is safe for deletion -not landedkeeps the branch - but it violates the skill's own rule that anything unsettled is reported asundetermined, and property 3 makes anot landedverdict a stop-and-investigate result, so landed work gets reported as real unlanded work needing attention. Guard X with "the branch adds at least one path", otherwise fall through to undetermined..agents/skills/grossreinschiff/SKILL.md:66- Rung E (branch tip tree equals merge-base tree) can never be reached in the stated A-P-C-X-E order. A branch with no content change relative to its merge base cannot makegit merge-tree --write-tree <default> <branch>conflict, so merge-tree always exits 0 and yields exactly the default branch's tree - which is rung C's condition. C therefore settles every content-free branch as plainlandedbefore X or E is ever consulted. The consequence is only a label difference (landedinstead oflanded (vacuous)), so nothing unsafe follows, but it means the ladder as written cannot reproduce the "2 landed vacuously (empty commits)" split that docs/grossreinschiff.md reports from the bridge measurement, and E is a rung that never fires. Either move E ahead of C or drop it and fold the vacuous case into C's reporting.🔧 Fix: correct due-line staleness prose and ladder rung X/E order
✅ Re-checked - no issues remain.
✅ **Test** - passed
✅ No issues found.
./bin/fm-test-run.sh tests/fm-grossreinschiff.test.sh— 24/24 ok (cadence, real bootstrap surfacing, disable knob, ladder rungs A/P/E/C/X on real git squash fixtures)./bin/fm-test-run.sh tests/fm-bootstrap.test.sh— the existing suite the new detect line broke; passes with the tests/lib.sh isolation./bin/fm-test-run.sh tests/fm-instruction-owners.test.sh tests/fm-supervision-instructions.test.sh tests/fm-ensure-agents-md.test.sh— the pure-contract-unit scripts mapped to the changed AGENTS.md and .agents/skills paths; includes the every-skill load-trigger check now covering grossreinschiff./bin/fm-test-run.sh --check-coverage— FM_TEST_COVERAGE ok total=122, the new suite is registeredManual end-to-end: drove the realbin/fm-bootstrap.shagainst a fixture home withFM_GROSSREINSCHIFF_CLOCKat Wed 08-05, Thu 08-06, Sat 08-08, Wed 08-12 23:59, Thu 08-13, Mon 08-17, Thu 08-27, plus a corrupted record — transcript capturedManual:bin/fm-grossreinschiff-due.sh --recordthen re-ran bootstrap to confirm the due line stops for the rest of that week, and inspected the writtenstate/grossreinschiff.last-sweep(<epoch> 2026-08-06)Manual: implemented the SKILL.md ladder rows verbatim and ran them over the 9 merged-PR branches present locally againstorigin/main, alongside the two rejected shortcuts (ancestry-only, merge-tree with exit status dropped)bin/fm-grossreinschiff-due.sh --help— confirmed the operator-facing help is the script header itselfbin/fm-session-start.sh:71- Pre-existing contradiction, not caused by this change and left untouched as out of scope: bin/fm-session-start.sh's header says "five mutating sweeps" (lines 21 and 71) while AGENTS.md section 3 (line 164) and bin/fm-bootstrap.sh (line 87) both say six, and AGENTS.md's own enumeration lists six items. The same header also omits the watcher-service and frequency-monitor convergence steps from its lock-gated set. Worth a follow-up that re-counts the lock-gated sweeps in fm-bootstrap.sh once and corrects the two non-owner statements to match.✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.