Skip to content

The merge entry points refuse a missing or below-floor pinned agentrepo #1010

Description

@tbhb-agent

Problem

The merge-pr and agent-merge-pr entry points resolve agentrepo through mise and then fall back to a binary on PATH (packages/agents/source/lib/lib.sh:146 and :149). A resolved pin below the floor already refuses: lib.sh:172 calls dt_die, which exits with status 1. The real gap is a missing pin, either no repository pin or a declared pin whose executable is unavailable. Both fall through to command -v agentrepo at lib.sh:149, so the Go merge gates (#980, #981, #982) would be judged by whatever binary is on PATH. A second gap is the status: the shell exits 1 where the launcher's contract is 2.

#932 owns the tested Go resolver, the version floor, the comparer and the single permanent launcher (exit 2, one stderr line), but it switches only entries whose subcommand is already ported, which excludes the merge entries until #874, #875 and #876 land. This issue owns only the missing-pin policy for the merge entries and must not add a second resolver or comparer. Part of #324 and #588; native links are applied separately.

Compatibility route: docs/compatibility.md:5 requires at least two releases of warning before removing behavior, and :7 allows an immediate break only as a security fix with the #631 transition declaration. This is not a security fix, so it takes the warning route. docs/agent-skills-migration.md:9 documents the PATH fallback as supported behavior, which is why removal cannot be immediate.

Acceptance criteria

  1. AC1: Given no repository pin and a current binary on PATH, when a merge-pr or agent-merge-pr entry starts, then it runs that binary once and prints exactly one warning line on standard error. A declared repository pin whose executable is unavailable, with a usable PATH binary, gives the same outcome. The warning precedes any configuration, gate or merge work.
  2. AC2: Given a pinned binary below the floor and a current binary on PATH, when an entry starts, then it refuses, the PATH binary never runs, and the status is 2, the launcher's status. This is a regression guard for the refusal that lib.sh:172 already gives; the delivered change is the status moving from 1 to 2.
  3. AC3: Given an eligible pin (present, at or above the floor) whose requested merge command is not available, when an entry starts, then the existing frozen workflow runs unchanged and the pinned binary is not invoked for the missing command. Once The lint, populate-description, squash-message and review scripts become wrappers over agentrepo merge #874, The squash-merge and merge scripts become wrappers over agentrepo merge #875 and The preflight scripts become wrappers over agentrepo merge #876 register the command, the entry dispatches to it with the subcommand and arguments unchanged. Other unported workflows behave as before.
  4. AC4: Given a version string, when checked against the floor, then the decision matches The binary resolver, permanent launcher and configuration bridges are resolved in Go #932's comparer: equal and newer accept, older refuses, a pre-release of the floor refuses, an unparsable string refuses, and DEV and a v0.0.0-<14 digits>-<12 hex> pseudo-version accept. These are the decisions the shell makes today. Decisions are identical on Linux, macOS and Windows.
  5. AC5: Non-functional: the decision logic is tested Go and the shell edit is forwarding only.

Scope and non-goals

In scope: the missing-pin warning and the below-floor status change for the merge entries, as tested Go behind a thin forwarding shim; the Compatibility: deprecates agentrepo-path-fallback since <version> declaration under ## Risk in the pull request; a migration note in docs/agent-skills-migration.md; the exact-head owner review of the protected lib.sh change.

Non-goal: the resolver, launcher, floor, comparer and capability probe (#932). Non-goal: gate wiring (#864 and #865). Non-goal: refusing a missing pin, which the follow-up enforcement issue owns.

Follow-up edit, applied to #874, #875 and #876 in one pass once the enforcement issue is Ready: "#874-#876 AC2 precondition becomes: the installed binary is at or above the floor and lacks the required merge command; blockers move to the enforcement issue." Those three bodies stay untouched until then. #1010 ends at the warning.

Approach

Release N is the first release that contains this change (v0.24.0 if it is cut next after v0.23.0). During the window a missing pin still runs the PATH binary, as it does today, and prints one stderr line naming the missing pin, the fix (pin agentrepo in the repository's tool configuration) and the enforcement release. A below-floor pin refuses with status 2. Enforcement is release N+2 (v0.26.0 on that numbering), delivered by the follow-up enforcement issue, which refuses a missing pin with status 2 after two releases of warning. That issue is blocked by this one and by the window.

Reuse #932's resolver, comparer and launcher. Write the failing tests for AC1 to AC4 first, then make the merge entries call the launcher's decision. Command availability comes from #932's capability probe; add no probe, resolver or comparer. The pull request declares Compatibility: deprecates agentrepo-path-fallback since <version> under ## Risk. The migration note gives the consumer steps (pin agentrepo in the repository's tool configuration), the warning release and the enforcement release.

Dependencies

Blocked by #932 and #753 (as #932 is). Blocks the follow-up enforcement issue, which is also blocked by the two-release window. #874, #875 and #876 depend on the enforcement issue for "refusal precedes fallback", not on this one.

Size

S. One pull request, estimated under 300 lines of code including tests.

Verify plan

  • AC1: fixtures (a) no repository pin, a current machine-wide mise pin and a PATH binary; (b) a declared repository pin whose executable is unavailable and a usable PATH binary. Marker files show which binary ran; a stub gate and a stub GitHub CLI log stay empty until after the warning, and exactly one warning line appears. Outcome (b) equals outcome (a).
  • AC2: a below-floor pin with a current PATH binary: a marker file shows the PATH binary never ran, and the status is 2 from the launcher.
  • AC3: a pin whose merge group holds only approval (internal/agentrepo/merge_approval.go:16 and :53): the frozen workflow runs unchanged and a marker file shows the pinned binary was not invoked for the missing command. A second case with the command registered shows dispatch with the subcommand and arguments unchanged. Other unported workflows show behavior unchanged.
  • AC4: the test reads the expected decisions from The binary resolver, permanent launcher and configuration bridges are resolved in Go #932's comparer, and a search shows no second comparer.
  • AC5: the diff shows tested Go for the decision and forwarding-only shell.
  • Generation: mise run generate-skills, then mise run skill-tokens, then apm install, then check-generated-skills, with both harness outputs committed.
  • Definition of done: D1 each AC is verified on the merged head with commands and results in the PR's Verification. D2 an independent reviewer approved the exact merged head and the author resolved each finding or filed it as a follow-up. D3 required checks pass, including mise run check, mise run cover-go-check and mise run cover-py-check. D4 check-leaks.sh with the private-strings list and gitleaks on the diff and each post. D5 signed commits. D6 the migration note in docs/agent-skills-migration.md. D7 Assisted-by trailers and the provenance:agent-assisted label. D8 the line count within the size limit. D9 follow-ups are filed with a Problem and numbered ACs and listed under Related in the PR, and deferred work left in code links its issue. D10 Closes this issue, the board showing Done, and a final Status: comment naming the merge commit. Consumer steps are in the migration note.

Risks

A warning on every start is noise for a machine with no pin; the line names the fix. The status change from 1 to 2 for a below-floor pin affects callers that test for 1. Undo: revert; the entries return to the previous behavior.

Operator authority

Owner-only GitHub action: the configured merge owner's exact-head approval of the protected lib.sh change, required after #753 (.agents/agent-skills.conf:9).

Activity

  1. tbhb-dev-reviews commented on Oct 31, 2026

    @tbhb-dev-reviews

    Refinement: NOT READY — settle activation, fallback and compatibility before the refusal cutover

    Checked

    Gaps

    1. Define the independently deliverable activation boundary. The binary resolver, permanent launcher and configuration bridges are resolved in Go #932 leaves unported entries unchanged; The merge entry points refuse a missing or below-floor pinned agentrepo #1010 must precede The lint, populate-description, squash-message and review scripts become wrappers over agentrepo merge #874–The preflight scripts become wrappers over agentrepo merge #876, which port those entries. Say what a successful merge entry does after The merge entry points refuse a missing or below-floor pinned agentrepo #1010 but before those ports, without adding shell decision logic, a second launcher or a second resolver. Add an observable AC and verification for an eligible pin whose requested merge command is unavailable: the existing frozen workflow remains usable. AC3 currently promises unconditional dispatch to the binary. Main already has a merge group containing only approval (internal/agentrepo/merge_approval.go:16 and internal/agentrepo/merge_approval.go:53), so group presence alone does not establish that a transaction command exists. Preserve The binary resolver, permanent launcher and configuration bridges are resolved in Go #932's boundary for other unported workflows and verify their behavior is unchanged.
    2. Close the fallback question in the bodies. The intended precedence is sound: missing/below-floor pin refuses first; only an eligible pin lacking the required command may reach the frozen fallback. The lint, populate-description, squash-message and review scripts become wrappers over agentrepo merge #874, The squash-merge and merge scripts become wrappers over agentrepo merge #875 and The preflight scripts become wrappers over agentrepo merge #876 already say refusal precedes fallback in Dependencies, but their AC2 preconditions still say only that the installed binary lacks merge. Narrow AC2 and its Verify case in all three, and reconcile The merge entry points refuse a missing or below-floor pinned agentrepo #1010 AC3 with that case. Keep all new policy in tested Go and retain only forwarding shell changes, as Replace complex shell with tested Go, starting with gates and merging #588 requires.
    3. Make missing-pin refusal distinguish repository configuration from tool availability. mise which can return a machine-wide pin, as feat(tools): pin agentrepo in the repository mise.toml #999 documents. AC1's test must include an absent repository pin with a current machine-wide mise pin as well as a current PATH binary. Also cover a declared repository pin whose executable is unavailable while PATH has a usable binary. State the result for that case, assert neither fallback binary runs, and assert refusal occurs before configuration, gates or merge work. An exit-code check alone does not prove those boundaries.
    4. Resolve compatibility and authority. docs/agent-skills-migration.md:9 explicitly supports PATH and no-binary shell fallback. Immediate refusal changes that contract. A Compatibility: deprecates declaration alone does not satisfy the two-release warning period in docs/compatibility.md:3 and docs/compatibility.md:5. Specify the warning/enforcement releases and preserved behavior during the window, or identify the security transition permitted by docs/compatibility.md:7 and the required authority. Replace Operator authority's None with Owner-only GitHub action: naming the exact-head approval for the protected lib.sh change; that approval is already required by AC5 and .agents/agent-skills.conf:9.
    5. Tighten ACs and delivery verification. AC4 requires identical version decisions but does not state which inputs accept or refuse; tie its observable expectations to The binary resolver, permanent launcher and configuration bridges are resolved in Go #932's complete version contract rather than creating another comparer suite with unspecified answers. Move implementation ownership from AC4 and the mixed architecture, documentation and review obligations in AC5 into Scope/Approach/DoD, leaving separate observable outcomes and explicitly prefixed non-functional criteria. Add the canonical generation, token and deployment sequence and verify both harness outputs. Complete the D2 finding-disposition and D9 deferred-code-link obligations below.

    Split

    No additional implementation issue is needed yet. Keep #932's resolver/comparer/launcher separate, keep this issue limited to merge refusal and its compatibility contract, and leave transaction wiring and frozen-fallback adoption with #874–#876. Reassess Size if resolving the pre-port activation requires moving transaction behavior here.

    Definition of done

    Ticks indicate coverage in the current ACs and Verify plan, not completed implementation.

    • D1 The author verified each AC on the merged head, and the Verification section of the pull request cites each AC ID with its evidence: the command and its result, or a link.
    • D2 An independent reviewer approved the exact merged head, and the author resolved each finding or filed it as a follow-up. The current plan names review but omits finding disposition.
    • D3 The required checks pass on that head, and any loosened gate has the owner's approval of that exact head.
    • D4 The leak scans come back clean: gitleaks, devtools' check-leaks.sh with the repository's private-strings list, and the leak review of each post.
    • D5 The author signed each commit, the squash included.
    • D6 The docs match the change (README, docs/, CHANGELOG or help text), or the Verification section says "No doc impact" and why.
    • D7 The commits and the squash have Assisted-by trailers, and the pull request has the provenance:agent-assisted label.
    • D8 The pull request stays within the size limits, or the owner approved its exact head over the hard limit.
    • D9 The author files follow-ups and deferred findings as issues with at least a Problem and numbered AC and lists them under the Related section of the pull request. Any work left in the code links its issue. The current plan omits that code-link requirement.
    • D10 The merge closes the issue through a closing reference and the board shows Done. The final Status: comment names the merge commit.
    • devtools-1 mise run check is green, and so are the coverage floors mise run cover-go-check and mise run cover-py-check, which check doesn't run.
    • devtools-2 A change to skill sources under packages/agents/source/ commits what mise run generate-skills, then mise run skill-tokens, then apm install rewrite, in that order, and check-generated-skills (part of lint) passes.
    • devtools-3 A consumer-visible change has its consumer steps in docs/agent-skills-migration.md or the adoption docs, or the PR's Verification says "No consumer impact" and why. AC5 requires a migration note but does not yet specify the consumer adoption steps and compatibility timeline.

    Round: 1 of 3

  2. tbhb-dev-reviews commented on Oct 31, 2026

    @tbhb-dev-reviews

    Refinement: NOT READY — warning scope is clearer, but handoffs and the capability contract still conflict

    Checked

    Gaps

    1. Reconcile the live warning/enforcement handoff. The merge entry points refuse a missing or below-floor pinned agentrepo #1010 now ends at warning, but The lint, populate-description, squash-message and review scripts become wrappers over agentrepo merge #874, The squash-merge and merge scripts become wrappers over agentrepo merge #875 and The preflight scripts become wrappers over agentrepo merge #876 still name The merge entry points refuse a missing or below-floor pinned agentrepo #1010 as the prerequisite that refuses a missing pin, and all three remain natively blocked by The merge entry points refuse a missing or below-floor pinned agentrepo #1010 rather than Refuse a missing or below-floor pinned agentrepo after the warning window #1012. Their AC2 and Verify still use the broad condition that the binary lacks merge, rather than an eligible repository pin lacking the requested command. Refuse a missing or below-floor pinned agentrepo after the warning window #1012 currently blocks none of them. The shared ownership row in Verify merge skill directories against base lock hashes in Go #981 and Run the skill-directory integrity gate in the merge entry points #982 and the cross-link comment on The binary resolver, permanent launcher and configuration bridges are resolved in Go #932 also still assign missing-pin refusal to The merge entry points refuse a missing or below-floor pinned agentrepo #1010. The promised edit once Refuse a missing or below-floor pinned agentrepo after the warning window #1012 is Ready records this inconsistency rather than resolving the previous finding. Align affected bodies, ownership references and native edges with The merge entry points refuse a missing or below-floor pinned agentrepo #1010 warning / Refuse a missing or below-floor pinned agentrepo after the warning window #1012 enforcement, including eligible-pin AC2 and Verify cases. Warning-only completion must not release work that still claims refusal is already delivered.

    2. Establish the shared capability contract AC3 needs. The merge entry points refuse a missing or below-floor pinned agentrepo #1010 excludes capability-probe work and says command availability comes from The binary resolver, permanent launcher and configuration bridges are resolved in Go #932, with no new probe. However, The binary resolver, permanent launcher and configuration bridges are resolved in Go #932 AC5 promises only config and review verdict capability detection; it does not promise detection of individual merge commands. Its launcher AC1 and non-goals describe locating the binary and executing the supplied command, and its transition excludes unported merge entries. Naming the desired frozen outcome closes the behavioral ambiguity, but does not establish the claimed reusable prerequisite. Identify the shared Go capability/launch contract that can select the unchanged frozen workflow for an eligible binary containing only merge approval, or amend the prerequisite and ownership boundary to supply it. Keep the decision in tested Go, reuse one resolver/comparer/launcher, and add no shell branching or second capability probe. The shell entry still sources the loader directly at packages/agents/source/skills/merge-pr/shared/scripts/preflight.sh:30; verify this warning adoption can ship independently before the transaction ports.

    3. Make Verify prove ordering and version parity. AC1's gate and GitHub CLI logs cannot establish that configuration reads also wait for the warning; add a configuration-read trace and assert the warning precedes it. Assert the intended single workflow dispatch explicitly, distinguishing it from version/capability probes. AC4 says the test reads expected decisions from The binary resolver, permanent launcher and configuration bridges are resolved in Go #932's comparer; reading expectations and searching for duplicate code does not compare actual startup decisions or prove platform parity. Exercise merge startup against the stated version fixtures using The binary resolver, permanent launcher and configuration bridges are resolved in Go #932's existing comparer/fixtures and cite the Linux/macOS/Windows checks. Reuse that coverage without adding another comparer or a speculative test framework.

    Split

    The warning/enforcement split into #1010 and #1012 is appropriate. No additional implementation issue is needed. Keep resolver/comparer/launcher ownership with #932 and transaction wiring with #874–#876; reconcile the capability boundary and live handoffs within that split.

    Definition of done

    Ticks indicate coverage in the ACs and Verify plan, not completed implementation. D1's evidence obligation is present; the specific proof gaps above still need correction.

    • D1 The author verified each AC on the merged head, and the Verification section of the pull request cites each AC ID with its evidence: the command and its result, or a link.
    • D2 An independent reviewer approved the exact merged head, and the author resolved each finding or filed it as a follow-up.
    • D3 The required checks pass on that head, and any loosened gate has the owner's approval of that exact head.
    • D4 The leak scans come back clean: gitleaks, devtools' check-leaks.sh with the repository's private-strings list, and the leak review of each post.
    • D5 The author signed each commit, the squash included.
    • D6 The docs match the change (README, docs/, CHANGELOG or help text), or the Verification section says "No doc impact" and why.
    • D7 The commits and the squash have Assisted-by trailers, and the pull request has the provenance:agent-assisted label.
    • D8 The pull request stays within the size limits, or the owner approved its exact head over the hard limit.
    • D9 The author files follow-ups and deferred findings as issues with at least a Problem and numbered AC and lists them under the Related section of the pull request. Any work left in the code links its issue.
    • D10 The merge closes the issue through a closing reference and the board shows Done. The final Status: comment names the merge commit.
    • devtools-1 mise run check is green, and so are the coverage floors mise run cover-go-check and mise run cover-py-check, which check doesn't run.
    • devtools-2 A change to skill sources under packages/agents/source/ commits what mise run generate-skills, then mise run skill-tokens, then apm install rewrite, in that order, and check-generated-skills (part of lint) passes.
    • devtools-3 A consumer-visible change (the vendored payload, a skill, a reusable workflow, a task or a pin) has its consumer steps in docs/agent-skills-migration.md or the adoption docs, or the pull request's Verification says "No consumer impact" and why.

    Round: 2 of 3

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

    refinement:activeA refinement round is open; not ready for implementation

    Fields

    Priority

    None yet

    Work type

    None yet

    Projects

    • Status
      Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions