Build the shared cli-common state components — §6 step 1 ("commons-first") of docs/working-with-state.md. Foundational layer; no CLI port in this issue (ports are per-unit, later, each with its own §3.2 migration matrix).
Scope (§5)
(a) Path/dir resolver (§5a) — owns the genuinely-common policy, not a thin os.User*Dir()+Join wrapper:
- credential-scope config-dir naming rule (§3) + per-binary cache-dir rule (§4.1)
- create vs. no-create split (resolver must not
mkdir on dry-run / config clear paths)
- a migration-source enumeration seam so each CLI's bespoke legacy probing plugs in without the resolver trying to be generic about legacy layouts
- does not ban a CLI from calling
os.User*Dir() for its own legacy-probe paths
(a) 7-var hermetic test helper (§3.1) — overrides the full set: HOME, USERPROFILE, AppData, LocalAppData, XDG_CONFIG_HOME, XDG_CACHE_HOME, XDG_DATA_HOME. Ships once here; no CLI re-derives the list. Load-bearing (HOME-only is a Windows real-dir leak).
(b) Tier-1 cache core (§5b) — directory-agnostic:
Envelope[T]{Resource,Instance,FetchedAt,TTL,Version,Data} + ReadResource[T]/WriteResource[T]
- atomic temp-file-in-same-dir → chmod 0600 → rename; dir 0700
- version-mismatch-as-miss (schema bumps self-heal)
- freshness
Classify/Age/Status (Fresh|Stale|Uninitialized|Manual|Unavailable; manual = never auto-expire)
- cache path from an injected
Locator{Root, InstanceKey} — receives Root, never derives it
Tier 2 (registry/DAG/fetchers/refresh wiring) is explicitly deferred (§5b rule-of-three; post-cfl).
Out of scope
- Any CLI port / config relocation / §3.2 migration matrix (per-unit, later)
- Tier-2 cache layer
- Cutting the INT-310 semver tag (only after the consumer matrix is green — §5 guardrail)
Acceptance
- One PR, decomposed commits: (1) resolver, (2) hermetic helper, (3) tier-1 cache core
make check green (tidy + lint + test); stdlib-only (no go.sum)
- Unit tests for resolver naming/create-split, hermetic helper env coverage, envelope round-trip / atomic-write / version-miss / freshness classification
- Codex architect convergence (blockers=0 majors=0)
Jira: MON-5364 (child of INT-310, Feature, 5pts, Sprint 71)
Build the shared
cli-commonstate components — §6 step 1 ("commons-first") ofdocs/working-with-state.md. Foundational layer; no CLI port in this issue (ports are per-unit, later, each with its own §3.2 migration matrix).Scope (§5)
(a) Path/dir resolver (§5a) — owns the genuinely-common policy, not a thin
os.User*Dir()+Joinwrapper:mkdiron dry-run /config clearpaths)os.User*Dir()for its own legacy-probe paths(a) 7-var hermetic test helper (§3.1) — overrides the full set:
HOME,USERPROFILE,AppData,LocalAppData,XDG_CONFIG_HOME,XDG_CACHE_HOME,XDG_DATA_HOME. Ships once here; no CLI re-derives the list. Load-bearing (HOME-only is a Windows real-dir leak).(b) Tier-1 cache core (§5b) — directory-agnostic:
Envelope[T]{Resource,Instance,FetchedAt,TTL,Version,Data}+ReadResource[T]/WriteResource[T]Classify/Age/Status(Fresh|Stale|Uninitialized|Manual|Unavailable;manual= never auto-expire)Locator{Root, InstanceKey}— receivesRoot, never derives itTier 2 (registry/DAG/fetchers/refresh wiring) is explicitly deferred (§5b rule-of-three; post-cfl).
Out of scope
Acceptance
make checkgreen (tidy + lint + test); stdlib-only (no go.sum)Jira: MON-5364 (child of INT-310, Feature, 5pts, Sprint 71)