Repository navigation
Commit standard: require model name/version attribution on agent-authored commits #1488
Description
Gap
Agent-authored commits do not record which model produced them. There is no commit-message standard at all: AGENTS.md says nothing about commit format, and no commit in the repo's history carries model attribution (name or version). When we later need to answer "which model wrote this change?" (regression archaeology, cost/quality analysis, incident review — cf. the identity-flip archaeology in #1316), the information is simply not there.
Analysis (current state)
Where commit messages are built — all inline git commit -m with plain conventional-commit subjects, no trailers:
| Site | Message shape |
|---|---|
scripts/dev-lead-fix-issue.sh:455 |
feat: implement issue #N — <title> |
scripts/dev-lead-fix-ci.sh:307 |
fix(ci): auto-fix for <check> [skip ci-relay] |
scripts/dev-lead-fix-reviews.sh:685 |
$commit_msg (review-changes) |
scripts/dev-lead-fix-reviews.sh:1015 |
$commit_msg (review-changes) |
scripts/release-notes.sh:237 |
chore: update CHANGELOG for <sha> [skip ci] |
The model IS already known at commit time. scripts/engine.sh tracks the exact model that produced the final output and exports it:
_CLAUDE_CHAIN_MODEL_USED(set atengine.sh:730,:807)_GEMINI_CHAIN_MODEL_USED(set atengine.sh:610,:658)
The commit sites never consume these variables. Note the variables hold the model of the last engine call; a task that fell through the fallback chain (claude-fable-5 → claude-opus-4-8 → claude-opus-4-7, engine.sh:149-151) may have had earlier calls served by a different model.
Related loose end: ci-failure-analyst-reusable.yml:165 and persona-runner-reusable.yml:164 pass --fallback-model opus as a bare alias — the model those jobs actually run on fallback is resolved at runtime and recorded nowhere. Model attribution in commits would make such drift visible.
Interactive commits: Claude Code CLI sessions append Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> — model name but not the exact model ID/version.
Requirements
- Every agent-authored commit MUST carry a machine-readable trailer with the exact model ID that produced the change, e.g.:
Git trailers are the native mechanism: parseable via
Model: claude-fable-5git interpret-trailers/git log --format=%(trailers:key=Model), invisible in--oneline. - The requirement is scoped to commits authored by bot identities (as declared in
personas/*/persona.ymlruntime.identity) — human commits and merge commits are exempt. - If the model cannot be determined, the trailer records
unknownrather than being omitted (so absence always means non-compliance, not a fallback path). - The standard is documented in AGENTS.md and enforced in CI — not aspirational.
Acceptance criteria
- AGENTS.md defines the
Model:trailer standard: format, scope (agent-authored commits), and the exemptions. - All five commit sites above append
Model: <resolved-model>sourced from_CLAUDE_CHAIN_MODEL_USED/_GEMINI_CHAIN_MODEL_USED(fallbackunknown). - Decision recorded (in AGENTS.md): trailer carries the final model only, or the full chain attempted (e.g.
Model-Chain:). -
lint.ymlgains a check that fails a PR if any commit authored by a persona bot identity lacks theModel:trailer (identities read frompersonas/*/persona.yml, not hardcoded). - CLAUDE.md instructs interactive Claude Code sessions to include the
Model: <exact-model-id>trailer alongside the existingCo-Authored-Byline. -
shellcheck scripts/*.shpasses; existing dev-lead flows produce compliant commits in a soak run.
Implementation plan
- AGENTS.md — add a "Commit standard" section defining the trailer, scope,
unknownrule, and the final-model-vs-chain decision. - Script changes — at each of the five sites, append the trailer as a second
-margument, e.g.:Confirm each script actually runs downstream of angit commit -m "$commit_msg" -m "Model: ${_CLAUDE_CHAIN_MODEL_USED:-${_GEMINI_CHAIN_MODEL_USED:-unknown}}"
engine.shinvocation in the same process tree; where it does not (e.g.release-notes.sh, which is mechanical and model-free), either exempt it in the standard or stampModel: n/a. - Enforcement — new
scripts/verify-commit-model-trailer.sh(pattern:verify-persona-identity.sh): for each PR commit, if the author matches a personaruntime.identityaccount, require aModel:trailer. Wire intolint.yml. (GitHub rulesetcommit_message_patternwas considered and rejected: rulesets cannot condition on commit author, so it would break human commits.) - CLAUDE.md — one-line instruction for interactive sessions.
- Rollout — enforcement lands lint-warning-first for one soak cycle, then flips to failing, so in-flight PRs with pre-standard commits are not bricked.
Activity
github-actions commentedon Aug 8, 2026
Thanks for such a thorough write-up — the analysis of where model variables are already tracked but never consumed is exactly the kind of detail that makes implementation straightforward. Before we proceed: the issue mentions a 'final-model-vs-chain' decision needs to be recorded in AGENTS.md — do you have a preference, or should the implementer make the call and document the rationale?
don-petry commentedon Aug 9, 2026
Let's include the entire chain if present.