gt compact is a no-op across all wisp categories while gt compact report counts tens of thousands as active
Summary
gt compact evaluates zero wisps for TTL compaction, while gt compact report — the same command family, same invocation — reports 22,582+ active wisps. Two queries over the same data disagree completely. The result is unbounded wisp growth.
This appears to be the root cause of Dolt wisp-table bloat (we hit an 11,491-wisp threshold breach, alert threshold is 800) and of repeated manual reaper dispatches to clear a backlog that continuous compaction should have prevented.
Reproduction
$ gt compact --dry-run --json
{
"promoted": null,
"deleted": null,
"skipped": 0,
"orphaned_wisp_deps": 0
}
$ gt compact report --dry-run
## Wisp Compaction: 2026-09-15
### Summary
| Category | Deleted | Promoted | Active |
|----------|---------|----------|--------|
| Patrols | 0 | 0 | 444 |
| Errors | 0 | 0 | 10 |
| Untyped | 0 | 0 | 22582 |Reproduced from multiple directories and rig scopes. A separate agent on the same town independently observed 27,553 active Untyped wisps with the same 0/0 result.
Why skipped: 0 is the key signal
gt compact --help documents TTLs for every category, including default (untyped): 24h and patrol: 24h. So with 444 patrol wisps and 22,582 untyped wisps present, compaction should either act on them or skip them.
skipped: 0 means zero wisps entered the evaluation at all — it is not scanning 22k and finding none past TTL, it is scanning an empty set. Meanwhile the report path sees 22,582. That points at a scope/filter mismatch between the two code paths rather than a TTL-comparison bug: candidate selection for compaction returns nothing, while the counting query returns everything.
Note on scope — this is broader than one category
It was initially diagnosed as "TTL not applied to the Untyped category". Our repro suggests that framing is too narrow: Patrols (444, 24h TTL) and Errors (10, 7d TTL) are also 0/0. Compaction appears to be a no-op across all categories, not just untyped. Worth checking the shared candidate-selection query rather than per-category TTL handling.
Impact
- Unbounded wisp accumulation; Dolt table growth with no automatic reclamation.
- Threshold alerts fire (11,491 vs 800) and get triaged as their own incidents, with manual reaper dispatches repeatedly summoned to clear a backlog that should never accumulate.
- Because the report path reports plausible-looking numbers, the failure is silent: a digest shows 'Active: 22582' without signalling that nothing is ever being compacted.
Suggested direction
Compare the candidate-selection predicate used by the compaction pass against the counting predicate used by compact report. The report path clearly resolves the wisp set correctly; the compaction path does not. Likely a differing rig scope, status filter, type predicate, or data source (wisp JSON files vs the Dolt table) between the two.
Environment: gt binary 2026-09-11, town at ~/gt, daemon uptime 41d.
Source: gastownhall/gastown