Skip to content

feat: let a deployment add its own sensitivity labels alongside the built in six - #489

Merged
imran-siddique merged 1 commit into
agentrust-io:mainfrom
qubeena07:feat/configurable-sensitivity-vocabulary
Aug 10, 2026
Merged

feat: let a deployment add its own sensitivity labels alongside the built in six#489
imran-siddique merged 1 commit into
agentrust-io:mainfrom
qubeena07:feat/configurable-sensitivity-vocabulary

Conversation

@qubeena07

Copy link
Copy Markdown
Contributor

Summary

Part of #479, the configurable vocabulary piece. Per call classification, the second piece of that issue, is left for a follow up.

The sensitivity vocabulary was six hardcoded labels, public, pii, confidential, hipaa_phi, mnpi, trade_secret, in SENSITIVITY_ORDER, and the catalog schema validated sensitivity_level against exactly that literal enum. A deployment whose own regulation names a tier above trade_secret, an Open, Confidential, Secret, Top Secret ladder for example, had no way to catalogue it truthfully.

sensitivity.vocabulary in config now lets a deployment add new labels at any rank.

Design decision worth flagging

The vocabulary is additive only, not a full replacement. Response inspection's content pattern detectors emit the built in tags pii and hipaa_phi directly when they spot an SSN, an email, a diagnosis code and so on. If a deployment's configured vocabulary could drop either of those names, those detections would rank at 0 by the ordinary unknown tag default, the same fail open hole #478 closed for the catalog schema, just moved to a different door.

So a vocabulary key that names a built in label is rejected at config load, and effective_sensitivity_order merges the built in table in last regardless of what config parsing did, so a built in name can never be shadowed even by a mistake elsewhere in the chain.

SessionManager and PolicyEvaluator each derive the effective vocabulary once from the same Config, so a session's max_sensitivity string and the sensitivity_level integer Cedar evaluates can never disagree about what a custom label ranks as.

Changes

  • src/cmcp_runtime/config.py: new SensitivityConfig, sensitivity.vocabulary parsing, additive only guarantee enforced at parse time.
  • src/cmcp_runtime/session/state.py: effective_sensitivity_order(), SessionState.sensitivity_order field, _max_sensitivity takes an optional order parameter.
  • src/cmcp_runtime/session/manager.py, src/cmcp_runtime/policy/evaluator.py: both derive the effective vocabulary once from the same Config and thread it through.
  • schemas/catalog-entry.schema.json, src/cmcp_runtime/catalog/loader.py: sensitivity_level enum moved out of the static JSON schema into a Python level check at load time against the effective set, so it stays closed, just not hardcoded. Same error message shape as before, existing tests unchanged.
  • src/cmcp_runtime/startup.py: wires config.sensitivity.vocabulary into load_catalog.
  • docs/spec/session-policy.md, docs/tutorials/tool-catalog-authoring.md, CHANGELOG.md.

Test plan

  • pytest tests/unit/ passes locally, 996 passed, 8 skipped
  • ruff check src/ tests/ clean
  • mypy src/cmcp_runtime/ src/cmcp_verify/ clean
  • bandit -r src/ clean
  • manual check: custom vocabulary loads, collision with a built in label rejected, catalog entry with a custom label rejected without the extension and accepted with it, session ranks a custom label correctly, built in tags like hipaa_phi still rank correctly with a custom vocabulary configured

…uilt in six

Part of agentrust-io#479. The sensitivity vocabulary was six hardcoded labels, public,
pii, confidential, hipaa_phi, mnpi, trade_secret, in SENSITIVITY_ORDER, and
the catalog schema validated sensitivity_level against exactly that literal
enum. A deployment whose own regulation names a tier above trade_secret, an
Open, Confidential, Secret, Top Secret ladder for example, had no way to
catalogue it truthfully.

sensitivity.vocabulary in config now lets a deployment add new labels at any
rank. It is additive only rather than a full replacement. Response
inspection's content pattern detectors emit the built in tags pii and
hipaa_phi directly, so a vocabulary that dropped either name would rank
those detections at 0, the same fail open hole agentrust-io#478 closed for the catalog
schema. Two layers enforce the additive guarantee: config parsing rejects a
vocabulary key that names a built in label outright, and
effective_sensitivity_order merges the built in table in last regardless, so
a built in name can never be shadowed even by a mistake elsewhere in the
chain.

SessionManager and PolicyEvaluator each derive the effective vocabulary once
from the same Config, so a session's max_sensitivity string and the
sensitivity_level integer Cedar evaluates can never disagree about what a
custom label ranks as. The catalog schema's sensitivity_level enum moved out
of the static JSON schema into a Python level check at load time against
this same effective set, so the vocabulary stays closed, just not
hardcoded.

Per call classification, the second half of agentrust-io#479, is left for a follow up
change.

Signed-off-by: Dipika Ranabhat <qubeena7@gmail.com>
@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 91.83673% with 4 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/cmcp_runtime/config.py 85.71% 4 Missing ⚠️

📢 Thoughts on this report? Let us know!

@imran-siddique imran-siddique left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving. Reviewed in a separate worktree and ran the three test files this touches: 93 passed.

Three things I checked rather than took on trust, all of which you got right.

effective_sensitivity_order merges the built ins last, so a colliding config key cannot shadow pii or hipaa_phi even if the parser somehow let one through. That matters more than it looks: response inspection emits those tags directly, and order.get(tag, 0) fails open to rank 0, so a shadowed built in would silently downgrade to public. Defending it in two places for a case that should already be impossible is the right instinct here.

There are only two SessionState construction sites. The manager passes the effective order; the other is the benchmark harness, which uses public. So no production path can silently fall back to the built in ranking.

Moving sensitivity_level out of the JSON schema does not open a hole, because the load time check is still closed, just against a config dependent set, and the schema says why the enum is gone.

One thing for the follow up rather than this PR: compliance_domain keeps its hardcoded enum, so a deployment can now name its own sensitivity tier but not its own compliance domain. Whoever hits that will hit it immediately after adopting this.

@imran-siddique
imran-siddique merged commit 346f396 into agentrust-io:main Aug 10, 2026
11 of 12 checks passed
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.

3 participants