Repository navigation
Description
Summary
The field-value gate added for #4063 (PR #4082, parse_app_manifest in pkgs/id/src/app_manifest.rs) rejects duplicate capabilities.request entries — with the stated rationale that a repeat is an authoring mistake the gate should name rather than silently absorb — but the same gate is inconsistent for the manifest's other list fields, and its own duplicate check is evadable by whitespace padding:
- Duplicate
interfaces.cli.commandsare accepted.commands = ["play", "play"]parses fine (app_manifest.rs:119-131 validates only non-empty + non-blank). The duplicate rule was applied tocapabilities.request(lines 146-155) but not to the CLI command list in the same function. - The capability duplicate check compares raw strings while the blank check trims.
request = ["store.read", " store.read "]is accepted as two distinct requests (lines 133-155: blank check usescapability.trim(), theHashSetinsert uses the rawcapability), and the padded entry is stored verbatim in the parsed manifest — so the manifest's "strict record of what an app asks for" carries a capability name (" store.read ") that no exact-match capability check will ever match, silently granting nothing while looking granted. The same padding evasion works for commands:["play", " play "]is accepted.
Evidence (verified this run)
Laptop probe ~/git/fleet/probe-2026-11-01-laneA-manifest/ — a crate that compiles the real app_manifest.rs from main 78b2bd26 (md5 d786541ff7045e599df9516605b129db, byte-identical to the repo file) and calls parse_app_manifest:
dup-cli-commands: ACCEPTED cmds=["play", "play"]
ws-evaded-cli-dup: ACCEPTED cmds=["play", " play "]
ws-evaded-cap-dup: ACCEPTED caps=["store.read", " store.read "]
untrimmed-cap-entry: ACCEPTED caps=[" store.read "] (stored verbatim, padding kept)
baseline-cli / baseline-web: ACCEPTED (unchanged)
Why it matters
#4082's gate is the strict authoring contract for app manifests; an app author who duplicates a command or pads a capability gets no error, and the padded capability silently never matches at enforcement time — the worst failure shape for a permission request (looks declared, never granted, no diagnostic anywhere). The inconsistency is also in the exact code the #4063 fix just touched, so the contract reads as deliberate where it is actually partial.
Acceptance criteria
parse_app_manifestrejects duplicateinterfaces.cli.commandsentries, with an error naming the field, mirroring thecapabilities.requestduplicate rule.- Duplicate/blank comparisons for list entries use one consistent normalisation: either entries are trimmed before storage and comparison (and the stored manifest carries the trimmed values), or padded entries are rejected outright — a padded duplicate of an existing entry must not parse as a distinct entry.
- Unit tests in
app_manifest.rscover: duplicate commands rejected;["store.read", " store.read "]rejected (or stored as one trimmed entry only if trimming is the chosen rule — the test pins whichever rule is chosen); the checked-in tic-tac-toe manifest and the existing fix(id): validate app manifest field values (#4063) #4082 tests still pass. - Not in scope: route/socket validation (id(app_manifest): interfaces.web route/socket are barely validated and discovery accepts duplicate routes #4060),
[resources].filesconfinement (id(app_manifest): [resources].files entries accept absolute and ../ paths — folder confinement is checked only for program.source #4059), symlinked app folders (id(app_manifest): discover_apps follows symlinked app folders — an app outside the apps root registers as a local app #4067).
Activity
dezren39 commentedon Nov 2, 2026
🤖 Agent note · filed
dezren39 commentedon Nov 2, 2026
🤖 Agent note · adopted as Phase 2 of design #4202
cli.commands,capabilities.request,[resources].files). The manifest is the strict record of what an app asks for; silently rewriting a capability name hides the authoring mistake the gate exists to name (the fix(id): validate app manifest field values (#4063) #4082 gate's own rationale for rejecting duplicates rather than deduplicating them). With entries unpadded by construction, duplicate comparison on the stored strings is exact. This ruling is also the normalisation rule id(app_manifest): [resources].files has no blank/duplicate gate — blank, whitespace-only, duplicate and padded-duplicate entries all parse, unlike cli.commands and capabilities.request #4168's acceptance defers to.source/design-architect. Existing queued triage packet stands.dezren39 commentedon Nov 2, 2026
agent-note:v1 — holder: repo-worker-code (OpenChamber scheduled lane), at=2026-11-02T15:07:19Z. event: picked-up; task: 5981; queue: repo-health-code-pilot. Fresh main inspection at origin/main 2ab643e (source md5 matches the filed probe) confirms the duplicate/padded-entry gap; #4085 remains OPEN and adopted as Phase 2 of #4202. No issue refile/re-scope/split.
41 remaining items
dezren39 commentedon Nov 2, 2026
agent-note:v1 — parked
dezren39 commentedon Nov 2, 2026
🤖 Agent note · picked up
dezren39 commentedon Nov 2, 2026
🤖 Agent note · done