feat(bin): retain recent Done outcomes as bounded fleet memory - #1211
Closed
yelenplays wants to merge 7 commits into
Closed
feat(bin): retain recent Done outcomes as bounded fleet memory#1211yelenplays wants to merge 7 commits into
yelenplays wants to merge 7 commits into
Conversation
This was referenced Jul 28, 2026
Open
Closed
…ne retention memory
yelenplays
force-pushed
the
fm/firstmate-deck-completion-memory
branch
from
July 30, 2026 02:26
d59055c to
cf4cefb
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
Implement the Firstmate-side memory source requested for Deck using the existing active backlog Done window plus durable archive split. Increase the tracked tasks-axi markdown done_keep policy from 10 to 50 and update its authoritative configuration documentation without duplicating the number elsewhere. Keep finished workers clean-up compatible while ensuring fm-fleet-snapshot still exposes recent structured Done outcomes as bounded memory after live metadata, status, and endpoints are gone. The safe Done shape should include structured title, repo, kind, completion date, and admitted artifact link, but must not expose task notes, decision bodies, report contents, raw status prose, absolute local paths, or arbitrary Done body prose. Prove tasks-axi pruning with an isolated temporary home: at 51 Done items it archives only the oldest and retains exactly the newest 50. Preserve decision holds, dependencies, archive format, Done chronology, session-start size bounds, secondmate isolation, and existing snapshot consumers. Do not edit Firstmate Deck and do not parse data/done-archive.md into Deck.
What Changed
.tasks.tomldone_keepfrom 10 to 50 so the active backlog keeps a 50-item recent-completion window and pruning only archives past it;bin/fm-teardown.shanddocs/configuration.mdnow name thedone_keepsetting instead of restating the number, andtests/fm-instruction-owners.test.shpins the documented value to.tasks.toml.bin/fm-fleet-snapshot.shnow emits Done records as structured identity plus artifact only -body_linesemptied andbody_excerptnull, with alocal mainbody line lifted intolocal_notebefore the drop - so recent outcomes survive a torn-down worker's meta/status/endpoint cleanup without carrying task notes, decision bodies, report prose, or absolute local paths. Unstructured Done lines keep their position but have their raw prose replaced by the fixed marker(unstructured done line).bin/fm-bearings-snapshot.sh --fields bodiesto queued items only (Done rows now carry no bodies) and updated its help and omitted-surface label to match; added regression cases for 51-item pruning (archives the oldest, retains the newest 50) and for Done body suppression after worker cleanup.Risk Assessment
✅ Low: The branch is well-bounded and every intent criterion verified end-to-end against the real tasks-axi binary and the changed scripts, and the single round-1 warning is now fixed with a one-line Done-scoped redaction that I confirmed closes the leak without touching in-flight/queued rows, main_inventory signals, or any other snapshot consumer.
Testing
Exercised the six targeted suites touching this change plus a manual end-to-end driver that reproduces the captain's actual experience. Pruning was proven in an isolated temporary home by closing 51 items through the realtasks-axiverbsfm-teardown.shemits: exactly 50 stay indata/backlog.mdnewest-first, onlyrecent-01moves intodata/done-archive.md, and the## Archived <date>archive format is unchanged. Done-as-memory was proven by renderingbin/fm-fleet-view.shon a home where a ship worker is live, then again after its meta, status log, worktree and tmux endpoint are removed - the worker drops out of Under Way while its Done row keeps id, title, repo, kind, completion verb/date and the admitted artifact (PR URL,data/<id>/report.md, orlocal main), withbody_lines: []andbody_excerpt: null. The same fixture carries a task note, a decision body, status prose, absolute/Users/yelen/private/...paths and a hand-written Done line; running base commit 514f0ab against that identical home leaks all of it through the snapshot, the view andbearings --fields bodies, while the target commit is clean on all five consumer surfaces and keeps the unstructured line in place as(unstructured done line). Queued bodies are still revealed, so the redaction is not over-broad. Session-start bounding, decision holds, dependency resolution and secondmate handoff isolation all stay green, anddone_keepappears only in.tasks.tomlanddocs/configuration.mdwithfm-teardown.shnow deferring to the config. No screenshot or rendered-HTML artifact applies: the change is entirely bash and markdown docs with no HTML, CSS or renderer surface, so the faithful end-user artifact is the CLI transcript offm-fleet-view.sh/fm-fleet-snapshot.shoutput, captured verbatim.Evidence: Full E2E transcript (pruning, Done-memory-after-cleanup, base-vs-target leak check)
Evidence: Reproducible evidence driver script
Evidence: Persisted backlog after 51 completions (50 retained, newest-first)
Evidence: Persisted durable archive (oldest item only)
Evidence: Fleet View after the finished worker is fully cleaned up - Done memory survives
Evidence: Base-vs-target leak check on the identical home
Evidence: tasks-axi pruning at 51 Done items in an isolated temporary home
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
🔧 **Review** - 2 issues found → auto-fixed ✅
bin/fm-fleet-snapshot.sh:381- The new Done body suppression only covers indented lines that attached to a structured row. Verified by running the changed script: with the Done section- [x] good - Structured done (repo: r) (kind: ship) (done 2026-07-01)/local main/PRIVATE BODY /Users/x/secret/Free-form done note about /Users/x/other-secret/INDENTED PRIVATE PROSE /Users/x/third-secret, the structured row is suppressed correctly (body_lines [], body_excerpt null, local_note lifted to "local main", private line absent). But the free-form line breaks body attachment at line 372 (.records[-1].structured == trueis false), so the following indented line falls through to line 378 and is emitted as{"structured":false,"raw":" INDENTED PRIVATE PROSE /Users/x/third-secret"}, andbin/fm-fleet-view.sh:60(dash($r.title // $r.raw)) renders both absolute paths in the Done table. This contradicts the new header claim at bin/fm-fleet-snapshot.sh:29-30 that the suppression keeps "absolute local paths written under a Done item out of every consumer", and it is reachable under the supportedconfig/backlog-backend=manualmode wheredata/backlog.mdis hand-edited. Fix would be to drop or redactrawfor unstructured Done records (or attach stray indented lines in the Done section to the preceding record regardless of structure), but that changes user-visible fm-fleet-view output, so it needs the author's decision..tasks.toml:6- Raising done_keep from 10 to 50 grows the unboundedfm-fleet-snapshot.sh --jsonpayload, which.agents/skills/bearings/SKILL.md:25names as the fallback whenfm-bearings-snapshot.shis unavailable. Measured on a home with 50 Done rows and no live tasks: 52568 bytes, since each Done record carries ~25 fields plusraw. The primary surfaces stay bounded - bearings emitted 1820 bytes withlandedcapped at 6 via FM_BEARINGS_LANDED_PER_HOME, and the session-start digest still caps at 80 items withtasks-axi listordering in_flight then queued then done so Done truncates last. Noting the tradeoff on the documented fallback path; no change required.🔧 Fix: redact unstructured Done prose from fleet snapshot
✅ Re-checked - no issues remain.
✅ **Test** - passed
✅ No issues found.
bash tests/fm-decision-hold-lifecycle.test.sh(includes the newtest_tasks_axi_default_done_keep_archives_oldest_at_51)bash tests/fm-fleet-snapshot-view.test.sh(includes the newtest_done_recent_history_survives_worker_cleanup_without_private_prose)bash tests/fm-teardown.test.sh(updated done_keep prompt assertions, compatible + manual-backend opt-out)bash tests/fm-bearings-snapshot.test.sh(existing snapshot consumers after the--fields bodiesnarrowing)bash tests/fm-session-start.test.sh(session-start digest size bounds preserved)bash tests/fm-backlog-handoff.test.sh(secondmate isolation and Done-record refusal preserved)bash tests/fm-brief.test.sh(crewmate scaffolding regression check)Manual E2E:REPO=<worktree> bash /var/folders/9d/8w50jhgd79x63rgbq_5cyyvm0000gn/T/no-mistakes-evidence/01KYN1F5W2FP0A3RE9T5Q88DBB/run-done-memory-e2e.shManual E2E part 1: isolated temp home + tracked.tasks.toml, 51 x (tasks-axi add/tasks-axi done --note "local main"), asserted 50 retained / 1 archived / recent-01 gone from backlog / recent-02 absent from archive /## Archivedformat / newest-first chronologyManual E2E part 1:tasks-axi list --file <backlog> --limit 80 --fields blocked_by,hold_kind,hold_reason- the exact callbin/fm-session-start.shmakes - over the 50-row Done window, confirming bodies stay out of the startup digestManual E2E part 2:FM_HOME=<home> bin/fm-fleet-view.shbefore and after deletingstate/<id>.meta,state/<id>.statusand the tmux endpointManual E2E part 2:FM_HOME=<home> bin/fm-fleet-snapshot.sh --json | jqon Done records for id/title/repo/kind/completion/artifact plusbody_lines == []andbody_excerpt == nullManual E2E part 2d:bin/fm-bearings-snapshot.sh --json --fields bodiesconfirming queued bodies still revealed and Done rows carry noneManual E2E part 3: leak-check offm-fleet-snapshot.sh --json,fm-fleet-view.sh,fm-bearings-snapshot.sh(default,--json,--fields bodies,paths,actions,endpoints) against the same home on base514f0ab(viagit archive) vs target88327f8Manual E2E part 4:grep -rn done_keep --include='*.md' --include='*.toml' --include='*.sh'andgrep -rl done-archiveto confirm the number is not duplicated and Deck is not coupled to the archivedocs/configuration.md:36- docs/configuration.md hand-copies thedone_keep = 50value from.tasks.toml, and nothing guards the two against drift. The placement policy asks for schema-backed facts to be generated or drift-checked rather than hand-copied, and this change just exercised that exact path (the number moved and the prose had to be updated by hand). A follow-up could add a cheap assertion - e.g. in tests/fm-instruction-owners.test.sh, which already pins the backlog-mechanics owner pointer, or in bin/fm-doc-audience-check.sh - that the value in docs/configuration.md matches the tracked.tasks.toml. Implementing it means editing a test or check script, which this documentation phase must not do.🔧 Fix: point docs at .tasks.toml for done_keep value
2 infos still open:
docs/configuration.md:36- You chose to fix the done_keep drift finding, whose proposed remedy was a test assertion pinning docs/configuration.md to .tasks.toml. This phase may not edit tests or check scripts, so I resolved it structurally instead: the doc no longer statesdone_keep = 50at all, it names thedone_keepsetting and declares the tracked.tasks.tomlthe single owner of its value. Drift is now impossible rather than guarded, and this matches the convention the branch already established in bin/fm-teardown.sh and AGENTS.md lines 318/439 ("the configured recent Done history"), plus AGENTS.md:438 which already names.tasks.tomla co-owner of backlog retention. Flagging it because it is a judgment call that removes a value operators previously read directly from this reference page: the surrounding section still lists literal config values elsewhere (for example.no-mistakes.yamlpinningcommands.lint, and the env-var default block). If you would rather the page keep stating the current number, revert this one line and add the drift assertion in a follow-up that is allowed to touch tests/fm-instruction-owners.test.sh.docs/decision-hold-lifecycle.md:55- The branch added one test case to tests/fm-decision-hold-lifecycle.test.sh (Done retention at 51) and one to tests/fm-fleet-snapshot-view.test.sh (Done body suppression), so both scripts now print one moreok -line than the transcript recorded here. I deliberately did not regenerate that block: it is a date-pinned archived evidence record (2026-07-14 / 07-17 / 07-22) scoped to the decision-hold guarantee, and it is already a curated subset - I ran tests/fm-fleet-snapshot-view.test.sh and it emits 16 ok lines while the doc lists the 3 decision-hold-relevant ones. Both new cases prove Done retention and snapshot redaction, not decision holds, so omitting them follows the same curation rule. If you instead treat these blocks as verbatim command output that must be refreshed on every colocated test addition, this record needs a re-run and update.🔧 Fix: pin documented done_keep value to .tasks.toml
1 info still open:
tests/fm-instruction-owners.test.sh:239- This phase's rules restrict it to documentation files and doc comments and explicitly forbid changing tests. The captain's finding done-keep-drift-assertion-still-missing directed the opposite: restore the documented value in docs/configuration.md and add the drift regression in the test/check owner, explicitly rejecting a doc-only pointer resolution. I followed the captain's directive, so this document-phase commit touches tests/fm-instruction-owners.test.sh in addition to docs/configuration.md. The test change is purely additive and assertion-only (no product code, no behavior change): it reads done_keep from the tracked .tasks.toml and asserts docs/configuration.md quotes the same number. Flagging so the outer executor knows the phase commit is not doc-only and can route it accordingly if a later gate expects a documentation-only diff.✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.