Capability Model

Apps, tiers, bundles & skills — how a new feature is built and referenced

When Koda gains a new ability it lands as one of two shapes. A capability is a platform function Koda does for you (research, transcribe, Drive, Gmail). An app is a product with its own UI, users and permissions (DET22, User Management). Anything that reaches an external system is built as a stack: a chokepoint (the one library allowed to touch the outside thing) → a bundle (the sanctioned wrapper that makes it discoverable and declares its guardrails). Separately, skills are playbooks that teach the AI how to do a task. Bundles and skills are siblings, not nested.

The four terms

TermWhat it isExampleLives in
App tierHow a feature is surfaced. Tier 1 = built into Core; Tier 2 = standalone app with UI/API/usersconsole (T1), DET22 (T2)apps/, koda_apps
CapabilityA platform function Koda performs, granted per-userresearch, drive.readpermissions table
BundleThe sanctioned wrapper around one external integrationdrive.yamlbundles/*.yaml
SkillA markdown playbook teaching the AI how to do a taskpdf, deploy-dashboardskills/*.md

Bundles & skills are siblings

Bundle

Wraps an external integration. Makes the chokepoint discoverable; declares permissions and destructive-action gates. Loaded when the matcher detects your message is reaching that integration.

Skill

Teaches the AI how to perform a task — a workflow. Loaded when its trigger words fire. Neither contains the other.

The one bridge between them

A special always-on skill, bundle-self-check, tells the AI: "before you call any external thing, verify it's going through its bundle." So a skill polices bundle usage — but they are not nested.

How a capability is built — the 5-layer stack

1
Chokepoint
Exactly one lib/*.js module is allowed to talk to the external system. lib/drive.js for Drive.
2
Lint enforcement
ESLint + pre-commit hook block any other file from importing the SDK.
3
Bundle (discoverability)
bundles/<name>.yaml declares the chokepoint functions, permissions, and destructive actions. The matcher pre-loads its instructions into the AI's prompt on the right turn.
4
Audit
Every external call logs a three-actor audit_log row.
5
Confirm-before-destructive
Any action in the bundle's destructive_actions[] needs explicit owner yes/no before running.

Worked example: Google Drive

lib/drive.js
chokepoint — auth, audit, 25MB cap
bundles/drive.yaml
discoverability + guardrails
matcher
"Google Drive" → loads bundle
AI prompt
calling pattern + cheatsheet

Drive is system infrastructure, not a Tier 2 app — there is no /drive slash command and no intent dispatch. The bundle is "bundle-as-docs": it routes the AI to the chokepoint and loads the guardrails. update_file_content is marked destructive, so a write requires owner confirm.

Failure mode worth knowing

If a bundle YAML fails schema validation (e.g. an action marked destructive: true but missing from top-level destructive_actions[]), the registry silently drops it — the integration won't auto-fire. Run /bundles list to see load errors, fix the YAML, then /bundles reload. Since koda-core 2.140.3, Core also DMs the owner at boot if any bundle failed to load.

I'm adding a new thing — what do I build?

Reaching a new external service (API, device, third-party)chokepoint lib/*.js + bundles/<name>.yaml
A new product with a UI and its own usersTier 2 app (subdomain, koda_apps, RBAC)
A thin wrapper around one external service, owner-onlyTier 1 slash command (ADR-019)
Teaching the AI a repeatable workflowa skill (skills/<name>.md)
A grantable per-user platform functiona capability (GRANTABLE_CAPABILITIES + permissions)