---
name: project-changelog
description: "Maintain repo-local changelogs."
---

# Project Changelog

A repo-portable convention + tooling for project history. Five buckets, one
frontmatter schema, a fixed backlog state machine, and six helper scripts.

## When to invoke this skill

- The user wants to add an entry to `changelog/` and is unsure which bucket.
- A repo has an inconsistent `changelog/` (mixed frontmatter, no index, worklog
  bloated with warning arrays) and needs tightening.
- A new repo needs `changelog/` bootstrapped.
- A backlog item needs closing with a link to its resolving change.
- A repo has PRDs, architecture docs, or a ticket pack outside `changelog/`
  and needs those converted into canonical backlog / phase / decision entries.

If the user just wants a release-notes CHANGELOG.md generated from git tags,
this is not the right skill.

## The five buckets

```
changelog/
├── README.md               # human-facing description (template included)
├── AGENTS.md               # agent routing rules (template included)
├── INDEX.md                # generated by build_index.py
├── changes/                # narrative implementation/migration notes
├── decisions/              # durable architecture/policy decisions (ADR-style)
├── phases/                 # staged milestones grouping changes (optional)
├── backlog/                # deferred or upcoming work with explicit status
├── archive/                # archived entries, structured as {bucket}/YYYY/
└── logs/
    └── worklog.jsonl       # append-only, one JSON object per pipeline run
```

Only the buckets a project uses need to exist. Small projects can skip
`phases/` and even `logs/`.

External planning docs such as PRDs, architecture notes, phased plans, and
source ticket packs stay outside `changelog/`. Use `artifacts` to link to
those docs from `backlog/`, `phases/`, or `decisions/`.

## Routing: which bucket?

Walk this list top-down and stop at the first match.

1. Locks a design/policy choice → `decisions/`
2. Something shipped, migrated, or was executed → `changes/`
3. Named milestone grouping multiple entries → `phases/`
4. Work not yet done (proposed / active / in-progress / deferred) → `backlog/`
5. One-line summary of a repeatable pipeline run → `logs/worklog.jsonl`

If multiple apply, the earlier bucket wins. When a change also locks a
decision, write both and cross-link via `refs`. Full tree in
`references/routing-rules.md`.

## Frontmatter schema (common to all entry types)

```yaml
---
title: Human-readable title
id: short-slug-id           # matches filename without .md
date: 2026-04-17            # ISO date
type: change | decision | phase | backlog
status: <type-specific>
tags: [showcase, media]
refs:
  resolves: [01-foo]
  implements: [2026-03-05-bar]
superseded_by: []
updated: 2026-04-17
---
```

Per-type status vocabularies and the full ref verb list are in
`references/frontmatter-schema.md`. Validation happens in `build_index.py`.

For ticket-grade backlog work, optional frontmatter fields can also be used:

- `priority`
- `owner`
- `depends_on`
- `artifacts`

## Backlog state machine

```
proposed → active → in-progress → done
         ↘        ↘              ↘ deferred ↘ dropped
```

Use `close_backlog.py` to move an item to `done`; it writes bidirectional
`refs` between the backlog entry and the resolving change. Full transition
table in `references/backlog-states.md`.

## Worklog discipline

`logs/worklog.jsonl` is one JSON object per line, ≤ 2 KB. Required:
`timestamp, run_id, action, status`. Optional summary counters and file
paths — never embed warning arrays inline. Full rules in
`references/worklog-conventions.md`.

`append_worklog.py` enforces the size cap.

## Helper scripts

All scripts are pure stdlib Python. Default to `./changelog/` or pass
`--changelog-dir <path>`.

| Script | Purpose |
|---|---|
| `scripts/new_entry.py` | Create a new entry from a template with correct frontmatter. |
| `scripts/close_backlog.py` | Close a backlog item and link the resolving change bidirectionally. |
| `scripts/build_index.py` | Regenerate `INDEX.md`; validate frontmatter and refs; report issues. |
| `scripts/import_backlog.py` | Import structured markdown ticket backlog into canonical `backlog/` entries. |
| `scripts/archive_done.py` | Move done/dropped items older than N days into `archive/backlog/YYYY/`. |
| `scripts/append_worklog.py` | Append a disciplined one-line JSON entry to the worklog. |

Common usage:

```bash
SCRIPTS="$HOME/.claude/skills/project-changelog/scripts"

python3 "$SCRIPTS/new_entry.py" --type decision --title "Canonical Archive Interfaces"
python3 "$SCRIPTS/new_entry.py" --type backlog --title "Showcase Source Categories Restoration"
python3 "$SCRIPTS/close_backlog.py" 01-showcase-categories --resolved-by showcase-subunits
python3 "$SCRIPTS/build_index.py"
python3 "$SCRIPTS/import_backlog.py" --source reports/prototype-ticket-backlog.md
python3 "$SCRIPTS/archive_done.py" --older-than 90d --dry-run
python3 "$SCRIPTS/append_worklog.py" --action export --status completed \
  --run-id 20260417T104205Z-export --count records=12101 \
  --warning-count 108 --warnings-path derived/run-logs/20260417T104205Z-export/warnings.json
```

## Bootstrapping a new repo

Full steps in `references/bootstrap-guide.md`. The short version, run from
the target repo root:

```bash
SKILL_DIR="$HOME/.claude/skills/project-changelog"
mkdir -p changelog/{changes,decisions,backlog,logs}
cp "$SKILL_DIR/assets/templates/changelog-README.md" changelog/README.md
cp "$SKILL_DIR/assets/templates/changelog-AGENTS.md" changelog/AGENTS.md
touch changelog/logs/worklog.jsonl
```

Add `phases/` only if staged milestones are in use.

## Templates

Entry templates live in `assets/templates/`:

- `change.md` — narrative change record
- `decision.md` — ADR-style decision
- `phase.md` — milestone summary
- `backlog.md` — backlog item with acceptance criteria
- `changelog-README.md` — drops into the target repo's `changelog/README.md`
- `changelog-AGENTS.md` — drops into the target repo's `changelog/AGENTS.md`

`new_entry.py` reads the right template and substitutes `{{TITLE}}`, `{{ID}}`,
and `{{DATE}}` placeholders.

## Integration with git

`changelog/` is the narrative layer alongside git, not derived from it.

- Git records bytes. `changes/` records accomplished work and why.
- Git branches die. `decisions/` endures.
- Commit entries with the code they describe. A change entry and its PR
  commit usually land together.

## Configuration

Optional `changelog/.config.json` for per-project overrides:

```json
{
  "phases_enabled": true,
  "backlog_naming": "numbered",
  "archive_threshold_days": 90,
  "worklog_max_line_bytes": 2048,
  "run_artifact_dir": "derived/run-logs"
}
```

All scripts read this if present and fall back to documented defaults.

## References

- `references/frontmatter-schema.md` — full field and validation reference
- `references/routing-rules.md` — detailed decision tree and tie-breakers
- `references/planning-bridge.md` — mapping from external planning docs into changelog entries
- `references/backlog-states.md` — state machine and transitions
- `references/worklog-conventions.md` — jsonl rules and size cap rationale
- `references/bootstrap-guide.md` — new-repo setup walkthrough
