{ "sourceHead": "c9e7a422be47b69d855f48cfce9024457e0dd4fc", "sessionId": "6ce43790-8ac2-45d1-a7ef-a764737578e3", "publicReportSha256": "4be3eeaaef4f9a8f28c09d0d86305a900902aa774bb840150f130875d70450ac", "writeLine": 2717, "ackLine": 2721, "report": "# Plan: cache profile summaries in one process\n\n> Reviewed by `/plan-ceo-review` on 2026-09-10, mode HOLD SCOPE. Original plan\n> text is preserved below. Accepted amendments are marked `[A1]`..`[A5]` inline\n> and defined in the review's Decision Registry. Nothing else in scope changed.\n\n## Measured problem and accepted scope\nThe existing profile-summary service has one active process. A one-week trace\nshows repeated reads of about 900 hot keys: DB CPU is 70%, with read p95 120 ms.\nAdd a process-local LRU wrapper to the existing repository. Acceptance targets\nare at least 60% cache hits, DB CPU below 50%, and read p95 below 60 ms, with the\nexisting error-rate and correctness SLOs unchanged. This is an internal backend\nchange with no UI, API, schema, pricing, or developer onboarding change.\n\n## Existing contracts retained\n- All reads and writes use this repository in the same process; there are no\n external DB writers. Multi-process operation remains unsupported and startup\n rejects that configuration while caching is enabled.\n- Authentication and authorization run before repository access. Keys encode\n the authenticated tenant ID and validated profile ID without ambiguity.\n Values are immutable profile-summary DTOs; secrets and cache keys are never\n logged. Cached results cannot bypass authorization.\n- The existing LRU adapter supports 1000 entries, a 16 MiB byte cap, and a\n 30-second TTL. Recorded hot data fits those limits. Absent records use a\n distinct sentinel with a 10-second TTL; undefined means a cache miss.\n- Cache operations are synchronous and atomic in the single JS event loop.\n On any cache failure the existing adapter bypasses the cache until an empty\n cache is reinitialized; repository errors keep the current typed API error\n mapping. The existing per-key\n single-flight wrapper coalesces simultaneous misses and releases on failure.\n- A read already in progress when a write commits may return its earlier DB\n snapshot to that caller. Every read begun after that write completes must\n observe the committed version. TTL expiry is not a substitute for this rule.\n **(Consistency invariant, unchanged. Review finding F1 shows the original\n sketch below did not meet it; amendment [A1] repairs the sketch.)**\n\n## Proposed wrapper integration\nKeep the current read-through repository interface and shared adapters.\n\n**Original sketch (superseded by [A1]/[A2], retained for the record).** It\nstated: \"no additional version checks or coordination between a cache fill and\na write are proposed.\"\n\n```javascript\n// ORIGINAL \u2014 violates the consistency invariant (see F1 schedule S2)\nasync function readProfile(key) {\n const cached = cache.get(key);\n if (cached !== undefined) return cached;\n const value = await repository.read(key);\n cache.set(key, value);\n return value;\n}\nasync function writeProfile(key, update) {\n const saved = await repository.write(key, update);\n cache.delete(key);\n return saved;\n}\n```\n\n**Amended ordering rules [A1][A2][A4].** A fill may land only if no write for\nthat key completed after the fill's DB read began. Write completion (success or\nfailure) invalidates, in one synchronous tick: the cache entry, the in-flight\nfill slot, and the single-flight slot, so later callers start a fresh DB read.\n\n```javascript\n/*\n * Fill/write ordering (keep this diagram current with the code):\n *\n * R1 miss \u2500\u2510 await read (V0) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 resolve: slot gone \u2192 DROP fill\n * W \u2502 await write \u2500\u2500 commit V1 \u2500\u2500 delete entry + forget slot (sync)\n * R2 \u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500 begins after W: miss \u2192 fresh read \u2192 V1 \u2713\n */\nasync function readProfile(key) {\n if (!flag.enabledFor(key)) return repository.read(key); // [A4] deterministic per-key bucket\n const cached = cache.get(key);\n if (cached !== undefined) return cached; // includes ABSENT sentinel\n return inflight.run(key, async (slot) => { // existing single-flight, now slot-aware\n const value = await repository.read(key); // may resolve after a commit\n if (inflight.current(key) === slot) cache.set(key, value); // else stale fill dropped, metric++\n else metrics.increment('profile_cache.stale_fill_dropped');\n return value;\n });\n}\n\nasync function writeProfile(key, update) {\n if (!flag.enabledFor(key)) return repository.write(key, update);\n try {\n return await repository.write(key, update);\n } finally { // [A2] success, typed failure, or indeterminate\n cache.delete(key); // same tick: entry,\n inflight.forget(key); // fill slot + single-flight slot\n }\n}\n```\n\n`inflight.forget(key)` is a new method on the existing single-flight wrapper.\nIt drops the current slot so joiners arriving after the write start a fresh\nread; callers already joined keep the in-progress promise (they began before\nthe write completed, which the invariant permits).\n\n## Verification and rollout\nExisting repository contract tests cover tenant isolation, key validation,\nabsence, DB failures, and authorization. New wrapper tests cover hit/miss,\neviction and byte limits, TTL, adapter-failure fallback, successful-write\ninvalidation, ~~failed-write preservation~~ **failed-write invalidation [A2]**,\nand concurrent-miss coalescing.\n\nAdded by review (each is a required assertion, see Section 6):\n- **[A1] Stale-fill ordering tests** with controlled pause/release points for\n schedules S2, S3 and S4 in the Async Ordering record.\n- **[A2] Indeterminate write test**: write rejects after the DB has committed;\n the next read returns the committed value.\n- **[A4] Flag transition tests**: read/write bucket agreement per key; any\n flag change clears entries and in-flight slots; flag evaluation error\n resolves to bypass.\n- **[A5] Clock injection**: TTL and sentinel-TTL tests use an injected clock.\n\nThe rollout uses the existing runtime feature flag: enable for 10% of keys,\nthen 50%, then all keys after one healthy hour at each stage. Bucketing is a\ndeterministic hash of the key, evaluated identically in read and write [A4].\nMonitor hit/miss, eviction, cache bytes, fallback errors, DB CPU, and read p95\nwithout raw IDs. **[A3]** Also emit `stale_fill_dropped`, `set_rejected_oversize`,\n`flag_eval_error`, and the gauge `cache_bypass_active`; alert when bypass is\nactive for more than 5 minutes or hit rate is below 60% for 15 minutes after\nthe 100% stage. Bypass entry logs at WARN with the adapter error class only.\nOn error-rate or latency regression, disable the flag immediately; both reads\nand writes bypass the cache while disabled, and enabling creates an empty cache.\n**[A4]** Any change to the flag value or bucket percentage reinitializes an empty\ncache and clears in-flight slots; a flag evaluation failure resolves to bypass.\nCold starts remain within the existing DB capacity. The service owner monitors\nthe rollout and records the results against the acceptance targets.\n\n## Out of scope\nDistributed caching, cross-process coherence, prewarming, changing consistency\nsemantics, or adding new product surfaces. The repository interface preserves a\nfuture replacement path without introducing a general cache framework now.\n\n---\n\n## CEO Review Record (HOLD SCOPE)\n\nRun conditions: automated execution, no human present. System audit, preamble,\ntelemetry, codebase exploration, learnings search, and brain context were\nskipped per run instructions (no shell available). Every decision point was\nauto-resolved to the skill's recommended option and is recorded below.\nNo repository code was read or changed.\n\n### Step 0\n\n**0A Premise.** Problem is measured, not hypothetical: one week of traces, ~900\nhot keys, DB CPU 70%, p95 120 ms. Doing nothing means the DB saturates with\ngrowth. Acceptance targets measure the real outcome (DB CPU, p95), not a proxy.\nRight problem, most direct path. No change.\n\n**0B Existing code leverage.** Sub-problem to existing code map:\n\n| Sub-problem | Existing | Reused? |\n|---|---|---|\n| Bounded storage, TTL, byte cap | LRU adapter | Yes |\n| Miss stampede | Per-key single-flight wrapper | Yes, extended with `forget` [A1] |\n| Gradual rollout / kill switch | Runtime feature flag | Yes |\n| Error surface | Typed API error mapping | Yes |\n| Tenant isolation, authz | Contract tests, pre-repo authz | Yes |\n\nNothing is rebuilt.\n\n**0C Dream state.**\n```\n CURRENT STATE THIS PLAN 12-MONTH IDEAL\n every read hits DB ---> read-through LRU in-process, ---> same interface, adapter\n DB CPU 70%, p95 120ms invariant-safe fills, swappable to shared cache\n flagged rollout if multi-process arrives\n```\nPlan moves toward the ideal; the retained repository interface is the seam.\n\n**0C-bis Implementation alternatives (D1).**\n\n| | A: Minimal (original sketch) | B: Sketch + fill/slot invalidation | C: Row-version check on fill |\n|---|---|---|---|\n| Effort | S | S | M |\n| Risk | High: violates invariant (F1) | Low | Med |\n| Completeness | 4/10 | 10/10 | 10/10 but needs schema version column |\n| Reuses | adapter, flag, single-flight | same + single-flight extension | same + DB change |\n\nD1 auto-decided: **B**. C is eliminated by the plan's \"no schema change\"\nconstraint. A fails the stated invariant.\n\n**0F Mode (D2).** HOLD SCOPE, set by the user for this run. Not auto-decided.\n\n**0D HOLD analysis.** Files touched: wrapper, single-flight adapter (one\nmethod), flag helper, tests. Under the 8-file smell threshold. Minimum set is\nthe wrapper plus the repairs required to meet the retained invariant; nothing\ndeferrable without breaking a stated requirement.\n\n**0E Temporal interrogation.** Resolved now: where the flag check lives (both\nentry points, per key); fill/write ordering (F1); slot invalidation hook (F1);\nindeterminate write handling (F2); flag transition semantics (F5); test clock\n(F8). Human ~6h / CC ~40 min for the full amended scope.\n\n### Decision Registry (all auto-resolved to recommended option)\n\n| ID | Decision | Options | Chosen | Why |\n|---|---|---|---|---|\n| D1 | Approach | A 4/10, B 10/10, C 10/10 blocked | B | Meets invariant, no schema change |\n| D2 | Mode | 4 modes | HOLD SCOPE | User-specified |\n| D3 | F1 remedy | A drop-stale-fill + slot forget 10/10; B per-key read lock 8/10; C do nothing 3/10 | A \u2192 [A1] | Explicit, bounded state, no added latency |\n| D4 | F2 remedy | A invalidate in `finally` 10/10; B allowlist pre-commit errors 9/10; C keep preservation 5/10 | A \u2192 [A2] | Smallest diff, no error taxonomy coupling |\n| D5 | F4 remedy | A metrics + alerts + WARN log 10/10; B metrics only 6/10 | A \u2192 [A3] | Zero silent failures |\n| D6 | F5 remedy | A clear on any flag change + bypass on eval error 10/10; B document only 4/10 | A \u2192 [A4] | Edge cases over documentation |\n| D7 | F8 remedy | A injected clock 10/10; B real sleeps 5/10 | A \u2192 [A5] | Flakiness is a test defect |\n| D8 | TODOS.md | none proposed | 0 items | All evidenced gaps fixed in scope |\n| D9 | Next review | A eng review; C manual | A recommended | Required gate; not runnable here |\n\nLake Score: 7/7 recommendations chose the complete option.\n\n### Findings Registry\n\n| ID | Sev | Section | Evidence | Remedy | Residual risk | Verification |\n|---|---|---|---|---|---|---|\n| F1 | CRITICAL GAP | 1, 2, 4, 5, 6 | Original sketch: fill after `await repository.read` has no guard; plan text says no fill/write coordination. Schedule S2 makes a post-write reader see V0 for 30 s; S3 does the same via single-flight joiners; S4 caches ABSENT after a create. Violates retained invariant. | [A1] drop fills whose slot was forgotten; write forgets slot + entry in one tick | Lost fill on race costs one extra DB read | Pause/release tests for S2, S3, S4; `stale_fill_dropped` metric |\n| F2 | WARNING | 2 | \"failed-write preservation\" keeps entry when `repository.write` rejects; a timeout after DB commit leaves V0 cached up to 30 s. Plan does not state failures are pre-commit. | [A2] invalidate in `finally` on every outcome | Failed writes evict one entry | Test: write rejects post-commit, next read returns V1 |\n| F3 | OK (noted) | 3 | Sentinel thrash: probing absent IDs fills 1000-entry LRU with sentinels; worst case is today's baseline, bounded by LRU size | None; hit-rate alert [A3] observes it | Degradation to baseline only | Hit-rate alert |\n| F4 | WARNING | 2, 8 | \"Any cache failure\" bypass is a catch-all with no named alert or reinit trigger; \"fallback errors\" monitored but no threshold | [A3] gauge + alerts + WARN log with error class | Bypass reinit policy stays adapter-owned | Alert fires in staging fault-injection |\n| F5 | WARNING | 9 | Rollout by % of keys; plan says enable clears cache but is silent on % changes and flag evaluation failure | [A4] deterministic per-key bucket, clear on any change, eval error \u2192 bypass | None material | Flag transition tests |\n| F6 | OK | 3 | Authz precedes repository; keys tenant-scoped; DTOs immutable; no keys logged | None | \u2014 | Existing contract tests |\n| F7 | OK | 7 | 1000 \u00d7 DTO within 16 MiB; O(1) sync ops; cold start within DB capacity; single-flight caps stampede | None | 100x load is a multi-process problem, out of scope | Rollout metrics |\n| F8 | WARNING | 6 | TTL and sentinel-TTL tests depend on wall clock | [A5] injected clock | None | Tests deterministic |\n\n### Async Ordering Record (Section 4)\n\nShared state: `cache[key]`, `inflight[key]`. Invariant boundary: a read that\n*begins* after `writeProfile` resolves must return the committed version.\n\n```\n Sched | R1 (miss, reads V0) | W (commits V1) | R2 (begins after W) | cache[key] | Result\n ------|----------------------------|-----------------------------|---------------------|------------|-------\n S1 | read\u2192set V0 | write\u2192delete | miss\u2192read V1\u2192set | V1 | OK\n S2* | await read ... | write commits, delete(noop) | | - |\n | resolves V0 \u2192 set V0 | | hit \u2192 V0 | V0 (30 s) | VIOLATION\n S2 A1 | resolves V0, slot gone\u2192drop| delete + forget (one tick) | miss\u2192read V1\u2192set | V1 | OK\n S3* | await read ... | write, delete | joins R1 \u2192 V0 | - | VIOLATION\n S3 A1 | await read ... | delete + forget | new slot\u2192read V1 | V1 | OK\n S4* | miss on absent \u2192 await | create commits | hit ABSENT (10 s) | ABSENT | VIOLATION\n S4 A1 | slot gone \u2192 drop ABSENT | delete + forget | miss\u2192read V1 | V1 | OK\n S5 | begins before W, joins | delete + forget | \u2014 | \u2014 | OK: R1 began before W completed (permitted clause)\n```\n`*` = original sketch. Mechanism preventing S2\u2013S4 after [A1]: the forget and\nthe slot comparison run in synchronous ticks; no await separates `delete` from\n`forget`, and no await separates the slot check from `cache.set`.\n\nShadow paths for the new flow (nil key, empty key, upstream error): nil/empty\nkeys are rejected by existing key validation before the cache; upstream read\nerror releases the slot and maps to the typed API error with no fill; upstream\nwrite error invalidates per [A2]. All three covered by existing contract tests\nplus [A2].\n\nNo user-visible interactions: interaction edge-case table not applicable.\n\n### Section Outcomes\n\n| # | Section | Outcome |\n|---|---|---|\n| 1 | Architecture | 1 issue (F1). Coupling added: wrapper \u2192 single-flight `forget`. Justified. SPOF unchanged (one process, DB). Rollback: flag off, seconds. Diagram below. |\n| 2 | Error & Rescue | 9 paths mapped, 2 GAPS (F1 stale fill, F2 indeterminate write), both remedied. F4 catch-all named and instrumented. Registry below. |\n| 3 | Security | 0 issues High. F3 and F6 evaluated, no change. No new endpoints, params, secrets, or dependencies. |\n| 4 | Data flow / ordering | 5 schedules mapped, 3 violating (S2\u2013S4), 0 unhandled after [A1]. |\n| 5 | Code quality | 1 issue (F1 sketch under-engineered). Sketch amended. Both functions branch \u22643 times. No DRY violation. |\n| 6 | Tests | Diagram below. 4 gaps (F1 ordering, F2 indeterminate, F5 flag transitions, F8 clock), all amended. Test pyramid: unit-heavy, correct. |\n| 7 | Performance | No issues (F7). Slowest new path: miss + fill, p99 \u2248 existing DB read. |\n| 8 | Observability | 1 gap (F4), remedied by [A3]. Runbook: bypass alert \u2192 inspect WARN error class \u2192 reinit or disable flag. |\n| 9 | Deployment | 1 risk (F5), remedied by [A4]. No migration. Old/new code overlap: none (single process). Post-deploy: hit rate rising, bypass gauge 0, p95 falling within 5 min. |\n| 10 | Trajectory | Reversibility 5/5. Debt items: 0 (ordering diagram lives in code comment, maintained with the change). Obvious to a new engineer in 12 months given the comment. |\n| 11 | Design & UX | SKIPPED, justified: no UI scope; plan states no UI, API, or onboarding change. |\n\n### Error & Rescue Registry\n\n| Codepath | What goes wrong | Exception | Rescued | Action | User sees |\n|---|---|---|---|---|---|\n| readProfile / cache.get,set | adapter internal failure | adapter error (named in log) | Y (adapter) | bypass mode, WARN, gauge=1 [A3] | Nothing (DB serves) |\n| readProfile / repository.read | DB timeout, connection | TimeoutError, ConnectionError | Y (existing) | slot released, typed API error | Existing error |\n| readProfile / repository.read | record absent | none (ABSENT sentinel) | n/a | cache sentinel 10 s | Existing not-found |\n| readProfile / fill | write completed during read | none | Y [A1] | drop fill, metric | Nothing |\n| readProfile / cache.set | DTO > 16 MiB | adapter reject | Y (adapter) | no fill, `set_rejected_oversize` [A3] | Nothing |\n| writeProfile / repository.write | validation, authz | ValidationError, AuthorizationError | Y (existing) | invalidate [A2], typed error | Existing error |\n| writeProfile / repository.write | timeout after commit | TimeoutError | Y [A2] | invalidate in `finally` | Existing error |\n| writeProfile / cache.delete | adapter failure | adapter error | Y (adapter) | bypass, WARN [A3] | Nothing |\n| flag.enabledFor | flag store unavailable | flag client error | Y [A4] | bypass, `flag_eval_error` | Nothing |\n| startup | multi-process + cache on | ConfigurationError | Y (existing) | refuse start, log | Deploy fails loudly |\n\n### Failure Modes Registry\n\n```\n CODEPATH | FAILURE MODE | RESCUED? | TEST? | USER SEES? | LOGGED?\n --------------------|---------------------------|----------|-----------|-----------------|--------\n read fill | stale fill after write | Y [A1] | Y [A1] | Nothing | metric\n read via joiner | joins pre-write read | Y [A1] | Y [A1] | Nothing | metric\n read fill | ABSENT cached after create| Y [A1] | Y [A1] | Nothing | metric\n write | indeterminate outcome | Y [A2] | Y [A2] | Existing error | existing\n write | typed pre-commit failure | Y | Y (exist) | Existing error | existing\n cache adapter | internal failure \u2192 bypass | Y | Y (exist) | Nothing | WARN [A3]\n cache adapter | bypass persists silently | Y [A3] | staging | Nothing | alert\n cache set | oversize DTO | Y | Y (exist) | Nothing | metric [A3]\n flag | eval error | Y [A4] | Y [A4] | Nothing | metric\n flag | % change leaves stale | Y [A4] | Y [A4] | Nothing | n/a\n startup | multi-process config | Y | Y (exist) | Deploy fails | Y\n```\nCRITICAL GAPS before review: 1 (F1). Open after accepted remedies: 0.\n\n### Test Diagram (Section 6)\n\n```\n NEW UX FLOWS: none\n NEW DATA FLOWS: read-through fill; write-through invalidate; flag gate\n NEW CODEPATHS: hit; miss+fill; miss+dropped fill; joiner; forget; finally-invalidate;\n flag off; flag eval error; flag change clear\n NEW ASYNC WORK: none (in-loop promises only)\n NEW INTEGRATIONS: none\n NEW ERROR/RESCUE PATHS: see Error & Rescue Registry\n```\nAssertions (unit unless noted): hit returns cached DTO and skips DB (rejects a\nDB call count of 1); miss fills once (rejects 2 DB calls under N concurrent\nmisses); S2/S3/S4 with paused read return V1 to the post-write reader (rejects\nV0 or ABSENT); post-commit write rejection leaves no entry (rejects a hit);\nadapter throw yields DB value and gauge=1 (rejects a thrown error); eviction at\n1001 entries and at 16 MiB + 1 byte; TTL 30 s and sentinel 10 s via injected\nclock; flag off skips both cache calls; flag change empties cache and slots.\n2am test: S2 under load harness. Hostile QA: S3 joiner after write. Chaos:\nadapter throws mid-fill while a write commits. Load test: 900-key replay at\n10x, assert hit rate \u2265 60% and no DB call amplification.\n\n### Diagrams\n\n**1. System architecture**\n```\n caller \u2500\u25b6 authn/authz \u2500\u25b6 readProfile/writeProfile (wrapper, NEW)\n \u2502 \u2502 \u2502\n \u25bc \u25bc \u25bc\n flag.enabledFor LRU adapter single-flight (+forget, NEW)\n \u2502 \u2502\n \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2518\n \u25bc\n repository \u2500\u25b6 DB\n```\n\n**2. Data flow with shadow paths**\n```\n key \u2500\u25b6 validate \u2500\u25b6 flag? \u2500\u25b6 cache.get \u2500\u25b6 [miss] slot \u2500\u25b6 repository.read \u2500\u25b6 slot check \u2500\u25b6 set \u2500\u25b6 DTO\n \u2502 \u2502 \u2502 \u2502 \u2502 \u2502\n nil/empty reject error\u2192bypass hit\u2192return error\u2192release, typed forgotten\u2192drop\n```\n\n**3. State machines**\n```\n entry: (none) \u2500\u2500miss\u2500\u2500\u25b6 FILLING \u2500\u2500resolve & slot ok\u2500\u2500\u25b6 PRESENT|ABSENT \u2500\u2500TTL/evict/write\u2500\u2500\u25b6 (none)\n \u2514\u2500write forget\u2500\u25b6 (none) [set after forget: impossible, slot check]\n adapter: ACTIVE \u2500\u2500any adapter error\u2500\u2500\u25b6 BYPASS \u2500\u2500reinit empty\u2500\u2500\u25b6 ACTIVE\n flag: OFF \u2500\u2500enable\u2500\u2500\u25b6 ON(p%) \u2500\u2500change p\u2500\u2500\u25b6 ON(p'%) (cache cleared) \u2500\u2500disable\u2500\u2500\u25b6 OFF\n```\n\n**4. Error flow**\n```\n adapter error \u2500\u25b6 BYPASS + WARN + gauge \u2500\u25b6 alert >5 min \u2500\u25b6 operator: reinit or flag off\n repo error \u2500\u25b6 release slot \u2500\u25b6 typed API error (unchanged) \u2500\u25b6 write: finally invalidate\n```\n\n**5. Deployment sequence**\n```\n deploy (flag OFF) \u2500\u25b6 10% keys \u25001h healthy\u2500\u25b6 50% \u25001h healthy\u2500\u25b6 100% \u2500\u25b6 record vs targets\n each step: watch hit rate, bypass gauge, stale_fill_dropped, DB CPU, p95\n```\n\n**6. Rollback flowchart**\n```\n regression? \u2500yes\u2500\u25b6 flag OFF (reads+writes bypass, seconds) \u2500\u25b6 still bad? \u2500yes\u2500\u25b6 git revert deploy\n \u2502no \u2502no\n \u25bc \u25bc\n continue ramp investigate WARN error class\n```\n\n### Required Prose Sections\n\n**NOT in scope** (all pre-existing, restated with rationale):\n- Distributed or cross-process cache: single process today; interface seam retained.\n- Prewarming: cold start fits DB capacity per trace.\n- Consistency semantics change: invariant retained as written.\n- Row-version fill check (Approach C): needs schema change, prohibited.\n\n**What already exists.** LRU adapter, single-flight wrapper, runtime flag,\ntyped error mapping, contract tests. All reused; single-flight gains one method.\n\n**Dream state delta.** After this plan the service has a correct, observable,\nflag-controlled local cache behind the unchanged repository interface. The\nremaining gap to the 12-month ideal is only the adapter swap if multi-process\never arrives, which the interface already permits.\n\n**TODOS.md updates.** 0 items. Every evidenced gap was repaired in scope (D8).\n\n**Stale diagram audit.** No existing ASCII diagrams in the plan or named\nfiles. One new diagram is added as a code comment with [A1] and must be kept\ncurrent with the wrapper.\n\n**Outside voice.** `codex_reviews: disabled` in the isolated config. The extra\noutside-review step was skipped entirely, including its native fallback.\nOutside coverage: disabled. The guarded review-log persistence for the disabled\nrecord was not run (no shell in this run).\n\n## Implementation Tasks\nSynthesized from this review's findings. Each task derives from a specific\nfinding above. Run with Claude Code or Codex; checkbox as you ship.\n\n- [ ] **T1 (P1, human: ~2h / CC: ~15min)** \u2014 wrapper + single-flight \u2014 Implement slot-aware fill drop and `forget` on write completion [A1]\n - Surfaced by: Section 4 \u2014 F1, schedules S2\u2013S4\n - Files: profile wrapper, single-flight adapter, ordering diagram comment\n - Verify: pause/release tests for S2, S3, S4 pass; `stale_fill_dropped` increments in S2\n- [ ] **T2 (P1, human: ~30min / CC: ~5min)** \u2014 wrapper \u2014 Invalidate in `finally` on every write outcome [A2]\n - Surfaced by: Section 2 \u2014 F2 indeterminate write\n - Files: profile wrapper, tests (replace failed-write preservation test)\n - Verify: post-commit rejection test returns V1 on next read\n- [ ] **T3 (P1, human: ~1h / CC: ~10min)** \u2014 flag helper \u2014 Deterministic per-key bucket, clear on any flag change, bypass on eval error [A4]\n - Surfaced by: Section 9 \u2014 F5\n - Files: flag helper, wrapper, tests\n - Verify: flag transition tests; `flag_eval_error` metric on injected failure\n- [ ] **T4 (P2, human: ~1h / CC: ~10min)** \u2014 observability \u2014 Metrics, gauge, WARN log with error class, two alerts [A3]\n - Surfaced by: Section 8 \u2014 F4\n - Files: wrapper, adapter bypass hook, alert config\n - Verify: staging fault injection fires bypass alert within 5 min\n- [ ] **T5 (P2, human: ~30min / CC: ~5min)** \u2014 tests \u2014 Injected clock for TTL and sentinel-TTL tests [A5]\n - Surfaced by: Section 6 \u2014 F8\n - Files: wrapper tests\n - Verify: tests pass with no sleeps; run 20x without flake\n- [ ] **T6 (P2, human: ~1h / CC: ~10min)** \u2014 tests \u2014 Load replay of 900 hot keys at 10x asserting hit rate \u2265 60% and no DB amplification\n - Surfaced by: Section 6 \u2014 test ambition check\n - Files: load test harness\n - Verify: harness report against acceptance targets\n\n_No new tasks from Section 3 (Security), Section 7 (Performance), Section 10 (Trajectory), Section 11 (Design, skipped)._\n\nJSONL task artifact: not written (no shell/jq in this run). Re-run the JSONL\nstep from the review sections when a shell is available.\n\n### Completion Summary\n```\n +====================================================================+\n | MEGA PLAN REVIEW \u2014 COMPLETION SUMMARY |\n +====================================================================+\n | Mode selected | HOLD SCOPE (user-specified) |\n | System Audit | SKIPPED per run instructions |\n | Step 0 | Approach B; premise holds; 0 scope changes |\n | Section 1 (Arch) | 1 issue found (F1) |\n | Section 2 (Errors) | 9 error paths mapped, 2 GAPS (remedied) |\n | Section 3 (Security)| 0 issues found, 0 High severity |\n | Section 4 (Data/UX) | 5 schedules mapped, 0 unhandled after A1 |\n | Section 5 (Quality) | 1 issue found (F1 sketch) |\n | Section 6 (Tests) | Diagram produced, 4 gaps (all amended) |\n | Section 7 (Perf) | 0 issues found |\n | Section 8 (Observ) | 1 gap found (F4, remedied) |\n | Section 9 (Deploy) | 1 risk flagged (F5, remedied) |\n | Section 10 (Future) | Reversibility: 5/5, debt items: 0 |\n | Section 11 (Design) | SKIPPED (no UI scope) |\n +--------------------------------------------------------------------+\n | NOT in scope | written (4 items) |\n | What already exists | written |\n | Dream state delta | written |\n | Error/rescue registry| 10 rows, 0 CRITICAL GAPS open |\n | Failure modes | 11 total, 1 CRITICAL GAP found, 0 open |\n | TODOS.md updates | 0 items proposed |\n | Scope proposals | 0 proposed, 0 accepted (HOLD) |\n | CEO plan | skipped (HOLD) |\n | Outside voice | codex: disabled (config), no fallback |\n | Lake Score | 7/7 recommendations chose complete option |\n | Diagrams produced | 7 (arch, data flow, state, error, deploy, |\n | | rollback, async schedule) |\n | Stale diagrams found | 0 |\n | Unresolved decisions | 0 |\n +====================================================================+\n```\n\nSkipped persistence (no shell in this run): `gstack-review-log`,\n`gstack-decision-log`, `gstack-review-read`, telemetry, learnings log,\nhandoff cleanup. No durable learnings this session beyond the plan itself.\n\nNext review: run `/plan-eng-review` (required gate) against this amended plan.\nDesign review not applicable.\n\n## GSTACK REVIEW REPORT\n\n| Review | Trigger | Why | Runs | Status | Findings |\n|--------|---------|-----|------|--------|----------|\n| CEO Review | `/plan-ceo-review` | Scope & strategy | 1 | clean (HOLD_SCOPE) | mode: HOLD_SCOPE, 1 critical gap found and remedied, 0 open; 5 amendments A1\u2013A5 |\n| Outside Review | codex via `/plan-ceo-review` (codex_reviews: disabled) | Independent 2nd opinion | 0 | disabled | \u2014 |\n| Eng Review | `/plan-eng-review` | Architecture & tests (required) | 0 | \u2014 | \u2014 |\n| Design Review | `/plan-design-review` | UI/UX gaps | 0 | \u2014 | \u2014 |\n| DX Review | `/plan-devex-review` | Developer experience gaps | 0 | \u2014 | \u2014 |\n\n- **OUTSIDE COVERAGE:** provider codex, phase plan-review, status disabled by `codex_reviews: disabled`; no outside CLI invoked, no native fallback dispatched; disabled record not persisted (no shell this run).\n- **VERDICT:** CEO CLEARED (HOLD SCOPE, amendments A1\u2013A5 accepted) \u2014 eng review required\n\nNO UNRESOLVED DECISIONS\n", "compact": "### Findings Registry\n\n| ID | Sev | Section | Evidence | Remedy | Residual risk | Verification |\n|---|---|---|---|---|---|---|\n| F1 | CRITICAL GAP | 1, 2, 4, 5, 6 | Original sketch: fill after `await repository.read` has no guard; plan text says no fill/write coordination. Schedule S2 makes a post-write reader see V0 for 30 s; S3 does the same via single-flight joiners; S4 caches ABSENT after a create. Violates retained invariant. | [A1] drop fills whose slot was forgotten; write forgets slot + entry in one tick | Lost fill on race costs one extra DB read | Pause/release tests for S2, S3, S4; `stale_fill_dropped` metric |\n| F2 | WARNING | 2 | \"failed-write preservation\" keeps entry when `repository.write` rejects; a timeout after DB commit leaves V0 cached up to 30 s. Plan does not state failures are pre-commit. | [A2] invalidate in `finally` on every outcome | Failed writes evict one entry | Test: write rejects post-commit, next read returns V1 |\n| F3 | OK (noted) | 3 | Sentinel thrash: probing absent IDs fills 1000-entry LRU with sentinels; worst case is today's baseline, bounded by LRU size | None; hit-rate alert [A3] observes it | Degradation to baseline only | Hit-rate alert |\n| F4 | WARNING | 2, 8 | \"Any cache failure\" bypass is a catch-all with no named alert or reinit trigger; \"fallback errors\" monitored but no threshold | [A3] gauge + alerts + WARN log with error class | Bypass reinit policy stays adapter-owned | Alert fires in staging fault-injection |\n| F5 | WARNING | 9 | Rollout by % of keys; plan says enable clears cache but is silent on % changes and flag evaluation failure | [A4] deterministic per-key bucket, clear on any change, eval error \u2192 bypass | None material | Flag transition tests |\n| F6 | OK | 3 | Authz precedes repository; keys tenant-scoped; DTOs immutable; no keys logged | None | \u2014 | Existing contract tests |\n| F7 | OK | 7 | 1000 \u00d7 DTO within 16 MiB; O(1) sync ops; cold start within DB capacity; single-flight caps stampede | None | 100x load is a multi-process problem, out of scope | Rollout metrics |\n| F8 | WARNING | 6 | TTL and sentinel-TTL tests depend on wall clock | [A5] injected clock | None | Tests deterministic |\n\n### Async Ordering Record (Section 4)\n\nShared state: `cache[key]`, `inflight[key]`. Invariant boundary: a read that\n*begins* after `writeProfile` resolves must return the committed version.\n\n```\n Sched | R1 (miss, reads V0) | W (commits V1) | R2 (begins after W) | cache[key] | Result\n ------|----------------------------|-----------------------------|---------------------|------------|-------\n S1 | read\u2192set V0 | write\u2192delete | miss\u2192read V1\u2192set | V1 | OK\n S2* | await read ... | write commits, delete(noop) | | - |\n | resolves V0 \u2192 set V0 | | hit \u2192 V0 | V0 (30 s) | VIOLATION\n S2 A1 | resolves V0, slot gone\u2192drop| delete + forget (one tick) | miss\u2192read V1\u2192set | V1 | OK\n S3* | await read ... | write, delete | joins R1 \u2192 V0 | - | VIOLATION\n S3 A1 | await read ... | delete + forget | new slot\u2192read V1 | V1 | OK\n S4* | miss on absent \u2192 await | create commits | hit ABSENT (10 s) | ABSENT | VIOLATION\n S4 A1 | slot gone \u2192 drop ABSENT | delete + forget | miss\u2192read V1 | V1 | OK\n S5 | begins before W, joins | delete + forget | \u2014 | \u2014 | OK: R1 began before W completed (permitted clause)\n```\n`*` = original sketch. Mechanism preventing S2\u2013S4 after [A1]: the forget and\nthe slot comparison run in synchronous ticks; no await separates `delete` from\n`forget`, and no await separates the slot check from `cache.set`.\n\n" }