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
7 changes: 7 additions & 0 deletions .apm/architecture/owners/install-deployment.json
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,13 @@
"owner": "models/dependency/selection.py (via DependencyReference)",
"selectors": ["src/apm_cli/models/dependency/selection.py"],
"guards": ["install-deployment-uninstall-selection"]
},
{
"id": "conflicted-lockfile-discard",
"decision": "Conflicted lockfile discard and rollback restore",
"owner": "install/transaction.py (InstallTransaction.discard_conflicted_lockfile)",
"selectors": ["src/apm_cli/install/transaction.py"],
"guards": ["install-deployment-conflicted-lockfile-discard"]
}
]
}
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

- A full `apm install` and `apm lock` now warn, discard `apm.lock.yaml`, and resolve from `apm.yml` when the lockfile still contains git merge conflict markers, instead of exiting with a YAML parse error. `apm install --frozen`, partial installs, and read-only commands such as `apm update` and `apm outdated` fail closed with an error that names the conflict and the next action, and the `--frozen` failure tip no longer points at commands that cannot read the lockfile. A run that fails after discarding the file restores it. (closes #2979) (#3028)
- Autopilot maintainer canvas removes a Decide row as soon as GitHub confirms `status/accepted`, without waiting for a full issue/PR refetch.
- Issue and PR triage no longer skip bot-authored items (Copilot, Dependabot, github-actions). They stay in the queue like any other contribution. (#3024)
- PR-review scheduler no longer queues every open pull request. A fresh review requires the `panel-review` label (same trigger as the Agentic Workflow), `status/accepted` on the PR, or an explicit named PR list. The reviewing session also requires `status/accepted` on the PR or a linked issue; otherwise scheduler and review-worker stop with no comment. The worker may clear `panel-review`; the scheduler does not comment or change labels. Both also apply a CODEOWNERS last-comment gate: read the last CODEOWNER comment as conditions and evaluate them against later comments AND labels on the PR and linked issues. Drop or `noop` only when those conditions are unmet or unclear. Named list does not bypass that gate.
Expand Down
17 changes: 15 additions & 2 deletions CONFORMANCE.json
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,19 @@
"tests/spec_conformance/test_lockfile_reqs.py::test_materialization_spelling_migrates_one_case_variant_transactionally"
]
},
{
"conformance_class": "consumer",
"id": "req-lk-023",
"keyword": "MUST",
"section": "5.4",
"status": "active",
"test_count": 3,
"tests": [
"tests/spec_conformance/test_lockfile_reqs.py::test_conflict_markers_are_named_at_lockfile_load",
"tests/spec_conformance/test_lockfile_reqs.py::test_discard_is_reverted_when_no_replacement_is_written",
"tests/spec_conformance/test_lockfile_reqs.py::test_full_re_resolution_discards_while_replay_and_subset_fail_closed"
]
},
{
"conformance_class": "producer",
"id": "req-mf-001",
Expand Down Expand Up @@ -1488,7 +1501,7 @@
"spec_version": "v0.1.1",
"summary_by_class": {
"consumer": {
"active": 91,
"active": 92,
"skipped": 1,
"unbound": 0,
"xfail": 0
Expand All @@ -1512,5 +1525,5 @@
"xfail": 0
}
},
"total_requirements": 123
"total_requirements": 124
}
3 changes: 2 additions & 1 deletion CONFORMANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Repository-coordinate segments are case-insensitive for `github.com`, GitHub Ent
| Class | Active | Skipped | Xfail | Unbound |
|-------|-------:|--------:|------:|--------:|
| Producer | 12 | 0 | 0 | 0 |
| Consumer | 91 | 1 | 0 | 0 |
| Consumer | 92 | 1 | 0 | 0 |
| Registry | 1 | 0 | 0 | 0 |
| Governance | 18 | 0 | 0 | 0 |

Expand Down Expand Up @@ -63,6 +63,7 @@ Repository-coordinate segments are case-insensitive for `github.com`, GitHub Ent
| [req-lk-020](docs/src/content/docs/specs/openapm-v0.1.md#req-lk-020) | MUST | 5.2 | consumer | active | 3 | - |
| [req-lk-021](docs/src/content/docs/specs/openapm-v0.1.md#req-lk-021) | MUST | 5.2 | consumer | active | 2 | - |
| [req-lk-022](docs/src/content/docs/specs/openapm-v0.1.md#req-lk-022) | MUST | 5.2 | consumer | active | 4 | - |
| [req-lk-023](docs/src/content/docs/specs/openapm-v0.1.md#req-lk-023) | MUST | 5.4 | consumer | active | 3 | - |
| [req-mf-001](docs/src/content/docs/specs/openapm-v0.1.md#req-mf-001) | MUST | 4.1 | producer | active | 1 | - |
| [req-mf-002](docs/src/content/docs/specs/openapm-v0.1.md#req-mf-002) | MUST | 4.1 | producer | active | 1 | - |
| [req-mf-003](docs/src/content/docs/specs/openapm-v0.1.md#req-mf-003) | MUST | 4.1 | producer | active | 1 | - |
Expand Down
5 changes: 5 additions & 0 deletions docs/public/specs/manifests/openapm-v0.1.requirements.yml
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,11 @@ requirements:
section: "5.2"
conformance_class: consumer
notes: "source-cased materialization_repo_url remains non-identity metadata and drives rollback-safe, collision-closed materialization/link spelling"
- id: req-lk-023
keyword: MUST
section: "5.4"
conformance_class: consumer
notes: "merge-conflict markers are detected at lockfile load; only a full re-resolution may discard the file, and the discard is reverted when no replacement is written"
- id: req-pl-001
keyword: MUST
section: "6.1"
Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/reference/cli/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ in `apm.yml`, then run `apm install` again.
- **Lockfile replay and Git ref freshness.** Plain and `--frozen` installs may trust `apm.lock.yaml` and the local Git cache, reusing the locked commit for unchanged Git dependencies across the full resolved graph. In contrast, `apm install --update`, `apm install --refresh`, [`apm update`](../update/) with or without `--force`, [`apm lock --update`](../lock/), and [`apm outdated`](../outdated/) establish mutable Git refs from upstream instead of accepting stale refs from a local bare Git cache. APM picks up upstream changes to a transitive package's `apm.yml` only when you regenerate the graph -- run `apm update` or `apm lock --update`. See the [lockfile specification](../../lockfile-spec/) for the replay contract.
- **Semver ranges on git deps.** `ref:` accepts semver ranges (`^1.2.0`, `~1.4`, `>=2.0 <3`, `1.5.x`) for git-source deps, including positional virtual-subdirectory references. APM runs `git ls-remote` against the dep, picks the highest tag matching the range, and pins the resolved tag plus commit SHA, version, and original constraint in `apm.lock.yaml`. Subsequent installs replay the lockfile without network; use `--update` (or change the manifest constraint) to re-resolve. See [manage dependencies](../../../consumer/manage-dependencies/#pin-a-semver-range) for the supported syntax.
- **No-op nudge.** When the lockfile is already satisfied and nothing needs deploying, install prints `[i] Run 'apm update' to check for newer versions.` so you know the silent success was not a missed refresh.
- **Frozen mode.** With `--frozen`, install resolves only what is in `apm.lock.yaml`. A missing lockfile, a direct dependency missing from it, or MCP config state that differs from `apm.yml` exits `1` before lockfile, target config, deployment, or cache mutation. Cold-cache installs (empty `apm_modules/`) with git `apm_package` deps are tolerated: MCP checks are skipped for absent package directories (the packages will be hydrated by the pipeline), and their MCP server configs are restored from the lockfile so no false drift is reported. Remote `claude_skill` dependencies declared at a repository root or subdirectory are also accepted from their locked type before materialization; once present, the lock type and detected skill shape must agree. Missing local paths still fail. See [`config-consistency`](../../baseline-checks/#config-consistency) for the full manifest rule. Run normal `apm install` to create or repair MCP-only lock state, then retry frozen mode. Add-style invocations (`apm install PACKAGE` and `apm install --mcp NAME`) are rejected because they mutate `apm.yml`. Orphan package lock entries are tolerated; local-path deps are skipped. This is a structural check, not a content check -- run `apm audit --ci` for hash verification.
- **Frozen mode.** With `--frozen`, install resolves only what is in `apm.lock.yaml`. A missing lockfile, a direct dependency missing from it, or MCP config state that differs from `apm.yml` exits `1` before lockfile, target config, deployment, or cache mutation. Cold-cache installs (empty `apm_modules/`) with git `apm_package` deps are tolerated: MCP checks are skipped for absent package directories (the packages will be hydrated by the pipeline), and their MCP server configs are restored from the lockfile so no false drift is reported. Remote `claude_skill` dependencies declared at a repository root or subdirectory are also accepted from their locked type before materialization; once present, the lock type and detected skill shape must agree. Missing local paths still fail. A lockfile that contains git merge conflict markers also exits `1` and is never rewritten under `--frozen`. See [`config-consistency`](../../baseline-checks/#config-consistency) for the full manifest rule. Run normal `apm install` to create or repair MCP-only lock state, or a full `apm install` (no package arguments, no `--only`) to discard a conflicted lockfile and resolve from `apm.yml`, then retry frozen mode. Add-style invocations (`apm install PACKAGE` and `apm install --mcp NAME`) are rejected because they mutate `apm.yml`. Orphan package lock entries are tolerated; local-path deps are skipped. This is a structural check, not a content check -- run `apm audit --ci` for hash verification.
- **Local `.apm/` deployment.** After dependencies are integrated, primitives in the project's own `.apm/` directory are deployed to the same targets. Local files win on collision. Skipped at `--global` and with `--only mcp`.
- **User-scope root context hint.** Compilation stays explicit. After `apm install -g`, targets with native user-scope instruction files pick up global instructions during install. Targets whose user-scope instruction surface is a root context file require [`apm compile --global`](../compile/#global-compilation); install prints a one-line `[i]` hint and writes no root context file.
- **OpenCode user scope.** `apm install -g --target opencode` deploys skills to
Expand Down
1 change: 1 addition & 0 deletions docs/src/content/docs/reference/cli/lock.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ apm lock --verbose
- **Write `apm.lock.yaml`.** The lockfile records every pinned ref, resolved commit, and content hash. Fresh lock-only runs add no deployed files, and existing deployed-file rows, hashes, and deployment-ledger entries stay recorded while those bytes remain on disk.
- **No files deployed or deleted.** The targets, cleanup, post-deps-local, and audit phases are skipped. The integrate phase runs but deploys nothing because the target set is empty. Running `apm lock` is safe to run before you are ready to install.
- **Idempotent.** If the lockfile already matches the resolution result, it is overwritten with the same content.
- **Recovers from merge conflicts.** If `apm.lock.yaml` still contains git conflict markers, `apm lock` warns, discards it, and resolves from `apm.yml`; a run that fails before writing a replacement restores the discarded file. See [lockfile versioning](../../lockfile-spec/#versioning).

## Export (SBOM inventory)

Expand Down
13 changes: 13 additions & 0 deletions docs/src/content/docs/reference/lockfile-spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,19 @@ constructs lock state. Pre-versioned legacy files migrate as v1 inputs. Fix or
remove other invalid files explicitly; APM does not silently replace them with
an empty lockfile.

A lockfile that still contains git merge conflict markers (`<<<<<<<`,
`>>>>>>>`, or `|||||||` at the start of a line) is reported as a merge
conflict rather than a YAML error. Commands that require the lockfile name
the file and the next action; best-effort readers such as `apm view` continue
without lock metadata as before. A full `apm install` (no package arguments, no
`--only`) or `apm lock` warns, discards the file, and resolves from
`apm.yml`, the same input a non-frozen install resolves from anyway;
deployed-file records from the conflicted file are not carried over, and a
manifest with nothing to lock ends with no lockfile. If that run fails before
writing a new lockfile, the conflicted file is put back. Partial installs and
`apm install --frozen` never rewrite the file: resolve the conflict in git, or
run a full `apm install` without `--frozen`.

## Example

A small project with one remote APM package, one MCP server, and its own
Expand Down
Loading