nightly-docs-and-changelog
A configuration of loop-controller. That skill supplies the loop
machinery — primitive selection, the full guardrail stack, state
externalization. This skill supplies the three things specific to "keep an
existing repo's docs+changelog from rotting on a nightly cadence": the
per-night recipe (it diffs since the last run and authors the doc +
changelog updates for that surface itself, following the documentation
conventions docs-agent defines — docs-agent is the orchestrator's
build-phase role and is not user-invocable on a schedule), the
mechanical proof (docs match the changed surface AND every user-relevant
change is recorded, ending in a reviewable PR), and the HITL boundary (it
opens a PR and stops — it never merges docs). Read loop-controller for the
guardrails; they're inherited, not repeated here.
Why disable-model-invocation: this loop edits docs, writes the changelog,
commits, and opens a PR on its own, on a schedule. It is user-scheduled — you
set it up once (or type /nightly-docs-and-changelog), not have Claude
silently start rewriting docs because some code changed during the day.
The 5-part contract
| Part |
This loop |
| trigger |
a nightly cron fire from the scheduled routine (default ~02:00 local), or an explicit /nightly-docs-and-changelog to run one sweep now |
| action |
ONE sweep: read the last-run marker → git diff <marker>..HEAD → for the changed surface, author the doc updates yourself (following docs-agent's conventions) → classify each change user-relevant vs internal → append user-relevant ones to CHANGELOG.md (or write a no-change record) → advance the marker → open ONE PR via git-pr |
| proof |
the docs for the changed surface match the current implementation AND every user-relevant change since the marker is recorded in CHANGELOG.md (or a no-change night is recorded), surfaced as a reviewable docs/changelog diff + PR — default-FAIL: assume docs are stale and the changelog incomplete until verified against the diff since the last run |
| memory |
the last-run marker (a git tag/ref or .claude/docs-sweep-last), CHANGELOG.md, git history, and the open PR thread — all durable across nightly fires and laptop-off gaps |
| stop |
proof met → PR opened (then HITL — stop, never merge) OR no diff since the marker (record a no-change night, no PR) OR iteration/pass cap OR no-progress for 3 nights (same surface still drifting) OR budget cap OR an HITL checkpoint is hit |
The proof: docs current AND changelog complete, default-FAIL
"Current" is not "the docs looked fine last week." It is two conditions
observed together against the diff since the last run: the docs covering the
changed surface match the implementation, and every user-relevant change
since the marker has a CHANGELOG.md entry (a quiet night gets an explicit
no-change record so a skipped night is distinguishable from a missed one).
Assume stale until a fresh sweep proves both — that's the default-FAIL stance,
and it's why every night re-diffs from the marker rather than trusting yesterday.
Name the artifact explicitly: the docs/changelog diff plus the PR the sweep
opens. The PR is the proof a human reviews; an empty diff with a no-change
record is a valid (and common) outcome.
Step 1 — Diff since the last run
The whole sweep is scoped by the last-run marker. Read it, diff marker..HEAD,
and that diff — nothing else — defines tonight's work surface. No marker yet
(first run) means bootstrap from the last release tag or a sensible recent
baseline, not the whole history. The marker mechanism (git tag vs .claude/
file), how to advance it atomically only after the PR opens, and how to recover a
missed night (the marker, not the clock, defines the window — a laptop-off gap is
just a wider diff next run) are in references/scheduling.md.
Step 2 — Classify user-relevant vs internal, then write the updates
Two distinct outputs come off the same diff:
- Docs that must track the code — for the changed surface, author the doc
updates yourself (you hold Write/Edit), following the documentation conventions
docs-agent establishes for this repo. docs-agent is the orchestrator's
build-phase doc role — disable-model-invocation, dispatched only inside a
contract-first build with a lead, contracts, and an ownership map — so a
user-scheduled nightly sweep on an already-shipped repo reuses its
conventions, not its dispatch. This loop adds the scheduling, diff-scoping,
and proof, and matches docs-agent's house style rather than inventing a second
one.
- The changelog — only user-relevant changes get an entry. A new flag,
a behavior change, a fixed user-facing bug, a breaking change: yes. An internal
refactor, a test-only change, a lint fix, a dependency bump with no user-visible
effect: no. The exact user-relevant-vs-internal rubric (with examples) is in
references/scheduling.md.
Step 3 — Open ONE PR, then stop (HITL)
Commit the docs+changelog changes and open one PR via git-pr titled for
the sweep date and summarizing what moved. Then stop. This loop never
auto-merges — docs and changelog wording is a human judgment call, and a PR
opened at 02:00 is meant to be reviewed at 09:00, not merged in the dark. One PR
per night keeps the review unit small; don't batch multiple nights into one PR.
How this differs from its neighbors
- vs
docs-agent — docs-agent is a build-phase role: it writes the docs
for a build in progress, dispatched by the orchestrator alongside the other
role agents. This skill is the recurring nightly sweep that keeps an
already-shipped repo's docs and changelog from rotting over time. It owns the
cadence, the diff-since-last-run window, the changelog discipline, the
default-FAIL proof, and the PR/HITL boundary — and authors the doc updates
itself in docs-agent's house style (docs-agent is orchestrator-only and not
user-invocable, so this loop reuses its conventions, not its dispatch).
Because the nightly sweep runs outside a parallel orchestrated build —
there is never a concurrent docs-agent — editing the docs/ + CHANGELOG.md
that docs-agent owns at build time creates no live ownership conflict; the
two never run at once, and this loop follows the same conventions. Remove this
skill and docs-agent still can't keep your repo current on its own; nothing
schedules it or scopes it to "what changed since last night."
- vs
git-pr — git-pr is the PR mechanics; this loop calls it as its final
step, it doesn't reinvent PR creation.
- vs a bare
/loop — see Step 4: /loop is the wrong primitive for a nightly
cadence, which is the whole reason this skill schedules differently.
Step 4 — Schedule it to survive laptop-off (the routine, NOT /loop)
Per loop-controller Step 1, the primitive choice is load-bearing here and it is
not /loop. /loop is session-scoped, expires after ~3 days, and does not
catch up on missed fires — a laptop closed overnight kills it, which is exactly
the window this loop must run in. So the primitive is a cloud routine /
Desktop scheduled task (via /schedule) that fires on a real cron independent
of any open session and survives the machine being off. The full /loop-vs-
scheduled-routine reasoning lives in loop-controller's
references/primitives.md — don't re-document it. The exact /schedule setup,
the cron line, and what the routine prompt invokes are in
references/scheduling.md.
Guardrails specific to this loop
Inherits the full stack from loop-controller → references/safety.md. The caps
this loop sets:
- Pass cap per night — one sweep = one PR; cap the within-night iterations
(default ~10) so a pathological diff can't burn the whole budget before
morning. Hitting it is a stop-and-open-a-partial-PR-and-flag, not a license to
hand-wave the docs.
- No-progress detection — if the same changed surface keeps drifting
across 3 consecutive nights (docs re-edited but never converge, or the same
file shows up undocumented night after night), stop and surface it. Three nights
on one surface means it needs a human, not a fourth automated pass.
- Budget cap — a nightly routine runs unattended for weeks; enforce a
token/cost ceiling that terminates the sweep (read it from
.claude/profile.yaml when present), not just warns.
- HITL is the exit, not a checkpoint mid-loop. Opening the PR is where this
loop hands off to a human — it never merges, never force-pushes, and never
rewrites a published CHANGELOG entry. Drafting docs + changelog and opening a PR
is reversible and AFK-safe; merging is the human's call.
- Never cheat the proof. Don't advance the marker without opening a PR (that
silently swallows a night's changes), don't drop a user-relevant change to keep
the changelog short, and don't mark docs "current" without checking them against
the diff. A clean night that came from skipping the diff is a finding, not a
win.
Choosing the driver primitive
The scheduled routine (/schedule) is the outer engine — it fires the sweep on
cron and survives laptop-off. Within a night, the sweep pushes to a finish line
(docs current + changelog complete + PR opened), provable from the diff and the
gh PR result, so the inner pass can run under auto mode unattended inside the
reversible boundary (draft + PR only). The loop as a whole has no /goal
finish line across nights — it watches the repo evolve and sweeps each night
until you unschedule it.
Reference files
references/scheduling.md — the per-night algorithm and the doc-writing
conventions; the last-run marker mechanism (git tag vs .claude/docs-sweep-last,
advancing it atomically, recovering a missed night); the user-relevant-vs-
internal changelog rubric with examples; the no-change-night record format; and
the /schedule routine setup (cron line, routine prompt, what survives
laptop-off).
1---2name: nightly-docs-and-changelog3description: Run a nightly sweep that keeps an existing repo's docs and changelog from rotting: diff the code since the last run, bring the docs for the changed surface back in line with the implementation, append every user-relevant change to CHANGELOG.md (or record a no-change night), and open ONE reviewable PR — never auto-merging. Scheduled to survive laptop-off via a cloud routine / Desktop scheduled task, NOT a session-scoped loop. Use when you want docs and the changelog kept current automatically while you sleep, when docs keep drifting behind code, when the changelog is always stale, or when you want a morning PR of doc+changelog updates to review. Trigger on: "keep the docs current", "nightly docs sweep", "update the changelog nightly", "docs keep drifting", "stale changelog", "overnight docs", "stop the docs rotting", "nightly doc PR". Drafts AFK; merges HITL. A configuration of loop-controller.4---56# nightly-docs-and-changelog78> **A configuration of [`loop-controller`].** That skill supplies the loop9> machinery — primitive selection, the full guardrail stack, state10> externalization. This skill supplies the three things specific to "keep an11> existing repo's docs+changelog from rotting on a nightly cadence": the12> **per-night recipe** (it diffs since the last run and authors the doc +13> changelog updates for that surface itself, following the documentation14> conventions [`docs-agent`] defines — docs-agent is the orchestrator's15> build-phase role and is not user-invocable on a schedule), the16> **mechanical proof** (docs match the changed surface AND every user-relevant17> change is recorded, ending in a reviewable PR), and the **HITL boundary** (it18> opens a PR and stops — it never merges docs). Read `loop-controller` for the19> guardrails; they're inherited, not repeated here.20>21> **Why `disable-model-invocation`:** this loop edits docs, writes the changelog,22> commits, and opens a PR on its own, on a schedule. It is user-scheduled — you23> *set it up* once (or type `/nightly-docs-and-changelog`), not have Claude24> silently start rewriting docs because some code changed during the day.2526## The 5-part contract2728| Part | This loop |29|---|---|30| **trigger** | a nightly cron fire from the scheduled routine (default ~02:00 local), or an explicit `/nightly-docs-and-changelog` to run one sweep now |31| **action** | ONE sweep: read the last-run marker → `git diff <marker>..HEAD` → for the changed surface, author the doc updates yourself (following [`docs-agent`]'s conventions) → classify each change user-relevant vs internal → append user-relevant ones to `CHANGELOG.md` (or write a no-change record) → advance the marker → open ONE PR via [`git-pr`] |32| **proof** | the docs for the changed surface match the current implementation **AND** every user-relevant change since the marker is recorded in `CHANGELOG.md` (or a no-change night is recorded), surfaced as a reviewable **docs/changelog diff + PR** — default-FAIL: assume docs are stale and the changelog incomplete until verified against the diff since the last run |33| **memory** | the **last-run marker** (a git tag/ref or `.claude/docs-sweep-last`), `CHANGELOG.md`, git history, and the open PR thread — all durable across nightly fires and laptop-off gaps |34| **stop** | proof met → PR opened (then **HITL** — stop, never merge) **OR** no diff since the marker (record a no-change night, no PR) **OR** iteration/pass cap **OR** no-progress for 3 nights (same surface still drifting) **OR** budget cap **OR** an HITL checkpoint is hit |3536## The proof: docs current AND changelog complete, default-FAIL3738"Current" is not "the docs looked fine last week." It is **two conditions39observed together against the diff since the last run**: the docs covering the40*changed surface* match the implementation, **and** every *user-relevant* change41since the marker has a `CHANGELOG.md` entry (a quiet night gets an explicit42no-change record so a skipped night is distinguishable from a missed one).43Assume **stale** until a fresh sweep proves both — that's the default-FAIL stance,44and it's why every night re-diffs from the marker rather than trusting yesterday.4546Name the artifact explicitly: the **docs/changelog diff plus the PR** the sweep47opens. The PR *is* the proof a human reviews; an empty diff with a no-change48record is a valid (and common) outcome.4950## Step 1 — Diff since the last run5152The whole sweep is scoped by the last-run marker. Read it, diff `marker..HEAD`,53and that diff — nothing else — defines tonight's work surface. No marker yet54(first run) means bootstrap from the last release tag or a sensible recent55baseline, not the whole history. The marker mechanism (git tag vs `.claude/`56file), how to advance it atomically only after the PR opens, and how to recover a57missed night (the marker, not the clock, defines the window — a laptop-off gap is58just a wider diff next run) are in `references/scheduling.md`.5960## Step 2 — Classify user-relevant vs internal, then write the updates6162Two distinct outputs come off the same diff:6364- **Docs that must track the code** — for the changed surface, author the doc65 updates yourself (you hold Write/Edit), following the documentation conventions66 [`docs-agent`] establishes for this repo. docs-agent is the orchestrator's67 build-phase doc role — `disable-model-invocation`, dispatched only inside a68 contract-first build with a lead, contracts, and an ownership map — so a69 user-scheduled nightly sweep on an already-shipped repo reuses its70 *conventions*, not its dispatch. This loop adds the *scheduling, diff-scoping,71 and proof*, and matches docs-agent's house style rather than inventing a second72 one.73- **The changelog** — only **user-relevant** changes get an entry. A new flag,74 a behavior change, a fixed user-facing bug, a breaking change: yes. An internal75 refactor, a test-only change, a lint fix, a dependency bump with no user-visible76 effect: no. The exact user-relevant-vs-internal rubric (with examples) is in77 `references/scheduling.md`.7879## Step 3 — Open ONE PR, then stop (HITL)8081Commit the docs+changelog changes and open **one** PR via [`git-pr`] titled for82the sweep date and summarizing what moved. Then **stop**. This loop **never83auto-merges** — docs and changelog wording is a human judgment call, and a PR84opened at 02:00 is meant to be reviewed at 09:00, not merged in the dark. One PR85per night keeps the review unit small; don't batch multiple nights into one PR.8687## How this differs from its neighbors8889- **vs [`docs-agent`]** — docs-agent is a **build-phase role**: it writes the docs90 *for a build in progress*, dispatched by the orchestrator alongside the other91 role agents. This skill is the **recurring nightly sweep** that keeps an92 *already-shipped* repo's docs and changelog from rotting over time. It owns the93 *cadence, the diff-since-last-run window, the changelog discipline, the94 default-FAIL proof, and the PR/HITL boundary* — and authors the doc updates95 itself in docs-agent's house style (docs-agent is orchestrator-only and not96 user-invocable, so this loop reuses its *conventions*, not its dispatch).97 Because the nightly sweep runs **outside** a parallel orchestrated build —98 there is never a concurrent docs-agent — editing the `docs/` + `CHANGELOG.md`99 that docs-agent owns *at build time* creates no live ownership conflict; the100 two never run at once, and this loop follows the same conventions. Remove this101 skill and docs-agent still can't keep your repo current on its own; nothing102 schedules it or scopes it to "what changed since last night."103- **vs [`git-pr`]** — git-pr is the PR mechanics; this loop calls it as its final104 step, it doesn't reinvent PR creation.105- **vs a bare `/loop`** — see Step 4: `/loop` is the wrong primitive for a nightly106 cadence, which is the whole reason this skill schedules differently.107108## Step 4 — Schedule it to survive laptop-off (the routine, NOT /loop)109110Per `loop-controller` Step 1, the primitive choice is load-bearing here and it is111**not `/loop`**. `/loop` is session-scoped, expires after ~3 days, and does not112catch up on missed fires — a laptop closed overnight kills it, which is exactly113the window this loop must run in. So the primitive is a **cloud routine /114Desktop scheduled task** (via `/schedule`) that fires on a real cron independent115of any open session and survives the machine being off. The full `/loop`-vs-116scheduled-routine reasoning lives in `loop-controller`'s117`references/primitives.md` — don't re-document it. The exact `/schedule` setup,118the cron line, and what the routine prompt invokes are in119`references/scheduling.md`.120121## Guardrails specific to this loop122123Inherits the full stack from `loop-controller` → `references/safety.md`. The caps124this loop sets:125126- **Pass cap per night** — one sweep = one PR; cap the within-night iterations127 (default ~10) so a pathological diff can't burn the whole budget before128 morning. Hitting it is a *stop-and-open-a-partial-PR-and-flag*, not a license to129 hand-wave the docs.130- **No-progress detection** — if the **same changed surface** keeps drifting131 across **3 consecutive nights** (docs re-edited but never converge, or the same132 file shows up undocumented night after night), stop and surface it. Three nights133 on one surface means it needs a human, not a fourth automated pass.134- **Budget cap** — a nightly routine runs unattended for weeks; enforce a135 token/cost ceiling that *terminates* the sweep (read it from136 `.claude/profile.yaml` when present), not just warns.137- **HITL is the exit, not a checkpoint mid-loop.** Opening the PR is where this138 loop hands off to a human — it **never merges**, never force-pushes, and never139 rewrites a published CHANGELOG entry. Drafting docs + changelog and opening a PR140 is reversible and AFK-safe; merging is the human's call.141- **Never cheat the proof.** Don't advance the marker without opening a PR (that142 silently swallows a night's changes), don't drop a user-relevant change to keep143 the changelog short, and don't mark docs "current" without checking them against144 the diff. A clean night that came from skipping the diff is a *finding*, not a145 win.146147## Choosing the driver primitive148149The scheduled routine (`/schedule`) is the outer engine — it fires the sweep on150cron and survives laptop-off. Within a night, the sweep pushes to a finish line151(docs current + changelog complete + PR opened), provable from the diff and the152`gh` PR result, so the inner pass can run under auto mode unattended **inside the153reversible boundary** (draft + PR only). The loop as a whole has no `/goal`154finish line across nights — it watches the repo evolve and sweeps each night155until you unschedule it.156157## Reference files158159- `references/scheduling.md` — the per-night algorithm and the doc-writing160 conventions; the last-run marker mechanism (git tag vs `.claude/docs-sweep-last`,161 advancing it atomically, recovering a missed night); the user-relevant-vs-162 internal changelog rubric with examples; the no-change-night record format; and163 the `/schedule` routine setup (cron line, routine prompt, what survives164 laptop-off).165166[`loop-controller`]: ../loop-controller/SKILL.md167[`docs-agent`]: ../../roles/docs-agent/SKILL.md168[`git-pr`]: ../../git/git-pr/SKILL.md