Repository navigation
Description
Problem
PR #801's new world.toml manifest model (pkgs/id/src/world_manifest.rs, branch id/phase-12) documents partial-manifest semantics that its own plan() does not implement — and the mismatch is destructive in one direction and privilege-widening in the other.
The struct doc (world_manifest.rs:23-24) says:
A declarative description of a world. Empty fields are left alone by
plan, so a minimal manifest drifts nothing.
That is only true for module/seed, and only against an already-empty world. plan() (world_manifest.rs:179ff) treats every other empty field as authoritative:
- Grants (
world_manifest.rs:197-205):grantsis a plainVec<String>with no way to express "unspecified". An omitted/emptygrantsagainst a world with grants producesSetGrants { grants: [] }—id world applyrevokes every grant. - Ceiling (
world_manifest.rs:208-212): an omittedceilingisNone, which the model defines as "the whole catalog". Against a world with a narrowed ceiling, a minimal manifest producesSetCeiling { ceiling: None }— apply widens the ceiling to the entire catalog. This is the security-sensitive direction: a manifest that never mentions the ceiling silently removes it. - Schedules (
world_manifest.rs:224-233): an omitted/emptyschedulesmap produces aCancelSchedulestep for every pending schedule in the current state. - Profile: an omitted profile is the all-
Nonedefault and replaces the current profile whenever it differs.
The PR's own test demonstrates the contradiction: an_empty_manifest_drifts_nothing (world_manifest.rs, tests) asserts an empty manifest against a default state drifts nothing, then asserts the same empty manifest against a state with grants + a ceiling produces exactly 2 steps (SetGrants + SetCeiling). The test's name and the struct doc claim the opposite of its second half.
A related rendering bug in the same file: diff() (world_manifest.rs:321) renders a SetCeiling step as ~ ceiling [{names}] with ceiling.as_deref().unwrap_or(&[]) — so None (whole catalog, the widening case) and Some([]) (no capability allowed, the most restrictive case) both render as ~ ceiling []. The two opposite operations are indistinguishable in id world diff output.
Why it matters
id world apply is an admin operation meant to be safe to run with a partial manifest ("a minimal manifest drifts nothing" is the documented contract a user will rely on). Today, running it with a manifest that only sets, say, a module hash against a live world would also revoke all grants, remove the capability ceiling, and (once schedule state is exposed over the wire) cancel all schedules — none of which the manifest mentions.
Evidence
- PR id: Phase 12 — world doctor + world.toml manifest (F12.16, F12.1) #801 (OPEN, branch
id/phase-12), commits408e664c3a50,0616553c34cd; file:line on the PR branch as fetched this run. Not onmainyet — fix-before-merge. - Dedup: id: F12.1 apply wiring — module install + schedules reconciliation #807 tracks wiring module/schedule apply steps over the wire; it does not cover plan-level empty-field semantics or the diff rendering. No other open/closed issue mentions manifest empty-field behaviour (searched "manifest empty grants").
Acceptance
- The manifest can distinguish "field unspecified — leave alone" from "field explicitly empty — clear it" for at least grants, ceiling, and schedules (e.g.
Option<Vec<String>>/Option<BTreeMap<..>>, or an equivalent explicit design), andplan()leaves unspecified fields alone. - The struct doc, the
an_empty_manifest_drifts_nothingtest name, andplan()'s actual behaviour agree; a regression test shows a minimal manifest (e.g. onlymodule_hash) against a fully populated state produces only the module step. - In particular, a manifest that omits
ceilingcan never widen an existing ceiling. diff()rendersNone(whole catalog) andSome([])(none allowed) distinctly.
Activity
dezren39 commentedon Oct 25, 2026
Triage (issue-sweep 2026-10-24 23:00 CDT): verified live on current main ff7a009 — world_manifest.rs is on main via merged Phase 12 PR #801, and the empty-field semantics described here are unchanged (plan() still treats omitted grants/ceiling/schedules as authoritative). This is the next top id item after the pending PRs #771/#810/#833 merge; it needs a dedicated implementation run (Option<> unspecified-vs-empty design + diff rendering fix), not a drive-by. No code change this run.
docs(id): Phase 14 plan — close-out: finish, measure, verify, open th…
fix(id-world): manifest plan leaves omitted grants/ceiling/schedules/…