A capability is a composed action: "Brian can publish a blog post" or "Brian can run the daily cross-post." It's not a single tool call; it's a declarative description of what's required, what happens, and at what risk.
{
"id": "cap.publish.fb_page_post",
"name": "Publish to Facebook page",
"requires": {
"resources": ["acc.brian.fb_page", "key.meta_page_token", "chan.agency_pipeline"]
},
"side_effects": ["writes-external", "publishes-public", "irreversible-without-deletion"],
"risk_level": "high",
"cost_class": "free",
"idempotency": "non-idempotent",
"approval_required": false,
"freshness_budget_hours": 24
}
The resolver walks requires, checks each dep's last_probe state, applies boundaries, and returns a verdict.
def resolve(cap_id):
cap = load_capability(cap_id)
blocking, warnings, required_actions = [], [], []
# 1. Walk deps
for dep_id in flatten(cap.requires):
state = atom_state(dep_id) # fresh / stale / red / unknown
if state == 'red':
blocking.append(f"{dep_id}: red")
elif state == 'stale':
warnings.append(f"{dep_id}: stale")
required_actions.append(f"probe:{dep_id}")
elif state == 'unknown':
warnings.append(f"{dep_id}: unknown")
required_actions.append(f"probe:{dep_id}")
# 2. Apply policy
policy_blocks, policy_approvals, policy_advisories = check_policies(cap)
blocking.extend(policy_blocks)
if policy_approvals:
required_actions.append("approval:jonah")
required_actions.extend(policy_approvals)
warnings.extend(policy_advisories)
# 3. Decide verdict
if blocking:
if any(b.startswith("policy:") for b in blocking):
return "blocked-by-policy"
return "no"
if required_actions:
if any(a.startswith("approval:") for a in required_actions):
return "yes-after-approval"
return "yes-after-probe"
return "yes"
check_policies(cap) walks boundaries.json rows where severity: hard. For each rule:
fired = (
(m_se specified) ⇒ (m_se ∩ cap.side_effects ≠ ∅)
∧ (m_cost specified) ⇒ (m_cost == cap.cost_class)
∧ (m_risk specified) ⇒ (m_risk == cap.risk_level)
∧ (m_id_re specified) ⇒ (re.fullmatch(m_id_re, cap.id))
)
AND across clauses within a rule, OR across rules. Different rules contribute independently; the most restrictive verdict wins.
If cap.id ∈ rule.exceptions, the rule is skipped entirely.
For decision: deny_unless_brian_account, the resolver additionally checks whether the cap's requires JSON contains the substring "brian" (case-insensitive). If yes → advisory. If no → block, "no brian account in deps."
| Cap | Verdict | Why |
|---|---|---|
cap.memory.bloom_recall |
yes |
green deps, low risk, no boundary fires |
cap.publish.fb_page_post |
yes-after-probe (typically) |
needs probe of brian's page token |
cap.publish.linkedin_post |
no |
LI auth red |
cap.business.stripe_charge |
yes-after-approval |
money — boundary.no_real_money_outflow_without_ask |
cap.publish.daily_blog |
yes-after-probe |
exception list (no account ambiguity for self-hosted blog) |
Three semantic bugs in the resolver were caught during full subcommand smoke testing and fixed same session:
boundary.no_paid_model_calls declared side_effects_any: [costs-money] AND cost_class: paid. Pre-fix matcher fired on either. cap.business.stripe_charge (cost_class: metered, side_effects: costs-money) wrongly resolved to blocked-by-policy under the LLM rule, when it should have hit boundary.no_real_money_outflow_without_ask for yes-after-approval.
Fixed by counting clauses_specified vs clauses_matched and requiring equality.
boundary.no_jonah_personal_gmail_via_browser regex too broadWas ^cap\.mac\..* — caught cap.mac.see_screen, cap.mac.listen_mic, etc. Tightened to ^cap\.mac\.(drive_chrome|run_command)$ (only caps that can actually open a browser tab on Jonah's gmail), and decision changed from deny to require_approval (runtime hook still inspects tool args for the gmail address).
boundary.brian_only_publisher and boundary.meta_only_brian_page regexes too broadbrian_only_publisher matched any writes-external. Tightened to ^cap\.(publish|ads)\..* with cap.publish.daily_blog exception (blog is intrinsically Brian-authored on Jonah's domain — no account-routing question).
meta_only_brian_page matched the same broad pattern. Narrowed to ^cap\.(publish\.(fb_page_post|ig_post|daily_cross_post)|ads\.meta_campaign)$.
_check_invariants() in arg validate runs four cross-row checks:
critical: true should have a probe block.grants.json row's from_party, to_party, scope_account must point to known IDs.cost_class: metered/paid or side_effects: costs-money, at least one money-relevant boundary must exist.Surfaced 9 real gaps on first run; all reconciled. Verified working with deliberate-orphan injection.