Development record
The managed-repo identity gate — detect the wrong author/committer, establish only on confirm
Summary
spc-34 delivers itd-131: extend ahoy's identity check from author-only to the
effective committer as well, and add a propose-and-confirm establish
path that never silently rewrites git config. Detection lives in
internal/core/identity; the gap and the guided fix live in
internal/core/ahoy; the CLI renders and prompts (adr-23). Scope is exactly the
delta the reviews confirmed against the code — the author-env case is already
handled by identity.EffectiveIdentity and is not rebuilt.
Scope
internal/core/identity:EffectiveCommitter(root)— resolve the committer identity env-first (GIT_COMMITTER_NAME/GIT_COMMITTER_EMAIL) then committer config, mirroring the existingEffectiveIdentityfor the author. Deliberately NOTgit var, which fabricates a gecos+hostname identity when unset and would collapse theStatusUnsetstate the pre-commit hook blocks on (review Finding 2).Checkgains a committer comparison against the pin, and a distinct committer-divergence result (author≠committer, or committer≠pin), preserving the existingStatusUnset/StatusMismatch/StatusNoPin/StatusOKauthor semantics unchanged.IsToolIdentity(name, email)— recognise an AI/tool identity (the harness defaultClaude <noreply@anthropic.com>, and the vendor-domain banscripts/check-attribution.shalready encodes) so the routine case is machine-visible. Shared predicate, not a third copy (one-canonical-primitive).
internal/core/ahoy:detectGitIdentityemits a newgit_identity.committergap (required, resolvable) when the committer diverges, alongside the existing author gaps.- A propose-and-confirm apply step: at a TTY, propose the pinned identity (else
the global git identity — disk-only, no
gh/network per adr-38) and write repo-localuser.name/user.emailonly on confirm, through the existing ConfigChange approval seam; never under--yes, never privileged. - Non-TTY / routine context: fail-closed — report the gap, write nothing unprompted, never block on an unanswerable prompt.
- CLI (
internal/surface/cli): render the committer gap and the propose prompt; a routine-context render names the tool identity and points at the runner (iss-2608210932052003) for establishment, not a silent write.
How each acceptance criterion is satisfied
- TTY propose-and-confirm on a repo-local override — the apply step proposes pinned→global and writes only on confirm; test drives a repo-local override differing from the pin and asserts no write until confirmation.
- Committer divergence detected (the delta) —
EffectiveCommitter+Check's committer comparison; test sets author≠committer and aGIT_COMMITTER_*override and watches the author-only check miss it (fail), then the new check catch it (pass). - Non-TTY fail-closed — the apply step detects no TTY and reports without writing; test runs the gate non-interactively against a divergence and asserts (a) no config write, (b) no block/hang, (c) the gap is reported.
- Routine tool-identity detected here, established at the runner —
IsToolIdentityflags the harness default as a divergence; the test asserts detection. Establishment is spc-out (verified against iss-2608210932052003), and the render says so rather than writing. - Never silently rewrite, never escalate — every write path is confirm-gated and unprivileged; a zero-write test asserts a declined confirm (and any non-TTY run) leaves git config untouched.
Deliberately out (carried from the intent)
The routine launcher and launch-time identity establishment
(iss-2608210932052003); a gh-authenticated proposal fallback (adr-38: no
network in the implicit path); the check_text trailer/footer enforcement
(itd-91 owns it — this intent owns check_ident, the identity-field half);
the CI-only-attribution-gate and working-tree-vs-commit gate gaps (separate
plumbing captures); history rewrite of already-produced wrong-identity commits.
Test-first order
EffectiveCommitter and the Check committer path land under failing tests
first (author≠committer, GIT_COMMITTER_*, and the preserved StatusUnset
case as a regression guard); then the ahoy gap; then the propose-and-confirm
and non-TTY-fail-closed apply behaviour; then the CLI render. No new
dependency. The StatusUnset-preservation test is the explicit guard against
the git var regression the review found.
Frontmatter
| id | spc-34 |
|---|---|
| type | spec |
| lifecycle | closed |
.abcd/development/specs/closed/spc-34-managed-repo-identity-gate.md
open on GitHub ↗ · commit history ↗
References1
- itd-131The managed-repo identity gate: the human is the author of record on every commit, before the first oneimplements
mentions6
- adr-23A transport-agnostic Go core behind thin front doors
- adr-38Implicit checks are disk-only — the network answers only an explicit ask
- iss-193A managed repo commits under whatever identity git happens to resolve, and when
- iss-2608210932052003abcd launches autonomous bug hunts (and other routines) for the user, opt-in — beyond handing the user a prompt to paste into a cloud routine. Today the bughunt is a Desktop prompt the user wires into an external cloud routine by hand; the direction is abcd owning the launch: the user opts in, and abcd assembles and starts the routine, applying the run contract it already governs (the human git identity per itd-131, the gates, the merge decision, the state issue). This is the realisation of the run seam — adr-27 (run is a pluggable seam, not a bespoke engine), itd-29 (the run operator surface: start/status/pause/resume/ship), itd-107 (routines assemble from one versioned template; the bughunt and a delivery-pipeline archetype), and the reframed iss-381 (the deterministic delivery pipeline survives as an itd-107 archetype, not an engine). When abcd launches the routine it sets the human git identity at launch, which is the clean mechanism the itd-131 identity gate points at for routine commits (vs today's prompt/env workaround). Big, cross-record capability — needs decomposition and likely ideate before it is filable; recorded so the direction is durable.
- itd-51abcd Knows What "Safe Enough to Adopt" Means Before a New Harness Arrives
- itd-91A Project Declares Once How It Wants AI Acknowledged — And Every Commit, PR, And Record Follows