Files
pad/internal
xarmian d0644a60fc refactor(textguard): extract the one decoded-NUL predicate and its corpus (DOC-2823 S1)
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
2026-09-01 04:24:45 +00:00
..
2026-03-26 01:52:36 +00:00