mirror of
https://github.com/PerpetualSoftware/pad.git
synced 2026-10-26 14:30:13 +00:00
c1076ce15f
* fix: a relation's "no target" has one stored form, and required means it names one (BUG-3028) A scalar relation had three stored spellings of "no target" (key absent, "" and whitespace-only), and required was enforced only against the first, so a required relation holding "" validated. The canonical form is now the one multi_relation uses: the key absent. Provenance decides the rest, per the lead's ruling. A blank a write SETS (create, fields_patch, a changed value in full fields, a move or copy override) is removed before validation, so a required one is refused as required. A patch blanking a required relation now gets the answer deleting it always got. A legacy blank a write only CARRIES is removed after validation and never refused: an untouched key under fields_patch, an unchanged full fields value, move, bulk update and bulk move, cross-workspace copy and its preflight, artifact import. On the patch path the store does this under its write lock (ItemUpdate.BlankRelationKeys), so a concurrent write's new value is never mistaken for a blank. An empty list filter on a scalar relation of a collection-scoped list (owner=) now matches all three spellings instead of only "". MCP ToolSurfaceVersion 0.40, a behavior bump. No migration: this instance has no relation fields, and the prod census is Dave's. Claude-Session: https://claude.ai/code/session_01X4uYKs5ZPF28eCWfm8VPvE * fix(items): a blank relation default is no default, so it cannot satisfy required (BUG-3028, codex round 1) Codex round 1 P2: a required relation whose schema default is "" had the blank injected by validation, which satisfied required, and was then normalised away after it, storing a required relation with no target. A blank default on a scalar relation now takes the no-default path: a required field is refused (the message names the blank default) and an optional one stays absent with nothing reported dropped. Claude-Session: https://claude.ai/code/session_01X4uYKs5ZPF28eCWfm8VPvE