Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- Per-dependency agent subsetting: object-form dependencies accept a non-empty
`agents:` list, and repeatable `apm install --agent NAME` selections persist
to `apm.yml` and `apm.lock.yaml` for deterministic bare installs and audit
replay. `--agent '*'` resets the dependency to all agents. (closes #2491)
- `apm install` now accepts `--trust-bin` / `--no-trust-bin` for per-invocation
consent over marketplace-plugin `bin/` executable deployment. `--trust-bin`
approves deployment silently; `--no-trust-bin` skips `bin/` even when policy
Expand Down
15 changes: 13 additions & 2 deletions CONFORMANCE.json
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,17 @@
"tests/spec_conformance/test_manifest_reqs.py::test_consumer_preserves_registry_identity_on_structured_rewrite"
]
},
{
"conformance_class": "consumer",
"id": "req-mf-025",
"keyword": "MUST",
"section": "4.3.2",
"status": "active",
"test_count": 1,
"tests": [
"tests/spec_conformance/test_manifest_reqs.py::test_consumer_persists_and_deploys_only_selected_agents"
]
},
{
"conformance_class": "governance",
"id": "req-pl-001",
Expand Down Expand Up @@ -1279,7 +1290,7 @@
"spec_version": "v0.1.1",
"summary_by_class": {
"consumer": {
"active": 82,
"active": 83,
"skipped": 1,
"unbound": 0,
"xfail": 0
Expand All @@ -1303,5 +1314,5 @@
"xfail": 0
}
},
"total_requirements": 112
"total_requirements": 113
}
3 changes: 2 additions & 1 deletion CONFORMANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ All four conformance classes (Producer, Consumer, Registry, Governance) carry ac
| Class | Active | Skipped | Xfail | Unbound |
|-------|-------:|--------:|------:|--------:|
| Producer | 12 | 0 | 0 | 0 |
| Consumer | 82 | 1 | 0 | 0 |
| Consumer | 83 | 1 | 0 | 0 |
| Registry | 1 | 0 | 0 | 0 |
| Governance | 16 | 0 | 0 | 0 |

Expand Down Expand Up @@ -77,6 +77,7 @@ All four conformance classes (Producer, Consumer, Registry, Governance) carry ac
| [req-mf-022](docs/src/content/docs/specs/openapm-v0.1.md#req-mf-022) | MUST | 4.3.2 | consumer | active | 1 |
| [req-mf-023](docs/src/content/docs/specs/openapm-v0.1.md#req-mf-023) | MUST | 4.5 | consumer | active | 1 |
| [req-mf-024](docs/src/content/docs/specs/openapm-v0.1.md#req-mf-024) | MUST | 4.3.2 | consumer | active | 1 |
| [req-mf-025](docs/src/content/docs/specs/openapm-v0.1.md#req-mf-025) | MUST | 4.3.2 | consumer | active | 1 |
| [req-pl-001](docs/src/content/docs/specs/openapm-v0.1.md#req-pl-001) | MUST | 6.1 | governance | active | 1 |
| [req-pl-002](docs/src/content/docs/specs/openapm-v0.1.md#req-pl-002) | MUST | 6.2 | governance | active | 1 |
| [req-pl-003](docs/src/content/docs/specs/openapm-v0.1.md#req-pl-003) | MUST | 6.4 | governance | active | 1 |
Expand Down
4 changes: 4 additions & 0 deletions docs/public/specs/manifests/openapm-v0.1.requirements.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ requirements:
keyword: MUST
section: "4.3.2"
conformance_class: consumer
- id: req-mf-025
keyword: MUST
section: "4.3.2"
conformance_class: consumer
- id: req-ext-001
keyword: MUST
section: "4.1"
Expand Down
1 change: 1 addition & 0 deletions docs/public/specs/schemas/lockfile-v0.1.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"depth": { "type": "integer", "minimum": 0 },
"resolved_by": { "type": "string" },
"package_type": { "type": "string" },
"agent_subset": { "type": "array", "items": { "type": "string", "pattern": "^(?!\\s*$)(?!\\.{1,2}$)[^/\\\\]+$" } },
"skill_subset": { "type": "array", "items": { "type": "string" } },
"deployed_files": { "type": "array", "items": { "type": "string" } },
"deployed_file_hashes": {
Expand Down
1 change: 1 addition & 0 deletions docs/public/specs/schemas/manifest-v0.1.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
"prerelease": { "type": "boolean" },
"path": { "type": "string" },
"alias": { "type": "string" },
"agents": { "type": "array", "minItems": 1, "items": { "type": "string", "pattern": "^(?!\\s*$)(?!\\.{1,2}$)[^/\\\\]+$" } },
"skills": { "type": "array", "items": { "type": "string" } }
},
"patternProperties": { "^x-[a-z][a-z0-9-]*$": {} },
Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/concepts/lifecycle.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ apm audit --file <path> # standalone: scan an arbitrary file

**Local mode** (`apm audit`, optionally with `--strip` or `--file <path>`) scans installed primitives -- or any file you point at -- for hidden Unicode and reports findings as text, JSON, SARIF, or markdown. With `--strip`, it removes hidden characters in place, preserving emoji and whitespace. Use `--dry-run` to preview the strip.

**CI mode** (`apm audit --ci`) runs the nine baseline consistency checks in order: `lockfile-exists`, `ref-consistency`, `deployment-ledger-owners`, `deployed-files-present`, `no-orphaned-packages`, `skill-subset-consistency`, `config-consistency`, `content-integrity`, and `includes-consent`. After those pass, it performs an install-replay drift check. APM rebuilds the deployed context in a scratch directory and diffs it against your working tree, catching hand-edits to `apm_modules/` or generated files before they ship. When `apm_modules/` is absent but the lockfile is present, `--ci` self-hydrates a lock-pinned scratch install first instead of reporting a green drift skip. Pass `--no-drift` to skip the replay in performance-constrained loops; pass `--no-fail-fast` to run all checks even after a failure. With `--policy <source>` it also evaluates org policy against the lockfile.
**CI mode** (`apm audit --ci`) runs the ten baseline consistency checks in order: `lockfile-exists`, `ref-consistency`, `deployment-ledger-owners`, `deployed-files-present`, `no-orphaned-packages`, `agent-subset-consistency`, `skill-subset-consistency`, `config-consistency`, `content-integrity`, and `includes-consent`. After those pass, it performs an install-replay drift check. APM rebuilds the deployed context in a scratch directory and diffs it against your working tree, catching hand-edits to `apm_modules/` or generated files before they ship. When `apm_modules/` is absent but the lockfile is present, `--ci` self-hydrates a lock-pinned scratch install first instead of reporting a green drift skip. Pass `--no-drift` to skip the replay in performance-constrained loops; pass `--no-fail-fast` to run all checks even after a failure. With `--policy <source>` it also evaluates org policy against the lockfile.

**Common surprises**

Expand Down
11 changes: 6 additions & 5 deletions docs/src/content/docs/concepts/the-three-promises.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,11 @@ apm install --dry-run <package>
`resolve_policy_chain()` implement the tighten-only enterprise
-> org -> repo flow with `_escalate()` enforcement.
- `src/apm_cli/policy/ci_checks.py` -- `run_baseline_checks()` is
the CI surface used by `apm audit --ci`. It runs 8 baseline
checks: lockfile-exists, ref-consistency, deployed-files-present,
no-orphans, skill-subset-consistency, config-consistency,
content-integrity, and includes-consent.
the CI surface used by `apm audit --ci`. It runs 10 baseline
checks: lockfile-exists, ref-consistency, deployment-ledger-owners,
deployed-files-present, no-orphans, agent-subset-consistency,
skill-subset-consistency, config-consistency, content-integrity, and
includes-consent.

### Read more

Expand Down Expand Up @@ -147,6 +148,6 @@ right now -- including hand-edits to files inside `apm_modules/`.
At install time: dependencies from disallowed sources or scopes,
primitives outside the allow-list, and transitive MCP servers that
fail any of the configured trust rules -- evaluated before any
download. In CI via `apm audit --ci`: the 8 baseline checks above,
download. In CI via `apm audit --ci`: the 10 baseline checks above,
which catch lockfile drift, missing deployed files, orphaned
packages, and content-hash mismatches before a PR can merge.
4 changes: 2 additions & 2 deletions docs/src/content/docs/enterprise/drift-detection.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@ happens; see [security model](../security/).

### `apm audit --ci`

The lockfile-consistency gate. Runs nine baseline checks in order and
The lockfile-consistency gate. Runs ten baseline checks in order and
exits non-zero on the first failure (or on any failure with
`--no-fail-fast`):

```
lockfile-exists -> ref-consistency -> deployment-ledger-owners
-> deployed-files-present -> no-orphaned-packages
-> skill-subset-consistency -> config-consistency
-> agent-subset-consistency -> skill-subset-consistency -> config-consistency
-> content-integrity -> includes-consent
```

Expand Down
6 changes: 3 additions & 3 deletions docs/src/content/docs/enterprise/enforce-in-ci.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ playbook, see [Governance deep-dive](../governance-guide/) and
apm audit --ci
```

One command. It runs the nine baseline lockfile checks
One command. It runs the ten baseline lockfile checks
(`lockfile-exists`, `ref-consistency`, `deployment-ledger-owners`,
`deployed-files-present`, `no-orphaned-packages`, `skill-subset-consistency`,
`deployed-files-present`, `no-orphaned-packages`, `agent-subset-consistency`, `skill-subset-consistency`,
`config-consistency`, `content-integrity`, `includes-consent`), the
install-replay drift check, and -- if an `apm-policy.yml` is discovered --
the org policy checks. Exit code is `0` clean, `1` on any violation.
Expand Down Expand Up @@ -134,7 +134,7 @@ The two patterns serve different goals:
| Full install then audit | Catching developers who skipped `apm install` after editing `apm.yml`; ensuring gitignored deployed files are present on a fresh runner |
| Audit-only (`setup-only: true`) | Zero-install CI gate for repos that commit deployed files: compare the checked-out commit against a lock-pinned scratch replay without rewriting the checkout |

Both patterns enforce policy and the nine baseline lockfile checks. The
Both patterns enforce policy and the ten baseline lockfile checks. The
difference is only in whether content-integrity can see tampered bytes.

## Recipe: SARIF for GitHub Code Scanning
Expand Down
1 change: 1 addition & 0 deletions docs/src/content/docs/enterprise/policy-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,7 @@ Deny patterns are evaluated first. If a reference matches any deny pattern, it f
| `ref-consistency` | Every dependency's manifest ref matches the lockfile's resolved ref |
| `deployed-files-present` | All files listed in lockfile `deployed_files` exist on disk |
| `no-orphaned-packages` | No lockfile packages are absent from the manifest |
| `agent-subset-consistency` | `agents:` selections in `apm.yml` match `agent_subset` in the lockfile |
| `skill-subset-consistency` | `skills:` selections in `apm.yml` match `skill_subset` in the lockfile |
| `config-consistency` | MCP server configs match lockfile baseline |
| `content-integrity` | Deployed files contain no critical hidden Unicode characters and their SHA-256 hashes match the lockfile |
Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/integrations/ci-cd.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ This step is not needed if your team only uses GitHub Copilot and Claude, which
run: apm audit --ci
```

This single command runs the nine baseline lockfile checks PLUS integration
This single command runs the ten baseline lockfile checks PLUS integration
drift detection (default-on) AND replays
the install pipeline into a scratch tree to detect missed `apm install`
runs, hand-edited deployed files, and orphaned files. See the
Expand Down
11 changes: 9 additions & 2 deletions docs/src/content/docs/reference/baseline-checks.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ first failure to skip expensive I/O.
| `deployment-ledger-owners` | block | `ci_checks.py` | yes |
| `deployed-files-present` | block | `ci_checks.py` | yes |
| `no-orphaned-packages` | block | `ci_checks.py` | yes |
| `agent-subset-consistency` | block | `ci_checks.py` | yes |
| `skill-subset-consistency` | block | `ci_checks.py` | yes |
| `config-consistency` | block | `ci_checks.py` | yes |
| `content-integrity` | block | `ci_checks.py` | yes |
Expand Down Expand Up @@ -116,9 +117,15 @@ the [policy schema](../policy-schema/).
- **Fails when.** The lockfile holds a package that the manifest no longer lists.
- **Remediation.** Run `apm install` to prune the orphan, then commit `apm.lock.yaml`.

### `agent-subset-consistency`

- **What it verifies.** That every dependency's `agents:` selection in `apm.yml` matches `agent_subset` in the lockfile.
- **Fails when.** The sorted manifest and lockfile agent selections differ.
- **Remediation.** Run `apm install` to regenerate the lockfile against the current selection.

### `skill-subset-consistency`

- **What it verifies.** That the `skills:` selection in `apm.yml` for each `skill_bundle` dependency matches the `skill_subset` recorded in the lockfile.
- **What it verifies.** That each skill bundle's `skills:` selection in `apm.yml` matches `skill_subset` in the lockfile.
- **Fails when.** The sorted manifest skill list differs from the sorted lockfile `skill_subset` for any skill bundle.
- **Remediation.** Run `apm install` to regenerate the lockfile against the current selection.

Expand Down Expand Up @@ -153,7 +160,7 @@ the [policy schema](../policy-schema/).

## Run order and fail-fast

The aggregate runner in `run_baseline_checks` evaluates checks in this order: `manifest-parse` (only when `apm.yml` is unparseable), `lockfile-exists`, `ref-consistency`, `deployment-ledger-owners`, `deployed-files-present`, `no-orphaned-packages`, `skill-subset-consistency`, `config-consistency`, `content-integrity`, `includes-consent`. Drift is invoked separately by the audit command after the baseline batch, but in `--ci` mode it shares the same cold-cache scratch materialization with `config-consistency`.
The aggregate runner in `run_baseline_checks` evaluates checks in this order: `manifest-parse` (only when `apm.yml` is unparseable), `lockfile-exists`, `ref-consistency`, `deployment-ledger-owners`, `deployed-files-present`, `no-orphaned-packages`, `agent-subset-consistency`, `skill-subset-consistency`, `config-consistency`, `content-integrity`, `includes-consent`. Drift is invoked separately by the audit command after the baseline batch, but in `--ci` mode it shares the same cold-cache scratch materialization with `config-consistency`.

With fail-fast on (the default), the runner stops at the first failing check. `apm audit --ci --no-fail-fast` evaluates every check so the report lists every problem at once.

Expand Down
8 changes: 6 additions & 2 deletions docs/src/content/docs/reference/cli/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,11 @@ auto-detection only when `apm.yml` declares no targets.

Transport env vars: `APM_GIT_PROTOCOL` (`ssh` or `https`) sets the default initial transport for shorthand deps; `APM_ALLOW_PROTOCOL_FALLBACK=1` mirrors `--allow-protocol-fallback`.

### Skill subset
### Agent and skill subsets

| Flag | Default | Description |
|---|---|---|
| `--agent NAME` | all | Install only named agents from an explicitly named dependency (at least one package argument is required). Agent names are flat filename stems (for example, `reviewer` selects `reviewer.agent.md`). Repeatable and additive across installs. The sorted selection is persisted as `agents:` in `apm.yml` and `agent_subset` in `apm.lock.yaml`, so a bare reinstall or audit replay deploys the same agents. Use `--agent '*'` to reset to all agents. |
| `--skill NAME` | all | Install only named skills from a dependency that exposes selectable skills. Applies to both git-longhand and registry-longhand (`id:`/`registry:`) dependencies. Repeatable. For plugin manifests, `NAME` may be the skill name or manifest path, such as `skills/productivity/grill-me`. A CLI name that matches no declared skill is an install error; the diagnostic lists the available names. If a previously persisted `skills:` pin later matches no available source skill, install stays successful but warns with the package, requested names, and available names instead of silently doing nothing. The selection is persisted to `apm.yml` and `apm.lock.yaml` only after a successful CLI match. `--skill` is additive across separate installs: a later `apm install <bundle> --skill X` adds `X` to the existing pin (union) rather than replacing it -- previously deployed skills are never silently removed. Use `--skill '*'` to reset to the full bundle; to drop a single skill, edit the `skills:` list in `apm.yml` and re-run `apm install`. |
| `--as ALIAS` | bundle id | Override the log/display label for a local-bundle install. Only valid with a single local-bundle `PACKAGE_REF`. |

Expand Down Expand Up @@ -203,12 +204,15 @@ apm install ./my-bundle.zip --as custom-name
apm install ./my-bundle --target opencode
```

### Install only a subset of skills from a bundle
### Install only a subset of agents or skills

```bash
apm install owner/skill-bundle --skill review
apm install owner/skill-bundle --skill refactor # adds refactor; review is kept (union)
apm install owner/skill-bundle --skill '*' # reset to all skills

apm install owner/agent-pack --agent planner --agent reviewer
apm install owner/agent-pack --agent '*' # reset to all agents
```

## Exit codes
Expand Down
2 changes: 2 additions & 0 deletions docs/src/content/docs/reference/lockfile-spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ Each item in `dependencies` describes one resolved package.
| `depth` | int | no | Position in the dependency tree. `0` is the project itself, `1` is a direct dep, higher is transitive. Defaults to `1`. |
| `resolved_by` | string | no | `repo_url` of the parent that pulled this transitive dep. Absent for direct deps. Rewritten by `apm uninstall` when a rescued transitive dependency's original parent is removed, so the entry stays keyed on a genuine surviving parent -- this never changes the entry's identity or lock key, only which parent it points to. |
| `package_type` | string | no | Kind of package: `apm_package`, `skill_bundle`, `claude_skill`, `hook_package`, `hybrid`, `marketplace_plugin`. Drives target placement. |
| `agent_subset` | list of strings | no | Sorted flat agent names selected by the manifest's `agents:` field. Empty means "all". |
| `skill_subset` | list of strings | no | For dependencies that expose selectable skills: the sorted subset of skill names the manifest selected. Empty means "all". |
| `target_subset` | list of strings | no | Sorted target names selected by a dependency's `targets:` subset. Empty means "all active install targets". |
| `deployed_files` | list of strings | no | Project-relative paths APM wrote for this dep. Sorted. Powers `prune` and `audit`'s file-presence check. A shared path has one canonical package owner; uninstall transfers ownership to a surviving provider. When the consumer manifest declares targets, reinstall preserves entries for other declared, gated, or dynamic targets and removes entries outside that target universe. On a target contraction, a normal install/prune run removes an obsolete target's file only when its recorded hash still matches; a user-edited file stays on disk and remains tracked for review. `apm lock` is non-destructive: if bytes remain on disk, the lockfile preserves their `deployed_files`, `deployed_file_hashes`, and deployment-ledger rows until the next normal install can prove and perform cleanup. Without a declared target set, reinstall preserves prior other-target entries. |
Expand Down Expand Up @@ -343,6 +344,7 @@ check maps to specific lockfile fields:
| `ref-consistency` | `resolved_ref` per entry vs. `apm.yml` |
| `deployed-files-present` | `deployed_files` per entry (and self entry) |
| `content-integrity` | `deployed_file_hashes` (and `local_deployed_file_hashes`) |
| `agent-subset-consistency` | `agent_subset` per dependency entry |
| `skill-subset-consistency` | `skill_subset` per `skill_bundle` entry |
| `config-consistency` | `mcp_configs` and `mcp_config_provenance` |
| `no-orphaned-packages` | `dependencies` keys vs. `apm.yml` |
Expand Down
Loading