| name | cms-curator |
| description | Run one incremental pass of the IT-Journey content-improvement loop. Use when asked to curate/improve content, run the daily CMS loop, work the .cms worklist, fix frontmatter/SEO/freshness across pages, or group content improvements into a PR. |
You are the content curator for IT-Journey. Each invocation is one bounded, incremental pass over the content tree that ends in either an auto-mergeable mechanical commit or a single reviewed PR โ never an open-ended rewrite.
The CMS engine has already mapped every file. Your job is to act on its worklist, validate, and package the result. Reuse the repo's existing fixers and validators; do not reinvent them.
All paths are relative to the repo root. This skill runs identically when driven locally (/loop) or in CI (.github/workflows/cms-daily-loop.yml).
0. Read the policy first
Before editing anything, read these so your changes match the house style and pass CI on the first try:
.cms/config.yml โ scope, lanes, safety globs, batch sizes.
.github/copilot-instructions.md โ the authoritative frontmatter constraints
table (title 30โ60, description 120โ160, ISO-8601 dates, YAML-list tags/categories) and the numbered recurring pitfalls.
.github/instructions/*.instructions.md โ the per-collection rules
(posts, quest, index-hub, brand, etc.). Load the one matching the files you touch.
_data/brand/ โ the brand store (voice, values, style, section guides), when a
worklist item is a brand_drift:* issue on a post.
AGENTS.md โ quest permalink regex and essential commands.
1. Orient โ refresh the index and read the worklist
make cms-all
make cms-status
Open today's worklist: .cms/worklists/<YYYY-MM-DD>.md. It has two lanes:
- Lane A โ Mechanical: deterministic fixes. Eligible for auto-merge.
- Lane B โ Substantive: needs authoring/judgment. Goes to a reviewed PR.
If the worklist is empty in both lanes, report "no work" and stop.
2. Hard safety rules (never violate)
- Never edit files flagged
read_only (vendored โ any upstream content
carrying source_repo/source_url frontmatter). You may report issues, not rewrite.
- Before editing ANY file not already on the worklist (e.g. a link target you
chased into another file), look it up in
.cms/index/content-index.json and abort the edit if its read_only or generated flag is true. Do not rely on the example globs above โ the index flag is authoritative.
- Never hand-edit
generated files (_data/quests/*.yml, navigation,
_data/content_statistics.yml). If quest frontmatter changes, regenerate with make quest-data and commit the result.
- Never commit to
main. Always work on a branch (Section 5).
- Never touch prose/body of structural files (READMEs, quest templates).
- Stay inside
pages/ for content. scripts/, TODO/, .cms/, docs/ are
tooling โ don't treat them as site content.
- Respect
taxonomyLimit: quests cap at 10 tags.
3. Lane A โ mechanical pass (auto-merge candidate)
These are deterministic and handled by the existing normalizer. Do not edit these by hand file-by-file:
make content-normalize-apply
Then handle the two safe edits the normalizer doesn't cover, only on the files the worklist lists:
null_permalink โ remove the permalink: key entirely (Jekyll falls back to
the collection default). Do not invent a permalink.
- (Leave
filename_has_spaces for Lane B โ renames require inbound-link updates.)
Re-validate, and regenerate quest data if any quest frontmatter changed:
make content-validate
make quest-data
Lane A changes are one commit, message: chore(content): normalize frontmatter [cms-mechanical]
4. Lane B โ substantive pass (reviewed PR)
Take the top N items only (N = loop.batch_size from config, default 25). Do not exceed it โ small PRs get reviewed; giant ones rot. For each item, apply the smallest change that resolves the listed issues:
- Missing
description โ write a 120โ155 char, single-sentence, benefit-led
description. No trailing period.
title_too_long โ tighten to โค 60 chars, keep the keyword.
thin_content โ only expand if you can add genuine value from the file's
own topic; otherwise leave it and note it. Never pad with filler.
- Missing
categories/tags โ choose from the existing taxonomy
(.frontmatter/database/taxonomyDb.json); do not invent near-duplicates.
broken_links โ fix the target if obvious; otherwise remove or comment the
link. Cross-check TODO/links/data/link-report.json.
filename_has_spaces โ git mv to kebab-case, then grep the repo for
inbound links to the old name and update them.
- Quest body work / new content โ follow
.github/instructions/quest.instructions.md
and the .frontmatter/templates/quests.md scaffold.
brand_drift:* (posts) โ load the brand-voice skill, resolve the section
guide, and apply the smallest term/voice fix (drop a banned word, fix a spelling, add a missing Verify step). Never rewrite a post wholesale; brand drift is advisory.
Group Lane B commits by collection (one commit per collection touched), e.g. content(posts): improve frontmatter + descriptions [cms].
5. Validate โ then hand the working tree to the caller
You do not own git. Do not create branches, commit, push, or open PRs. Your job is to leave a validated, dirty working tree; the caller packages it:
- Locally (
/loop): the human (or /loop) commits and opens the PR.
- CI: the workflow (
cms-daily-loop.yml) creates the branch, commits, and
opens the PR for you. (It also blocks git/gh tools, so don't try.)
Before handing off, run the same gates CI runs and fix anything red:
make prose-oneline-apply
make quest-data
make cms-all
make build-ci
make content-validate
make quest-audit
One paragraph per line. IT-Journey markdown keeps each prose paragraph on a single unwrapped line; the markdown-oneline CI gate fails any PR with soft-wrapped prose. Write unwrapped, and run make prose-oneline-apply before handoff โ it only joins prose, leaving code/tables/Liquid/front-matter byte-for-byte identical.
If a gate fails, fix it before finishing. Never leave a red tree for the caller.
The caller decides the lane outcome (mechanical โ optional hands-off merge after the gate; substantive โ reviewed PR). Make sure the regenerated .cms/index/summary.json, .cms/reports/<date>.md, and .cms/worklists/<date>.md are part of the dirty tree so the health delta ships with the change.
6. Close the loop (self-improvement)
If you hit a recurring failure (a fix that broke CI, a pitfall the worklist missed), record it: extend .cms/config.yml (e.g. reclassify an issue lane) or the relevant .github/instructions/*.md, and mention it in the PR body. This is the repo's standing "close the loop" practice (/retrospective).
7. Report
End every run with a short summary: files touched per lane, gates passed, and the PR/commit URL. If you skipped items (batch cap, ambiguous fixes, read-only), say so explicitly โ never imply full coverage when the run was bounded.
Local vs CI
- Local (
/loop): you may iterate interactively; still obey the batch cap so
each loop iteration stays reviewable. The human//loop handles git.
- CI: non-interactive, substantive (Lane B) only โ the mechanical lane runs
deterministically without you. The workflow owns the branch/commit/PR and blocks
git/gh. Bounded by --max-turns. Never attempt interactive prompts.