Repository navigation
Records/JSON sugar (pure) + heldout-v3 harness and before/after results - #154
Conversation
…pluck, select, group_by, merge; parse_json under -run Phase 1 of the heldout-v3 records slice, written blind (heldout-v3 specs not opened). - Desugar-only (internal/ast/desugar_records.go): no opcode, no capability. read_json/write_json reach the filesystem only via read_file/write_file. - Arity entries + construct drift; Go/JS reject with UNSUPPORTED_ON_TARGET; governed scan fails closed through every form. - Three-path parity tests (interpreter, -run-bc governed, HFIR bytecode). - parse_json runs under -run (same decodeJSONValue); parity manifest updated. - All existing run.sh / run_postcf.sh outputs byte-identical.
…rame baseline (a6bb53b features) and post-records sets, pinned results Specs untouched. HowlFrame/Python cl100k total 1.332 -> 1.249, median per-task 1.282 -> 1.186, W/T/L vs Python 3/0/32 -> 8/0/27. Records 1.244 -> 1.118.
howlcipher
left a comment
There was a problem hiding this comment.
Verdict: merge-ready (COMMENT). No blockers. One scope recommendation and a few nits below.
Reviewed head 52d84756ae8dcea0ca0d7336e501d6a1a61a95b6 (base a6bb53b6), using one fresh clone plus a worktree for the base. Tokens counted with tiktoken 0.8.0, cl100k_base.
1. Blindness: the order is consistent, but it can't prove the author never looked
- History is linear:
a6bb53b6(#152 merge, 17:42 ET) →e18ad5d(17:53, parenta6bb53b6) →52d84756(18:02, parente18ad5d). e18ad5dtouches only language and test files (12 files) and nothing underexperiments/. Its only "v3" mentions are the label "heldout-v3 records slice" in comments and docs. It references no task IDs (x01–x35), fixture names or file contents.52d84756touches onlyexperiments/token-efficiency-heldout-v3/**(195 files). No language change came after the solutions were written, so no forms were added post-blind.- What this doesn't prove: the specs were public on main 11 minutes before
e18ad5d, so commit order can't rule out that they were read. The circumstantial evidence points to blind: 4 of the 9 forms (pluck,select,group_by,merge) plusputare used by zero v3 references. Usage across the postrec set is read_json 20, get 18, write_json 16, rec 6. If the author had been fitting to v3, they wouldn't have built five forms the set never uses. Verdict: consistent with blind, not proven.
2. Baseline only uses features on a6bb53b6
I built the base binary from a6bb53b6 and ran heldoutv3verify -langs howl on solutions/howl: 35/35 pass the oracle. As a control, postrec/ on the base binary gets 12/35, which confirms the 23 rewrites really depend on the slice.
3. Fail-closed (governed build, -run-bc, grants filesystem:read=in, filesystem:write=out)
- read_json works inside the grant. Denied (
CAPABILITY_DENIEDat READ_FILE) for: an absolute path outside the grant,in/../secrettraversal, a symlink fromin/tosecret/, and no-allow-capsat all. - write_json is denied outside the grant, on
out/../secrettraversal, and with a read-only grant. Nothing was written tosecret/orout/. - Malformed JSON gives a clean
RUNTIME_ERROR parse_json failed: unexpected EOF, with no panic. Piping malformed input throughwrite_jsonleaves no partial file, because the parse fails before WRITE_FILE runs. -required-capson the artifact reportsfilesystemat both the READ_FILE and WRITE_FILE sites.- Go and JS give
UNSUPPORTED_ON_TARGETfor all 9 forms. ARITY_ERROR is checked for get, put, read_json (0 and 2 args), write_json, pluck (1 and 3), select, group_by and merge. Odd-lengthrecgives a clean error. - parse_json under
-run: a let-bound string now decodes, matching both bytecode paths, and needs no capability.read_json/read_textunder-runare stillcapability denied: filesystem. Governed still forbids literal(parse_json Any "..."), the same as on base. No capability is widened.
4. pluck, select, group_by, merge (and put)
No v3 reference uses them, and put is also unused. Across the earlier sets, only 3 solutions have the map_list x l (map_get x "k") shape that pluck would save a couple of tokens on (heldout-v1 v29/v30, heldout-v2 w33). Nothing uses a select, group or merge shape. pluck overlaps map_list, select overlaps map_list+dict, and group_by sits next to count_by. Recommendation: hold pluck/select/group_by/merge. Split them into a follow-up PR, or mark them experimental in the construct table, until a frozen set measures them. Each one is permanent surface (arity, drift, target rejection, docs). They're pure desugar and tested, so I'm not blocking on this. Keep put because it pairs with get, but it's unmeasured too.
5. Results reproduce exactly
- With
heldoutv3verify(PR binary) all 4 languages pass, 140/140, and postrec is 35/35.verification.jsonis identical to the pin. - Rerunning
count_tokens.py(baseline and postrec) andcompare_postrec.pyleaves the tree clean: every pinned results file comes out byte-identical. - vs Python: median 1.282 → 1.186, total 3783 → 3547 / 2840 = 1.332 → 1.249, W/T/L 3/0/32 → 8/0/27. Records: 1828 → 1643 / 1469 = 1.244 → 1.118. JS 1.103 → 1.024, Go 0.460 → 0.434, matching REPORT.md.
- Attribution: every task whose count changed uses at least one new form. No task changed without one.
- Sandbagging: I read the worst baseline ratios (x34 1.92, x32 1.78, x24 1.76). They're already tight (one while loop each; x24 does inline gcd/lcm with
%and//), and I found no padding I could golf out with main features. - Earlier frozen pins: the PR touches nothing under
experiments/outside heldout-v3.go test -count=1 ./experiments/...passes, 7 packages ok.
6. Parity across the 3 paths (interpreter, governed -run-bc, HFIR -run-bc)
Identical output on all 3 paths for: get on a missing key → null, nested get m "a" "b" 1 "c" (via read_json), put creating intermediate maps, put on a non-record (the same TYPE_ERROR map_set expected dict everywhere), empty group_by → {}, group_by order, merge conflict (later wins, inputs untouched), pluck/select with missing keys, unicode é😀 round-trip, and print of 2^53+1 (exact).
encode_jsonof 2^53+1 gives...992on the interpreter and AST bytecode. This is the filed precision issue.- The filed HFIR encode_json miscompile reproduces: HFIR prints the raw text. It does not affect any v3 result, because the verifier only uses the governed AST build.
- Separate finding, on base too, not caused by this PR: HFIR diverges from AST bytecode on 8/35 v3 programs, both baseline and postrec: x02, x03, x07, x08, x10, x11, x15, x27. On x10 and x11 HFIR lowering fails (
map_get requires a target name and key). On x02 it gives a runtime TYPE_ERROR. On x03 it gives a spuriousCAPABILITY_DENIEDat WRITE_FILE inside the grant. The base binary shows the same 8, so this is pre-existing. Please file it as a separate issue, broader than the encode_json one. It also strengthens the case against any HFIR default flip.
7. CI and tests
CI build is green. gofmt -l is empty, go vet ./... exits 0, and go test ./... exits 0 with no apps/* flake this run. go test -count=1 -run 'TestProdFlipCriteriaLock|Arity|Drift|Fuzz|Record' over root and internal exits 0.
8. Hard nos
No prod -compile-bc/HFIR default flip. No lock or tip files touched, and no tip-lock 4d74dbcf. No live LLM run, and no H1 PASS/KILL claim.
Nits (non-blocking)
- The doc says
getgives nil for a missing key, but that's only true at the last step. A missing intermediate key givesTYPE_ERROR map_get expected dict, got <nil>on all 3 paths. Either document this or makegetnil-propagate. - Statically typed homogeneous lists mean
(rec a (rec b (list 10 (rec c "x"))))is rejected at check time. Mixed-shape nested records only work via read_json. Worth a line in the construct table. - REPORT.md should say
putwas also unused on v3, not only the four list forms. - File the 8-task HFIR divergence described above.
Records/JSON slice measured blind on held-out v3 (#152). Two commits.
Phase 1 (
e18ad5d, written blind: heldout-v3 not opened): pure desugar records/JSON forms ininternal/ast/desugar_records.go(rec k v …)record literal (name or string keys),(get m k …)nested path read (int literal = list index),(put m k … v)nested in-place write (creates missing maps)(read_json p)/(write_json p v): one call each, only throughread_file/write_file, so they need exactlyfilesystemand fail closed without it (tested on all 3 paths, including read-only grant → write denied, no output)(pluck lst k),(select lst k …),(group_by x lst key),(merge a b …)(shallow, new dict)encode_jsonalready writes sorted keys, compact-run-bc/ HFIR bytecode), Go/JSUNSUPPORTED_ON_TARGETwith no output written, governed scan fails closed through every formparse_jsonnow runs under-run, using the samedecodeJSONValue. Stabilization parity manifest: interpreter UNSUPPORTED → PASS for fixture 10run.sh/run_postcf.sh(v0, heldout-v0/v1/v2) re-run: outputs byte-identicalPhase 2 (
52d8475):experiments/token-efficiency-heldout-v3harness that mirrors v2. Python/JS/Go refs; HowlFrame baseline (maina6bb53b6features, 35/35 also on ana6bb53b6binary) andpostrec/; results pinned; no gaps; specs untouched. Full tables and an honest read are inREPORT.md.Records (17): total vs Python 1.244 → 1.118. HowlFrame still loses to Python overall.
Findings (not fixed): an HFIR miscompile where
encode_json(parse_json s)prints rawswhen a later print argument has alet(also on main), andencode_jsonloses int precision above 2^53.Hard nos: no production -compile-bc/HFIR flip; #90 stays Partial. gofmt clean; go build/vet/test ./... pass.