Observation
On a busy monorepo, the dominant cost of `ferrflow check` is the per-package commit walk from last-tag to HEAD. On every CI run for the same SHA, this walk re-emits the same result. Today there's no caching across runs — we pay the walk every time, even when the input (HEAD sha + tag refs) hasn't changed since the previous `ferrflow` invocation.
For a 200-pkg monorepo with 10k commits, the walk is the largest chunk of the ~800ms current scan time.
Proposal
Persist a small JSON cache under `.git/ferrflow-cache/` (or `$XDG_CACHE_HOME/ferrflow/`) keyed on:
- HEAD sha
- tag refs sha hash (fnv of `refs/tags/* → oid` map)
- config file sha (.ferrflow content hash)
Layout:
```
.git/ferrflow-cache/
<head_sha>-<tags_hash>-<config_hash>.json # 4 KB typically
```
Cache content: per-package `{ commits: [...], last_tag: "...", bump_type: ... }`.
On `check` / `status` / `version` / `tag`:
- Compute the cache key
- If file exists and < 5 min old → load + return
- Otherwise compute + write back
`release` skips the cache (full recompute — too risky to trust stale data when actually mutating).
Numbers (rough estimate)
- Cache miss path: same as today (~800ms on mono-large)
- Cache hit path: ~15ms (JSON parse + key check)
- CI runs typically loop on identical SHAs during a PR (lint → build → test → check) → 3-4 `ferrflow check` invocations on the same head. Saves ~2-3s per PR run.
Edge cases
- Disabled if `.git` is read-only (CI sometimes mounts it ro after checkout)
- Invalidate on `.ferrflow` mtime change as well (config edits)
- LRU prune over 50 entries / 7 days old
- Add `ferrflow cache clear` subcommand for support
Test plan
- Cache miss writes file; cache hit returns identical result to fresh compute
- Tampering with the cache file → fall back to recompute (warn, don't crash)
- Read-only `.git` doesn't error, just no-ops the cache write
Related #471 (perf followups), #504 (in-run caching). This is the across-run analog.
Observation
On a busy monorepo, the dominant cost of `ferrflow check` is the per-package commit walk from last-tag to HEAD. On every CI run for the same SHA, this walk re-emits the same result. Today there's no caching across runs — we pay the walk every time, even when the input (HEAD sha + tag refs) hasn't changed since the previous `ferrflow` invocation.
For a 200-pkg monorepo with 10k commits, the walk is the largest chunk of the ~800ms current scan time.
Proposal
Persist a small JSON cache under `.git/ferrflow-cache/` (or `$XDG_CACHE_HOME/ferrflow/`) keyed on:
Layout:
```
.git/ferrflow-cache/
<head_sha>-<tags_hash>-<config_hash>.json # 4 KB typically
```
Cache content: per-package `{ commits: [...], last_tag: "...", bump_type: ... }`.
On `check` / `status` / `version` / `tag`:
`release` skips the cache (full recompute — too risky to trust stale data when actually mutating).
Numbers (rough estimate)
Edge cases
Test plan
Related #471 (perf followups), #504 (in-run caching). This is the across-run analog.