Skip to content

fix(capsule): make component→root capability merge exhaustive (#1232) - #1381

Open
jvsteiner wants to merge 1 commit into
astrid-runtime:mainfrom
jvsteiner:fix/1232-exhaustive-capability-merge
Open

fix(capsule): make component→root capability merge exhaustive (#1232)#1381
jvsteiner wants to merge 1 commit into
astrid-runtime:mainfrom
jvsteiner:fix/1232-exhaustive-capability-merge

Conversation

@jvsteiner

Copy link
Copy Markdown

Closes #1232.

Problem

discovery.rs folds each [[component]].capabilities into the root
manifest.capabilities field-by-field, by hand-enumeration — so any field
not explicitly listed is silently dropped. The security gate reads only the
root, so a component-only declaration of a forgotten field grants nothing, with
no error or warning (it still shows up in astrid capsule show).

Fields the old merge dropped: net_connect, uplink, kv, identity,
allow_persistent, allow_prompt_injection (and bind_workers once the
run-loop concurrency work lands). Concretely: a capsule declaring net_connect
at the component level couldn't open the outbound connections it asked for —
easy to lose an hour to.

Fix

Replace the enumerated merge with an exhaustive
CapabilitiesDef::merge_from(&mut self, other) (allowlists concatenated, flags
OR-ed). It destructures the source struct with no .. rest pattern, so
adding a field to CapabilitiesDef fails to compile there until it's handled —
this class of silent-drop bug can't recur.

discovery.rs now just calls manifest.capabilities.merge_from(caps).

Tests

  • merge_from_unions_every_field — two fully-populated (no ..Default) structs;
    asserts every field unions, and the literals are themselves a compile-time
    reminder when a field is added.
  • component_level_capabilities_merge_into_root — the discovery-seam regression:
    a component-only net_connect (the field the old merge dropped) surfaces in
    the root manifest.

cargo test -p astrid-capsule-types -p astrid-capsule green; clippy clean; rustfmt applied.

🤖 Generated with Claude Code

…d-runtime#1232)

discovery.rs folded each [[component]].capabilities into the root
manifest.capabilities field-by-field by hand-enumeration, silently dropping any
field not listed: net_connect, uplink, kv, identity, allow_persistent,
allow_prompt_injection (and bind_workers once the run-loop concurrency work
lands). The security gate reads only the root, so a component-only declaration
granted nothing -- no error, even though `astrid capsule show` displayed it.

Replace the enumerated merge with an exhaustive CapabilitiesDef::merge_from
(allowlists concatenated, flags OR-ed). It destructures the source struct with no
`..` rest pattern, so adding a field fails to compile until it is handled -- this
class of silent-drop bug can't recur.

Adds merge_from_unions_every_field (exhaustiveness guard) and
component_level_capabilities_merge_into_root (discovery-seam regression).

Closes astrid-runtime#1232.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@jvsteiner
jvsteiner marked this pull request as ready for review July 26, 2026 12:27
@jvsteiner
jvsteiner requested a review from joshuajbouw as a code owner July 26, 2026 12:27
@jvsteiner

Copy link
Copy Markdown
Author

@joshuajbouw ready for review — exhaustive CapabilitiesDef::merge_from so component-level caps (net_connect etc.) stop getting silently dropped (#1232).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: discovery.rs component→root capability merge silently drops fields not in its enumerated list

1 participant