Epic: madtea has no way to stay current with the git binary #526

Open
opened 2026-09-16 00:25:16 +00:00 by hexajon · 0 comments
Owner

The gap

madtea depends on two upstreams. Only one of them is watched.

The forge APIs are watched. .forgejo/workflows/drift.yml catches "regressions that appear WITHOUT the code changing". It walks the gitea client AST, extracts every endpoint madtea calls, and asserts each one exists in a vendored spec (docs/api-specs/: Gitea 1.25, Gitea 1.26-dev, Forgejo 14). A second job runs about 60 CLI commands against a live Forgejo in a sidecar. #504 is what a miss looks like there, and the mechanism is why it is visible at all.

The git binary is not watched. madtea shells out to git and parses what it prints (internal/service/git/, about 25 source files; no go-git or libgit2 in go.mod). Against that dependency there is:

  • no vendored contract to diff against,
  • no minimum version, documented or checked (the only --version calls in the tree are madtea's own binary and internal/cmd/install/install.go:976),
  • no version-named output format: all 35 --porcelain call sites use the bare, unversioned form, which git documents as free to change,
  • and no test coverage across versions. ci.yml:107 and :213 pin one image by digest, so CI tests one frozen git forever. git --version is printed at ci.yml:125 and :219, and nothing asserts on it.

The failure mode is quiet. A user on a newer git gets a wrong --porcelain parse in status, stage, or worktree, with no error.

Approach

Harden the contract first, then watch for what hardening cannot cover.

  1. #524 pins the output contract and declares a version floor. This removes the likely failure mode deterministically, with no CI cost.
  2. #525 adds a git version matrix to the drift cron. This catches a genuinely new behaviour that a pinned contract does not cover.

Order matters. A matrix over unversioned output tests a contract that is not yet stated.

On the architecture

This keeps the git binary as an L1 external dependency, as recorded in ADR 0014 (docs/decisions/0014-layered-dual-surface.md:60). Replacing the shelled-out paths with a library was weighed and set aside: it would fork that record, and the paths that must stay shelled out (worktrees, hooks, signing, credential helpers) would keep the same problem.

Exit criterion

A git release that changes output madtea parses is caught by the project, not by a user.

## The gap madtea depends on two upstreams. Only one of them is watched. **The forge APIs are watched.** `.forgejo/workflows/drift.yml` catches "regressions that appear WITHOUT the code changing". It walks the gitea client AST, extracts every endpoint madtea calls, and asserts each one exists in a vendored spec (`docs/api-specs/`: Gitea 1.25, Gitea 1.26-dev, Forgejo 14). A second job runs about 60 CLI commands against a live Forgejo in a sidecar. #504 is what a miss looks like there, and the mechanism is why it is visible at all. **The git binary is not watched.** madtea shells out to git and parses what it prints (`internal/service/git/`, about 25 source files; no `go-git` or libgit2 in `go.mod`). Against that dependency there is: - no vendored contract to diff against, - no minimum version, documented or checked (the only `--version` calls in the tree are madtea's own binary and `internal/cmd/install/install.go:976`), - no version-named output format: all 35 `--porcelain` call sites use the bare, unversioned form, which git documents as free to change, - and no test coverage across versions. `ci.yml:107` and `:213` pin one image by digest, so CI tests one frozen git forever. `git --version` is printed at `ci.yml:125` and `:219`, and nothing asserts on it. The failure mode is quiet. A user on a newer git gets a wrong `--porcelain` parse in `status`, `stage`, or `worktree`, with no error. ## Approach Harden the contract first, then watch for what hardening cannot cover. 1. #524 pins the output contract and declares a version floor. This removes the likely failure mode deterministically, with no CI cost. 2. #525 adds a git version matrix to the drift cron. This catches a genuinely new behaviour that a pinned contract does not cover. Order matters. A matrix over unversioned output tests a contract that is not yet stated. ## On the architecture This keeps the git binary as an L1 external dependency, as recorded in ADR 0014 (`docs/decisions/0014-layered-dual-surface.md:60`). Replacing the shelled-out paths with a library was weighed and set aside: it would fork that record, and the paths that must stay shelled out (worktrees, hooks, signing, credential helpers) would keep the same problem. ## Exit criterion A git release that changes output madtea parses is caught by the project, not by a user.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
sixfold-space/madtea#526
No description provided.