Adds a fourth state pillar — Data — to cli-common/docs/working-with-state.md, between the existing Config, Cache, and Secrets pillars. Driver: the new cr (codereview) CLI needs to persist a SQLite run ledger, findings JSON, log streams, and agent outputs that must survive config clear --all. Cache doesn't fit (loss tolerated by definition); config doesn't fit (user didn't author it); there was no slot.
Pillar shape
- Definition: program-managed working state. The program owns the lifecycle; the user shouldn't poke at it directly. Defined negatively as not config, not secret, not cache, with the tiebreaker default to cache when on the fence (drift-prevention without a strict positive criterion).
- Backing (Path A — STATE-flavored, decided over XDG_DATA_HOME):
- Linux:
XDG_STATE_HOME → ~/.local/state/<dir>
- macOS:
~/Library/Application Support/<dir>/data/
- Windows:
%LOCALAPPDATA%\<dir> (explicitly not %APPDATA% — Roaming would sync SQLite + logs + agent outputs over the network for users on roaming profiles)
- Naming rule: per-binary (matches cache §4.1, not config §3) — derived program-managed state has program-specific lifecycle, not credential-scope ownership.
- Format invariants: dir
0700 / file 0600; no atomic-write mandate (open-ended formats — SQLite has its own durability, logs/streams don't want temp-rename); schema migration is fail-loud + migrate (not cache's "version mismatch = miss" — data loss is not tolerated).
- Lifecycle invariants:
- The dir as a whole survives
config clear --all. Pillars have separate lifecycles.
- Whole-dir nuke is explicit and user-invoked (the
purge verb).
- Individual records may be removed by the program under a documented retention policy.
- Command surface: nuclear required, maintenance optional; suggested verb pair
<tool> data purge (nuclear) / <tool> data prune (maintenance). Severity encoded in the verb, not a flag. Both verbs support --dry-run; nuclear's dry-run reports paths-that-would-be-scrubbed. Nuclear obeys the §7.6 cleanup-command recovery contract (must scrub even if data unreadable). User-invoked, not uninstall-triggered.
- Retention (guidance, not mandate): if the dir can grow unboundedly in normal use, the CLI SHOULD declare a size/age/count cap with automatic enforcement at write-time. Generous-but-finite defaults beat unbounded.
- Test isolation:
statedirtest.Hermetic grows from 7 to 8 env vars — XDG_STATE_HOME joins the existing set so an XDG-aware dev env can't bleed into a Linux test run.
Cross-doc
working-with-secrets.md §1.7.2 reframed: no longer "factory reset of the active profile" — now "config + credentials + cache reset". Data pillar is explicitly excluded; users compose config clear --all AND <tool> data purge for the historical full reset.
working-with-state.md §1, §1.1, §2, §5 (new), §6a, §7 (rollout item 7), §8 all updated.
Pressure-test history
- Round 6 Codex pressure-test (2026-05-28):
blockers=1 majors=3 minors=2. All findings disposed in the same revision — Path A backing shift, per-binary naming, retention guidance, secrets-doc cross-update, --dry-run extension, code-comment cleanup. Full disposition table in working-with-state.md §8.
- Round 6 cleanup pass (2026-05-28): post-application re-read surfaced 5 cleanup items (lifecycle invariant contradiction, sticky factory-reset framing, 8-var propagation, resolver overclaim, stale primer). All applied.
- Round 7 confirmation pass recommended before merge — single-round-with-corrections is not the same as the 5-round convergence that the existing three pillars went through.
Scope
- Forward-looking pattern doc —
cr is the first consumer.
- No existing CLI ports to the data pillar; it's additive to the §7 rollout.
cli-common/statedir resolver does not yet expose a Data() method — that lands when the first data-holding CLI does (§7 rollout step 7); the package doc reflects this as not-yet-implemented.
data-pillar-primer.md retained as the "how the decision was reached" companion; superseded banner at top with deltas.
Adds a fourth state pillar — Data — to
cli-common/docs/working-with-state.md, between the existing Config, Cache, and Secrets pillars. Driver: the newcr(codereview) CLI needs to persist a SQLite run ledger, findings JSON, log streams, and agent outputs that must surviveconfig clear --all. Cache doesn't fit (loss tolerated by definition); config doesn't fit (user didn't author it); there was no slot.Pillar shape
XDG_STATE_HOME→~/.local/state/<dir>~/Library/Application Support/<dir>/data/%LOCALAPPDATA%\<dir>(explicitly not%APPDATA%— Roaming would sync SQLite + logs + agent outputs over the network for users on roaming profiles)0700/ file0600; no atomic-write mandate (open-ended formats — SQLite has its own durability, logs/streams don't want temp-rename); schema migration is fail-loud + migrate (not cache's "version mismatch = miss" — data loss is not tolerated).config clear --all. Pillars have separate lifecycles.purgeverb).<tool> data purge(nuclear) /<tool> data prune(maintenance). Severity encoded in the verb, not a flag. Both verbs support--dry-run; nuclear's dry-run reports paths-that-would-be-scrubbed. Nuclear obeys the §7.6 cleanup-command recovery contract (must scrub even if data unreadable). User-invoked, not uninstall-triggered.statedirtest.Hermeticgrows from 7 to 8 env vars —XDG_STATE_HOMEjoins the existing set so an XDG-aware dev env can't bleed into a Linux test run.Cross-doc
working-with-secrets.md§1.7.2 reframed: no longer "factory reset of the active profile" — now "config + credentials + cache reset". Data pillar is explicitly excluded; users composeconfig clear --allAND<tool> data purgefor the historical full reset.working-with-state.md§1, §1.1, §2, §5 (new), §6a, §7 (rollout item 7), §8 all updated.Pressure-test history
blockers=1 majors=3 minors=2. All findings disposed in the same revision — Path A backing shift, per-binary naming, retention guidance, secrets-doc cross-update,--dry-runextension, code-comment cleanup. Full disposition table inworking-with-state.md§8.Scope
cris the first consumer.cli-common/statedirresolver does not yet expose aData()method — that lands when the first data-holding CLI does (§7 rollout step 7); the package doc reflects this as not-yet-implemented.data-pillar-primer.mdretained as the "how the decision was reached" companion; superseded banner at top with deltas.