# Final state code review

Status: DONE

## Result

NEEDS CHANGES. The targeted regression passes all 13 checks, including the repaired expert-send, feedback-receipt, fixture, removal, reload, and isolation paths. A remaining local-file replacement path breaks the saved metadata-to-video-byte relationship when `localStorage` fails after IndexedDB has already overwritten the canonical video key.

## Deliverables and checks

| Deliverable | Requirement or verification | Result | Evidence |
|---|---|---|---|
| `site/app/app.js` expert-send and participant/day guards | Direct Rita exploration cannot create a participant readback, while the submitted-video loop stays scoped to the exact participant and day. | PASS | `node evidence/luna-state-fixes/verify-state-truthfulness.cjs` reported `13/13`, with its direct-exploration and isolation checks passing. Source guards are at `app.js:20`, `app.js:103`, and `app.js:124`. |
| `site/flow/flow.js` free-feedback and receipt transitions | A direct visit stays a sample; only a valid simulated submission plus an explicit receipt transition creates feedback availability. | PASS | Focused regression checks 10–12 passed. State predicates and defensive handlers are at `flow.js:4-8`, `flow.js:38-47`, and `flow.js:51-54`. |
| `site/app/app.js` fixture/removal failure handling | Failed metadata removal preserves persisted video bytes; failed fixture metadata saves do not delete the prior local bytes. | PASS | Focused regression checks 3–9 passed. The repaired branches are at `app.js:67-69` and `app.js:96-98`. |
| `site/app/app.js` daily local-file replacement on metadata-write failure | A failed metadata save must not replace bytes under the persisted record's key. | FAIL | At `app.js:95`, `videoStore('put', videoKey(...), f)` overwrites the existing IndexedDB blob before `saveState()`. A headless reproduction seeded `old.mp4` and `OLD` bytes, forced `localStorage.setItem` to throw, selected the real demo MP4, and read back persisted metadata `old.mp4` with a 20,551-byte new blob. The in-memory UI also showed the new MP4. |
| `site/app/app.js` initial local-file replacement on metadata-write failure | The initial-video equivalent must preserve the saved metadata-to-byte relationship. | FAIL | At `app.js:66`, `videoStore('put', key, f)` has the same order. The equivalent reproduction read back persisted metadata `old-initial.mp4` with a 20,551-byte new blob after the forced metadata-write failure. |

## Summary

The completed repairs correctly cover their stated fixture and removal branches, and the focused test has no page errors, bad responses, or non-GET requests. The two local-file selection handlers still violate reload truthfulness when an existing local video is replaced while metadata persistence fails.

## Issues

- **[severity: medium] Correctness:** Daily and initial local-file selection write the replacement blob to the fixed canonical IndexedDB key before confirming the metadata write. If `saveState()` fails, localStorage retains the old filename and submission record but the canonical blob is already the replacement. On a later reload, the app presents the old metadata for new bytes. Use a per-revision IndexedDB key referenced by metadata, then write metadata only after the new blob exists. If metadata persistence fails, leave the old referenced blob untouched; an unreferenced new blob can be cleaned up separately. Apply this to both `app.js:66` and `app.js:95`.

## Verdict

NEEDS CHANGES — the remaining failed-write replacement defect makes persisted local state misleading after reload.

## Limits or consequential decisions

This was a local prototype correctness review only. I did not require a backend, authentication, upload, payment, email, security layer, or visual screenshot QA. No production files were changed.
