Chio/Docs
LOGIN · JOIN

Delegation Hygiene

See which restrictions a delegator added to the authority it handed out, and how that record feeds issuance and passport decisions.

Concept chio@5f63e6e

On this page

Delegation hygiene scores how much authority a coordinator kept back when it handed out work. For every child capability it issued, the score records whether scope narrowed, expiry moved earlier, and a cap tightened.

The score belongs to the delegator, the issuer of each child, and its three rates show which restriction it added. It measures recorded lineage. The attenuation checks in Delegation & Attenuation are what refuse an invalid child.

Two edges from one delegator average to one score

The public reputation fixture gives one delegator two parent and child pairs. One child copies its parent; the other narrows it on every count.

Delegation hygieneChoose an edge.
scopeexpirybudgetadelegatorholdsissuesκa′parent-a · shell/execchild-acap 100cap 100000issuesκa′parent-b · net/postchild-bcap 50cap 5+ domain · no delegate · earlier expiry111aκa′parent-achild-acap 100cap 100000κa′parent-bchild-bcap 50cap 5111

adelegatorκheld parenta′issued childsignals: scope, expiry, budget

Child A keeps everything its parent had

parent-a and child-a both permit shell/exec with an invocation cap of 100 and the same expiry. Scope, expiry, and budget did not narrow, so the edge scores 0, 0, 0. Delegation Hygiene.

Two parent and child pairs from the public reputation fixture that one delegator holds and issues, with widths drawn to invocation caps. Unresolved parent records, which the score skips, and monetary caps are left out.

child-a keeps parent-a’s shell/exec permissions, its cap of 100 invocations, and its expiry, so its signals are 0, 0, 0. child-b takes parent-b’s net/post, removes delegation, adds a domain constraint, cuts invocations from 50 to 5, and expires earlier, so its signals are 1, 1, 1.

With the fixture’s issuance times and the default 30-day half-life, the edges weigh about 0.957398 and 0.959962. Each rate, and the mean of the three, comes to about 0.500669: the narrowing edge is slightly newer, so it weighs slightly more.

Each rate is a time-weighted share of narrowing edges

For every scored edge the weight halves every 30 days after the child’s issuance. Each rate divides the weighted positive signals by the sum of all scored-edge weights, and the score is the mean of the three rates, clamped to the unit interval.

Every rate and the score is a MetricValue: {"state":"known","value":0.5} or {"state":"unknown"}. Unknown means no edge could be scored. Known zero means edges were scored and none earned weighted credit, a different result that a policy reads differently.

Read more in the weighting recipe, with the half-life and edge cases.

The score sees children the subject issued whose parent resolves

compute_local_scorecard selects lineage records whose issuer_key is the subject and that carry a parent_capability_id. compute_delegation_hygiene then resolves that parent id against the corpus.

  • Issued by the subject, parent resolves: one observation, and all three predicates run.
  • A root with no parent id: outside the candidate set.
  • A parent record absent from the corpus: skipped before any rate or count changes.
  • No children, or every parent absent: unknown score and rates, with delegations_observed at 0.

A received capability is not an issued delegation, so a grandchild issued by another agent counts toward that intermediate agent. The local corpus builder combines snapshots the subject holds with snapshots it issued, so a parent the delegator holds resolves beside its children. The kernel derives a snapshot’s parent id from the last delegation-chain link when it records an observed capability, and writes no snapshot when no receipt store is configured.

To see the edges a delegator will be scored on, walk forward one level from a grant it holds. The fixer from Delegate Between Agents issued one child:

Terminalbash
chio lineage query --live --seed cap-01d2…2a90 --direction forward --depth-limit 1
Output
walk     forward from cap-01d2…2a90, depth limit 1
depth 0  cap-01d2…2a90  capability  fixer
depth 1  cap-01d2…6c13  capability  tester  delegated from cap-01d2…2a90
2 nodes, 1 edge, walk stopped at depth 1

That edge removed write, cut thirty minutes to ten, and lowered the cost ceiling from USD 2.00 to USD 0.50, so it scores 1, 1, 1.

Each predicate records that a restriction happened, whatever its size

Each edge answers three yes-or-no questions. Removing one operation and removing several earn the same credit.

  1. Scope. Any child tool grant without a compatible parent grant vetoes scope credit for the edge. Otherwise the edge earns credit for fewer tool, resource, or prompt grants, or for a matching tool grant that drops operations, adds or changes constraints, or tightens a cap.
  2. Expiry. The child’s absolute expires_at is strictly earlier than the parent’s. An inherited deadline earns zero even when the child was issued later, and this predicate runs whether or not tool grants match.
  3. Budget. At least one matching tool grant introduces or reduces max_invocations, max_cost_per_invocation, or max_total_cost, with monetary caps in the same currency. Any match earns it, with no edge-wide veto.

A separate expansion fixture shows why a middling score needs its rates read. A parent holds fs/read_file and its child holds fs/write_file with an earlier expiry and an invocation cap of 5. The tool cannot match, so the source asserts scope 0, expiry 1, and budget 0 over one observation: a mean of one third with no narrower authority behind it.

Read more in Scope reduction, Earlier expiry, and Tighter limits.

A copied child scores 0.0 and a narrowed child 1.0

Compare a child that copies its parent's scope, quota and expiry with a child that removes delegation, reduces the quota from 100 to 1 and shortens its lifetime. Each corpus contains a resolvable parent-child pair. The binary ships in the swarm authority project, which the Rust example setup builds.

Terminalbash
cargo run --locked --features reputation --bin hygiene
Captured output
passthrough: {"score":{"state":"known","value":0.0},"delegations_observed":1,"scope_reduction_rate":{"state":"known","value":0.0},"ttl_reduction_rate":{"state":"known","value":0.0},"budget_reduction_rate":{"state":"known","value":0.0}}
narrowed: {"score":{"state":"known","value":1.0},"delegations_observed":1,"scope_reduction_rate":{"state":"known","value":1.0},"ttl_reduction_rate":{"state":"known","value":1.0},"budget_reduction_rate":{"state":"known","value":1.0}}

You should see one observation per history, every rate known, and scores of 0.0 and 1.0. The scoring API reads lineage snapshots; establish where those records came from before an issuance or passport decision relies on the score.

Known hygiene feeds the composite, issuance tiers, and passport floors

Delegation hygiene has a default weight of 0.15 in the eight-metric scorecard. Only known metrics contribute their weighted value and their weight, so unknown hygiene drops out of both sides instead of counting as zero. A weight of 0.15 is exactly 15% only when the contributing weights total 1.0; read effective_weight_sum for the actual share.

  • Local issuance tier. The effective composite, under any probationary ceiling, resolves to a configured tier, which enforces its max_scope. With max_delegation_depth set to 0 a tool grant containing Delegate is refused; with constraints_required an unconstrained tool grant is refused.
  • Passport verifier policy. PassportVerifierPolicy can require minDelegationHygiene against the scorecard a ReputationCredential carries in credentialSubject.metrics. Unknown hygiene adds the reason “delegation_hygiene is unknown but policy requires a minimum,” even at a minimum of zero; a low known score adds its value and the minimum.
  • Marketplace feed tier. tier_from_deltas derives its tier from ScoreDelta feed inputs and reads no local hygiene field. The guard-market CLI passes an explicit parsed tier to its tenant context and uses the trusted fiscal service.

Tune the weight through extensions.reputation.scoring.weights.delegation_hygiene; validation requires a finite value in [0, 1], and the weights need not sum to one. The public passport example sets its floor to 0.70, a value each verifier chooses. Reasons accumulate across the policy checks, and the credential’s signature authenticates its issuer’s statement, so assess that issuer and its evidence.

Read more in Reputation Scoring and Agent Passport.

The score describes recorded behavior; provenance comes from its caller

The pure scoring function is deterministic over the corpus, time, and configuration it receives. Assembling the database and deciding whom to trust belong to its caller, and the metric reads snapshot fields as given, leaving capability signatures, parent-chain authenticity, and complete attenuation to the verifiers that check them.

A boolean credit shows that a recognized reduction occurred, not that the child held only the authority it needed. Zero observed looks the same whether nothing was delegated or the lineage was unavailable. Caps describe authority; spend and settlement live in the budget store. Sub-Agent Budgets covers enforcement once the child uses its grant.

chio reputation compare compares the composed hygiene score, so equal totals can hide changed scope, expiry, and budget rates. Portable normalization rounds known values to nine decimal places and keeps the three rates and the observation count. Portable Reputation explains the exported statement.

Source

Fields, predicates, and diagnostics

Result fields

FieldMeaning
score
Mean of the three rates, clamped to the unit interval, or unknown when no edge can be scored.
delegations_observed
Resolved parent-child pairs scored. Missing parent records are skipped, so this counts scored edges only.
scope_reduction_rate
Decay-weighted share of scored edges that pass scope_reduced.
ttl_reduction_rate
Decay-weighted share whose child has a strictly earlier absolute expires_at.
budget_reduction_rate
Decay-weighted share with at least one matching tool grant whose invocation or monetary cap tightens.

Inspect a subject

Request JSON to see the observation count and all three rates for one agent. --agent takes the name you gave the agent, such as fixer, or its did:chio identifier, and resolves it through your operator’s agents. Point at the receipt database that holds its lineage, then read scorecard.delegation_hygiene.

Terminalbash
chio --format json \
  --receipt-db ./receipts.db \
  reputation local --agent fixer

The human renderer shows only the score, to three decimals, or unknown. The JSON scorecard uses snake_case even inside an inspection that uses camelCase fields such as effectiveScore. --since and --until bound the receipt window in the local corpus builder and leave its lineage snapshots unfiltered. --policy selects local scoring and issuance-tier configuration; with --control-url the CLI uses the trust service’s policy and requires its control token, and combining it with --policy is refused.

ObservationInspect next
Unknown, with 0 observed
That the subject issued children, parent_capability_id is present, and parent records are in the corpus. Confirm the observing kernel writes to the queried receipt store.
Score near 0.33 or 0.67
All three rates. Several combinations give the same mean.
Scope 0, expiry 1, budget 0
Expiry moved earlier with no structural reduction found. An unmatched tool grant gives this pattern, as in the expansion fixture.
Scope 0, budget above 0
A mixed edge: an unmatched child tool grant vetoes scope credit while another matching grant earns budget credit.
Unexpected composite weight
effective_weight_sum against the configured weights of known metrics. Defaults total 1.0; custom weights need not.
Receipt metrics absent, hygiene present
trusted_kernel_keys and the receipt corpus. Hygiene scores lineage independently of receipt-signature validation.

Compatible parent tool grant

parent_grant_for first tries a parent grant with the same server_id and tool_name for which the child passes ToolGrant::is_subset_of, then falls back to any parent grant that passes the subset check, including wildcard server or tool grants. It takes the first qualifying match in each search. The subset check preserves operations, constraints, invocation caps, same-currency monetary caps, and any required DPoP, so an increased cap or an unmatched tool can prevent a match before reduction is measured.

Scope reduction

Check, in orderResult
Any child tool grant has no parent match
No scope credit for the edge.
Fewer tool, resource, or prompt grants
Scope credit, after the tool-grant check above.
A matching tool grant narrows
Scope credit for fewer operations, added or changed constraints, a tighter invocation cap, or a tighter monetary cap.
None of those conditions
No scope credit.

Resource and prompt lists contribute their counts; the metric runs no full resource or prompt attenuation validation. A tighter cap can earn both scope and budget credit.

Earlier expiry

The comparison is child.expires_at < parent.expires_at. An inherited deadline earns zero. The predicate is independent of parent tool-grant matching, so it can earn credit on an edge with no scope credit.

Tighter limits

Matching grant changeBudget credit
Introduce a cap where the parent has none
Yes, for max_invocations, max_cost_per_invocation, or max_total_cost.
Reduce an existing cap
Yes. Monetary amounts keep the same currency.
Preserve the cap, or remain uncapped
No reduction for that cap.
No compatible parent grant
No credit from this grant. Another matching reduced grant can still earn credit for the edge.

The predicate reads limits in the grant, never spending from the budget store.

Weighting recipe

Delegation score recipetext
age = max(now - child.issued_at, 0)
weight = 2 ^ (-age / half_life_seconds)
rate = sum(signal * weight) / sum(weight)
score = (scope + expiry + budget) / 3

A half-life of 0 disables decay and sets each weight to 1. A future issuance time gets age 0, which is arithmetic and no validity check. If floating-point decay makes the summed weight zero, weighted_average returns 0.0 while the observations stay counted.

Two-edge fixture inputs

Recorded edgeInputs and three signals
parent-a to child-a
shell/exec keeps Invoke and Delegate, no constraints, 100 invocations, the same expiry. Signals 0/0/0.
parent-b to child-b
net/post keeps Invoke, removes Delegate, adds DomainExact(billing.example.com), cuts invocations 50 to 5, cuts the per-call USD cap 500 to 50 base units, expires earlier. Signals 1/1/1.
Weights at 1710172800
Child issuance times 1710010000 and 1710020000 give about 0.957398 and 0.959962.
Computed metric
Two observations; each rate and the score about 0.500669.

These values are calculated from the public test’s inputs and the source formula. The test asserts two observations, a score between 0.45 and 0.85, and a scope rate between zero and one.

Expansion fixture

The expansion fixture constructs lineage records directly. It shows how scoring handles those inputs and says nothing about whether issuance would authorize the expanded child.

Scorecard encoding

An inspection of a subject with no lineage reports zero observed delegations, unknown rates and composite, and an effective weight sum of 0.0. It also lists the eight default weights. An agent with lineage and no receipts can still have known delegation hygiene.

Captured empty-subject inspectioncaptured output
{
  "weights": {
    "boundary_pressure": 0.2,
    "resource_stewardship": 0.1,
    "least_privilege": 0.15,
    "history_depth": 0.1,
    "tool_diversity": 0.05,
    "delegation_hygiene": 0.15,
    "reliability": 0.15,
    "incident_correlation": 0.1
  },
  "delegation_hygiene": {
    "score": {
      "state": "unknown"
    },
    "delegations_observed": 0,
    "scope_reduction_rate": {
      "state": "unknown"
    },
    "ttl_reduction_rate": {
      "state": "unknown"
    },
    "budget_reduction_rate": {
      "state": "unknown"
    }
  },
  "effective_weight_sum": 0.0,
  "composite_score": {
    "state": "unknown"
  }
}
exit 0

Next: carry the score into reputation

Delegation Hygiene · Chio Docs