ADR 0030 — git-mirrored CLI commands anchor their argument shape to git's signature

  • Status: Accepted
  • Date: 2026-07-17
  • Tracking: #171

Context

ADR 0016 pins CLI subcommand, flag, and MCP action names to gh then tea verbatim, and docs/contributing/naming-conventions.md records the one refinement it already needs: madtea’s thin wrappers over local git commands (branch, tag, checkout, stash, worktree, …) match git’s vocabulary, not gh/tea’s, because they are git — an agent or human reaching for them arrives with git’s muscle memory.

Names are not the only muscle memory a git-mirrored command inherits. The argument shape — which operands are positionals versus flags, and what a positional means — is just as load-bearing, and ADR 0016 does not speak to it. madtea worktree add diverged here: it took the branch as a positional (add <path> [branch]) and had the service layer silently decide the meaning — an existing branch was checked out, a missing one was created (git worktree add -b). Raw git’s signature is git worktree add <path> [<commit-ish>]: the positional after the path is a commit-ish to check out (an existing ref, never a silent create), and creating a new branch is the explicit -b <branch>. madtea’s worktree add had no -b at all.

The divergence was not academic. The README shipped worktree add -b feat/… as fiction until 2026-07-16, because -b is the git signature every author’s fingers reach for — the command’s documented examples drifted to git’s shape even though the code implemented a different one. A positional that silently creates is also a footgun git deliberately avoids: a typo in a ref name becomes a new branch instead of an error.

This is exactly the training-data-anchor argument ADR 0005 makes for MCP parameter names, and ADR 0016 makes for CLI/MCP command names, extended from names to argument shape for the class of commands whose anchor is git itself.

Decision

A CLI command that mirrors a local git command anchors its argument shape — flag versus positional, and each positional’s meaning — to git’s own signature for that command, not only its flag and subcommand names. git is the vocabulary anchor for these commands (naming-conventions.md), and its signature is part of that vocabulary.

Instantiated for worktree add (#171):

  • madtea worktree add <path> [<commit-ish>] — the positional after the path is a plain checkout of an existing ref, exactly git’s meaning; it never silently creates.
  • -b <branch> is the explicit create form (git worktree add -b). A positional that names no existing branch, tag, or commit is an error that points the caller at -b as the way to create a new branch — never a silent create.
  • With no positional, the worktree detaches at HEAD (git’s default).

The MCP surface is exempt — it is not a git mirror

The MCP surface is a named-parameter API, not a mirror of git’s argv, so its anchor is ADR 0005 / ADR 0016 (the parameter name carries the intent), not git’s positional shape. madt_worktrees action=add branch= keeps its single create-or-checkout convenience: because the operand is named branch=, the name itself disambiguates create-or-checkout intent, and there is no positional whose meaning could be silently overloaded. The ephemeral-worktree flow relies on this convenience.

One service layer carries both (ADR 0014): WorktreeAddInput.Mode selects git’s create (-b), git’s plain checkout (positional), or the MCP create-or-checkout convenience, so the two surfaces share the implementation while the CLI takes git’s shape and the MCP surface keeps its named-parameter one.

Scope

The rule governs the thin local-git wrappers — the commands whose naming anchor is already git rather than gh/tea (branch, tag, checkout, stash, worktree, and their kin). Forge commands (issue, pr, repo, release, …) anchor their shape to gh/tea under ADR 0016, unchanged. Where madtea adds a capability git has no signature for, ordinary flag-design judgment applies; the rule binds only where git already defines the shape.

Consequences

  • Breaking change to worktree add. The positional no longer creates a branch; callers that relied on the old create-on-missing behaviour move to -b <branch>. It lands in the next minor per the 0.x semver tiers (ADR 0028).
  • The next git-mirrored command does not relitigate this. The shape question is decided once, at the class level, so a future wrapper starts from git’s signature by default rather than re-arguing positional-vs-flag per command.
  • naming-conventions.md carries the pointer. The contributor doc names argument shape alongside argument names as git-anchored for these commands, so the rule is discoverable where a contributor is already looking when adding a git wrapper.

Relates to ADR 0016 (names match the upstream vocabulary verbatim; this extends the same anchor from names to argument shape for git-mirrored commands), ADR 0005 (the training-data-anchor argument, made for MCP parameter names), and ADR 0014 (one service layer under both surfaces, so the CLI’s git-shaped form and the MCP surface’s named-parameter form share a single implementation).

Canonical source: docs/adr/0030-git-mirrored-argument-shape.md in the madtea repo.