feat: managed-repo git-identity gate (iss-62) - #13
Merged
Conversation
…-62) The single source of truth for the git-identity gate: LoadPin reads the committed .abcd/config/identity.json pin, EffectiveIdentity resolves what git would stamp on a commit (local>global>system), and Check compares them into a Status (ok / no-pin / mismatch / unset). Result.Blocks() is the pre-commit verdict — mismatch or unset blocks; a match or an un-pinned (opted-out) repo does not, so an absent pin never breaks commits. TDD: 8 subtests, hermetic (git isolated from machine global/system config). NOT yet wired — the ahoy doctor gap, the pre-commit hook, and ahoy-install propose-confirm are the next increments. Assisted-by: Claude:claude-opus-4-8
detectGitIdentity wires identity.Check into the detection pass: a mismatch or unset identity is a required, resolvable config-change gap; an un-pinned repo gets an advisory gap to adopt the gate; a match is silent. Verified end to end — `ahoy doctor` on this (un-pinned) repo now reports git_identity.unpinned. TDD: mismatch/match/unpinned subtests, hermetic. Assisted-by: Claude:claude-opus-4-8
The prevention half of the gate. `abcd ahoy identity-check` is the canonical, tested entrypoint: it exits non-zero when the commit identity diverges from .abcd/config/identity.json (mismatch/unset) and zero on a match or an un-pinned repo. The pre-commit hook enforces the same rule self-contained in shell (no binary dependency, so it holds before abcd is built and can't be broken by a stale abcd on PATH), running first so the name-guard's early exits can't skip it. No-op when the repo has no pin. Dogfood verified end to end: with a REPPL pin, a `Test User` commit is blocked and a `REPPL` commit succeeds — so a sandbox re-seed of the identity is caught at commit time, not discovered later. Assisted-by: Claude:claude-opus-4-8
WritePin writes .abcd/config/identity.json; stepIdentityPin adopts the gate for an un-pinned repo on ConfigChange approval, pinning the current git identity (propose = current identity, confirm = category approval). A mismatch is never auto-resolved — abcd must not silently change the pin or the user's git identity — so it stays a guided manual fix (FixHint corrected accordingly). Also pins this repo to its REPPL identity, making the gate live here, and adds the CHANGELOG entry. TDD: WritePin round-trip + required-fields; stepIdentityPin writes-from-current and never-auto-resolves-mismatch. make preflight green. Assisted-by: Claude:claude-opus-4-8
- F1 (blocker): the pre-commit hook failed OPEN when a present pin could not be parsed (non-canonical JSON keys, empty values, malformed) — silently allowing any identity. Now it fails CLOSED: a present-but-unreadable pin blocks the commit. Added a shell-level hook test (7 cases, incl. the three fail-closed regressions) — the coverage gap (F4) that hid this. - F2: `ahoy install --yes` no longer auto-pins the current git identity (a non-interactive run could pin a sandbox identity as canonical). Adoption now requires an interactive confirmation; the un-pinned gap otherwise remains. - F3: a present-but-unreadable pin is now a required doctor gap (adopted-but-broken gate), distinct from git being absent. make preflight green. Assisted-by: Claude:claude-opus-4-8
…isory) Assisted-by: Claude:claude-opus-4-8
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements iss-62 (ledger issue; abcd tracks issues in
.abcd/work/).A managed-repo git-identity gate: a repo pins its expected commit identity
in
.abcd/config/identity.json, and every commit is checked against it — so astray identity (e.g. a sandbox default silently authoring commits) is caught at
commit time rather than discovered later.
What lands
internal/core/identity— the tested core:LoadPin(reads/validates thecommitted pin),
EffectiveIdentity(what git would stamp, viagit configlayering),
Check→ok/no-pin/mismatch/unset,Blocks()(thepre-commit verdict), and
WritePin(adoption).detectGitIdentitysurfaces a divergence/unset as a requiredahoy doctorgap, and an un-pinned repo as an advisory one.ahoy install(stepIdentityPin) pins the current gitidentity on ConfigChange approval; a mismatch is never auto-resolved (abcd
must not silently change the pin or the user's git identity).
abcd ahoy identity-checkexits non-zero on a block, and thepre-commithook enforces the same rule self-contained in shell (no binarydependency; runs before the name-guard's early exits). A repo with no pin is
unaffected.
Verification
subtests).
make preflightgreen.Test Usercommit is blocked anda
REPPLcommit succeeds. This repo is pinned, so the gate is live — thisbranch's own commits passed through it.
Origin
Surfaced by a real incident: a repo-local
test@example.comoverride silentlyauthored/committed 54 commits before detection, requiring a history rewrite.
This gate closes that class up front.
Security review
security-reviewer ran adversarially over the diff and returned PASS after one
BLOCK/fix round: F1 (the pre-commit hook failed open on an unparseable pin) was
fixed to fail closed and locked down with a 7-case end-to-end shell hook test;
F2 (
--yesauto-pinning a sandbox identity) and F3 (broken-pin gap severity)also fixed. Two advisory, non-blocking residuals (a rare quote-in-name
fail-closed edge; the deliberate per-category approval path) are tracked as
iss-63.
Not armed for auto-merge — a
feat:waits for maintainer merge.Assisted-by: Claude:claude-opus-4-8