fix: stream fleet-sized JSON into snapshot projections - #1442
Open
HelloWorldSungin wants to merge 2 commits into
Open
fix: stream fleet-sized JSON into snapshot projections#1442HelloWorldSungin wants to merge 2 commits into
HelloWorldSungin wants to merge 2 commits into
Conversation
added 2 commits
August 1, 2026 01:35
/bearings and /fleet failed outright in any home with a real backlog: bin/fm-fleet-snapshot.sh: line 567: jq: Argument list too long fm-fleet-snapshot: main inventory summary failed The cause is the per-argument limit, not the total one. Linux caps a SINGLE argv string at MAX_ARG_STRLEN (32 pages = 131072 bytes), independently of the much larger ARG_MAX, so execve fails with E2BIG once one --argjson payload crosses 128 KiB even though total argv size is far under the ceiling. The primary home's backlog JSON is 216172 bytes. Every jq payload whose size grows with the fleet is now bound with --slurpfile from a stream and unwrapped in the filter by a local `doc` helper, which also preserves --argjson's fail-closed behavior on an empty payload. Scalars, single paths, and per-record documents bounded by this script's FM_SNAPSHOT_* read bounds stay on --arg/--argjson. Sites converted: main inventory, secondmate home summary, the top-level assembly, the secondmate registry/task union, the per-secondmate record and its accumulator, the secondmate landed projection, the parent-evidence reconciliation, and Bearings' candidate-PR rows and accumulator. No schema, field, bound, or TOON change: output is byte-identical to the pre-bug behavior on a fixture the old code could still handle.
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
Fix /bearings, which is completely broken in any firstmate home with a real backlog: bin/fm-bearings-snapshot.sh and bin/fm-fleet-snapshot.sh --json both exit 1 with 'jq: Argument list too long' (the same shared snapshot code also broke /fleet).
Root cause, and a correction to the original bug report: the failure is the PER-ARGUMENT limit, not the total one. Linux caps a SINGLE argv string at MAX_ARG_STRLEN (32 pages = 131072 bytes), independently of the much larger total ARG_MAX. The primary home's backlog JSON is 216172 bytes, so passing it via --argjson makes execve fail with E2BIG even though total argv size is far under ARG_MAX. The backlog note blaming ARG_MAX was wrong.
Deliberate decisions made while doing the work, so they are not mistakes:
Chosen fix shape: every jq payload whose size grows with the fleet is now bound with --slurpfile from a process substitution and unwrapped inside the filter by a small local 'def doc($v): ($v[0] // error(...))' helper. The reference patch in the task brief used --slurpfile plus a leading rebinding line and was explicitly offered as evidence the approach works, not as a mandate; I kept that shape because it keeps each payload's name adjacent to its value and leaves the filters' data model untouched. The 'doc' helper is deliberate: plain $x[0] would turn an empty payload into a silently-null projection, whereas --argjson used to hard-error, so 'doc' preserves the previous fail-closed behavior.
Scope of the audit was deliberately wider than the three sites that fail today, as the task required. Nine sites were converted: main inventory, secondmate home summary, the top-level assembly, the secondmate registry/task union, the per-secondmate record, the record accumulator, the final secondmate assembly, the secondmate landed projection, the parent-evidence reconciliation, plus Bearings' candidate-PR rows and their per-repo accumulator. The per-secondmate summary was the sharpest non-obvious one: its own byte bound FM_SNAPSHOT_SECONDMATE_MAX_BYTES defaults to 262144, twice the argv ceiling, so it could fail on its own.
Deliberately NOT converted: scalars, single paths, and per-record documents already bounded by the script's FM_SNAPSHOT_* read bounds (parent activities bounded at 65536 bytes / 20 records, terminal evidence at 4096, registry at 65536 / 40 records, per-task fields derived from one task's own bounded reads). These cannot grow with the fleet, and argv keeps them adjacent to the names they bind. That reasoning is recorded as a comment in fm-fleet-snapshot.sh so a future reader knows the split was considered rather than missed.
Explicitly out of scope by instruction: no trimming, capping, or truncating of the backlog; no restructuring of the snapshot's data model or bounds; no change to schema, field names, bounds, or TOON rendering. Output was verified byte-identical to pre-fix behavior across --json, TOON, and --secondmate-home-summary on a fixture the old code could still handle.
The regression test in tests/fm-bearings-snapshot.test.sh was required to actually fail without the fix, and it does ('not ok - canonical snapshot failed on an oversized backlog'). It deliberately asserts its own precondition - that the parsed backlog JSON exceeds 131072 bytes - so it fails loudly rather than silently becoming a no-op if the record shape ever shrinks. It was colocated in the existing bearings test file rather than a new runner, per repo convention.
AGENTS.md was deliberately left untouched. This is script mechanics, which firstmate's own coding guidelines route to the script's header/comments rather than the always-loaded contract file; adding it to AGENTS.md would violate that file's size-discipline rule.
Constraint context: this is firstmate's shared tracked material, so repo style rules apply - one sentence per line in tracked prose, plain dash never em dash, shellcheck-clean bin scripts, colocated tests. bin/fm-lint.sh and bin/fm-doc-audience-check.sh both pass. This account has pull-only access upstream, so the PR is expected to wait for upstream approval.
What Changed
jqinstead of passing it through argument strings, preventing/bearingsand/fleetfailures at the per-argument limit.Risk Assessment
✅ Low: The change consistently moves fleet-growing JSON payloads off argv while preserving the existing data model, fail-closed behavior, output contract, and explicitly scoped bounds.
Testing
Diff/status baselines were clean, focused regression and adjacent-path tests passed, the base-versus-target counterfactual confirmed the argv defect, manual CLI evidence demonstrated all oversized snapshot modes, and small-fixture output remained byte-identical.
Evidence: End-to-end oversized CLI transcript
Oversized 180,063-byte backlog produced valid fleet JSON, a valid secondmate summary accounting for 200 landed records, and actual Bearings TOON with zero network calls.Evidence: Base-commit counterfactual
Base commit fails on the identical oversized workload withjq: Argument list too long.Evidence: Output compatibility evidence
Fleet JSON, secondmate summary, Bearings JSON, and Bearings TOON were byte-identical to the base implementation on a supported-size fixture.Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
✅ **Review** - passed
✅ No issues found.
✅ **Test** - passed
✅ No issues found.
Baseline inspection:git status --short,git diff --stat,git diff --name-only, andgit diff --check a8057666..cd026797.Focused selectortests/fm-bearings-snapshot.test.sh::test_oversized_backlog_survives_the_per_argument_execve_limit.Focused selectorstest_include_prs_is_the_only_fetch_path,test_pr_repository_cap_and_expansion,test_per_repository_pr_cap_is_disclosed, andtest_projection_and_toon_fail_closed.Ran the oversized regression against base commita805766622afb291681a15a80c5135517f1cb5ed; confirmed the expectedArgument list too longfailure.Manually exercisedbin/fm-fleet-snapshot.sh --json,--secondmate-home-summary, and defaultbin/fm-bearings-snapshot.shwith a parsed 180,063-byte backlog.Usedcmpto compare target and base output streams for fleet JSON, secondmate summary, Bearings JSON, and Bearings TOON on a supported-size fixture.✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.