Cairn
Write development logs from real repository history. Every statement traces back to a commit, a diff, or a file. A cairn marks the path someone already walked, so this skill records work that is already done — never work that is planned.
Workflow
1. Collect facts
Run the read-only commands in Collecting facts. You need the branch name, the commit list, which files changed and by how much, the latest tag, and any uncommitted changes.
If git rev-parse --is-inside-work-tree fails, go to
When there is no git history.
2. Locate the log file and determine the range
Probe for the log file in this order and use the first hit:
- A path the user names explicitly
DEVLOG.mdin the repository rootdocs/DEVLOG.md- Case and language variants such as
docs/devlog.mdorDEVLOG.zh-CN.md - Nothing found: create
DEVLOG.mdin the repository root and tell the user you created a new file
Never write to CHANGELOG.md. That file follows a separate strict convention and
belongs to someone's release process.
Determine the range:
| Situation | Range |
|---|---|
| The top entry has a commit footnote | Start from the to hash recorded there |
| The top entry has only a date | Start from that date, and say the anchor is less precise |
| No existing entries (first run) | The commits that satisfy both the last 20 commits and the last 30 days |
| The user names a range | Their range wins over everything above: after a tag, after a date, last N commits, or between two hashes |
When the first-run limit truncates history, report the range you actually used and tell the user how to ask for older history. A single entry covering two years is not readable.
If the range is empty, say there are no new commits since the last entry and stop. Never write an empty entry.
For a monorepo, when the user asks about one module only, append -- <path> only to
commands that accept a pathspec: git log, git diff, git show, git status,
git shortlog, and git grep. Do not append a path to repository-wide metadata commands
such as git rev-parse, git branch, git describe, or git tag. Note the path
restriction in the footnote.
3. Pick an output mode
Read the user's wording and choose. Do not ask first — decide, write, and state which mode you used so the user can redirect you.
| Wording | Mode | |---|---| | release note, 版本说明, 更新说明, changelog entry | Release notes | | 写篇文章, 发社区, 复盘, blog post, retrospective | Narrative post | | Anything else | Engineering journal (default) |
Load references/templates.md for the full template and a worked example. If that file is unavailable, the quick reference in Output modes is enough to produce a correctly structured entry.
4. Write the entry
Insert the new entry at the top of the log file: after the # DEVLOG heading, before
the previous first entry. Follow Log file format.
Fact discipline
These rules are checkable one by one. Rule 8 outranks every other rule in this file.
- Every statement must trace back to git history, a diff, or file contents.
- No performance numbers unless the repository holds a benchmark result you can cite, or the diff shows the corresponding implementation.
- No unimplemented features, even when a commit message mentions a plan.
- When a commit message is vague (
fix stuff,update,wip), read the actual diff. Do not copy the message and do not guess. - Write
TODO: needs detailwhere you are unsure. Do not paper over a gap with vague wording. - Cite files by relative path and commits by short hash.
- The "Next" section carries only items with evidence in the repository: TODO comments, unmerged branches, referenced issues. With no evidence, leave it out and say so.
- Sensitive data red line. Keys, tokens, passwords, connection strings, private URLs, internal hostnames, real names and email addresses, and customer names that appear in a diff never enter the log. When you must mention such a change, write a de-identified description such as "added an auth configuration option". If a diff looks like it contains committed credentials, warn the user first, then continue.
- Count each change once. A merge commit's diff repeats the commits it merged, so commit
enumeration commands below use
--no-merges.
Rule 4 is where most of this skill's value sits. Bad AI development logs almost always come from copying bad commit messages.
Rule 8 is a safety red line. A development log gets committed and pushed, which turns it into a second escape route for anything hidden in a diff. Leave a sentence out rather than leak one credential.
Collecting facts
Every command here is read-only. Never run git add, git commit, git checkout,
git reset, or anything else that changes repository state.
| Purpose | Command |
|---|---|
| Confirm a git repository | git rev-parse --is-inside-work-tree |
| Current branch | git branch --show-current |
| Commit list, with a start point | git log <from>..HEAD --no-merges --date=short --pretty=format:"%h %ad %s" |
| Commit list, first run | git log -20 --no-merges --since="30 days ago" --date=short --pretty=format:"%h %ad %s" |
| Files changed and by how much | git diff --stat <from>..HEAD |
| One commit at a glance | git show --stat <sha> |
| The actual range change | git diff <from>..HEAD -- <path> |
| One first-run commit's change | git show --format=fuller <sha> -- <path> |
| Latest tag | git describe --tags --abbrev=0 |
| Tag list | git tag --sort=-creatordate |
| Uncommitted changes | git status --porcelain |
| Who contributed | git shortlog -sn --no-merges <from>..HEAD |
| Leftover work, for the "Next" section | git grep -n -I -E "TODO|FIXME|XXX" |
| Unmerged branches, for the "Next" section | git branch --no-merged |
| Restrict to a subdirectory | Append -- <path> only to commands that accept a pathspec, as listed above |
Notes:
- A full
git diffcan be enormous. Read--statfirst, find the files that matter, then pull the complete diff for those few files only. git describefails in a repository with no tags. That is harmless. Move on.- The first commit has no parent, so
<from>..HEADdoes not apply. Use plaingit log. - On Windows PowerShell, avoid piping into
sort -uorhead. Every command above uses git's own capabilities instead. - Use
git greponly to source the "Next" section. When it returns a lot, keep the entries related to the files you just described. Do not dump every TODO in the repository into the log.
Output modes
This table is the minimum viable spec: with it alone you can write a structurally correct entry.
| Mode | Audience | Required sections | Voice | Never | |---|---|---|---|---| | Engineering journal | You and your collaborators | What changed · Why · Pitfalls · Next | Plain, implementation detail and file paths welcome | Marketing language | | Release notes | People who use the software | Added · Improved · Fixed | Behaviour changes a user can observe | Internal class names, refactoring detail, "improved code structure" | | Narrative post | A community audience | Problem · Attempts · Turning point · Conclusion | A timeline with the trade-offs left in | Manufactured drama, inflated difficulty |
Section names in Chinese output:
| English | 中文 | |---|---| | What changed | 改了什么 | | Why | 为什么 | | Pitfalls | 踩的坑 | | Next | 下一步 | | Added / Improved / Fixed | 新增 / 改进 / 修复 | | Problem / Attempts / Turning point / Conclusion | 问题 / 尝试 / 转折 / 结论 |
All three modes share one set of facts and change only the telling. Rewriting the same commits in a different mode must not change a single fact.
Log file format
# DEVLOG
<!-- Newest entry first. Maintained with Cairn-Skill. -->
## 2026-08-29 · Engineering journal
**What changed**
- ...
**Why**
- ...
**Pitfalls**
- ...
**Next**
- ...
<sub>Based on commits `a1b2c3d..e4f5g6h` · branch `main`</sub>
---
## 2026-08-20 · Engineering journal
...
Rules:
- One
# DEVLOGheading at the top, followed by the HTML comment noting reverse order. - Entries use
##, titleddate · mode. - Separate entries with
---. - Every entry ends with a
<sub>footnote recording the commit range and branch. The hash is what makes the next run's range precise; a date is not enough, because two entries written on the same day would overlap. - Sections inside an entry use bold text, not
###, to keep the outline shallow.
New entries always go to the top, which means concurrent authors collide in the same spot. That trade-off is deliberate: splitting entries into one file each would destroy the ability to read the whole log top to bottom. For a team, have one person write the log before a release rather than everyone appending their own.
Output language
Decide by priority:
| Priority | Condition | Language | |---|---|---| | 1 | The user asks for a specific language | What they asked for | | 2 | The log file already has entries | Match the existing entries | | 3 | New file | Follow the current conversation | | 4 | Undeterminable | English |
Priority 2 beats priority 3: consistency within one file matters more than matching this one conversation. When the two differ, write in the existing language and say why. The user can then ask to switch; never rewrite historical entries on your own.
Keep structural text — headings, bold section names, the footnote — in the same language as the body.
When there is no git history
For a directory that is not a git repository, or when git is unavailable:
- Tell the user plainly that this is not a git repository, so you cannot collect commit history.
- Ask what they did during the period in question.
- Read the relevant files as supporting evidence.
- Write the entry with this footnote instead:
<sub>Based on the user's account and current file state; no git record</sub>
The footnote must state the downgrade honestly. Otherwise the log loses its tiers of reliability, and a reader cannot tell a verified entry from a recalled one.
Boundaries
Cairn records completed work. It does not:
- Modify code
- Write commit messages
- Cut releases or create tags
- Produce a Keep a Changelog file, or touch
CHANGELOG.md - Write README files, API documentation, or design documents
Scan to join WeChat group