●ENTERPRISE — Claude Enterprise adds richer admin analytics, model-level entitlements, and spend alerts●DEVICES — Trusted Devices arrives for Team and Enterprise, verifying devices before remote Claude Code sessions●CODE — Claude Code adds org default models, readable session names, and clickable file attachments●MODEL — Claude Sonnet 5 is the default across all plans at $2/$10 per million tokens through August 31●FABLE 5 — Claude Fable 5 returns worldwide from July 1 after export controls lift, with a new cybersecurity classifier●LIMITS — Claude Code weekly usage limits are up 50% through July 13; Claude Science applications close July 15●ENTERPRISE — Claude Enterprise adds richer admin analytics, model-level entitlements, and spend alerts●DEVICES — Trusted Devices arrives for Team and Enterprise, verifying devices before remote Claude Code sessions●CODE — Claude Code adds org default models, readable session names, and clickable file attachments●MODEL — Claude Sonnet 5 is the default across all plans at $2/$10 per million tokens through August 31●FABLE 5 — Claude Fable 5 returns worldwide from July 1 after export controls lift, with a new cybersecurity classifier●LIMITS — Claude Code weekly usage limits are up 50% through July 13; Claude Science applications close July 15
Which Nightly Job Wrote This Commit? Threading a Correlation Key Through Claude Code's Readable Session Names
When you run unattended nightly jobs across several repositories, you lose track of which session produced which commit. Here is how I redesigned Claude Code's readable session names into a correlation key that ties commits, logs, and sessions into a single thread — with real numbers.
Last week I was scrolling through a git log over coffee when I stopped. One of my wallpaper app repositories had a changelog entry written by the previous night's job, and it referenced an old version number.
The mistake itself was small. What made it painful was that I had no idea which run had written it. I run a handful of my indie app repositories through Claude Code every night in headless mode — dependency bumps, changelog drafts, Crashlytics crash summaries, AdMob mediation config checks. On a busy night that is twelve sessions. The only handle on any given session was an opaque UUID, and nothing about the session's identity survived into the commit message.
In the end, cross-referencing log timestamps against commit times to find the culprit took about fifteen minutes. Finding the run that caused the problem took longer than fixing the problem itself.
Now that Claude Code assigns readable session names, this whole situation can be redesigned. The key was to treat the name as a single meaningful thread rather than decoration.
Treating the name as a correlation key
If you leave the session name as a cosmetic label, it does nothing for you the next morning. What I changed was to design the name as a correlation key — a shared heading that lets you line up separate records against each other after the fact.
Here is the shape of the key I settled on.
Part
Example
Purpose
App name
wallpaper-zen
Which repository the work is in
Task type
changelog-draft
What the run does
JST date
20260706
Which nightly batch
Short random value
a1b2c3d4
Collision guard for same-minute runs
Joined together, that becomes wallpaper-zen-changelog-draft-20260706-a1b2c3d4. You stamp this one string, in the same form, into all three places: the session name, the commit, and the log file name. From any one of those points, you can reach the other two. Tracing becomes a grep instead of archaeology.
Building the key in a run wrapper
First, a wrapper that generates the correlation key every time it launches a single nightly job and passes it in as the session name.
#!/usr/bin/env bash# run-nightly.sh — run one nightly job with a readable correlation keyset -euo pipefailAPP="$1" # e.g. wallpaper-zenTASK="$2" # e.g. changelog-draft# correlation key = app-task-JSTdate-shortrandom# Always make the timezone explicit. A bare date is UTC-based and# drifts to the previous day for late-night runs.DATE_JST="$(TZ=Asia/Tokyo date +%Y%m%d)"SHORT="$(head -c4 /dev/urandom | od -An -tx1 | tr -d ' \n')"export CC_SESSION_NAME="${APP}-${TASK}-${DATE_JST}-${SHORT}"LOG_DIR="$HOME/nightly-logs/${DATE_JST}"mkdir -p "$LOG_DIR"LOG="${LOG_DIR}/${CC_SESSION_NAME}.log"echo "[$(TZ=Asia/Tokyo date +%H:%M:%S)] start ${CC_SESSION_NAME}" | tee -a "$LOG"# Run Claude Code headless and pass this key as the readable session name.# The point is to hand CC_SESSION_NAME to whatever option sets the session name.# If your version cannot set the session name directly, the commit trailer and# the log side below still complete the correlation on their own.claude -p "$(cat "prompts/${TASK}.md")" \ --session-name "$CC_SESSION_NAME" \ 2>&1 | tee -a "$LOG"echo "[$(TZ=Asia/Tokyo date +%H:%M:%S)] done ${CC_SESSION_NAME}" | tee -a "$LOG"
Two things matter here. First, the log file name itself is the correlation key. Second, CC_SESSION_NAME is exported as an environment variable. That variable is the bridge to the commit hook next.
✦
Thank you for reading this far.
Continue Reading
What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.
WHAT YOU'LL LEARN
✦A correlation key built from app name, task, JST date, and a short random value, plus how to avoid the date drift a bare date command causes
✦A prepare-commit-msg hook that stamps the session name into commits, and a trace script that walks from a commit to its log with a single grep
✦Cutting incident tracing from about 15 minutes to roughly 20 seconds, and the concrete fixes for key collisions and timezone drift
Secure payment via Stripe · Cancel anytime
✦
Unlock This Article
Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.
When Claude Code makes a commit inside the repository, I want the correlation key attached to that commit message automatically. A prepare-commit-msg hook does this.
#!/usr/bin/env bash# .git/hooks/prepare-commit-msg# If CC_SESSION_NAME is set, append it to the commit message as a trailer.set -euo pipefailMSG_FILE="$1"[ -n "${CC_SESSION_NAME:-}" ] || exit 0# Do not write the same trailer twice (stay idempotent).if grep -q "^Session-Name: ${CC_SESSION_NAME}$" "$MSG_FILE"; then exit 0fiprintf '\nSession-Name: %s\n' "$CC_SESSION_NAME" >> "$MSG_FILE"
A Session-Name: line is read as a Git trailer — the same trailing-metadata mechanism as Signed-off-by:. The advantage is that it can be extracted mechanically, separately from the message body.
The commit message changes like this, before and after.
Before:
Update changelog for release
After:
Update changelog for 2.4.0 releaseSession-Name: wallpaper-zen-changelog-draft-20260706-a1b2c3d4
It looks like one extra line, but that line erases the next morning's fifteen minutes.
Walking from a commit straight to its log
With the key present in all three places, tracing is just a walk. Given a commit SHA, this script pulls the correlation key from the trailer and shows the tail of the matching log.
#!/usr/bin/env bash# trace.sh <commit-sha> — walk from a commit to its session and logset -euo pipefailSHA="$1"# Pull the correlation key out of the trailerKEY="$(git log -1 --format='%(trailers:key=Session-Name,valueonly)' "$SHA" | head -1)"if [ -z "$KEY" ]; then echo "No session-name trailer on this commit: $SHA" exit 1fi# Extract the 8-digit date to locate the log. Grab the date with a regex so# that hyphens in the app or task name do not break the parsing.DATE_JST="$(printf '%s' "$KEY" | grep -oE '[0-9]{8}' | head -1)"LOG="$HOME/nightly-logs/${DATE_JST}/${KEY}.log"echo "Session name: $KEY"echo "Log: $LOG"echo "----"if [ -f "$LOG" ]; then tail -n 20 "$LOG"else echo "Log not found (retention window may have passed)"fi
Type trace.sh a3f9c21 and the tail of the run that wrote that commit appears at once. The reverse direction uses the same key: spot a suspicious run in the logs, then git log --grep="Session-Name: <key>" pulls up every commit that session touched. Being able to move both ways, not just one, is the whole payoff of making the key a shared heading.
How much shorter it got
I timed "how long until I identify the source of a suspicious commit" three times, before and after.
Situation
Before
After
Procedure
Eyeball log times against commit times
One trace.sh <sha>
Average time
about 15 minutes
roughly 20 seconds
Chance of misattribution
Confused when several runs share a minute
Pinned uniquely by the random value
When twelve sessions run in the same late-night window, matching on timestamps alone is effectively unreliable. Threading a single correlation key turned the morning investigation from searching into opening.
Where I stumbled
The design paid off cleanly, but two spots caught me for sure.
The first was the timezone. I got lazy at first and wrote date +%Y%m%d, so runs after 11 p.m. drifted their key date to the next day and split the log folder in two. For unattended overnight runs, anything that handles a date must state TZ=Asia/Tokyo explicitly. This is not unique to the correlation key — I have hit the same trap on log dates — so I now add it reflexively.
The second was key collision. Back when I built the key only from app, task, and date, running the same task for the same app manually as a retry collided with the nightly file name and nearly overwrote a log. The short random value at the end is not decoration; it is insurance that reliably separates runs made under the same minute and the same conditions. Four bytes proved plenty in practice.
One more small caution: the prepare-commit-msg hook runs before the commit is actually created, so any separate script that runs with --no-verify drops the trailer. On the unattended side, it is safest to decide up front never to use --no-verify.
Which approach to choose
There are alternatives to the commit trailer for where you stamp the correlation. You can write it directly into the commit body, or push it into a separate metadata file. I prefer the trailer approach. The reasons are that it extracts mechanically without hurting the readability of the message body, and that you can walk both directions using nothing but standard git log features. No external tools, no database.
That said, inspection-style tasks that never create a commit — the kind that only check a config and send a notification — have no commit, so the trailer cannot be used. In that case, put the correlation key as the first line of the notification message, and you can walk from the notification to the log. The stamping site differs, but the principle stays the same: thread one key through several records.
Readable session names, on their own, only make the screen slightly more pleasant. But once you redesign that name into a correlation key for your own operations, the traceability of unattended runs changes outright. For me it was a quiet but real step toward being able to trust those nightly jobs.
If you want to try it, start by changing tonight's run log file name into a correlation key and dropping in a single prepare-commit-msg hook. That alone means the next time you wonder "which run wrote this," the answer comes back with one grep.
Thank you for reading. I am still refining my own setup, and I would be glad to keep improving the transparency of unattended runs alongside you.
Share
Thank You for Reading
Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.