docs(prd): consolidate git.user into git-gate per review
Move git.user under git-gate and remove git as a top-level key entirely, so all git configuration lives under a single section.
This commit is contained in:
@@ -7,19 +7,20 @@
|
|||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
Replace the `git.remotes` subsection in bottle manifests with a top-level
|
Replace the `git` top-level key in bottle and agent manifests with `git-gate`,
|
||||||
`git-gate` key whose `repos` map uses lowercase snake_case field names and
|
consolidating git-identity configuration (`user`) and git-gate sidecar
|
||||||
derives the local repo name from the YAML key. The change removes the
|
configuration (`repos`) under a single section. Within `repos`, field names
|
||||||
ambiguity that the current `git` block carries: its fields are not generic git
|
move to lowercase snake_case and the local repo name is promoted to the YAML
|
||||||
or SSH config — they are specifically the credential and host-trust material
|
key. The change removes the ambiguity in the current `git` block: its fields
|
||||||
the git-gate sidecar needs to mirror each upstream.
|
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
|
## Problem
|
||||||
|
|
||||||
The current bottle manifest uses a `git` top-level key that mixes two concerns:
|
The current bottle manifest uses a `git` top-level key that mixes two concerns:
|
||||||
|
|
||||||
- `git.user` — `git config --global user.name / user.email` identity, which
|
- `git.user` — `git config --global user.name / user.email` identity, which
|
||||||
the provisioner injects into the agent's shell and is not gate-specific.
|
the provisioner injects into the agent's shell.
|
||||||
- `git.remotes` — upstream URL, identity file, and host key material that the
|
- `git.remotes` — upstream URL, identity file, and host key material that the
|
||||||
git-gate sidecar consumes; the agent never sees these values.
|
git-gate sidecar consumes; the agent never sees these values.
|
||||||
|
|
||||||
@@ -29,6 +30,10 @@ credential material to push upstream after gitleaks passes; the agent's
|
|||||||
`.gitconfig` receives only the `insteadOf` rewrite that redirects traffic
|
`.gitconfig` receives only the `insteadOf` rewrite that redirects traffic
|
||||||
through the gate. Nothing in the current key name or field names signals this.
|
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`,
|
The field names inside each remote entry also use PascalCase (`Name`,
|
||||||
`Upstream`, `IdentityFile`, `KnownHostKey`), inconsistent with every other
|
`Upstream`, `IdentityFile`, `KnownHostKey`), inconsistent with every other
|
||||||
manifest section, which uses snake_case.
|
manifest section, which uses snake_case.
|
||||||
@@ -40,30 +45,34 @@ a local label. The host key and `Name` field are often redundant or confusing
|
|||||||
|
|
||||||
## Goals / Success Criteria
|
## Goals / Success Criteria
|
||||||
|
|
||||||
- `git-gate` is accepted as a top-level bottle key; `git-gate.repos` is a
|
- `git-gate` is accepted as a top-level bottle and agent key; `git` is removed
|
||||||
named map where each key is the local repo name exposed by the gate.
|
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`
|
- Each entry in `git-gate.repos` accepts exactly: `url` (required), `identity`
|
||||||
(required), `host_key` (optional).
|
(required), `host_key` (optional).
|
||||||
- The `git.remotes` subkey is removed from the `git` block; `git` accepts only
|
- `git-gate.user` replaces `git.user` on both bottles and agents, with the
|
||||||
`user` (unchanged).
|
same `name` / `email` fields and overlay semantics.
|
||||||
- The manifest parser rejects `git.remotes` with an error that points to the
|
- The manifest parser rejects `git.remotes` and `git.user` with errors that
|
||||||
new key.
|
point to the new keys.
|
||||||
- `GitEntry` internal fields are updated to match the new names; all callers
|
- `GitEntry` internal fields are updated to match the new names; all callers
|
||||||
(provisioner, git-gate render, plan, tests) compile and pass.
|
(provisioner, git-gate render, plan, tests) compile and pass.
|
||||||
- Existing unit tests in `tests/unit/test_manifest_git.py` are rewritten to
|
- Existing unit tests in `tests/unit/test_manifest_git.py` and
|
||||||
use the new YAML shape; all other manifest unit tests remain green.
|
`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
|
- The demo manifest (`bot-bottle.demo.json`) and any examples using the old
|
||||||
shape are updated.
|
shape are updated.
|
||||||
|
|
||||||
## Non-goals
|
## Non-goals
|
||||||
|
|
||||||
- No change to `git.user` semantics or field names.
|
- 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
|
- No change to git-gate runtime behavior (mirroring, gitleaks, access-hook
|
||||||
refresh).
|
refresh).
|
||||||
- No change to the `insteadOf` rewrite the provisioner emits.
|
- No change to the `insteadOf` rewrite the provisioner emits.
|
||||||
- No migration shim: the old `git.remotes` shape is rejected immediately with
|
- No migration shim: the old `git.*` shape is rejected immediately with clear
|
||||||
a clear error message.
|
error messages pointing to the new keys.
|
||||||
- No change to how agent-level `git.user` overlays the bottle-level value.
|
- No change to how agent-level user config overlays the bottle-level value.
|
||||||
|
|
||||||
## Design
|
## Design
|
||||||
|
|
||||||
@@ -87,12 +96,10 @@ git:
|
|||||||
**After**:
|
**After**:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
git:
|
git-gate:
|
||||||
user:
|
user:
|
||||||
name: implementer-bot
|
name: implementer-bot
|
||||||
email: eric+implementer@dideric.is
|
email: eric+implementer@dideric.is
|
||||||
|
|
||||||
git-gate:
|
|
||||||
repos:
|
repos:
|
||||||
bot-bottle:
|
bot-bottle:
|
||||||
url: ssh://git@gitea.dideric.is:30009/didericis/bot-bottle.git
|
url: ssh://git@gitea.dideric.is:30009/didericis/bot-bottle.git
|
||||||
@@ -100,9 +107,9 @@ git-gate:
|
|||||||
host_key: "ssh-rsa AAAA..."
|
host_key: "ssh-rsa AAAA..."
|
||||||
```
|
```
|
||||||
|
|
||||||
The `git` block is unchanged and remains optional; `git-gate` is a separate
|
`git-gate` is the single optional top-level key for all git configuration.
|
||||||
optional top-level key. Bottles that use only `git.user` continue to work
|
Bottles that previously used only `git.user` now use only `git-gate.user`;
|
||||||
without touching `git-gate`.
|
those that used only `git.remotes` now use only `git-gate.repos`.
|
||||||
|
|
||||||
### Key-name-as-repo-name
|
### Key-name-as-repo-name
|
||||||
|
|
||||||
@@ -122,26 +129,23 @@ without an artificial host-as-key constraint.
|
|||||||
|
|
||||||
### Parser changes
|
### Parser changes
|
||||||
|
|
||||||
- `manifest_schema.py`: add `"git-gate"` to `BOTTLE_KEYS`; leave `"git"` in
|
- `manifest_schema.py`: replace `"git"` with `"git-gate"` in `BOTTLE_KEYS`
|
||||||
`BOTTLE_KEYS` (it still carries `user`).
|
and `AGENT_KEYS_OPTIONAL`.
|
||||||
- `manifest.py`: add `_parse_git_gate_config(bottle_name, raw)` that validates
|
- `manifest.py`: replace `_parse_git_config` with `_parse_git_gate_config`
|
||||||
the new shape and returns `tuple[GitEntry, ...]`. Update `Bottle.from_dict`
|
that validates both `user` and `repos` subkeys. Update `Bottle.from_dict`
|
||||||
to call it for the `"git-gate"` key.
|
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
|
- Remove `from_remote_dict` and update `GitEntry._from_object` to accept the
|
||||||
new field names. Internal dataclass field names (`UpstreamUser`, etc.) are
|
new field names. Internal dataclass field names (`UpstreamUser`, etc.) are
|
||||||
unchanged — they are internal plumbing, not user-facing.
|
unchanged — they are internal plumbing, not user-facing.
|
||||||
- `_parse_git_config` narrows to reject `remotes` with a helpful error:
|
- Any existing `"git"` key raises a targeted error:
|
||||||
|
|
||||||
```
|
```
|
||||||
bottle 'dev' git.remotes is no longer supported; declare git-gate upstreams
|
bottle 'dev' uses 'git' which has been replaced by 'git-gate' (PRD 0047).
|
||||||
under 'git-gate.repos' instead (see PRD 0047).
|
Move git.user → git-gate.user and git.remotes → git-gate.repos.
|
||||||
```
|
```
|
||||||
|
|
||||||
### Error on rejected key
|
|
||||||
|
|
||||||
The parser emits the error above whenever `git.remotes` is present, regardless
|
|
||||||
of whether `git-gate` is also present.
|
|
||||||
|
|
||||||
## Testing Strategy
|
## Testing Strategy
|
||||||
|
|
||||||
Run:
|
Run:
|
||||||
@@ -155,14 +159,12 @@ Test files to update:
|
|||||||
- `tests/unit/test_manifest_git.py` — rewrite fixtures and assertions to use
|
- `tests/unit/test_manifest_git.py` — rewrite fixtures and assertions to use
|
||||||
`git-gate.repos` / lowercase fields. Cover: minimal entry, optional
|
`git-gate.repos` / lowercase fields. Cover: minimal entry, optional
|
||||||
`host_key`, missing `url`, missing `identity`, unknown key, IP-literal
|
`host_key`, missing `url`, missing `identity`, unknown key, IP-literal
|
||||||
upstreams, duplicate name rejection, old `git.remotes` shape rejected.
|
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
|
## Open Questions
|
||||||
|
|
||||||
- **`git.user` on agents after `git` narrows.** Today both bottle and agent
|
None.
|
||||||
`git` blocks are validated by the same `_parse_git_config` path. After this
|
|
||||||
change, bottle `git` allows only `user`; agent `git` already only allows
|
|
||||||
`user`. No code change needed — but confirm the agent-side rejection message
|
|
||||||
for `git.remotes` still makes sense once `remotes` is also invalid for
|
|
||||||
bottles (the current message says "remotes is bottle-only"; after this PRD
|
|
||||||
it's invalid everywhere).
|
|
||||||
|
|||||||
Reference in New Issue
Block a user