mirror of
https://github.com/PerpetualSoftware/pad.git
synced 2026-09-11 13:28:57 +00:00
d0644a60fc
Groundwork for S1's store-layer guard. The predicate lived in internal/server and could not be reached from internal/store, so a store guard would have had to reimplement it — two implementations of "does this decode to a NUL", which is the disagreement class this whole cluster exists because of. internal/textguard is a leaf package holding the predicate and the adversarial corpus. The HTTP gate now DELEGATES to it rather than carrying its own copy: stringIsJSONDocument and nestedDocumentDecodesNUL become one-line forwards, and valueDecodesNUL's unclassed arm is textguard's walk verbatim. Its existing 418-line corpus passes unchanged, which is the extraction's proof. What is deliberately NOT shared is named in the package doc: how a layer decides a value IS JSON. The gate derives that from request-body KEY NAMES; the store derives it from the COLUMN a parameter is bound to. Those cannot be merged, so every entry point takes the classification as an argument and the differential test pins that both derivations agree on one corpus. The corpus ships in the package rather than in a _test.go because three packages measure against it and three copies would drift. Fifteen cases, each carrying the reasoning or measurement that put it there — including the doubled-backslash false positive that made the raw-byte parity filter unsound, the raw-NUL-inside-a-JSON-value regression codex round 9 caught, and the nested-document asymmetry where the same escape is fatal one layer up. The escape text and the NUL character are constants, and a test asserts they did not decay into each other by comparing against a value it BUILDS rather than one it types. That mistake was made three times during BUG-2803 and twice more while writing this file. Claude-Session: https://claude.ai/code/session_01XLtX4dbjBpApbAv3SuBcTm