GENERATED VIEW · PRIVATE ARCHIVE · DO NOT SERVE
RobCo Industries · Archive Museum

record.md



BUGS

bugs/ai-inventory-overwrite/record.md

sha256 999cc3b553e0b447 · 4083 bytes · original held in the private archive

--- title: The Turn That Ate the Inventory status: IN-FLIGHT repo: app observed: 2026-07-18 caught_by: the owner's eye — tracing a live [DELTA] 1→0 against the code, confirmed real state loss not a display bug one_line: The AI-import path full-replaced state.inventory, so a turn that emitted a short or empty array silently deleted real items the player held. guard_location: js/services/api-import.js · autoImportState() reconcile (proposal, not full replace); js/services/api-directive.js directive verified: red→green regression tests landed with both fix commits (Protocol 13/14) exhibited: true lesson: An AI response echoed into state is untrusted input; full-replace-from-response can destroy durable data, so reconcile or confirm net removals — never blind-persist (Protocol 24). awaiting_tag: v2.8.5 provenance: - commits 8f834e6 + 36926f0 (app repo) — the symptom fix, then the class fix across every AI full-replace field - QUEUE_LOG.md item A0 (app repo) — the shipped account - related guard: commit 6d9d723, planning/2.8.5/audits/AUDIT_import_guard.md (registry-trust guard on the same path) --- ## What happened The AI-import path did a **full replace** of `state.inventory`, not a merge. When an AI response contained an `inventory` array, `autoImportState()` (`js/services/api-import.js`, ~line 379) ran `state.inventory = inv.map(...)` — the entire durable inventory overwritten by whatever the model returned that turn. The directive (`js/services/api-directive.js`, ~line 120) commands the AI to *"return the ENTIRE inventory array"* on any inventory-touching turn, so a turn where the model misjudged — e.g. a **failed** repair and an **aborted** craft — and emitted a short or empty array **deleted real items from state**. An empty array wiped everything. The `[DELTA] inventory: 1→0 items` line the owner saw was telling the truth: the item was genuinely removed, not miscounted. ## How it was caught The **owner's eye**, then confirmed against the code: the DELTA counter reads actual `state.inventory` length before/after, so `1→0` is a real deletion. Traced against the owner's live screenshots on 2026-07-18. The pre-existing registry-leak guard covered only cross-*game* mismatches; a same-game short/empty array was unguarded — a hole no machine was watching until the owner hit it. ## The guard The inventory array became a reconciled **proposal** instead of a full replace, and the follow-up commit widened the same treatment to **every** AI full-replace-from-response field — the class fix, not just the reported symptom. Protocol 24 (validate + field-map, never blind-persist): a sync returning a short or empty inventory array must not delete natively-held items; net removals are reconciled against a real narrative signal or confirm-gated. A related trust guard (commit `6d9d723`) makes the whole import path skip fields on a registry mismatch rather than accept the payload. ## What the guard costs Every AI-authored state field now pays a reconciliation step instead of a straight assign, and the AI can no longer unilaterally remove items in one turn — a net removal needs a real signal or a confirmation. The AI's authority over durable state is deliberately narrowed. ## Containment Confirmed real state loss (not a display bug) by tracing the code against live screenshots. Guarding regression tests landed with **both** commits (Protocol 13/14): a sync returning a short/empty inventory array must not delete natively-held items. The related registry-trust guard was independently reproduced red→green two ways in `AUDIT_import_guard.md` (PASS, test count 3213→3221, purely additive). **Not yet SEALED:** 2.8.5-era; app pinned to `v2.8.0`, `v2.8.5` untagged. ## Provenance Commits `8f834e6` + `36926f0` (symptom then class fix); `QUEUE_LOG.md` item A0 for the shipped account; commit `6d9d723` and `planning/2.8.5/audits/AUDIT_import_guard.md` for the sibling registry-trust guard on the same path. The `~line` references are from the QUEUE_LOG account at fix time and may have drifted.
STAMP · generated for RELEASE v2.8.5 commit 06e5180 (06e51801b38a) · archive input-tree hash c07fbfbdd2e1ddeb · 754 files · no wall-clock timestamp (regenerates identically when nothing changed).