mirror of
https://github.com/PerpetualSoftware/pad.git
synced 2026-10-25 14:00:40 +00:00
6cc2c7eb95
Item CREATE was the last door that stored a hand-written reserved metadata key (implementation_notes, decision_log, github_pr, convention). A `--field` or MCP `field` value landed as a string no extractor reads. For notes and decisions, that also made `note` / `decide` refuse on the item until the row was repaired. Create's `fields` now refuses all four keys with 400 validation_error. The check runs on the caller's map. Library activation, the only system writer that used this door, moves to a new typed `convention` create member, which is validated (an empty object is refused), normalized and lowered into the blob. Every convention writer switches to it: the CLI and remote MCP library activate, the web library activate, the web Conventions page create, and the e2e seed helper. The CLI checks the STORED `convention` key to confirm the member landed. It cannot use item.Convention for that, because that value is derived from the sibling keys and is non-nil even when an older server has dropped the member. A FULL `fields` update may CARRY stored reserved values unchanged. A differing value, or an OMITTED stored key (which the whole-blob replace would delete), is refused with 400. The comparison uses decoded JSON, never bytes, and runs in the update transaction against the row re-read under the write lock (composed into the precheck, like the BUG-3133 guard). A note appended concurrently therefore cannot be erased by a stale blob. A stored blob that is not a JSON object has no readable reserved metadata, so a full write can still repair it. Artifact import shares createItemChecked but needs no gate, because artifact.Decode fills Fields from a fixed frontmatter key list. Existing test fixtures that put reserved keys in through the two closed doors now seed at the store level. The stdio refusal texts are pinned to the classifier's validation_failed table. Claude-Session: https://claude.ai/code/session_01RYiqbqXmxQMoKHsGm8B77N