---
name: cs2-game-update
description: 'Sweeps this repo''s Cities: Skylines II prose after a game update. Use when the game has updated, when the decompile holds a newer version than the references claim, or when a baseline line lags the installed game.'
---

# Sweeping the repo for a game update

The game moved and the prose did not. This skill diffs the two decompile commits, re-derives every claim the diff reaches, and ends with every `Verified against game version` line on the new version, each one earned.

It works from the decompile as it stands: `cs2-modding-setup` owns the decompile, its per-version commits, its tool pin and the readable UI bundle copy, and its refresh branch runs first. Where `~/.cs2-modding/setup.md` records a game version newer than the decompile's HEAD, say so and stop there.

Where the decompile diff touches `src/cohtml.Net/Properties/AssemblyInfo.cs`, the embedded engine moved: say so, and name `gameface-update` as the sweep for the coherent-gameface plugin. The `docs/research/` lines recording the Cohtml version a live read observed stay this sweep's own.

## 1. Fix the two versions

- **New**: `VersionInternal` in `src/Game/Properties/AssemblyInfo.cs` at the decompile root's HEAD.
- **Old**: the version every baseline carrier states, and there are three — the shipped skills' `Verified against game version` lines, `docs/research/`'s `**Baseline.**` paragraphs, and the `VersionInternal("<version> …")` strings those paragraphs quote. `grep -rho 'Verified against game version [0-9].*' plugins | sort -u` reaches only the first; `grep -rn '^\*\*Baseline\.\*\*' docs/research` and `grep -rn 'VersionInternal(' docs plugins` reach the other two. All three must name one version; two means a previous sweep never closed, and resuming it comes first.

Equal versions mean nothing to sweep. Say so and stop.

## 2. Build or resume the plan

The plan lives at `.scratch/game-update-<new>/plan.md`, one checkbox per step. It exists so a compaction loses nothing: the checked boxes are where the sweep is, and the skill invoked over an existing plan resumes at the first unchecked step.

**One invocation does one thing: build the plan, or work one step of it.** Then report the step in two lines and end the turn, so the user can compact before invoking the skill for the next step.
Within the step, track its actions in the harness's task list — section 3's seven for a cluster — while the plan's checkboxes stay the record a compaction keeps.

Build a fresh plan from the decompile root's diff between HEAD and the newest commit still decompiled from the old version — the old-version commit itself, or the ilspycmd bump that re-decompiled it afterwards — in six parts.
Where decompiler commits landed after the new version's own commit, read claims from the diff up to that version commit instead: a re-decompile touches thousands of files and changes no game fact.

1. **The cite step.** Every line cite `docs/research/`, `docs/solutions/`, `docs/SOURCES.md`, `docs/ROADMAP.md` and the benchmark's `bench/questions/` hold into the decompile goes stale wherever the diff moved a line, whether or not the claim beside it changed, so re-pointing them is one step of its own rather than part of each cluster. On a clean working tree, record this repo's HEAD commit in the plan as the sweep's start, then run `mise research:cite-audit --decompile <root> --old <verified> --since <start> --apply`, saving its report into the plan folder — or, resuming a step whose report is already there, work from that report and skip the run, since a second `--apply` lists nothing the first one moved. `<verified>` is the decompile commit the cites were last checked against, which the previous sweep's baseline bump names on a `Decompile:` line of its commit message (`git log -1 --grep '^Decompile: '`); where no bump carries one, it is the old-version commit, never a later re-decompile of it, whose shifted lines would read as the cites' own. A re-decompile committed between sweeps gets this same step on its own, planned in `.scratch/cite-repoint-<short commit>/`, with `--new` at that commit and a `Decompile:` line on the re-point's commit (`--allow-empty` where nothing moved), before research cites anything from it, so that no cite was read at a commit other than the latest `Decompile:` one. The script checks each cite by text identity — the old line's text against the new line's — re-points every cite whose line moved intact, and lists the rest. A rerun no longer lists what `--apply` moved, so open every line of the saved report, the `MOVED` ones included: those are bare `:<line>` cites moved by the file the prose named last, whose file only a reader confirms, and weakly anchored moves (a short or renamed line, a range end placed by its partner, range ends that moved apart), whose line only a reader confirms; record each one's verdict under the step, keyed by doc, cite and target line. The step is done when every listed line carries a verdict; check its box and end the turn. The report is a worklist rather than a proof — the script's header states what it cannot see, a bare cite the parser gives the wrong file among them — so the review gate at close still reads the cites the sweep's edits touched.
2. **Diff clusters.** Group the changed files by the type or feature they belong to. Each cluster is one step, and carries every claim that cites it: a `VOLATILE:` marker across `plugins/cs2-modding/skills/` naming a changed file, or a namespace a changed file sits in; a cite into a changed file from `docs/research/`, `docs/solutions/`, `docs/SOURCES.md`, `docs/ROADMAP.md` or a benchmark answer key under `bench/questions/`; and any `UI bundle` cluster matched against the `cs2-modding-ui` markers by module path and export name, since the bundle copy diffs like the rest — its step also re-checks every verbatim quote of bundle code beside a cite, since re-minifying renames the identifiers the cite step re-points past. A marker naming neither a path nor a namespace cannot be matched and cannot be closed: propose dropping it in the closing message rather than guessing. Order the cluster steps by the cites and markers each carries, never by diff size — cluster size predicts reading cost, not claim risk, and a release's largest diff routinely holds the fewest claims while a two-line diff elsewhere in it moves one. The bundle cluster opens on a registration index of both revisions, `path -> {line, exports}`, set-diffed: no path or export removed means no path or export claim to re-derive, and budget for prettier's wrapped `Q.add(\n  "path",\n  {` form, which a naive `Q.add("...` regex drops about half of. That index is also the only thing that catches a NEW registration slotted into an existing render site, which moves no cite and rots no identifier, so read the render site of every added module. Verify a bundle cite by opening the line and never by matching text — re-minification renames every identifier, so a text match lands thousands of lines away — with the previous revision open beside it (`git show <prev>:src-ui/source.js`), which turns "does this cite still look plausible" into "is the span identical", and which is what shows that these files conventionally cite an IIFE-wrapped enum one line BEFORE the construct, a shape that reads as off-by-one and whose correction is the regression.
3. **The census step.** A count, percentage or file length measured over a span — a directory, an assembly, the whole tree — cites no single file, so no cluster reaches it and no untouched file clears it. Find every one in `docs/research/` and `plugins/cs2-modding/skills/` and re-run it at HEAD, here and once: a cluster step meets a tree-wide census with a partial view, so a census left to the clusters gets re-litigated once per cluster and settled differently each time. Where the prose leaves the method unstated, calibrate first — run the command in a `git worktree` of the old commit and reproduce the stated figure, and its sub-counts too where the sentence gives any, since a method that lands the headline and misses them is the wrong method — then state the counting rule in the prose beside the figure, because three passes disagreed on one count that had never recorded how it was taken. Record each figure's verdict in the plan beside it, calibrated or not, since that judgement otherwise dies with the session. **A figure no command reproduces is not thereby wrong.** Grep its value across the tree before touching it: another file may quote it as the wrong original of a documented correction, and moving it deletes the correction's evidence. Correct what calibrated, leave the rest with its reason. Then grep the value of every figure this step did move, because a count has siblings — a frozen seed survey carrying the same measurement, or a sentence in another file asserting agreement with it. The tell is a verb of agreement next to a cite (reproduced, matches, confirms, agrees with): that sentence is a claim about the other file rather than a copy of it, and it goes false the moment the number moves. The step is done when every figure carries a verdict.
4. **The toolchain step.** The build targets under `CSII_TOOLPATH` and the UI mod scaffold template ship with the game but sit outside the decompile, so nothing diffs them. Re-read them directly against `plugins/cs2-modding/skills/cs2-mod-project/`.
5. **The live-claim step.** A claim the decompile cannot settle — a figure read off a running game, a curve sampled from live asset data, an experiment's outcome — is cleared by no diff, and balance data is exactly what a patch moves. Collect them as the clusters meet them and close them together here: read what a read of the running game settles, and leave stamped at the old version, with its reason in the closing message, whatever needs a launch, a new game, a no-mods load or a purpose-built mod. An inline qualifier a cluster could not close routes here rather than staying in the qualifier sweep, which cannot close it either.
6. **The closing step**, from Close below.

`UNVERIFIED:` claims stay as they are: the token already says the claim was never established, and no diff settles one.

## 3. Work a step

For one cluster:

1. Open the diff hunks and the new source behind each claim.
2. Re-derive the claim from the new source alone. A cited file the diff left untouched clears its claim without a re-read; a file it touched does not, even where every cited line survived, since a hunk elsewhere in the file can change what those lines mean. A namespace-matched claim whose files the diff left untouched clears the same way. Where the cluster is a dependency bump — a vendored engine or SDK arriving at a new version — establish additivity before reading anything: a set difference of the assemblies' file lists, plus a public-member diff of each type the prose cites. Additions cannot break a standing claim, so an additive bump clears its cluster in two commands where reading it costs a day.
3. Edit shipped prose in place. A quoted source line is the correction; a paraphrase or an added rationale is what the next review indicts.
4. Edit `docs/research/` in place too: research evolves with its times, so a contradicted finding is rewritten and a finding whose subject the update removed is deleted. Git holds the history, and neither `## Dead ends` nor a dated note carries retired facts.
5. Re-point every line cite into a repo file the step changed, from an opened line and never from arithmetic — [an amendment that shifted every cite below it](../../../docs/solutions/an-amendment-that-shifted-every-cite-below-it.md) states why. A cite you add follows `docs/research/README.md`'s rule for inserted cites. Open the END of a range as well as its start: a range end landing on a blank line or on the class's closing brace survives every pass but this one. Where the sentence around a cite quotes a source line verbatim, assert the quote sits inside the range it points at — that sentence carries its own test, and a re-point is the step that breaks it.
6. Dispatch a verifier on opus with the changed prose and the opened source lines, pointed first at every re-point that CHANGED A LINE NUMBER, each quoting the line it opened. A re-point that only named the file on a bare cite is reliable; one that moved a number is where the misses are, and they arrive as a range extended onto a blank line or a cite slid one line past the construct it names. The step is done when the verifier finds no claim the source does not carry.
7. Check the box and end the turn.

Every step reads the diff hunks for its own cluster only, so a compaction between steps costs one re-read of the plan.

## 4. Close

The close starts only once every other box in the plan is checked, and runs in this order, so the review gate reads the finished sweep rather than one still missing a step:

1. `mise research:cite-audit --decompile <root> --old <verified> --since <start>`, rerun without `--apply`, lists nothing without a verdict in the plan: the cluster steps rewrite paragraphs the cite step never saw, so open every new line, whatever its kind, and record its verdict before moving on.
2. Every baseline carrier moves to the new version — the four shipped skills' lines, `docs/research/`'s `**Baseline.**` paragraphs and the `VersionInternal` strings they quote — and with them the version written into a claim's own prose ("never assigned at 1.6.0f1", "`ShowErrorDialog` is public at 1.6.0f1"). `grep -rn '<old version>' plugins docs bench` finds those, but read each hit with its surrounding sentence rather than from the line's start: a qualifier sitting late in a long line whose opening clause is about something else reads as a citation and gets filed as of-a-date. That classification is the expensive half. A static claim the sweep re-derived moves; a live-read stamp, a source-fetch date, and any string inside a dated `**Ruled**` / `**Established**` / `**Addendum**` paragraph stays, since a re-confirmation is not an overturn and nothing re-ran the read. Its commit message carries `Decompile: <the decompile root's HEAD>`, the next sweep's `<verified>`.
3. Where the sweep edited a file whose entries are dated rulings (`docs/research/conflicts.md`), audit its addendum rule mechanically, since a sweep applies it unevenly: walk `git diff -U0`, map each changed line to its entry heading and to the bold section it sits under, and flag an entry with a changed line inside an `**Established.**` section and no matching addendum. Only a changed FACT owes one — a re-pointed cite does not, and without that filter the audit is almost all false alarms and gets ignored.
4. `/review-gate high --loop` over the sweep's working diff, the bump included, returns green.
5. `mise check:agents` is green.
6. Commit, per step of the plan and once more for the bump: the plugin's edits as `feat(cs2-modding): <what the version changed>`, and the same step's `docs/research/` edits as their own `docs(research):` commit, since a commit under the plugin releases and one spanning both files research under its notes. The sweep is the commit instruction.

List in the closing message every marker the sweep proposed dropping, and leave the plan folder for the user to delete.
