Skip to content

feat: managed-repo git-identity gate (iss-62) - #13

Merged
REPPL merged 6 commits into
mainfrom
auto/iss-62-identity-gate
Jul 11, 2026
Merged

feat: managed-repo git-identity gate (iss-62)#13
REPPL merged 6 commits into
mainfrom
auto/iss-62-identity-gate

Conversation

@REPPL

@REPPL REPPL commented Jul 11, 2026

Copy link
Copy Markdown
Owner

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 a
stray 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 the
    committed pin), EffectiveIdentity (what git would stamp, via git config
    layering), Checkok/no-pin/mismatch/unset, Blocks() (the
    pre-commit verdict), and WritePin (adoption).
  • DetectiondetectGitIdentity surfaces a divergence/unset as a required
    ahoy doctor gap, and an un-pinned repo as an advisory one.
  • Adoptionahoy install (stepIdentityPin) pins the current git
    identity on ConfigChange approval; a mismatch is never auto-resolved (abcd
    must not silently change the pin or the user's git identity).
  • Preventionabcd ahoy identity-check exits non-zero on a block, and the
    pre-commit hook enforces the same rule self-contained in shell (no binary
    dependency; runs before the name-guard's early exits). A repo with no pin is
    unaffected.

Verification

  • TDD throughout (identity core: 12 subtests, hermetic; detection + apply step
    subtests). make preflight green.
  • Dogfood, end to end: with a REPPL pin, a Test User commit is blocked and
    a REPPL commit succeeds. This repo is pinned, so the gate is live — this
    branch's own commits passed through it.

Origin

Surfaced by a real incident: a repo-local test@example.com override silently
authored/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 (--yes auto-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

REPPL added 6 commits July 11, 2026 09:34
…-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
@REPPL
REPPL merged commit 740e8df into main Jul 11, 2026
12 checks passed
@REPPL
REPPL deleted the auto/iss-62-identity-gate branch July 11, 2026 09:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant