Repository navigation
Description
Problem
The merge policy decides which changed paths belong to APM deployments the base commit already owns, and it reads that from the pinned apm.lock.yaml with a hand-written parser in merge-policy.py (about 215 of its 816 lines: regex and policy validation, lock scalar, list and record parsing, and the base-owned deployment map). The Go lock parser is owned by #981 in internal/mergepolicy/lock.go; this issue consumes it and adds no second parser. It is Python run through uv, it is on the golden and owner path lists, and it is under the shell freeze in #588. The same program has to hold the policy and the merge it gates, so the first step is an agentrepo merge policy command group in Go that reads the lockfile through that parser and answers "is this path owned remotely". The frozen helper has no command that prints ownership (it is an internal map there), so this child defines the observable output.
Part of #324, within #588. Child 01 in #324's Children list. Replaces the lock and ownership half of packages/agents/source/skills/merge-pr/shared/scripts/merge-policy.py.
Acceptance criteria
- AC1: Given an
apm.lock.yamlblob at a pinned base commit, whenagentrepo merge policy owned <base>runs, then it prints one deployed path per line, sorted, for every path the lock marks as owned by a remote package, and none that a local dependency or an active local owner overrides. - AC2: Given lock fixtures, when the command reads them, then ownership matches the frozen parser's through the Verify merge skill directories against base lock hashes in Go #981 types.
- AC3: Given a lockfile that is absent at the base, when the command runs, then it prints an empty set and exits 0.
- AC4: Given a lockfile that is malformed or names a deployment path that escapes the repository, when the command runs, then it exits 1 with
merge policy unreadable: <reason>on stderr and prints nothing on stdout. - AC5: Given a policy regular expression that does not compile, when the command validates its configuration, then it exits 1 and names the configuration key without printing its value. This is an intentional improvement: the frozen helper does not attach the key to a compile error.
- AC6: Given
agentrepo merge policy owned <base> <path>..., when a path lies under a deployment the base lock marks as remotely owned (the most specific deployed owner wins), then it is printed as owned, including when the pull request edits the content of that path; given a deployment declared only in a lockfile that the pull request head adds or changes, then the paths it declares are not owned. - AC7: Non-functional: reading the lock never executes repository content and never needs
uv, and the new command runs on macOS, Linux and Windows in CI.
Scope and non-goals
Scope: the ownership layer and the agentrepo merge policy owned command with its two forms over the lock parser exported by #981 in internal/mergepolicy/lock.go, and table-driven tests with fixtures taken from policy.bats and pins.bats.
Non-goal: the loosened-gate scan, owner approval, required checks and cleanup, which are children 02, 03 and 12. Non-goal: changing any shell script; the frozen helper stays in place and is the parity oracle until child 14. Non-goal: the lock changes of #313, which stay parked and are re-landed as Go fixes after the port.
Approach
Meet AC1 to AC7 with failing cases first. The expected ownership for each fixture is derived once from the frozen helper by a throwaway harness that calls its ownership function and writes golden files; the Go command must reproduce them. Mechanism is the implementer's choice.
Dependencies
Prerequisites: #321 (configuration readers), #333 (binary distribution) and #981 (lock parser and integrity library).
Size
S. One pull request.
Verify plan
- AC2: golden comparison of ownership read through the Verify merge skill directories against base lock hashes in Go #981 types against the frozen parser's on the same fixtures.
- AC1, AC3, AC6: golden comparison against the frozen helper's recorded ownership on shared fixtures, including a fixture that edits an already owned path and a fixture that adds a deployment only on the head.
- AC4, AC5: table-driven error cases checking exit status and stream.
- AC7: the CI matrix run for the new code, a test that runs with
uvabsent from PATH, and a test whose fixture repository holds an executable hook and a script that would create a sentinel file, asserting the sentinel never appears. - Definition of done: D1 each AC is verified at the merged head, with the commands and results cited in the pull request's Verification. D2 an independent review approves the exact merged head, and every finding has a recorded disposition. D3 required checks pass with no gate loosened. D4 the leak scans the repository specifies are clean, and every post made for this work passes the per-post leak review before it is posted. D5 every commit is signed. D6 the documentation matches the change or the Verification says why there is no impact. D7 every commit has an
Assisted-bytrailer and the pull request has theprovenance:agent-assistedlabel. D8 the pull request stays within the size limits. D9 follow-ups are filed as issues and listed under Related. D10 the merge closes the issue through a closing reference, the board shows Done, and the finalStatus:comment names the merge commit.
Risks
A parser that reads a lockfile differently from the Python helper could mark a path as owned that the base does not own, which would hide it from the loosened-gate scan. Golden comparison on the fixtures is the control, and nothing calls the Go command until child 14. Undo: revert; no caller depends on it yet.
Operator authority
None. No owner-tier path is edited; protection of the new Go files is settled by #753 before any rollout.
Metadata
Metadata
Assignees
Labels
Type
Fields
Priority
Work type
Projects
- StatusShow more project fieldsBacklog
Activity
tbhb-agent commentedon Oct 27, 2026
Refinement: READY — The merge policy reads the APM lockfile and base-owned deployments in Go
Model: gpt-6-astra
Child 01 The merge policy reads the APM lockfile and base-owned deployments in Go
Verdict: READY
Checked: The ownership output, oracle, base-versus-head distinction and execution-safety verification close the earlier findings. Ownership and local overrides match
packages/agents/source/skills/merge-pr/shared/scripts/merge-policy.py:192; most-specific ownership matchespackages/agents/source/skills/merge-pr/shared/scripts/merge-policy.py:230. Scope, dependencies, verification and undo are bounded.Gaps: None.
Split: None needed.
Definition of done
The definition of done for this issue (Feature). Tick each item and give its evidence.
check-leaks.shwith the repository's private-strings list, and the leak review of each post.docs/, CHANGELOG or help text), or the Verification section says "No doc impact" and why.provenance:agent-assistedlabel.Status:comment names the merge commit.mise run checkis green, and so are the coverage floorsmise run cover-go-checkandmise run cover-py-check, whichcheckdoesn't run.packages/agents/source/commits whatmise run generate-skills, thenmise run skill-tokens, thenapm installrewrite, in that order, andcheck-generated-skills(part oflint) passes.docs/agent-skills-migration.mdor the adoption docs, or the PR's Verification says "No consumer impact" and why.Round: 3 of 3