Skip to content

feat(config): name the declarations that will never do anything - #185

Merged
RatulMaharaj merged 1 commit into
feat/174-operator-permission-floorfrom
feat/174b-inert-declarations
Aug 1, 2026
Merged

feat(config): name the declarations that will never do anything#185
RatulMaharaj merged 1 commit into
feat/174-operator-permission-floorfrom
feat/174b-inert-declarations

Conversation

@RatulMaharaj

Copy link
Copy Markdown
Member

Stacked on #184. Fourth in the series; base moves down as each merges.

Refs #174, which #184 closes. This is the other half of that issue's lesson and small enough to review separately.

Why

The most transferable finding in the whole research, from three projects hitting it three ways:

  • The Claude Agent SDK ships CanUseToolShadowedWarning because a permission callback can be configured, correct, and never consulted.
  • langgraph warns that severing a delta chain makes later state "silently reconstruct as empty (no error raised)".
  • letta compares a persisted file's version against the running one and, on mismatch, calls print.

A control that fails by being quietly ignored is worse than one that fails loudly, because nothing surfaces and the operator's mental model stays wrong. Every declaration either takes effect or says out loud that it did not.

What it checks

inertDeclarations(config), reported at startup and by the CLI next to the existing run-grant advisories.

A credential for a host the agent cannot reach. http.auth with url: https://api.stripe.com and api.stripe.com absent from permissions.net. The request is denied before the credential is reached, so the credential does nothing. The schema documents the requirement; nothing checked it.

A permissions.net entry that is not a bare host. Entries are compared against a hostname, so https://api.github.com matches nothing and denies everything. Same for a path, a port, or userinfo. The advice names the host to use instead. This one is a plain footgun: it reads as more precise than api.github.com and is strictly worse.

memory.compact_at_tokens without memory.scope: thread. The schema's own docstring says "without scope: thread there is no history, so the threshold is inert" - documented and undetected. compact_at_tokens: false is an author declining the feature, not a mistake, so it is not flagged.

limits.max_cost with no known price. Moved here from the ad-hoc warning I added in #183, so there is one mechanism rather than two.

Not a refusal

None of these fail a config, which is a deliberate difference from the floor in #184. A floor violation is an operator's policy being breached; an inert declaration is harmless in itself, and it is the author's belief about it that costs something. Printing it fixes that.

Every line names the location (http.auth[0].url) and the fix, and there is a test asserting the advice text actually contains one.

Testing

491 pass, deno task ok clean. Eight new: a clean config producing nothing; the unreachable credential; wildcard net covering a credential host, and the apex case not covering it; each malformed net shape, with bare hosts, * and a bracketed IPv6 literal all accepted; the advice naming the right replacement host; compaction with and without thread scope and the explicit false; the spend cap fixed by either model.pricing or max_cost: 0; and a check that every declaration carries a location and an actionable fix.

What I left out

Candidates I considered and rejected, in case you want them later:

  • env: entries never referenced elsewhere in the file. Too many false positives: subprocesses and MCP servers read the environment without the file mentioning the name.
  • permissions.read/write paths that do not exist on disk. A path can be legitimately created later, and the check would fire in CI for every agent.
  • skills: entries that no purpose text plausibly triggers. Not decidable without the model.

A config that loads, validates, and quietly does nothing is the worst
state a security control can be in: no error, no warning, no behaviour,
and an author who believes they configured something. Three projects in
the research hit this from different angles, and the Claude Agent SDK
ships a warning for exactly it.

Adds inertDeclarations(config), reported at startup and by the CLI
alongside the existing run-grant advisories. Four checks:

- an http.auth credential whose host is not in permissions.net, so the
  request is denied before the credential is ever reached
- a permissions.net entry carrying a scheme, path, port or userinfo,
  which is compared against a bare hostname and matches nothing.
  "https://api.github.com" looks right and denies everything
- memory.compact_at_tokens without memory.scope: thread, which the
  schema's own docstring already calls inert without detecting it
- limits.max_cost with no known price for the model, moved here from the
  ad-hoc warning added with the cost cap so there is one mechanism

This is a different failure from runGrantAdvisories, which is about
authority that is wider than it looks. These are statements the runtime
will never act on.

Nothing here refuses a config. An inert declaration is harmless in
itself; it is the belief about it that costs something, and every line
names both the location and the fix.

Refs #174

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CPftW3F8JTuivRhFmoY1wC
@RatulMaharaj
RatulMaharaj force-pushed the feat/174b-inert-declarations branch from 1e545d3 to cd8ea5d Compare August 1, 2026 18:44
@RatulMaharaj
RatulMaharaj merged commit d9c2c5e into main Aug 1, 2026
2 checks passed
@RatulMaharaj
RatulMaharaj deleted the feat/174b-inert-declarations branch August 1, 2026 18:49
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.

1 participant