# Changelog System — Conventions & Update Procedure

> How the two changelog pages work and how to add entries. Companion data doc:
> `app/docs/changelog-commit-map.md` (full commit ↔ patch mapping as of 2026-07-17).

## The two pages

| | Game changelog | Admin changelog |
|---|---|---|
| File | `app/p_changelog.cfm` | `app/Admin/assets/views/changelog.js` |
| URL | `i.cfm?p=changelog` (public shell via `i_p.cfm`) | `#/changelog` in the Admin SPA |
| Linked from | Public footer "Navigate" list (`i_p.cfm`), in-game News/Updates sidebar box (`i_f_800.cfm`) | Sidebar nav item **"What's New?"** (`app/Admin/assets/app.js` navDef) |
| Styling | `public-changelog.css` (`clog-*` classes, built on the `public-modern.css` token set); loaded conditionally in `i_p.cfm` | Panel design system (`admin.css`), rendered with `el()` helpers |
| Audience | Players. Player-visible changes only. | Panel staff. Admin tooling changes at every clearance level. |
| Access | Public | All admin levels (ACL section `changelog`, default level 0) |

## Content boundary rules

1. **Game page** — only what a player can see or feel: features, balance, display,
   bug fixes described by effect. Never implementation detail.
2. **Admin page** — everything under `app/Admin/`, **plus** admin-related changes
   to game files (force-relog hooks, admin logging, lastaccess tracking, logout
   notices for suspended players, etc.). These appear ONLY here, never on the
   game page.
3. **Legacy edmin (`app/edmin/`) is defunct and is NOT documented anywhere** —
   not on the admin page, not on the game page (Amanda, 2026-07-17). Changes to
   it go undocumented.
4. **Neither page** documents pure backend/internal work: security plumbing,
   session/cookie internals, per-user login overrides in `p_login.cfm`,
   CI/deploy/cache work, refactors with no behavior change, debug toggles.
   When backend work has a player-visible effect, describe the effect only
   ("Fixed lag on the Summary page"), not the mechanism.
5. **Undisclosed mechanics stay undisclosed** (Amanda, 2026-07-17): hidden
   formulas, thresholds, and tuning values players are meant to discover on their
   own (e.g. the Gordo cluster-conversion land threshold) are NOT documented on
   the game page, even when a patch changes them. Official posts written by staff
   set the disclosure line — if their own notes reveal a number, keep it; never
   reveal more than they did.
6. **Describe screens by what the player sees, not by file name** — legacy file
   names mislead (e.g. `s_com_market_use.cfm` is the *artifact use* screen, not a
   market page). Say "artifact use screen", not "Market screen".

7. **Use the game's own vocabulary. "Hull" is never a word for a ship**
   (Amanda, 2026-08-26) — a ship's *hull* is its HP, the `ship_type.hull` stat.
   Writing "Marauder hulls" or "premium hulls" to mean the vessels is wrong and
   reads wrong to players. They are **ships**. "Chimaera loses a third of its
   hull" is correct; "twelve hulls get changes" is not.

## Game page entry format

Entries are `<article class="clog-entry">` cards, **newest first**, inside the
`.clog-list` timeline column. Each has an `id` anchor (`patch-107`,
`patch-106-1`, …) that the `.clog-toc` jump index in the header links to.

```html
<article class="clog-entry" id="patch-XXX" aria-labelledby="up-patch-XXX">
    <header class="clog-entry-head">
        <span class="clog-badge clog-badge--improve">Improve</span>
        <span class="clog-version" id="up-patch-XXX">GC Patch &##35;XXX</span>
        <time class="clog-date" datetime="2026-07-16">July 16, 2026</time>
    </header>
    <h2 class="clog-headline">One-line summary</h2>
    <div class="clog-body">
        <p class="clog-subhead">Game Changes</p>
        <ul><li>…</li></ul>
    </div>
</article>
```

- Badge variants map to the Help Center title prefixes: `clog-badge--improve`
  (Improve), `--bugfix` (Bugfix), `--change` (Change), `--base` (the v100 entry).
- Reuse the established subheads: `Display / Styling Fixes`, `Bug Fixes / Changes`,
  `Game Changes`, `Developer's Notes`.
- Callouts go in `<div class="clog-note">`; small stat/formula dumps can use a
  plain `<pre>` inside `.clog-body`.
- **Percentage / per-type value tables use the `clog-mods` component** (see the
  #106 Industry Modifiers block for the reference implementation), per Amanda
  2026-07-18:
  - Group panels (`clog-mods-group` + `clog-mods-title`) organized by the
    game's own type groupings, preserving in-group order; masonry columns pack
    them on wide screens and collapse on mobile.
  - **List every type, including 0% baselines** when the values are new — 0 is
    information, not noise.
  - Color-code values: positives `clog-mods-val--pos` (green), negatives
    `--neg` (red), zero `--zero` (white).
  - Type names are near-white, bold, with a faint blue glow
    (`clog-mods-row span:first-child`) so the important details stand out
    rather than blending into the panel.
- Entries compiled from commits rather than an official Help Center post carry NO
  on-page marker (Amanda, 2026-07-17: "not official" wording just confuses
  players). Which entries are compiled vs official is tracked only in
  `changelog-commit-map.md`.
- CFML gotcha: the page body is one big `<cfoutput>`, so every literal `#` must be
  doubled — patch numbers are written `&##35;` (never `&#35;` or bare `#`).
- CSS gotcha: `bootstrap.min.css` sets `html { font-size: 10px }`, so rem values
  compute against a 10px root. In `public-changelog.css`, font sizes reuse the
  `public-modern.css` tokens (`--text-sm` etc.) so the page always matches the
  other modern public pages; layout WIDTHS are declared in px.

**Adding a new patch**: add the `<article>` at the TOP of the list, add a row to the
jump index `<nav class="clog-toc">`, and add a row to
`changelog-commit-map.md`. If the patch has an official Help Center post, reproduce
its wording (trim downtime scheduling paragraphs).

## Login page "Latest Updates" cards

`p_login.cfm` shows the **last 3** game-changelog entries as cards on the login
hero panel (under the "Return to command." copy), each deep-linking to its
anchor on `i.cfm?p=changelog`.

- Data lives in the `recentChanges` array inside `p_login.cfm` (search for
  "Latest updates"): `{ patchDate, title, anchor, summary }`.
- **Rolling window**: every time a new entry is added to `p_changelog.cfm`,
  prepend it here (slot 1) and delete the third so exactly three remain.
- `patchDate` = the **last** day of the entry's date span (`createDate()`).
- `summary` = one brief line; `title` uses `##` for literal `#` (cfset string).
- Relative date is computed at render (rules from Amanda, 2026-07-18):
  0 days → **Today** in bold lime green; 1 day → "1 day ago"; 2+ → "N days ago".
- Styling: `login-modern.css` "Latest-updates cards" section — near-transparent
  black fill (`rgba(0,0,0,0.1)`) + soft blue glow border so the hero art stays
  visible. Cards are hidden ≤1100px (stacked layout keeps login above the fold).
- Cache-busting is automatic since `s_assetver.cfm` (2026-07-26): the shells
  stamp `?v=#request.assetVer#` from the newest asset mtime, so there is no
  manual version number to bump when editing CSS.

## Quiet entries (minor one-offs — no thread, hand over Discord text)

Not every game entry needs the full patch ceremony. For a **minor one-off**
(e.g. #107.1's "chat display improved") the default is a **quiet entry**
(Amanda, 2026-07-22):

- Add the game changelog entry (its own version, e.g. `patch-107-1`) and the
  login "Latest Updates" card, exactly like a normal entry.
- **No** `patchThreadDefs` entry → no auto-created forum thread, no forum link.
- **No** auto Discord post. Instead, **hand the user ready-to-paste Discord
  text** in chat (no `@everyone`) so they can post it manually if they want.
  Do this only when the change is user-facing enough to be worth announcing —
  a quiet doc/admin-only change needs no Discord text.

A **full patch** (major, or anything worth pinging) still gets a
`patchThreadDefs` def with the thread + Discord fields (see below). When in
doubt about which packaging a game entry should use, ask.

## Forum threads (Help Center "Updates", ca=200)

Every patch entry links to its Help Center thread, resolved at runtime by
`app/s_patch_threads.cfm` (data: `patchThreadDefs`, resolver:
`gc_resolvePatchThread(def, allowCreate)` — 10-min application-scope cache,
cftry-safe, name-based LIKE scan of `he` where `type=200`, repo-era dates only).

- **`p_changelog.cfm`** resolves all defs with `allowCreate = def.createFlag`,
  iterating **oldest-first** — the Help Center defaults to "Date Posted" sort
  (`id DESC` via `session.heorb=1` in `s_he_firsttime.cfm`), so ascending
  creation ids keep the newest patch on top of the forum. Missing createFlag
  threads are auto-created (posted by **System**, `userid=0`, stamped `now()`
  so they read as just-posted) and a "Forum thread" link renders in the entry
  header. `threadDate` is **backfill-only** — never set it on a new patch or the
  forum shows the fresh thread as hours old (Amanda, 2026-07-31).
- **`i_f_800.cfm` News / Updates box** shows the first 3 `boxFlag` defs —
  majors only; sub-patch defs carry `boxFlag=false` (rolling window —
  prepending a new major rolls the third off). Lookup only, never creates; a
  miss links to `i.cfm?p=changelog#<anchor>` instead, which triggers generation.
  Below the patch links the box also carries Full Change Log, Older Change Log
  and (since 2026-08-11) a **Join Our Discord** button — same invite as the
  public header in `i_p.cfm`, styled per theme via `.gc-discord-link` in
  `theme.css`. Leave those three in place when editing the box.
- Pre-#107 patches are NEVER auto-created — if their thread was never posted
  they stay unlinked (Amanda, 2026-07-18), with one-time backfill exceptions
  **#106.1 / #106.2** (shipped without threads; createFlag + backdated
  threadDates). Scan by name, never id (prod ids differ from the local dump).
- **Link style rule** (Amanda, 2026-07-18; retargeted 2026-09-16): forum links
  point at the rebuilt forum, root-relative —
  `/Forum/index.cfm?f=he_detail&hi=<id>&ch=200` — never
  `https://gcc.wrindustries.com/…` (breaks local builds; the old absolute links
  were a mistake) and never with the legacy `&NNNN&` link-randomizer segment.
  Applies everywhere: the change log, the News box, the public nav/footer Forum
  links (the main "Forum" nav links go to `/Forum/index.cfm?p=index`, the
  Forum front page). The legacy `hef.cfm` board still works, but nothing
  should link to it.
  The new forum reads the old `f` / `hi` / `ch` query string itself and runs on
  the same tables, so thread ids carry over unchanged. **Exception — do not
  path-swap these two:** `f=he&ch=0` was the old *Inbox* and is
  `?b=he&p=tickets` (a straight swap reads `ch=0` as a section and 404s), and
  `f=he_new` is `?b=he&p=compose&s=<section>` (no compat mapping exists).
- **Discord**: creating a ca=200 thread (auto or via the admin form) announces
  it through `s_discord_notify.cfm` to the `GCC_DISCORD_WEBHOOK` webhook —
  silent no-op when unset. Absolute links, per-def blurb/ping fields, @everyone
  only on majors. Setup guide: `app/docs/discord-updates-bot.md`.
- **Editing an entry? Purge the LOCAL thread, restart the app container, then
  load the change log twice** so it regenerates with the new text and re-fires
  Discord to the test channel (Amanda, 2026-08-11). Threads are written on
  creation only, so an edit is invisible until the row is recreated — and the
  10-minute resolver cache means deleting the row alone is not enough. Exact
  sequence in the `patch-forum-threads` skill. Local only — live threads are
  never touched by the resolver.
- Full mechanics + per-field rules: `.claude/skills/patch-forum-threads/SKILL.md`.

## Admin page entry format

`app/Admin/assets/views/changelog.js` exports a static `ENTRIES` array, newest
first:

```js
{ date: "2026-07-16", title: "Short title", tag: "panel" | "game-side",
  items: ["bullet", …] }
```

The view renders them as cards with a colored tag badge (`panel` gold,
`game-side` blue). No API call — content is baked into the module (same
no-build-step rule as every other view; the shell's mtime cache-busting picks up
edits automatically).

**Adding an entry**: prepend to `ENTRIES`. Group a day's related commits into one
entry with bullets; commit subjects on the `Admin panel:` commits are usually
already changelog-quality. `game-side` is for admin features living in game files
(force relog, logout notice, lastaccess) — remember these must NOT appear on the
game page.

## Wiring (done 2026-07-17 — don't re-add)

- `i_p.cfm`: conditional `<link href="public-changelog.css?v=…">` for
  `url.p is "changelog"` (auto-versioned via `s_assetver.cfm`), footer link, and the
  amber **"What's new?"** nav link (between Rules and Forum, `.nav-whatsnew`
  styles in `public-modern.css`).
- `p_login.cfm`: "Latest Updates" cards (see section above).
- `s_patch_threads.cfm`: patch↔forum-thread defs + resolver (see Forum threads
  section); consumed by `p_changelog.cfm` and the `i_f_800.cfm` News box.
- `i_f_800.cfm`: "Full Change Log" link in the News/Updates sidebar box.
- `app/Admin/assets/app.js`: route `/changelog`, navDef item "What's New?"
  (sec `changelog`), in the Panel group.
- `app/Admin/api/components/Base.cfc`: `aclRegistry()` gained
  `"changelog": { def: 0, group: "Panel" }` so every clearance level can read it
  (configurable from Access Control like any section).

## Running a generation pass in a fresh session

Paste the prompt from `app/docs/changelog-fireup-prompt.md` — it points a cold
session at every doc + skill and enforces the "Last processed commit" marker in
`changelog-commit-map.md` (scan `<marker>..HEAD`, generate, then bump the
marker to the branch tip as the final commit).

**The process always runs on the `Change-Logs` branch** — the permanent home
for changelog work, no matter what (Amanda, 2026-07-19). Confirm you are on it
and synced with origin before scanning.

**The scan marker is stored twice and both must match**: `changelogLastReviewedCommit`
at the top of `app/p_changelog.cfm` (in-app copy, added 2026-07-31 at Amanda's
request so the value lives in the code) and "Last processed commit" in
`changelog-commit-map.md`. Every pass bumps both to the branch tip.

## Related skills

- `.claude/skills/game-changelog/SKILL.md` — update the game page.
- `.claude/skills/admin-changelog/SKILL.md` — update the admin page.
  (Live in the main checkout's `app/.claude/skills/`; `.claude/` is gitignored.)
