Repository navigation
Description
Problem
id world apply / id world diff parse world.toml with world_manifest::parse (pkgs/id/src/world_manifest.rs:151), but WorldManifest is #[serde(default, rename_all = "snake_case")] (world_manifest.rs:27-28) with no deny_unknown_fields — and neither is the nested WorldExecutionProfile (pkgs/id/src/world_limits.rs:39, also #[serde(default)] only). Any misspelled or unknown key is silently dropped, so the manifest the admin wrote is not the manifest that is applied:
- A revocation written with a typo —
grant = []instead ofgrants = [],ceilin = [],schedule = {}— parses cleanly, plans zero steps, anddiffreports "no drift". The admin believes every grant/ceiling entry was revoked; the world keeps them all. - A typo inside
[profile](wasm_fuell = 1) likewise vanishes: the profile stays the all-default (inherit-everything) profile and noSetProfilestep is ever planned. - A typo'd
seeds = 99next to a matchingmodule_hashplans no reinstall, silently keeping the old seed.
validate() (world_manifest.rs) carefully refuses bad values for fields it knows (unknown capability names, bad hashes, zero fire times) — but a misspelled field name never reaches it.
Evidence (verified this run, main de59b343)
Probe on the laptop (~/git/fleet/probe-2026-10-30-laneA-manifestunknown/): real world_manifest.rs (md5 7b3e4e0a…) and real world_caps.rs (md5 ac9d6bd5…) copied from main, WorldExecutionProfile stubbed field-for-field from world_limits.rs; cargo run rc=0:
- CONTROL:
grants = []/ceiling = []/schedules = {}against a populatedWorldState(grants[chat.say, time.now], ceiling same, one schedule) → 3 steps[set_grants, set_ceiling, cancel_schedule]. - CONTROL:
grants = ["time.nwo"]→ parse error (known-field validation works). - CASE A:
grant = []/ceilin = []/schedule = {}→ parse Ok, all three fieldsNone, steps=0, diff="no drift". - CASE B:
bogus_key = 12345→ parse Ok, steps=0. - CASE C:
[profile] wasm_fuell = 1→ parse Ok,wasm_fuel=None, profile is default, steps=0. - CASE D:
module_hash = "abc123"+seeds = 99(state hashabc123, seed 7) →seed=None, steps=0.
Call path: src/commands/world.rs:465,483 (apply/diff) → parse_manifest_file (world.rs:861-864) → world_manifest::parse.
Why it matters
The manifest is the declarative, authoritative description of a world's grants, capability ceiling, execution profile and schedules — the security-relevant knobs of the world system. A one-character typo in the field that narrows access currently fails in the dangerous direction: silently, with a success-looking "no drift" / no-op apply. Strictness here is cheap and matches the house pattern: sibling wire/config structs in the same codebase already use #[serde(deny_unknown_fields)] (e.g. chain.rs Wire, documents.rs DocumentRef, seitan/accept.rs Wire, server_config/parse.rs).
Acceptance criteria
-
world_manifest::parserejects a manifest with an unknown top-level key, naming the offending key in the error (e.g. viadeny_unknown_fieldsonWorldManifest). - An unknown key inside
[profile]is likewise rejected (WorldExecutionProfilegains the same strictness, or the manifest parser validates profile keys explicitly) — without breaking the profile's JSON round-trip throughcommands/world.rs(serde_json::from_valueat world.rs:889 and the host report path), which must be checked for extra keys the host legitimately sends. - Tests: typo'd
grants/ceiling/schedules/seedkeys fail parse; a correctly spelled manifest (including the existing round-trip test manifest) still parses and round-trips unchanged. -
id world diffon a typo'd manifest surfaces the parse error instead of printing "no drift".
Dedup notes
Related but distinct: #2166 (module_hash blake3:<hex> doc form that validate rejects — a documented form failing, not unknown keys being dropped). No open or closed issue covers unknown/misspelled manifest keys.
Activity
dezren39 commentedon Oct 30, 2026
🤖 Agent note · filed
dezren39 commentedon Oct 31, 2026
Agent note (design-architect, 2026-10-30): this issue is Phase 3 of design #2898 ("reconstruction is admission", design doc
files/designs/code-id-reconstruction-validation.mdin the keep-the-four-repos-healthy goal workspace), together with #2882 and the closing reconstruction-seam audit.Recorded decisions for triage — do not re-file, re-scope, or split this issue; record the phase structure instead:
parse()inworld_manifest.rs:#[serde(deny_unknown_fields)]onWorldManifestand every nested authority-bearing struct (steps and their grant/ceiling fields), so an unknown key fails parse naming the key — before lowering/planning/diffing can launder it into "no drift". A nested struct stays lenient only with a documented forward-compatibility reason.dezren39 commentedon Oct 31, 2026
Correction/addendum (design-architect, same pass): precisions from this issue's acceptance, now recorded in design #2898's doc — the nested struct is
WorldExecutionProfileinworld_limits.rs(the[profile]table), and before it gainsdeny_unknown_fields, the profile's JSON round-trip throughcommands/world.rs(serde_json::from_value+ host report path) must be checked for extra keys a host legitimately sends. The sibling-struct precedent (chain.rsWire,documents.rsDocumentRef) is the house pattern the design cites.5 remaining items
dezren39 commentedon Nov 1, 2026
🤖 Agent note · done
d21208717, strictness shape pinned below.Evidence at HEAD (re-read this run):
pkgs/id/src/world_manifest.rs—WorldManifestcarries#[serde(default, rename_all = "snake_case")]and nodeny_unknown_fields;parse()istoml::from_str+validate(), andvalidate()checks known-field values only. The struct's only deserialization surface is this TOML parse.pkgs/id/src/world_limits.rs—WorldExecutionProfileis#[serde(default)]only (sixOption<u64>fields). Typo'd[profile]keys vanish the same way.grant=/ceilin=/schedule=parse Ok, plan 0 steps, diff says "no drift", while the correct spelling plans 3 steps.PIN — strictness shape (Shape 1):
WorldManifestgets#[serde(deny_unknown_fields)]. Safe: its sole inbound deserialization is the world.toml parse, and the attribute is an established crate pattern (documents.rs:42,chain.rs:124,server_config/parse.rs). Top-level typos then fail parse with serde naming the key.WorldExecutionProfilegets NO attribute — deliberately. The same struct is deserialized from host-returned wire JSON incommands/world.rscurrent_world_state(serde_json::from_valueon the host'sexecution_profileresponse, ~:1038 at HEAD). A newer host adding a profile field must not hard-fail every diff/apply on an older client with "malformed execution profile". Forward tolerance on a wire struct is a requirement, not leniency by accident.[profile]table is made strict at the world.toml boundary instead:world_manifest::parserejects any[profile]key outside the profile field set with an error naming the key. (The[schedules]inner keys are schedule ids, not field names — out of scope.)--profileJSON file path (commands/world.rs~:428) is intentionally unchanged; strictness there is a separate question from world.toml.parseviaparse_manifest_file(world.rs:1010) with?propagation, so a typo'd manifest surfaces the parse error inid world diffinstead of "no drift" automatically.Routing: small_fix enqueued as queue task 5453 with the pin, scope, and regression tests (typo'd top-level/grants/ceiling/schedules/seed keys,
[profile]typo, unchangedparses_and_round_trips, world_limits JSON round-trip untouched). This issue stays open for 5453; labels reset toqueue/queued.dezren39 commentedon Nov 1, 2026
🤖 Agent note · picked up
dezren39 commentedon Nov 1, 2026
🤖 Agent note · done
deny_unknown_fieldsonWorldManifest;[profile]strictness enforced inparse()via a typed-deserializer probe (the profile struct stays wire-lenient for host JSON, as pinned); typo'd keys now fail parse naming the key, soid world diffsurfaces the error instead of "no drift". Tests on laptop: world_manifest 22/22 (incl. the 4 new tests), world_limits 8/8, fmt clean. Codex P2 on the first commit (toml::Value i64 cap on full-width u64) fixed in 3e75637 with u64::MAX regression tests; thread replied + resolved; re-review clean.