Skip to main content
← Back to list
01Issue
BugShippedSwamp CLI
Assigneesstack72

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  0B

Expected

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 get also serves latest, so it returns the empty one too.
  • The real content survives at the earlier version and swamp data versions lists it, but neither report get nor data get takes a version selector. The only way I found to read it back is opening .swamp/data/<type>/<model-id>/<record>/<version>/raw directly, 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:

  1. 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.

  2. swamp data gc then 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 gone

    The 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: 5 but has never been collected — so a single swamp data gc would 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:

  1. Treat an empty markdown return as "not applicable" — skip both persistence and rendering.
  2. 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)
02Bog Flow
OPENTRIAGEDIN PROGRESSSHIPPED+ 1 MOREASSIGNED+ 2 MOREREVIEW+ 4 MOREPR_MERGED+ 1 MORECONTRIBUTOR_NOTIFIED

Shipped

7/28/2026, 3:44:26 PM

Click a lifecycle step above to view its details.

03Sludge Pulse
stack72 assigned stack727/28/2026, 2:50:14 PM
Editable. Press Enter to edit.

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.