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-bas 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.