Skip to content

Read commit workflow state in Go instead of shell #850

Description

@tbhb-agent

Problem

The commit workflow's reading of repository state lives in shell spread across preflight.sh, commit.sh, review.sh and the shared agent.sh: the head (or unborn), an in-progress rebase, cherry-pick or base merge, the merge head, the default branch, ahead/behind counts and the recommended rebase base. The same shell decides whether a base merge is authorized: it rejects reverts and other interrupted operations, selects the open pull request's base, fetches it, and accepts only that base's current tip as the merge head. Each script re-derives these facts its own way, and the shell freeze in #588 rules out porting any of the three scripts until one tested implementation exists.

Part of #322, within #588. Child 01 in #322's Children list. Delivers in Go the behavior of the state-reading functions in packages/agents/source/skills/commit/shared/scripts/preflight.sh and of dt_commit_context and dt_commit_merge_head in packages/agents/source/lib/agent.sh, which only this child ports.

Acceptance criteria

  1. AC1: Given a repository with no commit, when the workflow state is read, then the head is reported as unborn and the recommended base is "none (root commit)".
  2. AC2: Given a rebase, cherry-pick or base merge in progress, when the state is read, then the operation is named with the same guidance text the shell prints today.
  3. AC3: Given origin/HEAD is set, when the default branch is resolved, then that name wins; given it is unset, then main wins, and master wins only when main is absent and master exists.
  4. AC4: Given local and origin copies of the default branch in each combination (both, origin only, local only, neither, local behind origin), when the base is recommended, then the recommendation and its reason line match the shell output.
  5. AC5: Given two refs where one is missing, when ahead/behind is asked, then the answer is "unknown (missing ref)" and no error is raised.
  6. AC6: Given a base merge in progress, when the merge head is read, then it is the value of MERGE_HEAD; given none, then "none".
  7. AC7: Given a bounded fetch is requested and the fetch times out or fails, when the state is read for reporting, then the report says "failed (origin counts below may be stale)" and continues.
  8. AC8: Given the commit guard mark is written, when HEAD later moves, then the mark records the head it was armed at, in the same file the guard hook reads.
  9. AC9: Given a base merge in progress on a branch with an open pull request, when merge authorization is checked, then it fetches that pull request's base, and it passes only when MERGE_HEAD is exactly that base's current tip and the merge has a single other parent.
  10. AC10: Given a base merge in progress and a pull request lookup that succeeds but finds no pull request for the branch, when merge authorization is checked, then it selects the trusted default branch, fetches it, and passes only when MERGE_HEAD is exactly that fetched tip; this outcome is distinct from a failed lookup.
  11. AC11: Given a revert, a cherry-pick or rebase in progress, a merge head that is another branch or a stale base tip, an octopus merge, or a failed pull request lookup or fetch, when merge authorization is checked, then it refuses with the same message and exit status as the shell, and nothing is authorized on a failure to look up or fetch.
  12. AC12: Non-functional: the optional reporting fetch (AC7) and the authorization fetch (AC9, AC10) retain the effects of today's plain git fetch commands (fetched objects, remote-tracking refs, FETCH_HEAD, automatically fetched tags, reference-log updates and automatic maintenance); apart from those and the guard mark the state reads write nothing, and the only network use is those fetches and the single pull request lookup that merge authorization needs. A reporting-fetch failure never changes an authorization result.

Scope and non-goals

Scope: reading commit-workflow state, merge authorization and the guard mark in Go, exposed as an agentrepo commit state plumbing subcommand.

Non-goal: printing the preflight report, which child 02 does. Non-goal: reading review or confirmation state, which #321 owns. Non-goal: changing guard-git.sh, agent.sh or any generated copy of them; the shell stays frozen with no new logic. Non-goal: switching any shell wrapper to call the subcommand, which child 07 does once. This child alone owns the Go port of dt_commit_context and dt_commit_merge_head; Their port belongs to this child rather than the shared-library area (#321); the #321 body still needs this ownership boundary recorded.

Approach

Meet AC1 to AC12 with failing table-driven cases first, using real scratch repositories for refs and injected runners for the fetch and the pull request lookup, and mirroring the existing merge cases in tests/skills/agent-commit/base-merge.bats. Reuse the repository and worktree context that #321 delivers rather than duplicating it. Merge authorization gates what an agent may commit, so the pull request receives heightened agent review; it is not an owner-tier path.

Dependencies

Blocked by: #753 (the golden-pattern edit covering the new Go enforcement paths).

Prerequisite: the repository and worktree context delivered by #321.

Size

M. One pull request.

Verify plan

  • AC1, AC2, AC3, AC4, AC5, AC6: Go tests over scratch repositories compare each case with the captured shell output.
  • AC7: a test with a stub git that sleeps past the bound.
  • AC8: a test reads the mark file and the guard's own check.
  • AC9, AC11: tests over scratch repositories with a stub pull request lookup and fetch, covering the cases in tests/skills/agent-commit/base-merge.bats (other branch, stale tip, octopus, revert, lookup failure, fetch failure).
  • AC10: a test with a stub lookup that succeeds with an empty result, passing on the fetched default tip and refusing on a stale one, beside a failed-lookup case that refuses.
  • AC12: tests record every network call and compare repository and guard-directory state before and after the reporting fetch alone and authorization fetch alone, permitting only the ordinary fetch effects listed in AC12 and the guard mark. Include remote-tracking advancement and a reachable-tag fetch, and verify that a reporting-fetch failure leaves the authorization result unchanged.
  • 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 subtle ref-resolution difference would recommend the wrong rebase base, and a lenient merge check would let a foreign merge commit through; the merge authorization is a gate on agent commits, which is why it needs heightened agent review. The parity and refusal cases above are the controls. Undo: revert the change; no caller uses the subcommand until child 07.

Operator authority

None.

Activity

  1. tbhb-agent commented on Oct 27, 2026

    @tbhb-agent
    ContributorAuthor

    Refinement: READY — Read commit workflow state in Go instead of shell
    Model: gpt-6-astra

    Child 01 Read commit workflow state in Go instead of shell

    Verdict: READY

    Checked: Reviewed against an independent clone with origin verified as tbhb-dev/devtools, at origin/main commit fd7b680432ed2040c6e40626778acdf79dc8e317. Relevant live issues were read using GET requests. No builds, tests or GitHub mutations were performed.

    The binding allocation resolves ownership of dt_commit_context and dt_commit_merge_head. #753 now precedes this child. The successful empty PR lookup is distinguished from lookup failure, and reporting and authorization fetches have separate failure contracts. These match packages/agents/source/lib/agent.sh:72, packages/agents/source/lib/agent.sh:81 and packages/agents/source/skills/commit/shared/scripts/preflight.sh:165.

    AC1–AC12 have corresponding checks. Scope excludes shell-library edits and caller switching, making authority consistent with .agents/agent-skills.conf:9. Risks and revert-based undo are appropriate.

    Gaps: None.

    Split: None. M remains credible with shared context supplied by #321.

    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: 3 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: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