Skip to content

design: add crow attribution to Claude Code commits #246

@dhilgaertner

Description

@dhilgaertner

Context

When Claude Code makes a commit, it appends a co-author trailer:

Co-Authored-By: Claude <noreply@anthropic.com>

This happens because Claude Code's built-in instructions tell it to add that trailer. The result is genuinely useful: a reader of git log / GitHub PR pages / git blame can see at a glance that the commit was AI-assisted, and tooling can grep for it.

When the Claude Code session was launched by crow, that fact is currently invisible in the commit history. A reader can see "Claude helped write this" but not "crow orchestrated the session." There's an opportunity to make crow's involvement visible the same way Claude's is — both as a small bit of vanity ("crow is what got me to this commit") and, more usefully, as a traceability hook back to crow session metadata.

Goal

When a crow-orchestrated session commits, append a second trailer alongside Claude's. Shape options to discuss:

Co-Authored-By: Claude <noreply@anthropic.com>
Crow-Session: 9B753450-3A6A-4DED-BFE3-A681480B8FA3

or vanity-only:

Co-Authored-By: Claude <noreply@anthropic.com>
Orchestrated-By: Crow <https://github.com/radiusmethod/crow>

or a tagline plus the existing trailer:

🤖 Generated with Claude Code, orchestrated by Crow
Co-Authored-By: Claude <noreply@anthropic.com>

Design questions to brainstorm

1. Trailer format

Probably structured (Crow-Session: <uuid>) wins over free-text. Git has native trailer support (git interpret-trailers, --trailer flag, %(trailers) log format) — structured trailers are greppable, parseable, and play well with existing tooling. Free-text taglines are throwaway.

2. What to include in the trailer value

Candidates:

  • Just "this commit came from crow" — pure vanity, no value beyond the existing Co-Authored-By
  • Session UUID — lets future archeologists match a commit to "what task was Claude working on at the time" via crow get-session <uuid>
  • Workspace name
  • Crow version

Recommendation: session UUID. It's the most useful — a stable handle back to session metadata. Privacy concern is essentially zero (UUIDs are locally-meaningful only). Workspace and version can be looked up via the session if needed.

3. How to inject the trailer

Three real approaches, ordered by complexity:

A. Bootstrap-prompt instruction. crow already writes a per-session prompt to .claude/prompts/crow-prompt-{session_name}.md. Add one line: "When committing, include a Crow-Session: {session_id} trailer." Cheapest. Relies on Claude following instructions — the same mechanism Claude already uses for its own trailer, so the trust model is identical.

B. Worktree-local CLAUDE.md snippet. crow could write a small CLAUDE.md (or merge into the existing one) inside the worktree with the trailer instruction. Survives prompt compaction; gets re-loaded each turn. Belt-and-suspenders alongside A.

C. Git commit-msg hook. Install a hook in the worktree that appends the trailer to every commit. Most deterministic — works for human commits too, not just Claude. But touches .git/hooks (a shared resource between worktrees), can collide with user hooks, doesn't work for users with core.hooksPath set, and is generally surprising tooling behavior.

D. Claude Code settings.json PostToolUse hook. Intercept git commit and rewrite the message. Most invasive; probably not worth it.

Recommendation: A + B together. A is cheap and gets 90% of the way. B is the safety net for long-running sessions where the bootstrap prompt has scrolled out of context. C is interesting but hook ergonomics don't justify it for a single trailer line.

4. Opt-in vs default

Default-on (matches Claude's own trailer) with a crow.attributionTrailers: false config flag for users who'd rather not. People with strict commit-message conventions or open-source repos with explicit "no AI attribution" rules will want to disable it.

5. Composability with Claude's own trailer

Make sure the trailers stack cleanly:

fix(ui): center the modal

Co-Authored-By: Claude <noreply@anthropic.com>
Crow-Session: 9B753450-3A6A-4DED-BFE3-A681480B8FA3

Order doesn't matter for git's trailer parser. Both appear; neither replaces the other.

Concrete proposal (Approach A + B)

  1. In setup.sh, when writing the bootstrap prompt, append a section:

    ## Commit attribution
    
    When you make commits in this workspace, include a `Crow-Session: {session_id}` trailer in the commit message footer alongside your existing `Co-Authored-By: Claude` trailer:
    
        Co-Authored-By: Claude <noreply@anthropic.com>
        Crow-Session: 9B753450-3A6A-4DED-BFE3-A681480B8FA3
    
  2. Also write/merge into a worktree-local .claude/CLAUDE.md with the same instruction, so it survives prompt compaction.

  3. Add a crow.attributionTrailers boolean to .claude/config.json (default true) so users can opt out without editing code.

  4. (Optional, future) A crow log subcommand that takes a commit SHA, parses the Crow-Session: trailer, and prints the matching session metadata. Nice add but not required for this ticket.

Acceptance criteria

  • Bootstrap prompt template includes the commit-attribution instruction with {session_id} interpolated.
  • A CLAUDE.md snippet inside each new worktree carries the same instruction so it persists past prompt compaction.
  • Commits made in a crow-orchestrated session contain both Co-Authored-By: Claude and Crow-Session: {uuid} trailers.
  • Config flag crow.attributionTrailers: false disables the instruction (no trailer added).
  • Commits made outside a crow session in the same repo are unaffected.

Out of scope

  • Wrapping or intercepting git commit itself. We rely on Claude following instructions, same as its own trailer.
  • Post-commit verification / enforcement. If Claude omits the trailer occasionally, no big deal.
  • A crow log archeology command that searches git log for Crow-Session: and links back to session metadata. Genuinely useful future feature but separate ticket.
  • Backfilling trailers on past commits.

Effort

~30 min: prompt-template change + CLAUDE.md snippet + config flag + smoke test that the trailer actually lands in a real commit.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions