Skip to content

Skills and agents as first-class APM packages: intra-repo local-dep resolution + per-asset lockfile hashes #896

@danielmeppiel

Description

@danielmeppiel

Problem

APM today treats the contents of a skill bundle (.apm/skills/<name>/) as opaque local content: bulk-copied during install, hashed at the directory level in apm.lock.yaml, with no notion of one skill referencing another primitive (skill, agent, instructions). This leaks across several surfaces:

  • Implicit cross-primitive references. A skill's SKILL.md or assets can reference any agent/skill in the repo via prose. There is no declared dependency edge, so the relationship cannot be validated, audited, or migrated.
  • Per-asset integrity is not tracked. Edits to a file inside .apm/skills/<name>/assets/ do not change apm.lock.yaml because only the bundle root path is recorded under local_deployed_files. Surfaced concretely on harden(apm-review-panel): one-comment discipline + Hybrid E auth routing + apm-primitives-architect persona #882 by copilot-pull-request-reviewer (comment) — the verdict-template asset edit was invisible to the lockfile.
  • No policy enforcement of dependency closure. apm-policy.yml cannot express "a skill's primitives must come from its declared dep closure" because skills don't have a dependency closure today.
  • Plugin export semantics undefined for sub-bundle deps. apm pack --format plugin flattens the dep graph at the project level. Per-bundle dep graphs (e.g. this skill needs that agent) are not modeled.

Goal (direction of travel)

Skills and agents become first-class APM packages addressable via local-path deps within their parent repo. A primitive that consumes another primitive declares it in its own apm.yml. The resolver walks intra-repo manifests. The lockfile enriches with per-bundle deps + per-asset hashes. An apm-policy.yml check makes the dependency closure load-bearing. apm pack --format plugin continues to flatten the resolved graph for runtime distribution.

This dissolves the per-asset-hash gap (each bundle's apm.yml is the natural unit to enrich the lockfile against) and makes cross-primitive references explicit, validatable, and migratable.

Conceptual modelling question to settle first

Before designing implementation, decide which mental model apm-review-panel (and similar multi-persona skills) should adopt:

Option A: skill-with-transitive-skill-dep

apm-review-panel/apm.yml declares local-path deps to the agent persona files it orchestrates. Each agent becomes a tiny package (folder with apm.yml + agent.md).

  • Pros: Granular, composable; each persona is independently versionable; matches the mental model of "this skill orchestrates these specific personas."
  • Cons: Requires promoting every loose .agent.md to a folder; many tiny apm.yml files; agents lose their "loose file under agents/" affordance.

Option B: plugin-bundle

The whole orchestration unit (review-panel skill + its 7 personas + verdict template) becomes a plugin packed via apm pack --format plugin. Cross-references inside the bundle are intra-bundle, no dep declaration needed.

  • Pros: Matches industry convention (one shippable artifact); fewer manifests; apm pack --format plugin already exists as the export path.
  • Cons: Personas can't be reused independently across skills; encourages duplication when two skills want the same persona; runs counter to PROSE "Favor small, chainable primitives over monolithic frameworks".

Option C: hybrid

Personas live as standalone primitives (Option A's leaf packages). Skills declare them as deps. apm pack --format plugin is the export path that flattens the resolved graph for runtime distribution. Same outcome at runtime as Option B, but source layout preserves composability.

  • Pros: Composable source, monolithic ship artifact; aligns with how npm + bundlers work.
  • Cons: Most engineering work; requires both the manifest plumbing AND the plugin flattener to understand sub-bundle deps.

Recommendation: Option C. It's the only model that both (i) makes cross-references declarable and enforceable at source, and (ii) preserves the proven plugin distribution channel. It also generalizes — third-party APM packages publishing reusable personas would slot in naturally.

A decision-record-style brainstorm via superpowers:brainstorming is warranted before committing.

Concrete required changes (assuming Option C)

1. Bundle authoring convention

  • Add apm.yml to .apm/skills/<name>/ for any skill that depends on other primitives.
  • Promote .apm/agents/<name>.agent.md to .apm/agents/<name>/{agent.md, apm.yml} (or extend the package marker set to allow a bare .agent.md; decide in Option A vs B/C debate).

2. Resolver enhancement

  • During the local-content phase (src/apm_cli/install/phases/local_content.py), scan .apm/skills/*/apm.yml and .apm/agents/*/apm.yml; resolve their deps as transitive locals before the bulk copy.
  • Validate no cycles; respect existing local-path dep semantics (source: local, no resolved_commit).

3. Lockfile enrichment

  • Replace the single local_deployed_files: [.github/skills/apm-review-panel] line with per-bundle entries that enumerate constituent files + hashes.
  • Update apm.lock.yaml schema (versioned bump if needed).
  • Update integrity checks in src/apm_cli/policy/ci_checks.py (_check_content_integrity) to walk the per-bundle file list.

4. Policy check

  • Add apm-policy.yml rule (audit-only initially, install-blocking later): "a skill's prose may not reference a primitive outside its declared dependency closure." Implement as a content scan against the resolved dep graph.
  • Define a stable "primitive reference" syntax (e.g. apm:agent/python-architect) so the check has something concrete to scan for, rather than fuzzy path-matching.

5. Plugin export verification

  • Confirm apm pack --format plugin (src/apm_cli/bundle/plugin_exporter.py) flattens sub-bundle deps into the platform output. If lockfile enrichment (src/apm_cli/bundle/lockfile_enrichment.py) drives plugin output correctly, this should fall out for free; verify with a fixture covering nested local deps.

6. Migration of microsoft/apm itself

  • This repo is the canonical dogfooding site. After (1)-(5) land:
    • Add apm.yml to .apm/skills/apm-review-panel/ declaring local deps on its 7 personas.
    • Decide skill vs plugin packaging for review-panel (likely skill with transitive deps, since personas are reused across apm-strategy, apm-review-panel, etc.).
    • Restore the explicit cross-reference in .apm/skills/apm-review-panel/assets/verdict-template.md once the dep is declared and scannable.
    • Update apm-policy.yml to enforce the new closure rule on this repo.

7. Documentation

  • Update docs/src/content/docs/guides/dependencies.md with the intra-repo local-dep pattern.
  • Update docs/src/content/docs/introduction/anatomy-of-an-apm-package.md with the skill-as-package and agent-as-package conventions.
  • Update packages/apm-guide/.apm/skills/apm-usage/package-authoring.md per doc-sync rule.

Sequencing

  1. Brainstorm + decision record for Option A vs B vs C (superpowers:brainstorming).
  2. Schema spike: design lockfile v2 enrichment + manifest format for nested bundles.
  3. Resolver implementation: phases 1-3 above.
  4. Audit/policy integration: phase 4.
  5. Self-host migration: phase 6 (this repo).
  6. Docs: phase 7.

Each lane is review-panel-worthy on its own. Phase 1 (brainstorm/decision) gates the rest.

Acceptance criteria

  • A skill's SKILL.md or any asset can reference another primitive only if the primitive appears in its apm.yml declared dep closure (audit check passes).
  • Edits to any file inside a skill bundle change the corresponding lockfile hash; CI integrity check (apm audit --ci) catches bundle drift at file granularity.
  • microsoft/apm itself is migrated: apm-review-panel declares its persona deps; apm-policy.yml enforces the closure check; CI is green.
  • apm pack --format plugin of a project containing nested-dep skills produces a runtime layout identical to today's flattened output (no behavioral regression for existing consumers).

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    architectureDeprecated: use type/architecture. Kept for issue history; will be removed in milestone 0.10.0.enhancementDeprecated: use type/feature. Kept for issue history; will be removed in milestone 0.10.0.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions