Relationships
#1443 Empty report returns persist 0-byte versions that mask the real report — and `swamp data gc` then deletes the real version, keeping the empties
Opened by vcjdeboer · 7/28/2026· Shipped 7/28/2026
A method-scoped report that returns empty markdown still persists a 0-byte
version. That version becomes latest, so swamp report get returns nothing
and the last real report is masked. And because the version cap is applied by
version number rather than by content, a later swamp data gc will silently
discard the real report and keep the empty ones.
Why a report returns empty
Reports attach to a model type and run after every method execution. The
report contract is name, description, scope, optional labels, and
execute — there is no way to declare which methods a report applies to. So a
report that is only meaningful for one method has exactly one option: return
{ markdown: "", json: {} } for the others. That is the sanctioned idiom;
@swamp/software-factory's own work-item-summary report uses it in four
places.
The side effect is that opting out costs you the previous report.
Reproduce
With @swamp/software-factory@2026.06.24.1 and a factory with a started work
item — its work-item-summary report returns empty for every method except
summary:
$ swamp model method run <factory> summary --input workItem=ITEM-1
$ swamp report get @swamp/software-factory/work-item-summary --model <factory> --markdown
## @swamp/software-factory/work-item-summary
Model: <factory> … | v5 | …
# Work Item: ITEM-1
Currently: active at build (cycle 1) ← real content
$ swamp model method run <factory> status # unrelated method; report returns empty
$ swamp report get @swamp/software-factory/work-item-summary --model <factory> --markdown
## @swamp/software-factory/work-item-summary
Model: <factory> … | v6 | …
← nothing
$ swamp data list <factory>
report-swamp-software-factory-work-item-summary v6 text/markdown 0BExpected
A report whose execute returns empty markdown should not persist a version and
should not be rendered. The previous version stays latest.
Actual
A 0-byte version is written and becomes latest.
swamp report get— the documented retrieval path — returns an empty report.swamp data getalso serveslatest, so it returns the empty one too.- The real content survives at the earlier version and
swamp data versionslists it, but neitherreport getnordata gettakes a version selector. The only way I found to read it back is opening.swamp/data/<type>/<model-id>/<record>/<version>/rawdirectly, which is the thing the CLI exists to avoid.
What this looks like on a real factory
Not synthetic. A factory of mine that has been driven normally for months, with eight work items:
$ swamp report get @swamp/software-factory/work-item-summary --model <factory> --markdown
## @swamp/software-factory/work-item-summary
Model: <factory> | Scope: method | v356 | 2026-07-08T21:00:51
← empty
$ swamp data versions <factory> report-swamp-software-factory-work-item-summary
356 versions; 353 of them are 0 B.
Exactly three carry content — v164 (24196 B), v179 (28015 B), v181 (28015 B),
all written within six minutes of one another.Three real summaries out of 356 writes. The other 353 are empty artefacts left
by unrelated method calls, and because the newest of them is latest, the
documented way to read the report has returned nothing ever since.
Two consequences:
Masking is immediate and silent. A driver loop that calls several methods per cycle masks a real report within seconds of producing it, with no error anywhere. There is no signal that the content you are looking for exists at an older version.
swamp data gcthen deletes the real report and keeps the empty ones. Measured on a clean repo. A record with nine versions, only v2 holding content:before: v9 v8 v7 v6 v5 v4 v3 [v2 = 262B] v1 $ swamp data gc -y GC complete: deleted 0 expired items, 20 excess versions reclaimed after: v9 v8 v7 v6 v5 ← all 0 B; v2 is goneThe version cap is applied by version number with no regard for content, so the documented maintenance command preserves five empty placeholders and permanently deletes the only work product. On the real factory above, the record declares
garbageCollection: 5but has never been collected — so a singleswamp data gcwould delete v164, v179 and v181 and retain five 0-byte versions.
It also renders a frame — header, blank line, horizontal rule — which reads as a broken report. That is what led me to file swamp-club/swamp#1043 against the extension when the cause is here; the extension was doing the only thing the contract allows.
Not specific to @swamp/software-factory: it applies to any scope: "method"
report that handles a subset of a model type's methods. Reproduces identically
on extension 2026.06.16.1 and 2026.06.24.1, so it is not a regression in
either.
Suggested fix
Either of these closes it; the second is the better shape:
- Treat an empty
markdownreturn as "not applicable" — skip both persistence and rendering. - Let the report contract declare applicability, e.g. an optional
methods?: string[], so a report can decline to run rather than run and return nothing.
Environment
- swamp
20260728.002907.0-sha.a809c490 - macOS 15 (darwin-aarch64), bundled deno 2.8.3
- Repro extension:
@swamp/software-factory@2026.06.24.1(registry latest)
Shipped
Click a lifecycle step above to view its details.
stack72 commented 7/28/2026, 3:44:33 PM
Thanks @vcjdeboer for reporting this! The fix has been merged and a release is on its way. We appreciate your contribution to swamp.
Sign in to post a ripple.