Skip to content

Rework Clint frontend updater: manifest-driven updates#648

Merged
janhenckens merged 15 commits into
developfrom
feature/clint-manifest-updater
Jun 22, 2026
Merged

Rework Clint frontend updater: manifest-driven updates#648
janhenckens merged 15 commits into
developfrom
feature/clint-manifest-updater

Conversation

@KarelJanVanHaute

Copy link
Copy Markdown
Contributor

Summary

Reworks the Clint frontend updater from a linear semver high-water mark to a manifest-driven, sync-then-execute model, designed by a multi-agent council analysis of the problem.

Why: We fix random frontend issues through PRs, so a single linear frontend/package.json version no longer fits. String comparison misorders versions, a hotfix can't land before an in-flight refactor, a scalar can't merge across branches, and state was written once at the end (a mid-batch crash left no record).

How it works

  • Manifest (clint/updates/index.json, CI-generated) is the source of truth for what updates exist and in what order — each entry has an explicit seq (never derived from the folder name), id (date-slug), requires, and legacyVersion.
  • Applied log (frontend/.clint-applied, committed, sorted line list with merge=union) is the source of truth for what this project has applied.
  • A run pins one immutable SHA, fetches the manifest at that SHA, computes pending = manifest − applied-log, sparse-fetches only the pending folders into a temp dir (never into the working tree), applies them in seq order as per-update transactions (recorded on success, scoped restore on failure), and shows a changelog.
  • One-time migration seeds the applied log from the legacy version field (dry-run + confirm) for projects predating this system.

Authoring

  • clint update:new "<title>" scaffolds a <YYYYMMDD>-<slug> folder + update.json + CHANGELOG.md.
  • clint update:index generates/validates index.json (intended to run in CI on merge).

Latent bugs fixed along the way

  1. frontend.packagePath pointed at ../package.json, which doesn't exist (real file is ../frontend/package.json).
  2. findAndReplaceInFile dropped every non-/.../g rule (the .map() returned undefined).
  3. Changelog reader used changelog.md while files are CHANGELOG.md → blank changelogs.

Verification

  • yarn build-cli builds clean; tsc --noEmit shows no new errors (only pre-existing .default/html-validate noise the whole codebase has).
  • update:index and update:new exercised end-to-end (including accent/symbol slugging and folder/manifest validation).

Follow-ups (need maintainer decisions — not in this PR)

  • Publish channel + fetch ref: indexGitUrl/updateRef currently point at master. Recommendation was a dedicated channel + tagged releases. Until index.json is on the fetched ref, a live frontend check fails closed by design.
  • CI action wiring update:index + a serialized merge queue so seq stays unique/monotonic.
  • frontend/package.json version left frozen (migration reads it).

🤖 Generated with Claude Code

KarelJanVanHaute and others added 7 commits June 19, 2026 16:52
Replace the linear semver high-water mark with a CI-generated manifest
(clint/updates/index.json) as the source of truth for which updates exist
and in what order, plus a committed per-project applied-update log
(frontend/.clint-applied) as the source of truth for what has been applied.

A run pins one immutable SHA, reads the manifest at that SHA, computes
pending = manifest - applied-log (set diff), sparse-fetches only the pending
folders into a temp dir, applies them in manifest `seq` order as a per-update
transaction (recorded on success, scoped restore on failure), and never syncs
the updates folder into the working tree.

- appliedState.ts: owns the sorted line-list log, gitignored meta sidecar, and
  an O_EXCL lock; git-history reconcile for a deleted log.
- git.ts: resolvePinnedSha + getManifest + addSparsePaths (one pinned clone),
  listTreeDir drift check; getRemoteFiles pins to the run SHA (fixes content
  skew); retired working-tree sync of clint/updates.
- updateChecker.ts: fetchManifest fails closed; pending via set diff; signals
  migration when the log is absent. CLI self-update stays semver.
- updater.ts: pinned apply loop, requires validation, no-gap prefix picker,
  per-update recording, one-time migration from the legacy version.
- update:index / update:new commands for authoring + manifest generation.
- .gitattributes: frontend/.clint-applied merge=union for conflict-free appends.

Also fixes three latent bugs: frontend packagePath pointed at a nonexistent
file, findAndReplaceInFile dropped plain-string rules, and the changelog
reader used the wrong filename casing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Runs on PRs/pushes touching clint/updates or clint/src: builds the CLI, runs
`update:index` (which fails on missing update.json/CHANGELOG.md, id != folder,
or invalid JSON), then enforces that the committed clint/updates/index.json is
current by diffing the regenerated manifest's `updates` array (ignoring the
volatile generatedAt timestamp).

No bot writes or merge queue required: concurrent PRs that both touch updates
resolve as an ordinary index.json merge conflict, fixed by re-running the
generator.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
indexGitUrl and updateRef now resolve against a dedicated clint-updates
publish branch instead of master, so consumers fetch the manifest and update
content from a channel they never carry in their own working tree.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
On pushes to develop that touch clint/updates or frontend, force-updates the
clint-updates branch to develop's commit, keeping the channel consumers fetch
(manifest + update folders + frontend content) an exact snapshot of develop.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rewrites updates.md to match the new updater: manifest (index.json on the
clint-updates channel) + committed applied log (frontend/.clint-applied)
instead of a semver version, date-slug update folders, no-gap prefix
selection, per-update transactions, and migration for legacy projects.

Adds a full "Authoring a frontend change" guide (update:new -> fill
update.json/CHANGELOG -> update:index -> PR to develop -> publish to
clint-updates), stressing that a frontend change only reaches existing
projects when it ships with an update folder, plus idempotent findAndReplace
guidance.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Documents the new frontend config fields (indexGitUrl, updateRef,
appliedStatePath), corrects packagePath to ../frontend/package.json, marks
packageGitUrl/version as legacy (migration-only), and replaces the version
tracking section with applied-log-based state tracking. Sample config now
matches the shipped cli.config.json (master + clint-updates channel).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Guides analyzing a frontend diff and producing update.json (modify +
findAndReplace) and CHANGELOG.md, then regenerating the manifest. Encodes the
exclude-vs-modify classification, idempotent findAndReplace rules, record-only
updates, and the never-hand-edit-index.json rule.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
KarelJanVanHaute and others added 8 commits June 19, 2026 17:38
New AuthorFlow wraps update:new (scaffold) and update:index (regenerate
manifest) so maintainers can author updates from the yarn start menu instead
of only via `node dist/cli.js ...`. The argv commands stay for CI/scripting.
Docs note both paths.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The changelog HTML (opened after an update) now splits each update's
"Manual intervention" section into a highlighted callout, adds a summary
banner at the top linking to every update that needs manual work, and echoes
a manual-intervention summary to the terminal. Styling uses inline CSS so it
doesn't depend on the purged Tailwind build.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rebuilds the changelog HTML around the Statik brand tokens (Jost, yellow
accent, soft card shadow, paper background) with a sticky header, collapsible
per-update cards (native <details>), and an index/table-of-contents shown when
multiple updates are applied. The index and entry headers flag updates that
need manual intervention, and a top alert links straight to each manual
section. showChangelog now provides the index/count/manual data.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds components/report.css (yellow accent bar + sticky branded header) to the
Clint frontend CSS and switches the index, a11y, HTML, links, CO2, and diff
report headers to it, so every report matches the redesigned changelog. Bodies
and interactions are unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Switches the report and changelog headers to a 3-column grid (logo left,
title centered, meta right) so the title sits in the middle within the
container bounds; long titles ellipsize via a min(0,auto) middle column.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reverts the centered header: logo + title are left-aligned again, and the
header inner now uses the shared .container so it shares the body's left/right
bounds. Applied to the report chrome and the changelog header.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds reusable report content components (stat toolbar, cards, issue rows,
pills, code blocks) to report.css and rebuilds the a11y report around them:
stats toolbar, per-URL cards with a chevron toggle, red issue pills, copy
button + selector chip, and a dark context block. All interactive hooks
(toggle, retest, copy) are preserved. a11y renderer now passes urlCount and
totalErrors for the stats.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rebuilds the remaining report templates on the shared report-* components:
- HTML: stats toolbar + per-URL cards with rule/line/column meta
- Links: broken/valid pills, broken+ok link rows with status badges
- CO2: green/not-green alert banner + non-collapsible metric cards with a
  rating dot
- Diff: cards wrapping the image-compare + diff-map toggle (script preserved)
- Index landing: project cards with a grid of pass/fail result tiles

Extends report.css with alert, metric, rating, link-row, status, meta-line,
diff and result-tile components. html/links/co2 renderers now pass stat
totals. All interactive hooks (toggle, retest, copy, image-compare) preserved.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@janhenckens
janhenckens merged commit 63225d9 into develop Jun 22, 2026
3 of 5 checks passed
@janhenckens
janhenckens deleted the feature/clint-manifest-updater branch June 22, 2026 13:02
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants