Split out of #17783 at the review of PR #18753 (PRR_kwDODSospM8AAAABNyLlNg, RA-1): that PR delivers #17783's R3 half, and a Resolves on a ticket still carrying R2 would have closed R2's only open home in the org. This ticket is that home. The R2 analysis below was authored on #17783 by @neo-opus-vega (Ledger row, AC) and @neo-opus-grace (the 2026-09-15 dual-caller correction) and moves here unchanged in substance.
Context
neo-agent-brain:ai/scripts/lint/check-commit-authorship.mjs refuses agent commits that carry the operator's identity or credit a co-author outside the agent roster. It was hardened by #17195 (the author-scoped boundary: an agent-authored commit admits no non-roster trailer at any domain, while an outside contributor's commit is never inspected). Nothing runs it. It has already cost once: an agent-checkout PR authored under the operator's personal account passed every gate and was caught by eye.
Measured 2026-09-16:
| surface |
state |
Brain dev@4f6473aeb8 — package.json, .github/, .husky/, ai/scripts/, test/ |
the only reference is the guard's own spec, test/playwright/unit/ai/scripts/lint/checkCommitAuthorship.spec.mjs |
neo .husky/pre-push at origin/dev |
runs check-branch-discipline.mjs and check-spec-retirement.mjs — not this guard |
neo .github/workflows/spec-retirement-lint.yml:22 |
still says "The two existing pre-push guards (check-branch-discipline, check-commit-authorship) are hook-only" — prose describing a caller that no longer exists in this tree |
The Problem
The guard is already dual-caller by design, and says so in its header — "Two unforgeable sources, one per caller: Hook: checkout ownership … CI: --author-login, which the workflow fills from the GitHub-authenticated PR author. A PR cannot forge who opened it.":
function isAuthenticatedAgentLane() {
const login = …process.argv… '--author-login';
return isAgentCheckout() || Boolean(rosterEmailForLogin(login))
}Two checks, two callers the guard was built for, neither wired:
| check |
unforgeable source |
caller |
operator-identity leak (operatorEmail() vs an agent checkout) |
local git topology — git-dir !== git-common-dir, or the NEO_AGENT_IDENTITY pin |
.husky/pre-push, which already captures the push payload once and hands each guard its own copy |
unknown co-author trailer (findUnknownCoAuthors) |
the GitHub-authenticated PR author, via --author-login |
a CI job — the caller authority neo-agent-skills#14 holds |
The roster coupling blocks neither. The remaining work is wiring. Refuted by measurement (see Placement below): neomjs/neo cannot resolve a Brain script, and publishing the guard would publish its roster. The roster decided where the guard lives.
The Architectural Reality
- The hook half is neo-local:
.husky/pre-push (set -e, payload captured once, one printf … | node … line per guard).
- The CI half is cross-repository:
neo-agent-skills#14 owns the reusable PR-governance workflow each repo calls, so a per-repo workflow here is acceptable only as that ticket's recorded interim child step.
- The guard source lives in the Brain. Whether the hook calls it across a checkout boundary or it relocates beside its caller (as #17783's R3 guard did in PR #18753) is this ticket's first measured decision, not a presumption.
- Placement, decided 2026-09-19 (@neo-opus-ada's comment 5743105316, after @tobiu's input; @neo-opus-vega's measurement in 5702467350):
- The guard's logic publishes through
neo-agent-skills, which every calling repo already depends on.
- The team roster is a caller input (
--roster), never package data. It draws the line between our team and every other deployment of the Agent OS, so no package may carry it.
- With no roster, the trailer check inspects nobody: the tenant-safe default. The operator-identity check needs no roster and runs in any deployment.
- Our roster stays in the Brain. Agent seats pass it from their Brain runtime, and our CI callers from a pinned Brain checkout.
Contract Ledger
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
| Agent-commit authorship enforcement |
Producer: the guard logic published by neo-agent-skills (moved from neo-agent-brain:ai/scripts/lint/check-commit-authorship.mjs), the team roster a caller input. Caller authority: the neo-agent-skills#14 reusable PR-governance workflow, invoked in each repo whose commits it polices |
The guard runs as a PR-blocking check via the Skills-owned caller, and in .husky/pre-push; an interim repo-local workflow caller is acceptable only if recorded as neo-agent-skills#14's child step |
None — a new caller; non-enforcement continues only as an explicitly accepted risk with named owner + date |
script header + neo-agent-skills#14 body |
red control: a synthetic non-compliant authorship reds the check; a compliant commit is green — both caller-visible |
Retirement: fold into neo-agent-skills#14's unified governance when its reusable workflow supersedes per-repo callers.
Acceptance Criteria
Out of Scope
R1 (no required status context → #17171 + neo-agent-skills#14) · R3 (the parity guard → #17783 / PR #18753) · R4 (→ neo-agent-brain#250) · rewriting the guard's checks (the roster becomes an input; what the checks refuse stays).
Related
#17783 (source ticket) · neo-agent-skills#14 (caller authority) · #17195 (the author-scoped boundary this guard enforces) · #16143 · #16280
Live latest-open sweep: checked the latest 20 open issues at 2026-09-16T08:04Z; no equivalent. Cross-org search for check-commit-authorship across neo, neo-agent-brain, neo-agent-skills, neo-agent-institution: only #17783 and neo-agent-skills#14 are open, and #14 carries no AC for this guard. A2A claim sweep: no claim on this scope. Memory Core rationale sweep: #17195's design history; nothing contradicts a dedicated caller ticket.
Owner: @neo-opus-ada (claimed 2026-09-19, after the operator's "it absolutely matters").
Origin Session ID: a758f0e1-0dc1-4e4e-a975-4824a9319487
Retrieval Hint: "commit authorship guard caller pre-push hook author-login CI Skills 14"
Split out of #17783 at the review of PR #18753 (PRR_kwDODSospM8AAAABNyLlNg, RA-1): that PR delivers #17783's R3 half, and a
Resolveson a ticket still carrying R2 would have closed R2's only open home in the org. This ticket is that home. The R2 analysis below was authored on #17783 by @neo-opus-vega (Ledger row, AC) and @neo-opus-grace (the 2026-09-15 dual-caller correction) and moves here unchanged in substance.Context
neo-agent-brain:ai/scripts/lint/check-commit-authorship.mjsrefuses agent commits that carry the operator's identity or credit a co-author outside the agent roster. It was hardened by #17195 (the author-scoped boundary: an agent-authored commit admits no non-roster trailer at any domain, while an outside contributor's commit is never inspected). Nothing runs it. It has already cost once: an agent-checkout PR authored under the operator's personal account passed every gate and was caught by eye.Measured 2026-09-16:
dev@4f6473aeb8—package.json,.github/,.husky/,ai/scripts/,test/test/playwright/unit/ai/scripts/lint/checkCommitAuthorship.spec.mjs.husky/pre-pushatorigin/devcheck-branch-discipline.mjsandcheck-spec-retirement.mjs— not this guard.github/workflows/spec-retirement-lint.yml:22The Problem
The guard is already dual-caller by design, and says so in its header — "Two unforgeable sources, one per caller: Hook: checkout ownership … CI:
--author-login, which the workflow fills from the GitHub-authenticated PR author. A PR cannot forge who opened it.":function isAuthenticatedAgentLane() { const login = …process.argv… '--author-login'; return isAgentCheckout() || Boolean(rosterEmailForLogin(login)) }Two checks, two callers the guard was built for, neither wired:
operatorEmail()vs an agent checkout)git-dir !== git-common-dir, or theNEO_AGENT_IDENTITYpin.husky/pre-push, which already captures the push payload once and hands each guard its own copyfindUnknownCoAuthors)--author-loginneo-agent-skills#14holdsThe roster coupling blocks neither. The remaining work is wiring.Refuted by measurement (see Placement below):neomjs/neocannot resolve a Brain script, and publishing the guard would publish its roster. The roster decided where the guard lives.The Architectural Reality
.husky/pre-push(set -e, payload captured once, oneprintf … | node …line per guard).neo-agent-skills#14owns the reusable PR-governance workflow each repo calls, so a per-repo workflow here is acceptable only as that ticket's recorded interim child step.neo-agent-skills, which every calling repo already depends on.--roster), never package data. It draws the line between our team and every other deployment of the Agent OS, so no package may carry it.Contract Ledger
neo-agent-skills(moved fromneo-agent-brain:ai/scripts/lint/check-commit-authorship.mjs), the team roster a caller input. Caller authority: theneo-agent-skills#14reusable PR-governance workflow, invoked in each repo whose commits it polices.husky/pre-push; an interim repo-local workflow caller is acceptable only if recorded asneo-agent-skills#14's child stepneo-agent-skills#14bodyRetirement: fold into
neo-agent-skills#14's unified governance when its reusable workflow supersedes per-repo callers.Acceptance Criteria
check-commit-authorship.mjsruns as a PR-visible check in the repo(s) whose commits it polices via theneo-agent-skills#14-sanctioned caller, with the red/green control pair from the Ledger — or its non-enforcement is recorded as an accepted risk with a named owner and a date..husky/pre-pushinneomjs/neo, fed the captured payload like its siblings, with a red control from an agent checkout carrying the operator identity..github/workflows/spec-retirement-lint.yml:22states the pre-push guards the hook actually runs.revalidationTrigger— ifneo-agent-skills#14's caller design or the guard's location changes before this closes, the body is re-measured rather than trusted.Out of Scope
R1 (no required status context → #17171 +
neo-agent-skills#14) · R3 (the parity guard → #17783 / PR #18753) · R4 (→neo-agent-brain#250) · rewriting the guard's checks (the roster becomes an input; what the checks refuse stays).Related
#17783 (source ticket) ·
neo-agent-skills#14(caller authority) · #17195 (the author-scoped boundary this guard enforces) · #16143 · #16280Live latest-open sweep: checked the latest 20 open issues at 2026-09-16T08:04Z; no equivalent. Cross-org search for
check-commit-authorshipacrossneo,neo-agent-brain,neo-agent-skills,neo-agent-institution: only #17783 andneo-agent-skills#14are open, and #14 carries no AC for this guard. A2A claim sweep: no claim on this scope. Memory Core rationale sweep: #17195's design history; nothing contradicts a dedicated caller ticket.Owner: @neo-opus-ada (claimed 2026-09-19, after the operator's "it absolutely matters").
Origin Session ID: a758f0e1-0dc1-4e4e-a975-4824a9319487
Retrieval Hint: "commit authorship guard caller pre-push hook author-login CI Skills 14"