Files
bot-bottle/docs/prds/0047-git-gate-manifest-redesign.md
T
didericis-claude 9cd2272498
test / unit (pull_request) Successful in 40s
test / integration (pull_request) Successful in 55s
docs(prd): activate git-gate manifest redesign
PRD 0047 is now shipped to main.
2026-06-03 03:56:20 +00:00

6.3 KiB

PRD 0047: Git-gate Manifest Redesign

  • Status: Active
  • Author: didericis
  • Created: 2026-06-03
  • Issue: #160

Summary

Replace the git top-level key in bottle and agent manifests with git-gate, consolidating git-identity configuration (user) and git-gate sidecar configuration (repos) under a single section. Within repos, field names move to lowercase snake_case and the local repo name is promoted to the YAML key. The change removes the ambiguity in the current git block: its fields are not generic git or SSH config — they are specifically the credential, host-trust, and identity material that is managed in relation to git-gate.

Problem

The current bottle manifest uses a git top-level key that mixes two concerns:

  • git.usergit config --global user.name / user.email identity, which the provisioner injects into the agent's shell.
  • git.remotes — upstream URL, identity file, and host key material that the git-gate sidecar consumes; the agent never sees these values.

That grouping suggests the remotes entries behave like an SSH config or a generic .gitconfig remote declaration. They do not. The gate reads the credential material to push upstream after gitleaks passes; the agent's .gitconfig receives only the insteadOf rewrite that redirects traffic through the gate. Nothing in the current key name or field names signals this.

Splitting git.user into a separate section from git.remotes also doesn't help: both concepts exist because of git-gate, and keeping them under a single git-gate key makes their relationship and purpose explicit.

The field names inside each remote entry also use PascalCase (Name, Upstream, IdentityFile, KnownHostKey), inconsistent with every other manifest section, which uses snake_case.

The current git.remotes dict is keyed by upstream host, which works for simple remotes but forces a separate Name field to give the gate's bare repo a local label. The host key and Name field are often redundant or confusing (e.g., IP-literal upstreams where the key carries no semantic meaning).

Goals / Success Criteria

  • git-gate is accepted as a top-level bottle and agent key; git is removed from both allowed-key sets.
  • git-gate.repos is a named map where each key is the local repo name exposed by the gate (bottle-only; rejected at the agent level).
  • Each entry in git-gate.repos accepts exactly: url (required), identity (required), host_key (optional).
  • git-gate.user replaces git.user on both bottles and agents, with the same name / email fields and overlay semantics.
  • The manifest parser rejects git.remotes and git.user with errors that point to the new keys.
  • GitEntry internal fields are updated to match the new names; all callers (provisioner, git-gate render, plan, tests) compile and pass.
  • Existing unit tests in tests/unit/test_manifest_git.py and tests/unit/test_manifest_git_user.py are rewritten to use the new YAML shape; all other manifest unit tests remain green.
  • The demo manifest (bot-bottle.demo.json) and any examples using the old shape are updated.

Non-goals

  • No change to git.user / git-gate.user semantics or field names (name, email).
  • No change to git-gate runtime behavior (mirroring, gitleaks, access-hook refresh).
  • No change to the insteadOf rewrite the provisioner emits.
  • No migration shim: the old git.* shape is rejected immediately with clear error messages pointing to the new keys.
  • No change to how agent-level user config overlays the bottle-level value.

Design

New manifest shape

Before (bottle frontmatter):

git:
  user:
    name: implementer-bot
    email: eric+implementer@dideric.is
  remotes:
    gitea.dideric.is:
      Name: bot-bottle
      Upstream: ssh://git@gitea.dideric.is:30009/didericis/bot-bottle.git
      IdentityFile: ~/.ssh/gitea-delos-2.pem
      KnownHostKey: "ssh-rsa AAAA..."

After:

git-gate:
  user:
    name: implementer-bot
    email: eric+implementer@dideric.is
  repos:
    bot-bottle:
      url: ssh://git@gitea.dideric.is:30009/didericis/bot-bottle.git
      identity: ~/.ssh/gitea-delos-2.pem
      host_key: "ssh-rsa AAAA..."

git-gate is the single optional top-level key for all git configuration. Bottles that previously used only git.user now use only git-gate.user; those that used only git.remotes now use only git-gate.repos.

Key-name-as-repo-name

The YAML key in git-gate.repos becomes the local repo name (previously Name). The upstream host is no longer the primary key; the provisioner and gate derive it from the url field during parse. IP-literal upstreams work without an artificial host-as-key constraint.

Field renames

Old field New field
Name (from dict key) YAML key in repos
Upstream url
IdentityFile identity
KnownHostKey host_key

Parser changes

  • manifest_schema.py: replace "git" with "git-gate" in BOTTLE_KEYS and AGENT_KEYS_OPTIONAL.

  • manifest.py: replace _parse_git_config with _parse_git_gate_config that validates both user and repos subkeys. Update Bottle.from_dict and Agent.from_dict to call it for the "git-gate" key.

  • Agent.from_dict continues to reject repos at the agent level with a clear error.

  • Remove from_remote_dict and update GitEntry._from_object to accept the new field names. Internal dataclass field names (UpstreamUser, etc.) are unchanged — they are internal plumbing, not user-facing.

  • Any existing "git" key raises a targeted error:

    bottle 'dev' uses 'git' which has been replaced by 'git-gate' (PRD 0047).
    Move git.user → git-gate.user and git.remotes → git-gate.repos.
    

Testing Strategy

Run:

python3 -m unittest discover -s tests/unit

Test files to update:

  • tests/unit/test_manifest_git.py — rewrite fixtures and assertions to use git-gate.repos / lowercase fields. Cover: minimal entry, optional host_key, missing url, missing identity, unknown key, IP-literal upstreams, duplicate name rejection, old git.remotes and bare git key both rejected.
  • tests/unit/test_manifest_git_user.py and tests/unit/test_manifest_agent_git_user.py — update fixtures to use git-gate.user at both bottle and agent level.

Open Questions

None.