Development record
abcd never mutates a remote uninvited, and identity derives from caller-local facts
adracceptedadr-44
Context
Two rules surfaced during the collaborator-fence field work
(2026-08-19-collaborator-fence-field-research.md)
and were living, homeless, in a draft intent's prose. Both are trust rules that
outlive any one feature — the itd-84 routing (capability → intent, trust rule →
ADR + brief invariant) puts them here, following the auto-merge and itd-111
precedents. One of them already binds shipped code.
Decision
- abcd never mutates a remote uninvited. Reading remote state (a probe, a doctor, a drift diff) is the default and needs no ceremony. Writing remote state — settings, rulesets, environments, webhooks, roles — happens only through an explicit, dedicated verb the user invokes and confirms, never as a side effect of install, doctor, or any read-shaped command. A verb that applies remote configuration owns its own ordering (a required check's workflow lands on the default branch before any ruleset requires its context) and refreshes the committed mirror in the same change.
- Identity derives from caller-local facts, never from repository
ownership. Any probe that needs to know who the caller is — to redact
their identity, to suppress their public handle, to scope a verdict — reads
caller-local sources (git
user.name/user.email, the login embedded in ausers.noreply.github.comaddress where present,$HOME). It never infers the caller from who owns the remote: ownership and identity diverged the day this repository transferred to an organisation, and the launch scanner's ownership-keyed suppression produced 13 false hard-fails within hours (iss-283; fixed ininternal/adapter/scanner/identity.go, pinned byTestIdentityNameEqualsNoreplyLogin).
Alternatives Considered
- Keep both rules as intent prose. Rejected: an intent ships and closes; these rules bind every future remote-touching verb and one already-shipped subsystem. Prose in a draft is not a home a linter or a reviewer can hold other code to.
- Auto-apply safe-looking remote settings at install. Rejected outright: changing a stranger's repository settings uninvited is the wrong altitude whatever the setting, and "safe-looking" is exactly the judgement the confirm step exists to give the human.
Consequences
- itd-92's doctor is read-only by construction; the apply capability is a separate intent bound by rule 1.
- The corresponding brief invariant is invariant 10 in
02-constraints/03-invariants.md, landed with this record's acceptance. - Rule 2 is already enforced in the launch scanner; any new probe (the doctor) inherits it as a requirement, not a suggestion.
Frontmatter
| id | adr-44 |
|---|---|
| type | adr |
| status | accepted |
| date | 2026-08-19 |
.abcd/development/decisions/adrs/0044-remote-mutation-and-caller-identity.md
open on GitHub ↗ · commit history ↗
References2
Referenced by4
- adr-45Record ids are timestamp-numeric, capture-stable, and never allocated by looking at the maximumrelated
- adr-2610120558328615A private advisory's record never enters the public ledger, its store is never committed, and nothing public carries an advisory id or hintrelated
- adr-2610120937435856A cloud session's unchecked work goes only to a declared work-in-progress prefix that never reaches the default branchrelated
- adr-2610161940004937Spent reservation branches are deleted only by one GitHub App the ruleset namesrelated
mentions19
- dcn-26082000000000052026-08-20 — bughunt round 3 (branch bughunt-a/round-3): captured twelve findings (iss-360..iss-369, plus two renumbered to iss-373/iss-374 after round 2's own collision re-mint landed inside this round's block — the week's fourth collision,
- dcn-26102221050368442026-10-22 — The status-branch ruleset is applied by the person by hand, by design, and C15 is corrected to say so (facilitator, on the delegation of 2026-10-14; iss-2610222040438015, itd-2609151838312703): every ruleset in .abcd/work/rulesets is applied by the person with the README's gh api command and refreshed into the mirror, and invariant 10 lets abcd change a remote only through a dedicated, confirmed apply verb, which no record ever planned for rulesets; so C15's last clause, 'applied through the rulesets' own apply verb', was a drafting slip, not a promise owed, and becomes 'applied by the person from that mirror'. The outcome C15 asks for (the forge refuses deletion and force-push under abcd-status/) is unchanged and live as ruleset 25302453. A verb that applies or verifies rulesets would belong with the branch-protection doctor draft (itd-92), not here (grounds: .abcd/work/rulesets/README.md; brief invariant 10; adr-44)
- iss-283The real_name suppression breaks on an org-owned remote: newIdentityMatchers suppresses real_name only when git user.name equals the username parsed from remote.origin.url, so transferring the repo to an organisation (remote owner Partnermedia, user.name set to the product thinker's login) turns the caller's public GitHub handle into 13 hard_fail findings and blocks the launch. The noreply email (id+login@users.noreply.github.com) carries the caller's login locally and is the right second source for the suppression
- iss-296The hand-maintained ADR index table (.abcd/development/decisions/adrs/README.md) ends at adr-42 and omits committed records adr-43 and adr-44; the record-lint index_drift rule registers four indexes but not the ADR index, so this largest hand index is ungated. Same file: .abcd/work/rulesets/README.md says 'seven required status checks' where main-protection.json now lists eight (external-review added)
- iss-374The hand-maintained ADR index lists adr-44 as proposed but the record frontmatter is accepted; the index drifted after status was flipped without updating the index
- iss-2608211144555085The ADR index (.abcd/development/decisions/adrs/README.md) ends at the adr-44 row while adr-45 is accepted (2026-08-20) and cited as settled law by six live records (the invariants brief, itd-114, spc-33, .abcd/work/issues/README.md, two open issues, DECISIONS.md). 37 ADR files vs 36 index rows — exactly one missing. The index is the declared hand-maintained roll-call (adrs/README.md) and is not among index_drift's four registered indexes, so it is structurally ungated. Third recurrence of this class in the same table (after resolved iss-296 and iss-374), which iss-38's decision (hand-kept indexes must be deleted or gated) predicts.
- iss-2608270636272755abcd's remote-config apply verb should enable GitHub native secret scanning AND secret-scanning push protection by default on every managed repo (belt-and-braces alongside the CI gitleaks full-history scan): push protection blocks a secret at push time, earlier than CI, and secret scanning covers the default branch continuously. Enabled by hand on [redacted-user]/abcd 2026-08-27 via PATCH /repos/{o}/{r} security_and_analysis (both were disabled). Notes: push protection requires secret scanning enabled first; both are free on public repos; secret_scanning_non_provider_patterns and secret_scanning_validity_checks remain optional further hardening. Homes: the read-only VERIFY side is itd-92 (the doctor now reports these toggles); the APPLY-by-default belongs to the separate adr-44-bound apply intent itd-92 defers to, adjacent to itd-106 (abcd sets up the CI a repo requires); the desired state should be mirrored in the repo-settings.json sibling (iss-2608270512210664).
- iss-2608271817300337ADR seed from the security-advisory pilot's verified trust rules (itd-149 decomposition, part 2): auto-merge arms only after a comprehensive no-bypass required-check set AND an independent adversarial APPROVE, never on CI-green alone (F-N); the two release gates that are human-by-design stay human (F-Q); a verdict is never transferred — every required pass re-runs against the final content commit (F-U). These are trust-boundary rules, not intent scope: file the ADR with its brief invariant, then link itd-149 to it at planning.
- iss-2609091037191879A release cut in progress is invisible to every other session, so a change landing on main during a cut is discovered in the changelog rather than chosen. abcd should let launch declare a cut lease that peer sessions, merges and record closes can see: a session about to merge or close a record during a lease is told which cut it would join and who is cutting, and the cutting session rules on the ordering. The convention is in AGENTS.md (concurrent sessions); this seeds the mechanical form, alongside session presence (iss-2608220750029993) and itd-33.
- itd-84An Intent Is Decomposed Before It Is Filed, And A Reversal Is Only Ever Flagged
- itd-106abcd Sets Up the CI a Repo Requires, and Reports What It Did
- itd-111A Stale abcd Never Answers Silently
- itd-114Two Agents Can Mint At The Same Instant And Never Collide
- itd-153abcd's remote-config apply verb should enable GitHub native secret scanning AND secret-scanning push protection by default on every managed repo (belt-and-braces alongside the CI gitleaks full-history scan): push protection blocks a secret at push time, earlier than CI, and secret scanning covers the default branch continuously. Enabled by hand on [redacted-user]/abcd 2026-08-27 via PATCH /repos/{o}/{r} security_and_analysis (both were disabled). Notes: push protection requires secret scanning enabled first; both are free on public repos; secret_scanning_non_provider_patterns and secret_scanning_validity_checks remain optional further hardening. Homes: the read-only VERIFY side is itd-92 (the doctor now reports these toggles); the APPLY-by-default belongs to the separate adr-44-bound apply intent itd-92 defers to, adjacent to itd-106 (abcd sets up the CI a repo requires); the desired state should be mirrored in the repo-settings.json sibling (iss-2608270512210664).
- itd-2610112239022766Private security reports stay private until their fix ships
- itd-2610161647248332abcd finds out what the forge can do before choosing a route
- itd-2610290746077716abcd audits and applies a repository's GitHub settings
- spc-46abcd-s-remote-config-apply-verb-should-enable-github-native
- spc-2610120557432880when-someone-reports-a-security-hole-in