I0821 — Flattened YAML is cached under .ai/local/cache by content hash
Cache the output of mj_yaml_flatten under .ai/local/cache/flat/<parser sha256>/, keyed by the file's content (held in the entry and compared in full, because hashing the file costs the process a hit is there to save) and the flattener's own version, so a second command on unchanged files does no parsing.
VERIFY wave 1 · p0 · implementation profile · runs alone
Part of M004 — Performance is executable evidence, and the hot path does no canonical work twice.
Implementation is complete and the required evidence is not. It stays here until every token below has a command behind it — completion is gated on evidence, not on a claim.
Objective
Cache the output of mj_yaml_flatten under .ai/local/cache/flat/<parser sha256>/, keyed by the file's content (held in the entry and compared in full, because hashing the file costs the process a hit is there to save) and the flattener's own version, so a second command on unchanged files does no parsing.
Why
doctor flattens 181 files and knowledge nodes 146; across the hook, watch and context the same files are parsed again and again.
Current state
.ai/README.md already reserves local/cache/ for rebuildable caches; nothing uses it.
Desired state
A miss parses and writes atomically; a hit copies; a changed file cannot hit because its bytes are not the ones the entry holds; the cache is bounded by an entry cap in the policy and trimmed oldest first.
Scope
- lib/common.sh
- share/allow/policy.txt
- share/skeleton/policy.yaml
- .ai/repo/policy.yaml
- docs/SCHEMAS.md
- test/cases
Out of scope
- A cross-process cache of command results
Dependencies
What waits on this
- I0840BLOCKED context and its subcommands under a second
- I0841BLOCKED Cache equivalence proved by property-shaped cases
Acceptance criteria
- Byte-identical output of every read-only command with a cold and a warm cache, proved by case 3090
- A changed file is re-parsed, proved by mutation
- The cache never grows past the cap
- yaml_flatten count is unchanged; a new counter flatten_cache_hit reports hits
Validation
- bash test/run.sh 3090_a_flatten_is_cached_by_its_content
- MJ_TIMING=1 bin/majordomus doctor
Evidence required
- flatten_cache
Evidence
| covers | type | command | result | at commit |
|---|---|---|---|---|
| flatten_cache | test | bash test/run.sh 3090_a_flatten_is_cached_by_its_content && MJ_TIMING=1 bin/majordomus doctor | ok 3090: every parseable tracked YAML file (546 of 551) flattens byte-identically to the parser alone, cold and warm, each warm call a counted hit; a same-length change in the same slot is parsed again; truncated, garbage, forged-source, forged-length, empty and unreadable entries are never served and are rewritten; a failed parse prints and exits 3 as before and is never kept; the directory never exceeds a cap of 3, oldest written removed first; no key or 0 is no cache; a changed parser gets another directory; doctor, context, history, prompt list, skills list and check print the same cold and warm in a fresh repository with yaml_flatten counted equally. Per call on 44 issue files: uncached 171-175 ms (about 4.0 ms each), warm 49-68 ms (about 1.2 ms), cold 400-606 ms once (the write and rename). doctor on this repository: yaml_flatten 571 cold and warm, flatten_cache_hit 16 cold and 337 warm, output identical but for clock-driven lines; wall time 511 s cold and 523 s warm at load average 40-62, which the load dominates. The other 234 calls run in processes that never load the policy and stay uncached. | d116266 |
Risk
Hashing a file costs a subprocess too; the cache pays only when a flatten is materially dearer than a hash, which the counters must show before this lands.
Timeline
- started
- 2026-10-16T09:50:41Z
- verified
- 2026-10-16T09:50:55Z
- completed
- —
Those three fields, the evidence above and the state of the dependencies are all the status is made of. There is no status field to disagree with them.
Canonical record: .ai/repo/project/issues/I0821.yaml. Read it back with majordomus plan show I0821.