Skip to content

feat(deliver): baton deliver pushes a ready feature and opens its pull request - #45

Merged
Bakhomious merged 1 commit into
mainfrom
step/deliver-github
Nov 4, 2026
Merged

Bakhomious merged 1 commit into
mainfrom
step/deliver-github

Conversation

@Bakhomious

Copy link
Copy Markdown
Contributor

baton deliver <slug> --title <title> pushes a ready feature's branch to origin and opens its pull request with the user's own gh. This is plan row 68, deliver-github.

What

  • Flow: a new status, delivered, which the orchestrator still holds. Flow.Deliver takes a ready feature only (its work is the lead's final report). The orchestrator can hand a delivered feature back to the lead, as from any status it holds.

  • State: feature.json keeps the pull request's link (pullRequest), and the journal gets a deliver event with the link as its text. The format stays 1: no baton was released without the new status.

  • Push: repo.Push runs git push --set-upstream origin refs/heads/<branch>, with the user's git and credentials.

  • gh: the new internal/github runs gh pr create --head=<branch> --title=<title> --body-file=<report> and returns the URL that gh prints; gh pr comment --body-file=<report> -- <url> for a later delivery. baton's values always come after an = or a --.

  • Command, in this order:

    1. It refuses a role's pane (BATON_ROLE set to anything but orchestrator). The user's shell is fine.
    2. It refuses a feature that isn't ready, a first delivery without a title, and a later one with a title (the pull request keeps its own).
    3. It pushes, then opens the pull request or comments the new report on it.
    4. It records the delivery in one change.

    A push or a gh that fails leaves the feature ready, for the user to deliver again; a second push of the same commits changes nothing.

  • Guard: in the orchestrator's pane, baton deliver asks the user first; in a role's pane it is denied ("isn't a role's command").

  • Prompt: when it reports a ready feature, the orchestrator proposes a title and gives the report's path, since the delivery publishes the report as it is. It pushes and opens pull requests with baton deliver alone.

  • Docs:

    • README: gh in the requirements, and a new Pull requests section.
    • Plan: row 68 is in review, and jira-login is Done (feat(jira): tell how to replace a token that Jira refuses #44). Decision 61, fact 43 (gh 2.103.2's pr create and pr comment, read in its source) and risk 15 are new. Decisions 34, 41 and 54 and the delivery, commands, state and config paragraphs are amended. Deliver ideas are added.

Size

About 245 added lines outside the tests, comments included, above the step's usual 150: the push, the gh runner and the command only make sense together.

Flagged (decision 61, risk 15)

  • Base branch: gh opens the pull request into the repo's default branch, so a feature started from another branch brings that branch's commits too (decision 41 amended). branch.<head>.gh-merge-base is the way in, later.
  • Forks: not handled. With an upstream remote, gh takes it for the base repo and looks for the branch there.
  • Unrecorded pull request: if baton stops between gh and the record, the pull request is open but the feature doesn't keep it. The next delivery fails with gh's already exists and the link; the user closes it or carries on by hand.
  • The body is the lead's report as it is: the guard's question shows the command and the title, not the report, so the orchestrator gives the report's path for the user to read first.

Suppression

There is one new //nolint:gosec (G204) plus a // NOSONAR (go:S4036), each with its reason, at gh's one exec site, as at git's. It follows the user's answer of 2026-11-04 through nudge ("nolint + NOSONAR with reasons, as git's"), and decisions 34 and 61 record it.

Checks

  • mise run check: green; coverage 99.5% in total, 100% for internal/github and internal/flow, 99.9% for internal/cli.
  • Mutation probes: all 46 mutants are caught. They covered the new status, the push, the gh runner, the command's order, refusals, record and output, the guard's ask and the usage. Three didn't compile in the first run and were rewritten. One of them dropped the output's error, which got a test: a delivery that can't print what it did fails, and stays recorded.
  • Smoke run of the real binary, with no network and no real gh. It had a temp HOME and config, its own tmux server (-f /dev/null, its own TMUX_TMPDIR), the stand-in copilot, a local bare origin and a stand-in gh that logs its arguments and the body. It covered:
    • a full round, then the orchestrator's baton wait;
    • the refusals: a role's pane, no title on the first delivery, a second delivery of a delivered feature;
    • the delivery: the branch on origin with its upstream, gh's arguments and body, feature.json (delivered, pullRequest), the journal and baton status;
    • a hand-back to the lead and a second round by hand, a refused title, then a delivery from the user's shell: the new commit on origin, and gh pr comment with the new report;
    • the guard: ask in the orchestrator's pane, deny in the lead's;
    • the usage and -h.
  • No live run, no real model, no GitHub call: 0 credits.

…l request

baton deliver <slug> --title <title> takes a ready feature, whose work
is the lead's final report, pushes its branch to origin as the branch's
upstream, and opens its pull request with the user's gh: gh pr create
with --head, --title and --body-file, so gh pushes nothing, asks nothing
and applies no template. The feature becomes delivered, which the
orchestrator still holds, keeps the pull request's link, and logs a
deliver event with it.

Once the user hands a delivered feature back to the lead and it's ready
again, baton deliver <slug>, without a title, pushes the new commits and
comments the new report on that pull request. A push or a gh that fails
leaves the feature ready, for the user to deliver again.

It runs in the orchestrator's pane, where the guard asks the user first,
or in the user's shell, and refuses a role's pane. The orchestrator's
prompt proposes a title when it reports a ready feature and gives the
report's path.

Decision 61, fact 43, risk 15 and the README's Pull requests section
explain it. Decision 34 records the user's call on gh's exec.
@sonarqubecloud

sonarqubecloud Bot commented Nov 4, 2026

Copy link
Copy Markdown

@Bakhomious
Bakhomious merged commit 9de9dc0 into main Nov 4, 2026
3 checks passed
@Bakhomious
Bakhomious deleted the step/deliver-github branch November 4, 2026 22:05
Bakhomious added a commit that referenced this pull request Nov 4, 2026
#46)

The user dropped Homebrew on 2026-11-04 and chose, through nudge, GitHub releases that mise's github: backend installs, as nudge's releases were before. Decision 5 records that call in place of the formula in codedogapp/homebrew-tap, and the homebrew step becomes release, whose archive still carries mvdan.cc/sh's BSD-3 notice (decision 50).

The new pane-path step answers what puts baton on a pane's PATH without Homebrew: today nothing does. A pane gets the PATH of the baton that ran tmux, so a baton started by its path leaves the agents' baton commands without it, as in the guard-orchestrator live check (fact 37). deliver-github is done (#45).

Merged under the user's standing OK of 2026-10-31.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant