Repository navigation
Description
Problem
install_module_session in pkgs/id/src/world_session/mod.rs is the one remaining ClientFrame parse path that does not route through the module's strict duplicate-key parser. Every other peer-frame entry point refuses a frame containing duplicate JSON keys at any depth; the module-install path parses chunk and completion frames with plain serde_json::from_str::<ClientFrame>, which refuses duplicate known fields (serde derive) but silently collapses duplicate keys in fields the frame type ignores — the exact residual class PR #6508 pinned and fixed for query_session.
Evidence (origin/main 6046e1d, pkgs/id/src/world_session/mod.rs):
- Strict helper:
parse_json_without_duplicate_keysat mod.rs:880. Used by the first frame (mod.rs:1026),query_session(mod.rs:1721–1722, via PR fix(id): refuse duplicate JSON keys in world_session first/joined frames (#6501) #6508 for issue id(world_session): first-frame and joined-frame parsers collapse duplicate JSON keys before their checks — a hidden first type/world/capability value is never seen, and the sibling session paths reject the same bytes #6501), and the joined-frame handler (mod.rs:2663). - Install path: chunk frames parsed at mod.rs:2189 with
serde_json::from_str::<ClientFrame>(&text); theinstall_endcompletion check at mod.rs:2208 matchesserde_json::from_str::<ClientFrame>(text)the same way. Neither sees the strict helper. - Issue id(world_session): first-frame and joined-frame parsers collapse duplicate JSON keys before their checks — a hidden first type/world/capability value is never seen, and the sibling session paths reject the same bytes #6501 described the install path as the strict sibling oracle — true only for duplicate known fields. Duplicate keys in ignored/unknown fields, and duplicates nested inside an ignored object, pass both install parses untouched.
Verified this run on the laptop with a probe driving the real id crate's public run_session_with against a clean origin/main export (probe ~/git/fleet/probe-id-install-dupkeys; WorldService with admin token, install_begin with total_bytes: 1, one 1-byte chunk 0x00, then install_end; oracle = which error the session ends with — "Wasm module failed validation or initialization" means the frames were accepted and the upload reached module installation, "expected module chunk frame" means the chunk was refused at parse):
- Control: valid chunk + valid
install_end→ "Wasm module failed validation or initialization" (accepted; reached install). - Chunk
{"type":"install_chunk","offset":0,"data_hex":"00","bogus":1,"bogus":2}→ same Wasm error: duplicate unknown keys silently accepted. - Chunk with
"extra":{"a":1,"a":2}→ same Wasm error: duplicates nested inside an ignored object silently accepted. install_endas{"type":"install_end","bogus":1,"bogus":2}after a valid chunk → same Wasm error: the completion frame matched despite duplicate keys.- Chunk with duplicate known field
"offset":0,"offset":0→ "expected module chunk frame" (refused by serde derive) — confirming the gap is precisely the ignored-field class, not all duplicates.
Why it matters
The install path is the admin module-upload gate: it runs after admin authorization and assembles the exact bytes whose hash is checked and which are then installed as a world module. It is the last session path where two readers of the same frame bytes can disagree about what was sent — the inconsistency the duplicate-key contract (design #6520; sibling fixes #6473, #6474, #6491, #6499, #6504, #6501) exists to remove. #6521 (the gated closing inventory) does not name this site; this issue is its disposition for the install path: it is a gate, not a not-a-gate.
Acceptance criteria
- Chunk frames and the
install_endframe ininstall_module_sessionare parsed throughparse_json_without_duplicate_keys+serde_json::from_value::<ClientFrame>(or an equivalent strict route), so any duplicate key at any depth — known, unknown/ignored, or nested inside an ignored object — refuses the frame before any chunk bytes are appended or the upload is treated as complete. - Refusal behavior matches the current parse-failure behavior: a refused chunk ends the install with "expected module chunk frame"; a refused completion frame ends it with "expected install_end frame".
- Regression tests (sandbox feature, mirroring the existing
admin_upload_pins_validates_and_activates_a_roc_programharness and PR fix(id): refuse duplicate JSON keys in world_session first/joined frames (#6501) #6508's query-session cases) pin: duplicate unknown key in a chunk, duplicate key nested in an ignored chunk object, and duplicate unknown key ininstall_end— each refused; the valid upload flow still installs. - Existing
world_sessiontests keep passing (cargo test -p id world_sessionon the laptop or CI).
Related: #6501 (first/joined/query paths), #6520 (duplicate-key contract), #6521 (closing inventory — install site not named there).
Activity
developing-today commentedon Nov 6, 2026
CODE-1978
dezren39 commentedon Nov 6, 2026
🤖 Agent note · filed
dezren39 commentedon Nov 6, 2026
agent-note:v1
opencode. Checkpointedhanded_offfor the opencode claimant to launch the bound session.19 remaining items
dezren39 commentedon Nov 6, 2026
🤖 Agent note · parked
dezren39 commentedon Nov 6, 2026
Picked up task 11174 from its awaiting_ci checkpoint. Fresh state: #6523 is open; PR #6576 is open on the task branch, and its basic required checks have remained QUEUED since 17:10:45Z. No PR changes or merge action taken; parking under the five-minute CI rule.
dezren39 commentedon Nov 6, 2026
Task 11174 is parked for 20 minutes. Resume when PR #6576 basic checks finish; then revalidate GitHub state, review the diff and ownership restrictions, and decide the next action. Required basic checks were still QUEUED more than five minutes after starting. Issue labels now reflect parked state; work/assigned remains.
dezren39 commentedon Nov 6, 2026
🤖 Agent note · handed off
opencodequeue/11174-6523-duplicate-key-install-framesis OPEN, not draft. Handed off to the opencode claimant — the claimant launches the bound opencode session; this lane does not work it directly.