Skill System (Aggregation, Naming, Discovery)
Context
autograph is composable: packages contribute skills (on-demand competencies) and attitudes (always-on behavioral postures) to the AI agent. Skills come from three sources:
- Project-local - Written by the project team in
workspace/agent/skills/ - Engine core packages - Shipped by autograph's core packages (pkg.agent, pkg.delivery), resolved in
engine/deps/modules/ - Third-party packages - Community or custom packages, resolved in
deps/modules/
OpenCode (the agent runtime) discovers skills by scanning a single directory: OPENCODE_CONFIG_DIR/skills/. This means skills from packages - mounted read-only via /hostspace/ - are invisible to OpenCode without an aggregation mechanism.
Additionally, as the ecosystem grows, skill names may collide between packages and frameworks. A naming convention is needed to identify the origin of a skill at a glance.
Options considered
Aggregation mechanism
Option A - Symlinks at container startup (chosen)
The entrypoint script scans package skill directories and creates symlinks in the OpenCode skills directory.
- Advantage: Simple, no runtime dependency, works with existing OpenCode, reversible
- Disadvantage: Startup delay (negligible for symlinks), requires write access to skills dir
Option B - OpenCode skill search path
Extend OpenCode to support multiple skill directories via configuration.
- Advantage: No startup script needed, native solution
- Disadvantage: Depends on upstream OpenCode feature (not available today), vendor lock-in on a feature that may never exist
Option C - Copy at install time
During augr:install, copy skills from resolved packages into workspace/agent/skills/.
- Advantage: No startup overhead, skills are "materialized" in workspace
- Disadvantage: Duplicates content, stale copies if package updates, pollutes workspace with generated files, breaks the RO principle for package content
Naming convention
Option A - Namespace prefix by origin (chosen)
Skills are prefixed with a namespace that identifies their origin tier:
| Origin | Prefix | Example |
|---|---|---|
| Engine core packages | augr- |
augr-delivery-tracking, augr-git-conventions |
| Third-party packages | <vendor>- |
acme-code-review, myframework-testing |
| Project-local | no prefix (or project-specific) | onboarding, api-design |
- Advantage: Origin is immediately visible. No collision between tiers.
ls skills/tells you what comes from where. - Disadvantage: Longer names. Rename needed if a skill moves between tiers (unlikely).
Option B - No prefix, flat names
Skills use short descriptive names regardless of origin. Collisions resolved by priority.
- Advantage: Short, simple names. Less typing for
/skillcommand. - Disadvantage: Cannot tell origin at a glance. Silent override if names collide (confusing).
ls skills/is opaque.
Option C - Directory-level namespacing
Skills grouped in subdirectories by origin: skills/augr/delivery-tracking/, skills/vendor/acme-review/.
- Advantage: Clean separation in filesystem.
- Disadvantage: OpenCode expects
skills/<name>/SKILL.md(flat structure). Would require OpenCode customization or flattening at discovery time. Over-engineering.
Priority (override order)
Option A - Local wins (chosen)
Project-local > Third-party packages > Engine core packages
- Advantage: Project always has final say. Can override any package skill with a local version. Explicit customization.
- Disadvantage: A local skill could accidentally shadow a package skill (mitigated by prefix convention - names won't collide if prefixes are different).
Option B - Package wins
Packages override local skills of the same name.
- Advantage: None meaningful - this is counterintuitive.
- Disadvantage: Project loses control. Unexpected behavior on package update.
Decision
Aggregation: Symlinks at container startup. The entrypoint scans package skill directories and symlinks them into
OPENCODE_CONFIG_DIR/skills/.Naming: Namespace prefix by origin.
- Engine core skills:
augr-<name>(e.g.,augr-delivery-tracking) - Third-party skills:
<vendor>-<name>(e.g.,acme-code-review) - Project-local skills: unprefixed or project-prefixed at team's discretion
- Engine core skills:
Priority: Local > third-party > core. If a skill with the same name exists locally, the package version is not linked.
Attitudes: Continue using glob patterns in
opencode.jsoninstructions (already working). Same prefix convention applies to attitude directory names.
Consequences
Positive
- Origin of any skill is identifiable from its name alone
- No collision risk between engine skills and project skills
- Project always has override authority
- Mechanism works today without upstream changes to OpenCode
- Simple to debug:
ls -la workspace/agent/skills/shows symlinks with targets
Negative / Risks
- Renaming existing skills (adding
augr-prefix) is a breaking change for current lab setup - Prefix adds characters to the
/skillcommand invocation - If OpenCode adds native multi-directory support later, the symlink mechanism becomes redundant (acceptable - easy to remove)
Implementation notes
Entrypoint aggregation logic
# Scan order (last wins for same name within a tier, but local always wins)
# 1. Engine core: /hostspace/engine/deps/modules/*/agent/skills/*/
# 2. Third-party: /hostspace/deps/modules/*/agent/skills/*/
# Local skills (already in OPENCODE_CONFIG_DIR/skills/) are never overwritten.
Skill directory naming
The skill directory name IS the name used in the /skill command. Therefore:
- Package
autograph.pkg.deliveryships skill atagent/skills/augr-delivery-tracking/SKILL.md - After aggregation:
/workspace/agent/skills/augr-delivery-tracking→ symlink - Agent loads it via:
/skill augr-delivery-tracking
Readiness gate
The container is not ready until aggregation + config generation complete. A healthcheck validates the presence of a readiness file (/tmp/.augr-agent-ready).
Attitudes follow the same prefix convention
- Engine attitudes:
augr-<name>(e.g.,augr-egoless-augmentation) - Third-party attitudes:
<vendor>-<name> - Project attitudes: unprefixed
Attitudes are loaded via glob patterns in opencode.json instructions and do not require symlinks (they support glob paths natively).