Repository navigation
Description
Problem
The loosened-gate scan is the part of merge-policy.py (about 300 lines: comment and exclusion patterns, version-pin classification, constitutional paths, scan_loosening and inspect) that decides whether a pull request weakens a gate or touches a golden or constitutional file. It reads the pinned base and head, never the working tree, and its JSON is what unattended merge gates judge. It is Python run through uv, it is on the golden list, and it is under the shell freeze in #588.
This child ports the frozen behavior as it is on the main branch today. The open fixes #292 and #313 change the shell and are parked: they are re-landed as Go fixes after this port and are not prerequisites or requirements here.
Part of #324, within #588. Child 02 in #324's Children list. Replaces the scan and inspect half of packages/agents/source/skills/merge-pr/shared/scripts/merge-policy.py.
Acceptance criteria
- AC1: Given a base and head commit and the policy keys (golden paths, loosening markers, gate configuration paths, owner paths, disposable ignored paths), when
agentrepo merge policy inspectruns, then it prints JSON whosefindings(a list of path and reason pairs) andowner_pathsequal the frozen helper's output on the same fixtures. - AC2: Given a changed path that matches the constitutional-path pattern read from the pinned base, when the scan runs, then a
changes a constitutional filefinding is reported for it; given a changed path that matches the owner-path pattern, then it is listed inowner_paths(deletions and renames included) and is not by itself a finding; the two matches are independent, so a path that matches both is reported as a finding and also listed inowner_paths, exactly as the frozen helper does. - AC3: Given a changed golden path that the base lock owns remotely, when the scan runs, then no golden or loosening finding is reported for it; given any other golden path, then a
changes a golden filefinding is reported. - AC4: Given a newly added active suppression marker in a comment after code, or a newly added exclusion line in a gate configuration file, when the scan runs, then an
adds a suppression or exclusionfinding is reported; given a full-line comment, marker text inside a quoted value, or the exempt vendorednolintcase, then none is. - AC5: Given a removed hook or gate line in a gate configuration file, when the scan runs, then a
removes a hook or gatefinding is reported, except when a lockfile's removed checksums are all re-recorded by added ones. - AC6: Given a diff that edits only version pins in the pins file form, when the scan runs, then
findingsis empty, even if golden or constitutional paths matched; given any other finding alongside, then all findings are reported. - AC7: Given a base or head that cannot be resolved, a pinned change with no paths, or a policy value that does not compile, when the command runs, then it exits 1 with
merge policy unreadable: <reason>and prints no JSON. - AC8: Non-functional: the scan reads Git objects with no diff driver, text conversion or repository-controlled command, and the new command runs on macOS, Linux and Windows in CI.
Scope and non-goals
Scope: the scan, pin-only diff recognition, the owner-path and constitutional-path handling, and the agentrepo merge policy inspect command, with tests ported from loosens.bats, pins.bats and replacements.bats.
Non-goal: the behavior of #292 and #313 (gate replacement pairing, grouped reports, extended exclusions and lock semantics): they stay parked and are re-landed as Go fixes after this port. Non-goal: owner approval and required checks (child 03). Non-goal: editing the shell scripts, which stay the parity oracle until child 14.
Approach
Meet AC1 to AC8 with failing cases first: build scratch repositories from the fixtures in the three Bats files, record the Python helper's JSON, and match it in Go. Fixtures that only pass under the parked fixes are not part of this child. Mechanism is the implementer's choice.
Dependencies
Prerequisites: #321 (configuration readers) and #333. #292 and #313 are not dependencies.
Size
M. One pull request.
Verify plan
- AC1, AC2, AC3, AC4, AC5, AC6: golden JSON comparison against the frozen helper on scratch repositories, with a case per finding reason and per exemption; for AC2 a constitutional-only path, an owner-only path and a path matching both (the policy-file path that is on both lists today).
- AC7: error-path table checking exit status and streams.
- AC8: a test that sets a hostile diff driver and textconv in the scratch repository and sees them ignored; the CI matrix run.
- 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 scanner that finds less than the Python helper would let a loosened gate through the unattended path. Golden comparison on every fixture, plus the exemption cases, are the controls, 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 here; 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 scans a pinned change for loosened gates in Go (frozen behavior)
Model: gpt-6-astra
Child 02 The merge policy scans a pinned change for loosened gates in Go
Verdict: READY
Checked: AC2 now correctly treats constitutional and owner matches independently and verifies all three combinations. This matches
packages/agents/source/skills/merge-pr/shared/scripts/merge-policy.py:508andpackages/agents/source/skills/merge-pr/shared/scripts/merge-policy.py:523. The frozen-first scope and deferred #292/#313 disposition match the binding decision and index.Gaps: None.
Split: None needed; the later behavior fixes remain separate.
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