Skip to content

Verify commit identity before the first commit, and require OPERATIONS.md - #481

Merged
ptr727 merged 7 commits into
developfrom
feature/onboarding-identity-and-operations
Jul 31, 2026
Merged

Verify commit identity before the first commit, and require OPERATIONS.md#481
ptr727 merged 7 commits into
developfrom
feature/onboarding-identity-and-operations

Conversation

@ptr727

Copy link
Copy Markdown
Owner

Two onboarding gaps, both surfaced by the Blog standup (#456).

1. Identity and signing were absent from the scaffolding path

The Blog agent set a repo-local user.email while scaffolding, then caught it itself when verifying the first commit was signed. The rule was never missing: GOVERNANCE.md "Git and Commit Rules" states it plainly, and AGENTS.md routes to it. The problem is that STANDUP.md, the procedure an agent standing up a repo actually follows, never mentioned identity, signing, git init, or the first commit at all. And docs/host-setup.md actively modeled the failure, telling the reader to set user.email to you@example.com with no pointer to the rule.

There is also an ordering trap: the first commit of a new repo happens before the repo contains AGENTS.md/GOVERNANCE.md, because the agent is creating them. The router that would have pointed at the rule is not in the target repo yet.

The window matters more than most. A repo whose initial history carries the wrong identity or no signature blocks its own first develop -> main release, and repairing it needs the ruleset temporarily disabled plus a maintainer force-push that docs/repo-config-carry.md explicitly forbids an agent to run. Cheap to prevent, expensive to fix, and the agent is barred from fixing it.

The framing: verify, never set

STANDUP.md gains a step 0 that verifies identity and signing before git init. The key correction is that the agent should not be setting identity at all:

git config --get user.email # the GitHub noreply address
git config --get commit.gpgsign # true
git config --get gpg.format # ssh
git config --local --get user.email # expect empty - a repo-local override is itself a finding

The host carries the identity globally, so a repo-local user.email is redundant where the global is right and a wrong identity where it is not, and it shadows the global silently. On this machine the global was already correct, so the agent's override was pure regression. A mismatch is a host fault to surface, not a per-repo patch, because patching locally hides a broken host that then commits wrongly in every other repo on that machine.

GOVERNANCE.md's "verify... and fix it if not" is narrowed to match, since that clause is what licensed the override. This re-vendors to every downstream repo, accepted deliberately.

docs/host-setup.md now carries the noreply form plus a pointer to the owning rule instead of you@example.com.

2. OPERATIONS.md becomes required for an operational repo

Declared appliesTo: ["operational"] and presence-checked only, the same footing as README.md and HISTORY.md, so its content stays entirely the repo's own.

The evidence for mandating rather than advising is that the convention was already emerging unevenly:

RepoOPERATIONS.mdOther operational docs
ESPHome-ConfigyesDEVICES.md
HomeAssistant-Configyes
HomeAutomation-ConfignoHardware.md, ZFS.md
Vantage-Configno

Two of four wrote one unprompted; a third scattered the same material across ad-hoc names, which is exactly the improvisation these destinations exist to prevent. The drift wave is 2 repos, and both get a consolidation rather than paperwork.

ARCHITECTURE.md stays advisory, deliberately: "a code repo" is not one selector token (it would need a disjunctive list that goes stale as types are added), and unlike the operational case mandating it would produce genuinely empty files. Promote it if the same unprompted-convention evidence appears.

Scope notes

  • The dash and semicolon backlog in spec/section-model.md and docs/host-setup.md is cleared, per the corrected-as-next-edited rule. Both are hub-only, so no re-vendor debt from that part.
  • reports/conformance-matrix.md keeps its backlog (38 findings). Only one table cell is touched here, and sweeping that table would bury the change. Stated rather than silently skipped; happy to do it as a follow-up.

Verification

Full local suite: editorconfig-checker, markdownlint (0 issues over 39 files), the 180-case scripts/ suite, scripts/repo_gate.py, spec/validate.py, spec/audit.py --selftest, and the blocking prose run clean tree-wide. All five edited markdown files report zero on every prose rule.

🤖 Generated with Claude Code

…S.md
Two onboarding gaps, both surfaced by the Blog standup.
The Blog agent set a repo-local user.email during scaffolding, then caught
it while verifying the first commit was signed. The rule was never missing:
GOVERNANCE.md "Git and Commit Rules" states it, and AGENTS.md routes to it.
STANDUP.md, the procedure an agent standing up a repo actually follows,
never mentioned identity, signing, or the first commit at all, and
docs/host-setup.md modeled the failure by telling the reader to set
user.email to "you@example.com" with no pointer to the rule.
The window matters more than most: a repo whose initial history carries the
wrong identity or no signature blocks its own first develop -> main release,
and repairing it needs a maintainer force-push that repo-config-carry.md
forbids an agent to run. Cheap to prevent, expensive to fix, and the agent
is barred from fixing it.
STANDUP.md gains a step 0 that verifies identity and signing before git
init. The framing is verify, never set: the host carries the identity
globally, so a repo-local user.email is redundant where the global is right
and wrong where it is not, and it shadows the global silently. A mismatch is
a host fault to surface, not a per-repo patch, since patching locally hides
a broken host that then commits wrongly in every other repo on that machine.
GOVERNANCE.md's "fix it if not" is narrowed to match, which re-vendors.
Second, OPERATIONS.md becomes required for an operational repo, declared
appliesTo ["operational"] and presence-checked like README.md and
HISTORY.md, so its content stays entirely the repo's own. The evidence for
mandating rather than advising is that the convention was already emerging
unevenly: of four operational repos two wrote one unprompted, while another
scattered the same material across Hardware.md and ZFS.md, which is the
improvisation the destinations exist to prevent. ARCHITECTURE.md stays
advisory, because "a code repo" is not one selector token and mandating it
would produce genuinely empty files.
The dash and semicolon backlog in spec/section-model.md and
docs/host-setup.md is cleared, per the corrected-as-next-edited rule. Both
are hub-only. reports/conformance-matrix.md keeps its backlog: only one
table cell is touched here and sweeping 38 findings across that table would
bury the change.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CopilotAI review requested due to automatic review settings July 31, 2026 20:33

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR closes two onboarding/documentation gaps in the hub’s standup flow: ensuring commit identity/signing are verified before the first commit (when it’s still cheaply fixable), and making OPERATIONS.md a required presence-checked baseline file for operational repos.

Changes:

  • Add a new STANDUP “step 0” to verify global Git identity + signing configuration before git init / first commit, and adjust governance wording to “verify, don’t set” identity.
  • Require OPERATIONS.md for operational repos via spec/files.json, and document that requirement in the section model + conformance matrix.
  • Update host setup documentation to model the correct GitHub noreply email form and link to the owning governance rule.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Show a summary per file
FileDescription
STANDUP.mdAdds pre-first-commit verification step; documents OPERATIONS.md as required for operational repos; adds host-setup link.
GOVERNANCE.mdNarrows “fix identity” guidance to “verify only” to prevent repo-local overrides.
docs/host-setup.mdUpdates Git identity setup snippet to GitHub noreply format and links to governance + STANDUP step 0.
spec/files.jsonMakes OPERATIONS.md required (presence-checked) for appliesTo: ["operational"].
spec/section-model.mdDocuments OPERATIONS.md as the required destination for operational runbooks; minor prose cleanup.
reports/conformance-matrix.mdUpdates the operational row to reflect required OPERATIONS.md and references the new STANDUP step 0.

Comment threadSTANDUP.md Outdated
Step 0 told the agent that `git verify-commit HEAD` reports `G`. It does
not. Verified locally: verify-commit signals through its exit code and
writes "Good "git" signature for ..." to stderr, while the `G` status letter
comes from `%G?` in git log's pretty format.
An agent following the old text would have looked for output that never
appears, on the one check standing between it and an unrepairable initial
history. One command now prints both facts the step needs: `git log -1
--format='%G? %an <%ae>'` gives the status letter and the identity together.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CopilotAI review requested due to automatic review settings July 31, 2026 20:36

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.

Suppressed comments (3)

STANDUP.md:17

  • This line will fail unless git init has already been run, because --local requires a repository. Consider making the “after git init” ordering explicit so the procedure is runnable for greenfield repos.
git config --local --get user.email # expect empty - a repo-local override is itself a finding

STANDUP.md:9

  • git config --local only works inside an initialized repo, so saying to do this “before git init” conflicts with the later local-config check and will lead to a fatal error in a new directory with no .git/.

This issue also appears on line 17 of the same file.

Do this before `git init` or any commit, because the window closes at the first one. A repo whose initial history is unsigned or committed under the wrong identity cannot be cleanly repaired: `Require signed commits` blocks the first `develop -> main` release, re-signing that history is a non-fast-forward the `Block force pushes` rule rejects, and completing it needs the ruleset temporarily disabled plus a maintainer force-push that [`docs/repo-config-carry.md`][repo-config-carry] forbids an agent to perform. Greenfield repos where signing is live before the first commit never hit this.

STANDUP.md:16

  • GOVERNANCE.md states commit signing can be SSH or GPG, so documenting gpg.format as strictly ssh here can incorrectly flag a valid GPG setup as misconfigured. Either clarify this step is SSH-specific, or allow openpgp as an acceptable value.
git config --get gpg.format # ssh

Three suppressed findings, all correct, all verified rather than accepted.
The step said to run its checks before `git init` but included `git config
--local`, which exits 128 with "fatal: --local can only be used inside a git
repository". The procedure was unrunnable exactly where it is aimed, a
greenfield directory. Confirmed in an empty scratch directory, where every
other read returns the global value and only --local fails. The effective
reads now run first, before there is a repo, and the local-override check
moves to its own block after git init and before the first commit.
The step also required gpg.format to be ssh. GOVERNANCE.md "Git and Commit
Rules" says commits are signed with SSH **or** GPG, so a valid GPG host
(gpg.format unset or openpgp) would have been reported as misconfigured and
sent to a maintainer as a fault. The check now names both forms and asks for
the configured format to have a matching loaded agent, which is what that
rule actually prescribes.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CopilotAI review requested due to automatic review settings July 31, 2026 20:42
@ptr727

Copy link
Copy Markdown
OwnerAuthor

Round 2: all three suppressed findings accepted and fixed in 9f3b9ee. Each verified rather than taken on faith.

1 and 2, the --local ordering (STANDUP.md:9 and :17). Correct, and the step was unrunnable exactly where it is aimed. Tested in an empty scratch directory:

$ git config --local --get user.email
fatal: --local can only be used inside a git repository # exit 128
$ git config --get user.email # every other read works fine outside a repo
ptr727@users.noreply.github.com
$ git config --get commit.gpgsign
true

So only --local needs a repository. Step 0 now runs the effective-config reads first, before there is a repo, and moves the local-override check into its own block after git init and before the first commit, with the reason stated inline.

3, SSH versus GPG (STANDUP.md:16). Also correct. GOVERNANCE.md "Git and Commit Rules" line 33 says "All commits must be cryptographically signed (SSH or GPG)" and names ssh-agent for SSH or gpg-agent for GPG. Requiring gpg.format to be ssh would have reported a valid GPG host as misconfigured, and step 0 tells the agent to escalate a misconfiguration to the maintainer, so the false positive would have become a false bug report. The check now reads ssh for an SSH key; unset or openpgp for GPG and asks that the configured format have a matching loaded agent, which is the check that rule actually prescribes.

Worth noting these were suppressed, so a thread-only poll would have shown unresolved=0 and read as clean. That is the second PR running where the suppressed-findings digest (#477) carried the only real defects in a round.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.

Suppressed comments (1)

STANDUP.md:29

  • The post-first-commit verification uses %an <%ae> (author), but the governance rule defines the identity constraint in terms of both author and committer, and GitHub’s signature verification is tied to the committer identity. Using author-only here can miss a wrong committer identity and still appear to “pass”.
After the first commit, confirm it took with `git log -1 --format='%G? %an <%ae>'`, which prints the signature status letter followed by the identity, so `G` plus the expected `noreply` address is the passing result. `git verify-commit HEAD` is the pass/fail form, exiting non-zero on a bad signature and writing its "Good signature" line to stderr rather than emitting a status letter.

The post-commit check read %an <%ae>. The rule it verifies covers both:
"The author and committer on every agent-authored commit are the GitHub
noreply address of the account whose key signs the commit". GitHub verifies
the signature against the committer, which this repo's own
docs/repo-config-carry.md states while explaining a history rewrite.
An author-only read passes on the case most likely to produce the fault. A
rebase, amend, or cherry-pick rewrites the committer and leaves the author
intact, so the wrong identity lands in the field the check never looked at
and the check reports success.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CopilotAI review requested due to automatic review settings July 31, 2026 20:45
@ptr727

Copy link
Copy Markdown
OwnerAuthor

Round 3 suppressed finding: accepted and fixed in eb15c58. Correct on both halves, and verified against this repo's own rules rather than assumed.

The rule covers both identities. GOVERNANCE.md "Git and Commit Rules": "The author and committer on every agent-authored commit are the GitHub noreply address of the account whose key signs the commit." My check read %an <%ae> only.

GitHub verifies against the committer, which this repo states itself, in docs/repo-config-carry.md while explaining the history-rewrite migration: "set each commit's committer to the signing identity before re-signing so the committer GitHub verifies matches your key". So the field I omitted is the one the platform actually checks.

The failure mode is the pointed part: a rebase, amend, or cherry-pick rewrites the committer and leaves the author intact. That is the single most likely way a wrong identity appears, and an author-only check passes on precisely that case while reporting success.

Now:

$ git log -1 --format='%G? author=%an <%ae> committer=%cn <%ce>'
G author=Pieter Viljoen <ptr727@users.noreply.github.com> committer=Pieter Viljoen <ptr727@users.noreply.github.com>

Run against this PR's own head, so the documented command is one that has been executed rather than one that looks right.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.

Suppressed comments (2)

STANDUP.md:11

  • The instructions say these git config --get checks “work in any directory”, but if the user happens to run them from inside an existing Git worktree, repo-local overrides in that other repo will be included and can mask the host/global settings you’re trying to validate. Suggest clarifying that this should be run from a directory that is not inside any existing Git repository (or otherwise ensure no repo-local config can shadow the values).
**Verify the inherited configuration. Never set it.** The host already carries the correct identity, so a repo-local `user.email` is redundant at best and a wrong identity at worst, and it silently shadows the global it overrides. These read the effective configuration and work in any directory, so run them first, before there is a repo:

STANDUP.md:26

  • git config --local --get user.email prints nothing when unset, but it also exits non-zero; someone copy/pasting this into a set -e shell (or interpreting non-zero as failure) may treat the expected “empty” case as an error. Consider explicitly tolerating the empty/unset case while still treating any printed value as the finding.
git config --local --get user.email # expect empty - a repo-local override is itself a finding

Two suppressed findings, both reproduced before fixing.
The pre-init block read the effective config and claimed it works in any
directory. It does, but it answers a different question depending on where
it runs: inside a repository carrying a local override, `git config --get
user.email` returns that repository's identity, not the host's. Reproduced
by setting user.email to wrong@example.com in a scratch repo, where the
effective read returned the override while the global stayed correct. The
block now reads --global explicitly, which is the scope the step is actually
verifying, and the post-init --local check is what proves nothing shadows
it. Two narrow scopes, rather than one broad read that silently changes
meaning with the working directory.
The local check also treated an unset key as an empty success. It exits 1
with no output, so a set -e script aborts on the passing case and a reader
who tests the exit status inverts the check. The finding is a printed value,
never the exit code, and a tolerant form is given for set -e.
Every command in the step was executed as written before committing: from a
non-repo directory, from inside a repo carrying a bogus override, and under
set -e.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CopilotAI review requested due to automatic review settings July 31, 2026 20:51
@ptr727

Copy link
Copy Markdown
OwnerAuthor

Round 4: both suppressed findings accepted and fixed in e412c45. Both reproduced before fixing.

1, effective config masks the host setting (STANDUP.md:11). Correct, and the claim "work in any directory" was true but answered a different question depending on where it ran. Reproduced:

$ git init -q . && git config --local user.email "wrong@example.com"
$ git config --get user.email # inside that repo
wrong@example.com
$ git config --global --get user.email # the host setting it was masking
ptr727@users.noreply.github.com

An agent running step 0 from inside any existing checkout would have validated that checkout, reported the host healthy, and moved on. The block now reads --global explicitly, which is the scope the step is actually verifying. The decomposition is also cleaner than before: --global proves the host is right, and the post-init --local check proves nothing shadows it, so the two together establish the effective identity rather than one broad read that changes meaning with the working directory.

2, the unset exit code (STANDUP.md:26). Also correct:

$ git config --local --get user.email
[exit=1] # no output

So the passing case is a non-zero exit with empty output. A set -e script aborts on success, and anyone testing the exit status inverts the check. The doc now states that the finding is a printed value and never the exit code, and gives the tolerant form for set -e.

Process change on my side. Every command in step 0 was executed as written before this commit: from a non-repo directory, from inside a repo carrying a bogus override, and under set -e. Five of the six findings on this PR have been tool behavior I asserted from familiarity instead of running, so running them is now part of writing them.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.

Suppressed comments (3)

STANDUP.md:18

  • The step 0 verification snippet says signing can be SSH or GPG, but the commands currently only run ssh-add -L, which is SSH-specific. For GPG signing, the snippet should show the GPG key check explicitly (otherwise a GPG-configured host will appear to fail this step).
git config --global --get user.email # the GitHub noreply address, per GOVERNANCE.md "Git and Commit Rules"
git config --global --get commit.gpgsign # true
git config --global --get user.signingkey # set
git config --global --get gpg.format # ssh for an SSH key; unset or openpgp for GPG
ssh-add -L # SSH: at least one loaded key. GPG: gpg --list-secret-keys

spec/section-model.md:15

  • The sentence ending "since applicability is per-repo and the wording is not" is grammatically incomplete/ambiguous ("not" what?), which makes this definition harder to parse.
- **verbatim** - universal fleet-law rule *text*, byte-identical in every repo after EOL and action-pin normalization. The audit content-hashes each downstream copy against the hub's `## <heading>` block. A verbatim section may carry clauses only some repos exercise (for example "a source-only repo carries no build task"). The *text* is still identical everywhere, since applicability is per-repo and the wording is not.

STANDUP.md:28

  • This command is presented for copy/paste, but the following paragraph notes that the passing case exits 1 and will abort under set -e. Including || true in the snippet itself prevents readers from accidentally treating the expected "unset" case as a failure.
git config --local --get user.email # expect no output

Three suppressed findings, all accepted. Two share one principle: a snippet
presented for copy-paste has to be correct by itself, because the paragraph
explaining it does not travel with it.
The agent check ran ssh-add -L with the GPG alternative mentioned only in a
trailing comment, so a GPG host copying the block ran an SSH-specific
command and saw it fail. Both forms are now their own line, with the prose
saying to run the one matching the configured format rather than both.
The local-override check carried its set -e caveat in the following
paragraph instead of the snippet. The tolerant tail is now in the command,
so a copy into a strict script does not abort on the passing case. The
paragraph still states that the finding is a printed value and never the
exit code, which is the part a reader has to understand rather than paste.
The verbatim section definition in spec/section-model.md ended on an
elliptical "and the wording is not", which is grammatical but asks the
reader to supply the missing predicate in a definition that should not need
parsing twice. It now reads "applicability varies by repo while the wording
does not".
Every command was run as written before committing, from a non-repo
directory and under set -e. gpg --list-secret-keys executes here but this
host signs over SSH and holds no secret key, so its passing output is
documented rather than observed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CopilotAI review requested due to automatic review settings July 31, 2026 20:56
@ptr727

Copy link
Copy Markdown
OwnerAuthor

Round 5: all three suppressed findings accepted and fixed in 6a0952e.

Two of them share one principle worth naming, because I got it wrong twice in the same block: a snippet presented for copy-paste has to be correct on its own, since the paragraph explaining it does not travel with the paste.

1, the GPG agent check (STANDUP.md:18). The runnable line was ssh-add -L with the GPG alternative demoted to a trailing comment. A GPG host copying the block runs an SSH-specific command, sees it fail, and step 0 tells it to escalate to the maintainer, so the false positive becomes a false bug report. This is the same defect as the gpg.format one from round 2, which I fixed in the prose while leaving it in the commands. Both forms are now their own line, and the prose says to run the one matching the configured format rather than both.

2, the set -e tail (STANDUP.md:28). Same principle, and I had explicitly written the caveat one paragraph below rather than in the command. The tolerant tail is now in the snippet. The paragraph keeps the part a reader has to understand rather than paste, that the finding is a printed value and never the exit code.

3, the elliptical definition (spec/section-model.md:15). Fair. "applicability is per-repo and the wording is not" is grammatical ellipsis rather than an error, so this one is a clarity call rather than a defect, but a definition that asks the reader to supply a missing predicate is worth one more word. Now reads "applicability varies by repo while the wording does not".

Verification. Every command was run as written before committing, from a non-repo directory and under set -e. One honest gap: gpg --list-secret-keys executes here, but this host signs over SSH and holds no secret key, so its passing output is documented rather than observed. Flagging that rather than implying I confirmed it.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.

Suppressed comments (1)

STANDUP.md:19

  • The step 0 verification snippet tells the reader to run bothssh-add -L and gpg --list-secret-keys, but the paragraph immediately below says to run the agent check that matches the configured signing format, not both. As written, an SSH-signing host without GPG installed will fail this “verification” even when correctly configured, and the instructions are internally inconsistent.
git config --global --get gpg.format # ssh for an SSH key; unset or openpgp for GPG
ssh-add -L # SSH host: at least one loaded key
gpg --list-secret-keys # GPG host instead: the signing key is present

Listing ssh-add -L and gpg --list-secret-keys as consecutive lines reads as
a sequence to run, while the paragraph below said to run only the one
matching the configured format. An SSH host, which need not have gpg
installed at all, would fail a verification step it actually passes, and
step 0 routes a failure to the maintainer, so the false positive becomes a
false bug report.
The two forms now sit in one conditional keyed on gpg.format, so the block
selects the right check instead of asking the reader to. Both forms stay
visible, which was the round 5 finding, and only one executes, which is the
round 6 finding. Those two pulled in opposite directions and a branch is
what satisfies both.
Verified on both paths: run verbatim here, where gpg.format is ssh and the
ssh branch returns the loaded key, and again with gpg absent from PATH,
where the ssh branch is taken and gpg is never invoked.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CopilotAI review requested due to automatic review settings July 31, 2026 21:01
@ptr727

Copy link
Copy Markdown
OwnerAuthor

Round 6 suppressed finding: accepted and fixed in e28935f. Correct, and worth noting that this one and round 5's pull in opposite directions.

Round 5 said the snippet ran only ssh-add -L, so a GPG host would appear to fail and should see the GPG check explicitly. Round 6 says listing both reads as a sequence, so an SSH host, which need not have gpg installed at all, would fail a step it actually passes. Both are right, and a plain list cannot satisfy both. A branch can:

if [ "$(git config --global --get gpg.format)"= ssh ];then ssh-add -L;else gpg --list-secret-keys;fi

Both forms stay visible, which was round 5's point, and exactly one executes, which is round 6's. The block now selects the correct check instead of asking the reader to.

The consequence was not cosmetic: step 0 routes a failed check to the maintainer, so an SSH host without gpg would have escalated a false bug report about a correctly configured machine. That is the third time on this PR that a wrong check would have produced a false escalation rather than a silent miss, which is the failure mode this step is least able to afford.

Verified on both paths, not just the happy one:

$ if [ "$(git config --global --get gpg.format)" = ssh ]; then ssh-add -L; else gpg --list-secret-keys; fi
ssh-ed25519 AAAAC3NzaC1lZDI1NT... # exit 0
$ env PATH=/usr/bin:/bin bash -c '...' # gpg absent from PATH
took ssh branch, gpg never invoked

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.

Suppressed comments (1)

STANDUP.md:25

  • Grammar: “The agent check branches” should be “The agent checks branches”.
The agent check branches rather than listing both forms, because they are alternatives and running the wrong one fails on a correctly configured host: an SSH host need not have `gpg` installed at all. Signing is **SSH or GPG**, so judge the format and its agent together rather than requiring `ssh`: what matters is that the configured format has a matching agent holding the key, which is the check [GOVERNANCE.md "Git and Commit Rules"][governance-git-and-commit-rules] prescribes. Any of these wrong or absent is a **host** misconfiguration to surface to the maintainer ([`docs/host-setup.md`][host-setup] is the setup procedure), not something to patch per repo. Patching it locally hides a broken host that then produces wrong identities in every other repo on that machine.

@ptr727

Copy link
Copy Markdown
OwnerAuthor

Round 7 suppressed finding: declined, with reasoning. This is the first finding on this PR I am not taking, and I want to be explicit about why rather than accept it for consistency with the previous ten.

The sentence is "The agent check branches rather than listing both forms." Its subject is the noun phrase "the agent check" and its verb is "branches", third-person singular, parallel to the gerund in "rather than listing". That is correct as written.

The proposed "The agent checks branches" re-reads "check" as a verb and "branches" as its object, which changes the meaning to the agent inspecting git branches. In a step 0 about commit identity and signing, sitting directly beneath an if statement that selects between ssh-add and gpg, that reading is not just different, it is wrong, and on a repo whose branching model is load-bearing it would be actively confusing.

No change made, and no new round is owed for this one, since declining with evidence produces no new head.

I considered rewording to "The check branches on the configured format" to remove the brief noun-pile ambiguity that probably triggered this. I have not, deliberately. Ten findings on this PR were correct and I took all ten. Editing an eleventh that is not correct, purely because it was raised, is how a review loop stops being a check and becomes a ratchet. If the phrasing genuinely reads badly to a human reviewer, I will happily change it on that basis instead.

@ptr727
ptr727 merged commit 77be3a3 into developJul 31, 2026
7 checks passed
@ptr727
ptr727 deleted the feature/onboarding-identity-and-operations branch July 31, 2026 21:08
ptr727 added a commit that referenced this pull request Aug 1, 2026
Forward promotion of five commits. `main` carries no content beyond the
merge-base (`8771e62`, #478), so this is a clean forward merge with no
conflicts.
**This one has a consumer waiting.** The Blog standup (#456) is
mid-correction and has been told to carry its instruction set from
`main`, because `main` is the audit ground truth. Every fix for the
failure it just hit is currently on `develop` only: `main`'s
`STANDUP.md` still begins at "1. Classify and Catalog", with no step 0
and no step 1A, and `main` does not yet require `OPERATIONS.md`. Until
this promotes, a repo following the corrected instructions re-runs the
uncorrected procedure.
## What promotes
- **#480 - Name where repo-specific content goes.** `STANDUP.md` step 2
named no destination for content that is not a carried file. The three
destinations existed only in `spec/section-model.md`, which nothing on
the scaffolding path pointed at.
- **#481 - Verify commit identity before the first commit, and require
`OPERATIONS.md`.** Step 0 verifies identity and signing before `git
init`, framed **verify, never set**, because the host carries the
identity globally and a repo-local override shadows it silently.
`OPERATIONS.md` becomes required for an `operational` repo,
presence-checked like `README.md`.
- **#482 - State what a host must provide.** The tooling contract,
naming no installer so it stays true on every platform, plus a route to
the write-safety kit which the host-setup document never referenced.
- **#484 - Prefix the PowerShell invocation, and name the interpreter
per platform.** Both corrections came from real Windows and WSL2 testing
in #483 and disproved something #482 asserted.
- **#485 - Require the instruction set before any authoring.** Step 1A,
the direct fix for the Blog failure.
## Verification on the merged `develop`
The full 180-case `scripts/` suite, `scripts/repo_gate.py`,
`spec/validate.py` and `spec/audit.py --selftest` all pass. The blocking
prose run (charset, dupword, spelling) is clean tree-wide, markdownlint
reports 0 issues over 39 files, and editorconfig-checker is clean.
## Release
A human merge never auto-publishes, so this fires no release, as
designed. The latest release stays `2.0.108`.
## Re-vendor debt
#481 changed `GOVERNANCE.md` "Git and Commit Rules", a `verbatim`
section, so this promotion re-vendors it across the fleet. That was
accepted deliberately when the change was made. The debt is pre-existing
and still untracked, and `spec/audit.py --branch <ref>` checks a repo's
convergence before it promotes.
## Known backlog, not in this promotion
The hub's own `.editorconfig`, `.gitattributes` and `.gitignore` carry
44 comment-shape findings, which downstream repos inherit by copying
them and reading them for house style. #485 works around it by telling
agents to trust the rule text over a carried file's formatting. Cleaning
those three files is owed and deliberately separate.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
ptr727 added a commit that referenced this pull request Aug 8, 2026
The round-12 finding claimed a subject-verb disagreement in STANDUP.md
section 0. The subject is the noun phrase "the agent check" and
"branches" is its verb, describing the if/else that selects ssh-add or
gpg by the configured format, so the sentence is correct as written and
the finding is declined.
A decline that lives only in a pull request comment is re-derived the
next time the line is read, which is what the Disproved Claims record
exists to prevent, so the entry goes in with the reading, the revision it
was proved against, and what ends it.
The sentence itself is untouched. It landed in 77be3a3 under #481 and
this change reaches it only as diff context.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for freeto 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.

2 participants

@ptr727