Skip to content

The binary resolver, permanent launcher and configuration bridges are resolved in Go #932

Description

@tbhb-agent

Problem

The shared library chooses which agentrepo binary the skills run and how repository and machine configuration reach them, in shell: _dt_agentrepo_resolve in packages/agents/source/lib/lib.sh prefers the pinned binary from the repository root, falls back to the PATH binary, compares the version against a hard-coded floor with an awk script, and probes the capabilities (config, review verdict) once per process; dt_conf in the same file reads machine configuration through the binary or, when the binary is absent or too old, through a frozen shell reader; dt_repo_conf in packages/agents/source/lib/agent.sh is the separate bridge for repository configuration. #755 excludes this logic and no other issue owns it, so neither shell library can be deleted while it stays.

A binary must be found before any command in it can run, so one small piece of startup code has to remain outside Go. It is a single shared launcher that stays permanently (this is the thin wrapper that #334 allows). It is not copied into each skill entry, and lib.sh does not survive in reduced form.

Transition contract: the transaction ports (#322 to #327) have not landed, and their scripts still run substantial shell after loading the library (for example packages/agents/source/skills/commit/shared/scripts/commit.sh:51 through :95), because internal/agentrepo/command.go:85 registers none of those transactions. So the launcher is introduced here and adopted per entry script only for subcommands that are already ported to Go and registered in internal/agentrepo/command.go at the merged head, with one exception: the two analyze-session entries preflight.sh and new-retro.sh, whose transition #331 owns. Both reach #331 child 04 unchanged and still sourcing the shared library; #331 child 04 activates their normal skill use through the launcher while retaining the original scripts as the frozen fallback; #331 child 06 later replaces their implementations after the N-2 window. Every other entry script keeps sourcing the shared shell library, with its resolver, dt_conf and dt_repo_conf behavior unchanged, until its own port lands and adopts the launcher. A missing or too-old binary stops only the entries switched here, with the one-line message and exit status 2 (which blocks a guarded call in a hook declaration, so it fails closed); nothing else changes behavior before the transaction ports land.

Part of #588 and #321. Replaces _dt_agentrepo_resolve, the version-floor comparison and the capability probes in packages/agents/source/lib/lib.sh, the binary-backed paths of dt_conf and dt_repo_conf, and the resolver startup of each entry script whose subcommand is already ported, except the two analyze-session entries named above. The shell code stays in place for everything not yet ported and is deleted by the area's after-N-2 child and the cutovers.

Acceptance criteria

  1. AC1: Given the launcher file, when it is read, then it is shorter than 30 lines and its only behavior is to locate the agentrepo binary at the pinned version and run it with the arguments it was given; it dispatches by name, so the first argument is the agentrepo subcommand and the remaining arguments pass through unchanged.
  2. AC2: Given no binary at all, or a binary older than the pinned version, when an entry script switched by this issue (an already-ported subcommand other than the two analyze-session entries preflight.sh and new-retro.sh) starts, then the launcher prints exactly one install or upgrade line, which is the whole stderr message so a block explains itself, and exits with status 2 before any configuration is read (status 2 blocks a guarded call in a hook declaration, so it fails closed), and no frozen shell behavior runs.
  3. AC3: Given a pinned binary in the repository root's tool configuration and a different binary on PATH, when a skill resolves the binary, then the pinned binary is chosen, and given the pinned binary is older than the floor, then it is rejected even though the PATH binary is current.
  4. AC4: Given versions equal to the floor, newer than the floor, a source-build marker, a pseudo-version, a pre-release of the floor, and an unparsable string, when the floor is checked, then the first four are accepted, the pre-release and the unparsable string are rejected, and the decisions are identical on Linux, macOS and Windows.
  5. AC5: Given a binary that lacks the config or review verdict capability, when the capabilities are probed, then each absent capability is reported as absent without falling back for the others, and the probe runs once per process.
  6. AC6: Given repository and machine configuration, when a machine key and a repository key are read through the resolved binary, then the value and precedence match the frozen shell readers' for the same fixtures; and given a present binary that fails or returns unusable output, then the read refuses with no fallback to the shell.
  7. AC7: Given the generated skills, when mise run generate-skills runs, then every entry script whose subcommand is already ported, except the two analyze-session entries (preflight.sh and new-retro.sh), which switch only through the session-command activation issue that owns them, starts through the launcher and none of those calls the resolver functions of lib.sh, every other entry script still sources the shared library and is byte-identical to its previous generated form, the output is regenerated from the canonical sources, mise run check-generated-skills passes, and the local deployments carry the regenerated wrappers.
  8. AC8: Given the golden-path protection of build(agents): protect replacement Go enforcement paths #753, when the launcher path is read from it, then the launcher is covered by the heightened agent review and is not in owner_paths.
  9. AC9: Non-functional: the Go tests pass on Linux, macOS and Windows in CI.
  10. AC10: Given a supported binary, when a subcommand switched here runs through its entry script and an existing not-yet-ported workflow (the commit preflight) runs through its unchanged entry script, then both complete successfully with exit codes, output and files written identical to the same fixtures before the change; and given no binary, the unported workflow behaves exactly as before.

Scope and non-goals

Scope: the launcher, binary selection, the version floor, the capability probes, the binary-backed machine and repository configuration reads, and switching the startup of each entry script whose subcommand is already ported to the launcher, except the two analyze-session entries preflight.sh and new-retro.sh, which #331 activates (child 04) and later replaces (child 06) and which this issue leaves unchanged.

Non-goal: changing any entry script whose subcommand is not yet ported, or either of those two analyze-session entries; each transaction port adopts the launcher with its own port. Non-goal: deleting lib.sh, agent.sh or the shell readers, which the area's shell-deletion issue and the final cutover own after the N-2 window. Non-goal: changing the floor value or raising the documented minimum version. Non-goal: porting any other function of lib.sh. Non-goal: any launcher behavior beyond finding the binary at the pinned version, execing it with the subcommand and arguments it was given, and printing one install or upgrade line with exit status 2.

Approach

Meet AC1 to AC10 with failing cases first, using real scratch repositories and stub binaries. The mechanism is the implementer's choice within those outcomes. The intentional behavior change (a missing or too-old binary now stops the entries switched here instead of using the frozen shell reader) is listed in the pull request; the shell reader keeps serving everything not yet ported.

Dependencies

Blocked by: #755 (context discovery, which excludes this logic and fixes where repository roots are found), #753 (golden-file protection for the Go enforcement paths, whose path list the launcher joins).

Prerequisite: the repository configuration reader in internal/config is already on main.

Size

M. One pull request, within 800 lines of code and 2,000 prose lines: a launcher under 30 lines, the Go resolver and bridges, the transition tests of AC10, and a one-line startup change in each already-ported entry script (generated output is not counted). Limiting the switch to ported subcommands removes the broad entry-script edit, so M stays and the transition tests take its place.

Verify plan

  • AC1: a line count and a read of the launcher in the pull request, plus a check that it contains no branching beyond the missing and too-old cases, and a Bats case that runs the launcher with a subcommand name and extra arguments against a stub binary and asserts they arrive unchanged.
  • AC2: Bats cases with no binary and with an older stub binary against an already-ported entry script, asserting the single line is the whole stderr, the exit status is 2 and that no shell reader ran.
  • AC3, AC4: Go tests with stub binaries on a scratch PATH and a scratch repository with a pinned tool configuration, and a table test over the version strings run in the CI matrix.
  • AC5: a Go test with stub binaries lacking one capability at a time, counting probe invocations.
  • AC6: Go tests that read each key through a stub binary and compare with the frozen shell readers on the same fixtures, plus a Bats case against a stub that exits non-zero and one that returns unusable output, asserting the refusal.
  • AC7: mise run generate-skills leaves no diff, mise run check-generated-skills passes, a listing of the local deployment shows the regenerated wrappers, a search of the already-ported entry scripts (the two analyze-session entries excepted) for the resolver function names finds none, and a diff of the generated output of every other entry script, including those two, against main is empty (they stay shell and still source the library).
  • AC10: recorded fixture runs before and after of one already-ported entry script with a supported stub binary and of the commit preflight (which stays shell), with and without a binary, compared for exit code, output and files; this is the successful-workflow evidence that regeneration and a name search cannot give.
  • AC8: the path list of build(agents): protect replacement Go enforcement paths #753 read at the merged head.
  • AC9: the CI matrix jobs for the Go packages.
  • 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-by trailer and the pull request has the provenance:agent-assisted label. 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 final Status: comment names the merge commit.

Risks

A wrong floor comparison could run a skill against a binary without the subcommands it needs, or reject a good one and stop all skills; a launcher fault stops every skill. An entry script wrongly switched before its subcommand is ported would break its workflow; AC7 and AC10 check the boundary. Undo: revert the change, which restores the shell resolver and the startup it replaced.

Operator authority

Owner-only GitHub action: the owner's exact-head review. Owner-tier paths named: packages/agents/source/lib/lib.sh (golden, owner and constitutional) and packages/agents/source/lib/agent.sh (golden and owner), with their generated scripts/ copies, are on the path lists in .agents/agent-skills.conf, so the pull request needs the owner's exact-head review at merge. The new launcher is not an owner path; it joins the golden paths through the heightened review of #753.

Activity

  1. tbhb-agent commented on Oct 27, 2026

    @tbhb-agent
    ContributorAuthor

    Refinement: READY — The binary resolver, permanent launcher and configuration bridges are resolved in Go
    Model: gpt-6-astra

    #321 Child 06 The binary resolver, permanent launcher and configuration bridges are resolved in Go

    Verdict: READY

    Checked: 06-binary-resolver-and-configuration-bridge-in-go.md:7, :9, :14 and :26 now consistently exclude both analyze-session entries. AC7 and verification preserve them unchanged. This closes the previous transition-contract finding: child 04 activates normal skill use, and child 06 of #331 later replaces the retained implementations. The shared-launcher decision, bounded scope, verification and owner-review requirement remain intact.

    Gaps: None.

    Split: None demonstrated.

    Definition of done

    The definition of done for this issue (Feature). Tick each item and give its evidence.

    • 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 PR's Verification says "No consumer impact" and why.

    Round: 2 of 3

  2. tbhb-agent commented on Oct 31, 2026

    @tbhb-agent
    ContributorAuthor

    Cross-link: #1010 owns the refusal policy for the merge entry points (a missing or below-floor pin refuses with status 2, even when a current binary is on PATH). It reuses this issue's resolver, comparer and permanent launcher and adds no second resolver. This issue's scope is unchanged.

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:readyPassed the Definition of Ready; may be picked up

    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