The hand-rolled -report json emitter in runtests.tcl classified values as
JSON numbers via `string is entier -strict`, which (a) is deprecated in Tcl 9.0
per TIP 514 (string is integer became the canonical unlimited-range form) and
(b) accepted several forms that are NOT valid JSON numbers, emitting them bare:
007 (leading zeros), +5 (leading plus), 0x10/0o17/0b101 (hex/octal/binary).
The G-093 work already patched one symptom (a "0\n" result embedded a raw
newline) but left the rest of the class open. The string escaper
(runtests_json_string) also left most C0 control chars (0x00-0x1F) raw, which
RFC 8259 forbids in strings - a test whose result_was carries a bell or NUL
corrupted the report the same way.
Hardening (src/tests/testsupport/json_emit.tcl, extracted from runtests.tcl so
the new suite can unit-test it without a child run):
- runtests_json_int? replaces `string is entier -strict` with an RFC 8259
integer regex (^-?(0|[1-9][0-9]*)$) - version-independent, eliminates entier
entirely (no Tcl-9 deprecation exposure), subsumes the G-093 whitespace guard
via ^...$ anchoring.
- runtests_json_string now escapes the whole C0 range: 0x08 -> \b, 0x0C -> \f,
others -> \u00XX. The existing \n \r \t \ \" escapes are preserved
byte-for-byte (the multilinebanner.test end-to-end suite probes exact
substrings such as "result_was":"x\n").
Pinning suite (src/tests/runner/testsuites/parser/jsonemit.test, 8 tests):
RFC 8259 number syntax (007/+5/0x10/0o17/0b101 emitted as quoted strings; valid
integers bare), C0 control-char escaping (no raw 0x00-0x1F survives; \b \f
short escapes; \u00XX for the rest), entier-deprecation hygiene (asserts the
loaded predicate proc body uses regexp and mentions no entier), named-escape
byte regression, and tcllib-json round-trip of a representative testdict array.
DOX: src/tests/AGENTS.md testsupport bullet records json_emit.tcl;
src/tests/runner/AGENTS.md parser description records jsonemit.test.
Verification: jsonemit.test 8/8 on Tcl 9.0.5 and Tcl 8.6; multilinebanner.test
2/2 (no regression to the existing emitter output contract); full runner/
subtree 40/40; mode parity runtests_parity.tcl PARITY: ok on a broad real-data
subset (modules/punk/args, 35 files / 341 tests, singleproc vs multiproc).
Tests-only change - no punkproject.toml bump per the versioning policy.
Assisted-by: harness=pi; primary-model=zai-org/GLM-5.2; api-location=huggingface.co