Skip to content
FunCoding

update-changelog

Update public/documentation/changelog.html with commits for a new Chamilo release. Applies the filtering and formatting rules from tests/scripts/packaging/gitlog.php, classifies commits into changelog categories, and reports a line-count summary per category. Use when the user wants to update the changelog, prepare a release entry, add a version section, or runs /update-changelog.

Star
1,011
License
GPL-3.0
Last commit
Oct 13, 2026

.claude/skills/update-changelog/SKILL.md

View on GitHub

Update Changelog

Update public/documentation/changelog.html with the commits for a new Chamilo release. The changelog is progressive: a version section may be created and updated multiple times before the release tag is actually set. Handle both cases.


Step 1: Determine the target version

Ask the user: "Which version number are we adding or updating? (e.g. 3.0.2)"

Once you have the version, check whether a section for it already exists in public/documentation/changelog.html by looking for <a id="X.Y.Z">.


Step 2: Determine the commit range

The range is "commits not yet listed in the changelog for this version". How to find it depends on whether the section already exists:

If the section already exists (progressive update):

  • Extract every commit SHA already present in that version's <li> entries across every category in the section, not just the first one. Category <h3> blocks are each sorted newest-first independently, so the top entry of whichever section happens to appear first in the file (usually "Security fixes") is not necessarily the most-recently-added commit overall — a later section (e.g. "Changed") can easily have a newer top entry. Picking the wrong "most recent" boundary silently re-processes commits that are already listed elsewhere in the section.
  • Run git log --pretty=format:'%H' HEAD and skip every SHA that appears (by its 8-char or 12-char prefix) in the existing section.
  • The range to process is everything since the newest already-listed commit. Concretely: find the most recent commit in the section, then use git log --pretty=... <that-SHA>..HEAD as the range.
  • If the section exists but has no <li> entries yet, fall back to the previous-release tag as the start point (see below).
  • Don't trust a single computed "boundary commit" blindly. Before writing anything, take the final candidate list of commits you intend to add and grep each one's SHA directly against the entire existing version section (all categories, not just the one you think is the boundary). Any hit means that commit is already listed and must be dropped from the new batch — this simple per-commit membership check is cheap and catches boundary-detection mistakes before they turn into duplicate <li> entries.
  • If the user hands you a pre-curated commit list directly (e.g. a file with already-formatted <li> lines they generated themselves), treat it as the authoritative "what to add" source, but still: (a) run the per-commit membership check above — a curated list can still overlap with what's already in the file; (b) de-duplicate exact-repeated messages inside the list itself (different SHAs with identical text happen after rebases or duplicate merges — keep only the first occurrence); (c) still apply the full Step 3 / Step 5 cleanup below, since a pre-generated list is normally raw gitlog.php output and needs the same trimming.

If the section does not yet exist:

  • Identify the most recent existing release tag on this branch via git tag --merged HEAD --sort=-version:refname | grep -E '^v[0-9]'.
  • The most recent tag is the start of the range: git log v3.0.X..HEAD.
  • If the tag name is ambiguous (e.g. v2.0.2 vs 2.0.2), ask the user to confirm before running the log command.

⚠️ RTK / token-proxy hooks can silently corrupt git log output

If this environment has an rtk (or similar token-savings) hook rewriting git commands transparently, do not trust its output for changelog work. It has been observed to both truncate long lines (e.g. cutting a subject at ~80 chars and appending ..., even when output is redirected to a file, not just displayed) and to silently drop commits from the result set — in one session, git log <sha>..HEAD | wc -l returned 50 through the hook and 168 through an unfiltered call for the exact same range. A truncated message is merely annoying (it gets caught during cleanup); a silently dropped commit means a real change never makes it into the changelog at all, with nothing to signal the omission.

Mitigation: for every git log call used to build the commit range or inspect messages, use rtk proxy git log ... (or whatever the local token-proxy's raw/debug passthrough is — check the tool's own docs, e.g. a CLAUDE.md/RTK.md reference) instead of the plain git log ... the hook would intercept. If you're unsure whether such a hook is active, run the same git log count both ways once at the start of the session and compare — mismatched counts confirm the hook is interfering.


Step 3: Apply the gitlog.php filtering rules

Read tests/scripts/packaging/gitlog.php to confirm the rules are unchanged before processing. Then apply them to each commit's subject line in the script's own order: 3b, the 3a text skips, 3c, the 3a Minor skip, 3d, 3e.

Prefer running the real script over hand-simulating it. gitlog.php requires a php-git/src/Git.php dependency in the same directory (tests/scripts/packaging/php-git/) — it may be present in the working tree even if untracked by git (check with ls). When it's available, run it directly instead of manually replaying steps 3a-3e in your head across dozens of commits — that manual replay is error-prone (mis-tracking which known mistake applies, mis-computing issue-reference stripping, etc.):

cd tests/scripts/packaging && php gitlog.php <boundary-commit-sha>

This stops at <boundary-commit-sha> (printing it too — drop that last line since it's already in the changelog) and echoes real <li> HTML using the exact same logic this skill documents, eliminating transcription errors.

Caveat: the script's output can still include full commit-body text appended after the subject (e.g. See advisory GHSA-xxxx-xxxx-xxxx, trailing Author: ... <email> trailers, or multi-sentence rationale paragraphs). This is not part of the clean subject line — strip it during Step 5 cleanup the same as a typo.

3a. Hard-skip entire commits

Skip commits whose (3b-normalised) subject contains Update language terms anywhere, or starts with any of:

  • Update language vars
  • Update lang vars
  • Merge (also lowercase merge)
  • Scrutinizer Auto-Fixes
  • Update changelog
  • Fix PHP Warning

After 3c, also skip any subject starting with Minor (case-insensitive, first 5 characters). This includes [Minor] subjects that 3c renamed to Minor:.

Note: gitlog.php defines a $skipTechnicalPrefixes array (QA, Internal, Display, Fix, Refactor, Migration, UI, …) but never uses it — it is dead code. Do NOT filter on those prefixes; the existing changelog includes Internal:, Display:, and Fix: entries.

3b. Normalise separator punctuation (applied in this exact order):

  1. ^(\w+) - (.*) → $1: $2
  2. ^(\w+ \w+) - (.*) → $1: $2
  3. ^(\w+) : (.*) → $1: $2

3c. Rename known prefix mistakes via sanitizeCategory():

Apply the full substitution table below (case-insensitive prefix match, replace prefix only, keep the rest of the message):

Wrong prefixCorrect prefix
Quiz / ExercisesExercise
LP / Learning Paths / LearningPath / LearnpathsLearnpath
DocumentsDocument
AnnouncementsAnnouncement
RemedialCoursePlugin: RemedialCourse
Groups / [usergroup]Group
Survey report / Survey list exportSurvey
Learnpath reportLearnpath
TopLink / TopLinksPlugin: TopLinks
SessionsSession
CasAuthentication: CAS
Webservices / WebService / Web servicesWebservice
BBBPlugin: BigBlueButton
My Progress / My Progres / Reports / ReportingTracking
CoursesDisplay
[LP]Learnpath
Student follow pageTracking: Student follow-up
RESTWebservice: REST
Import CSV / ImportCSV / Import_csv.phpAdmin: CSV import
[Minor]Minor:
[admin]Admin
MySpaceTracking
Career diagram / CareersCareer
UsersUser
Style:Display:
Course AnnouncementAnnouncement
Testing / CIQA
BlogsBlog
Gradebook evalGradebook
Survey testQA: Survey
EditorWYSIWYG
GlobalInternal
Extra fieldExtra Fields
SettingsAdmin
ChangelogDocumentation
Session importAdmin: Session import
XAPIxAPI
CourseCopy / Course CopyMaintenance
Course BackupMaintenance
SSOAuthentication: Single Sign On
SkillsSkill
MessagesMessage
Security fixes -Security:
Work / Works / Pending worksAssignment
Improve codeInternal: Improve code
Thematic / Thematic advanceCourse Progress
AgendaCalendar
Course importMaintenance
Student publication / Student publicationsAssignment

Known false positive: REST / CI and other short prefixes. sanitizeCategory() matches these terms as a plain string prefix (strncasecmp), with no word-boundary check. REST (4 chars) matches the first 4 letters of any word starting "Rest…", so a commit titled Restore the language switch... gets mangled into Webservice: RESTore the language switch.... This has already happened repeatedly and gone uncorrected in the existing changelog (search for RESTore — multiple pre-existing entries). When you hit this on a new commit you're adding, fix it for that entry (strip the bogus Webservice: REST prefix, restore the plain subject) and note the correction in the Step 8 report — but don't retroactively edit old, already-published entries with the same bug (surgical changes only; flag it to the user instead as a possible upstream fix to sanitizeCategory(), e.g. requiring a word boundary or capitalization check after the matched term). The same risk applies to any other short 2-4 letter term in the table above (e.g. CI, Cas, LP) — double check any prefix match shorter than ~5 characters against what it actually matched before trusting it.

3d. Strip issue references from the message text:

If the subject matches ((BT)?#\d{2,5}), remove the first match and anything after these patterns: see ISSUE, - ref, -refs, - refs, ISSUE.

3e. Finalise:

Apply ucfirst() (capitalise the first character of the message).


Step 4: Classify commits into categories

Use the normalised message prefix (the part before the first :) to assign each commit to a category. Apply in priority order — stop at the first match:

CategoryMessage prefix matches (case-insensitive)
Security fixesSecurity
FixedFix, Bug, Install, Language, Hotfix
AddedAdd, New, Enable, Feature, Implement, Create, Include, Introduce
RemovedRemove, Delete, Drop, Deprecate
ChangedEverything else (Internal, Display, Refactor, Plugin, Auth, Learnpath, Exercise, …)

4a. "Add" verb fallback (Changed → Added only)

A commit whose own prefix doesn't match Security fixes / Fixed / Added / Removed (so it would otherwise land in the Changed catch-all) should be reclassified into Added if the first word after the prefix is add or Add. To find that word: strip the first Category: prefix, then keep stripping any further Subcategory: prefixes (e.g. Plugin: BuyCourses: Add country info in sales detail — strip Plugin:, then BuyCourses:, leaving Add country info...). If the resulting first word is add/Add, use Added instead of Changed.

This fallback only ever moves a commit out of Changed — it never overrides Security fixes, Fixed, or Removed, even when their message also starts with "Add" right after the colon:

  • Security: Add native FIM feature → stays Security fixes
  • Language: Add missing translation for Back to account → stays Fixed (Language already matches the Fixed prefix list)
  • Learnpath: Add support for post-unload event Chrome... → Changed → Added
  • User: Add invitation to course/session... → Changed → Added
  • Plugin: CStudio: Add AI-assisted content generation → Changed → Added (nested prefix stripped: Plugin: then CStudio:)

Omit a category section entirely if it has zero entries.


Step 5: Build each <li> line

Format (newest-first, matching existing changelog style):

<li>[YYYY-MM-DD] (<a href="https://github.com/chamilo/chamilo-lms/commit/SHA12">SHA8</a>) Message</li>

When a BT# or GH# issue link exists, add it between the commit link and the closing ):

<li>[YYYY-MM-DD] (<a href="https://github.com/chamilo/chamilo-lms/commit/SHA12">SHA8</a> - <a href="https://task.beeznest.com/issues/NUM">BT#NUM</a>) Message</li>

Where SHA12 = first 12 chars of the full SHA, SHA8 = first 8 chars.

Correct any obvious typos in commit messages when formatting for publication (the HTML is user-facing; the git history is not changed). Note corrections in the final report.

Additional cleanup learned in practice:

  • If the raw message ends with body-only content that leaked into the subject (advisory IDs like See advisory GHSA-xxxx-xxxx-xxxx, Author: Name <email> trailers, or multi-sentence rationale paragraphs), strip all of it — keep only the clean subject clause. This is common when running the real gitlog.php script (see Step 3 note) against squash-merged commits whose body carries PR metadata.
  • If an issue number appears in the message text in parentheses (e.g. ...reorder course tools (#8868)) and a separate BT#/GH# link is also being added per this step, drop the redundant (#NNNN) from the visible text — showing the same issue number twice (once inline, once as a link) is noise. gitlog.php's own ref-stripping regexes don't catch this case (they only strip text following specific separator patterns like - refs, not a bare parenthetical), so this needs a manual pass.

Step 6: Ask for the release codename

Ask: "What codename should I use for version X.Y.Z? (Leave blank to insert a placeholder)"

  • If provided, use it: Chamilo X.Y.Z - Codename, YYYY-MM-DD
  • If blank, use: Chamilo X.Y.Z - [TBD], YYYY-MM-DD
  • Use today's date as the release date unless the user specifies another.
  • Historical context: codenames have been small cities/towns/villages in the Somerset/Cheddar region of England (Cadbury, Blackford, Little Weston, Axbridge).

Step 7: Write or update the changelog

If the section does not yet exist: insert it above the previous release's <a id="..."> anchor. Also add an entry to the <div class="toc"> <ul> at the top, above the previous release's TOC line.

If the section already exists (progressive update): append the new <li> entries into the correct category <ul>. If a commit's category <h3> block does not exist yet in that section, create it in the correct order: Security fixes → Added → Changed → Fixed → Removed → Known issues.

The release summary <p> should be written or updated to reflect the nature of the commits in this update. If the section is new, write a brief paragraph. If updating an existing section, revise the summary if the new commits materially change its character.


Step 8: Report

Print a summary table:

Changelog updated for Chamilo X.Y.Z - Codename

Category         | Lines added
-----------------|------------
Security fixes   |  N
Added            |  N
Changed          |  N
Fixed            |  N
Removed          |  N
Total            |  N

Filtered out (not included):
  - M merge commits
  - K minor/noise commits

Typo corrections applied:
  - <SHA8>: "<original>" → "<corrected>"  (or "none")

Guidelines

  • Always read tests/scripts/packaging/gitlog.php at the start to check for rule changes before applying the filter logic above.
  • The changelog is the user-facing record. Prefer clarity over verbatim faithfulness to commit messages.
  • Never invent commits. Only include SHAs that appear in the git log output.
  • Do not modify any section other than the target version.
  • If the user provides a specific date, use that; otherwise use today's date.

Similar Skills

using-git-worktrees

OBobra/superpowers298k2,631 new stars in the last 7 days

verification-before-completion

OBobra/superpowers298k2,631 new stars in the last 7 days

finishing-a-development-branch

OBobra/superpowers298k2,631 new stars in the last 7 days

wizard

MAmattpocock/skills288k10,178 new stars in the last 7 days

git-guardrails-claude-code

MAmattpocock/skills288k10,178 new stars in the last 7 days

triage

MAmattpocock/skills288k10,178 new stars in the last 7 days