dev
← All ADRs

Package Interface Contract

ACCEPTEDEngine2026-05-30

Context

autograph is composable: packages add capabilities (Docker services, tasks, skills, attitudes). For this to work reliably, packages need a formal interface contract that:

  • Declares what the package provides
  • Declares what it requires (engine version compatibility)
  • Enables diagnos to validate conformity
  • Enables the engine to compose Docker mounts and Taskfile includes automatically

Options considered

Option A - Convention only (no manifest)

Packages follow a directory convention (e.g., agent/skills/ always means skills). No explicit declaration file.

  • Advantage: Zero overhead, just follow the pattern
  • Disadvantage: No machine-readable metadata. diagnos must infer everything. No compatibility checking. Fragile.

Option B - West userdata only

All metadata lives in the West manifest's userdata.augr field.

  • Advantage: Single source of truth (the manifest)
  • Disadvantage: userdata is limited (no rich structure). The package itself has no self-description. You need the project manifest to understand a package.

Option C - Package manifest file `augr.pkg.yml` (chosen)

Each package has a augr.pkg.yml at its root that declares everything about itself. West userdata contains minimal metadata (role, kind) for discovery.

  • Advantage: Self-describing packages. Rich metadata. diagnos can validate independently. Package repos are understandable without the project context.
  • Disadvantage: Two places for metadata (augr.pkg.yml + west userdata). Must stay in sync.

Decision

Option C - augr.pkg.yml as the package manifest.

Packages must be self-describing. The West manifest handles discovery and resolution; augr.pkg.yml handles identity and contract.

Consequences

Package manifest format

The manifest declares identity, compatibility, and pointers to standard files - not their content. The engine discovers capabilities by reading the files at those paths.

name: autograph.pkg.<name>
version: "0.1.0"
description: "Human-readable description"

contract:
  engine_version: ">=0.1.0"       # Semver range of compatible engine versions

# Pointers to standard files/directories (all optional, convention-based paths)
compose:                            # Compose file(s) for this package's services
  - infra/compose.yaml              # Can be a single file or a list
  - infra/compose.dev.yaml          # e.g., dev-specific overrides
taskfile: Taskfile.yaml             # Single entry point (includes internally if needed)
skills: agent/skills/               # Directory containing skills
attitudes: agent/attitudes/         # Directory containing attitudes

The manifest does NOT duplicate content from these files. It only declares where to find them. The engine/diagnos reads the actual files to discover services, tasks, skills, etc.

compose: Can be a single path or a list of paths. This allows packages to provide multiple compose files (e.g., base + dev overrides, or separate concerns).

taskfile: Always a single entry point. If the package needs to split tasks internally, it uses Taskfile includes - that's the package's concern, not the engine's.

Standard package layout

autograph.pkg.<name>/
├── augr.pkg.yml                 # Package manifest (required)
├── Taskfile.yaml                # Single task entry point (required)
├── README.md                    # Description and usage (required)
├── infra/                       # Infrastructure (optional)
│   ├── compose.yaml             # Main compose (services for this package)
│   ├── compose.dev.yaml         # Dev overrides (optional)
│   └── docker/
│       └── <service>/Dockerfile
├── agent/                       # Agent contributions (optional)
│   ├── attitudes/
│   │   └── <name>/ATTITUDE.md
│   └── skills/
│       └── <name>/SKILL.md
└── docs/                        # Documentation (optional)

Convention: if a directory/file exists at its standard path, the engine assumes it's active. The manifest confirms and allows diagnos to validate.

Validation by diagnos

diagnos checks:

  • augr.pkg.yml exists and is valid YAML
  • contract.engine_version is satisfied by the current engine
  • Declared paths actually exist (compose, taskfile, skills/, attitudes/)
  • Compose file is valid (parseable)
  • Taskfile is valid (parseable)
  • Package is mounted read-only in the agent container (L2 conformity)

West userdata (minimal)

userdata:
  augr:
    role: package
    kind: core          # or "third-party"

Trade-offs

  • Packages must maintain both augr.pkg.yml and appear in a West manifest
  • The format will evolve - v0 is intentionally simple
  • No runtime validation (contract is checked at prep/diagnos time, not at every boot)
  • Convention-based discovery means renaming standard paths breaks things silently - diagnos mitigates this

Open: agent package extension

The contract above covers the generic package interface. Packages of type "agent" (pkg.agent, pkg.metalab) provide additional artifacts that are not yet formalized in the manifest:

  • agent/opencode.json.template -- configuration template (rendered by entrypoint via envsubst)
  • agent/prompts/ -- agent system prompts
  • agent/plugins/ -- OpenCode plugins (aggregated by entrypoint)
  • agent/AGENTS.md -- agent operating manual

These artifacts follow the same convention-based pattern (if the path exists, the engine assumes it's active) but are not declared in augr.pkg.yml and not validated by diagnos.

The companion diagram (package-interface-contract.drawio) maps the full asset lifecycle for agent packages: source location, aggregation mechanism (entrypoint scripts, volume mounts, envsubst), and runtime destination in the container. It captures the current state and the questions to resolve for formalizing this extension:

  • Should agent-specific paths be declared in augr.pkg.yml (new pointers)?
  • Should diagnos validate agent artifacts?
  • How to handle aggregation filtering by agent role (today all skills go to both agents)?
  • How to support multi-package prompt and agent contributions?

These questions are tracked in E03 (P1-P4) and will result in an amendment to this ADR when resolved.