Skip to content

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

Description

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    architectureLegacy classification name; prefer type/architecture. Retained for history.enhancementLegacy classification name; prefer type/feature. Retained for history.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions