Releases: ColorMath/ci
Release list
v3.1.0
MINOR. Three new plugin skills and a new vendored file are additive per
LIFECYCLE.md, and the skill rename below — while a real break
for anyone with the old command in their fingers — cannot turn a consumer's CI
red, which is the test that makes a release MAJOR.
Fixed
-
make coverage-diffdid not source.colormath/ci.env
(Makefile.colormath). CI'sdiff-coveragegate sources it before running
pytest; the local mirror of that gate did not. So a consumer whose app needs
test configuration to import at all — a session secret, a provider key —
got a passing gate in CI and a collection error locally, from the same
commit.make preflightis only useful if it runs what CI runs.Found while bumping intendent to v3.0.0:
make testhad been fixed to
mirror CI, andcoverage-diffwas the one remaining target still running
pytest with the wrong environment. The fix uses the sameif [ -f … ]; then set -a; . …; set +a; fiform as the CI step, so the two are literally the
same idiom.This closes the half colormath owns, and only that half. Whether the app
also reads a local.envis the consumer's business: a.envholding
container-only paths still leaks into host runs, and neutralizing that stays
with the consumer. So a consumer workaround can shed its ci.env-sourcing
half at this release, but not necessarily all of it. -
The two stamped refs could drift, and had (
Makefile.colormath,
.github/workflows/ci.yml,LIFECYCLE.md).gates.yml'scolormath-ref
default is how CI fetches its gate scripts;Makefile.colormath's
COLORMATH_REFis howmake preflightfetches the same ones. The release
checklist stamped only the first, soCOLORMATH_REFsat atv2.0.0while
CI moved tov3.0.0— preflight fetching scripts from a tag three releases
behind the workflow.Harmless so far purely by luck:
audit-deps.sh,migrations-sync.shand
diff-coverage.share byte-identical betweenv2.0.0andv3.0.0. The
first script change would have made local and CI disagree with no signal.The
refs-lockstepjob added here was the right instinct and the wrong
check. It compared the two stamps to each other and never asked whether the
ref resolved — so when this release was stampedv3.1.0before the tag
existed, it passed while every consumer'smake preflightwould have 404'd
on every gate script. See the release-machinery entry below, which replaces
it and makes hand-stamping impossible in the first place. -
Releases are now atomic (
release/,.github/workflows/release.yml,
.github/workflows/ci.yml,LIFECYCLE.md). Releasing was a six-step
checklist, and the steps came apart. An audit of all sixteen published tags
found nine internally inconsistent:v2.1.0throughv2.4.0each ship a
gates.ymlthat fetches its gate scripts fromv2.0.0, andv3.0.0's
Makefile.colormathpoints three releases back. This release was itself
stamped intomainand written up here without ever being tagged, leaving
mainadvertising a ref that 404s.The root cause was stamping forward: a human wrote the next version into
the tree days before the tag existed, and the window between the two closed
only if they remembered. Stamps now move only in the release commit, which is
tagged with the version it stamps in a singlegit push --atomic— git
updates both refs or neither, so the window is gone rather than merely
shortened. In steady statemainis stamped at the last released tag and
every ref in it resolves.release/cut.shis the one gesture: it refuses to start unless the tree is
clean, synced withorigin/main, and green in CI; stamps; re-verifies;
commits; creates an annotated tag (the history alternates between
lightweight and annotated); pushes atomically; then publishes the GitHub
Release from this file's section for that version. A rejected push rolls the
local commit and tag back, and a failure after the push is resumable, because
publishing is idempotent.release/verify.shreplacesrefs-lockstepin CI and is a strict superset:
it coversplugin.json— a stamp site nothing checked, and which had been
missed twice — asks whether the stamped ref resolves, and requires
example/'s vendored copies to stay byte-identical to the root ones.
--audit-allproduced the drift table above and stays advisory, because
published tags are never rewritten. -
Documentation no longer names a version (
README.md,
.github/workflows/gates.yml,.github/workflows/review.yml). Nine
copy-paste pins had rotted — the README told consumers to pin@v2.0.0and
the two workflow usage-comments said@v1.1.0and@v1.0.0, while the repo
was onv3.1.0. They now read@vX.Y.Zand point at
/releases/latest, andverify.shfails the PR if a concrete version
reappears. Automating the stamping of prose would have worked; deleting the
data was cheaper and cannot regress. -
Every tag has a GitHub Release. All sixteen were bare; the notes existed
only here.release/backfill-releases.shcreated them retroactively from
this file, andcut.shcreates them going forward, so
/releases/latestis now a real answer to "what should I pin to?"
Changed
-
/colormath:review-ticketis now/colormath:refine-ticket
(plugin/skills/refine-ticket/). Same skill, same behavior, same contract
surfaces — only the command name moves, so that the two grooming skills read
as the pair they are:refine-ticketfor a ticket,refine-initiativefor
the initiative above it. "Review" also collided with the other review in
this plugin — the Thermonuclear Review thatshipwaits on — which is an
adversarial audit of a diff, not grooming.This breaks muscle memory and any docs that name the old command.
/colormath:review-ticketstops existing at this release; there is no alias.
MINOR rather than MAJOR under LIFECYCLE.md's test, which is
about a consumer's CI going red without them editing anything — a skill is
invoked by a person, and no gate, workflow input or Makefile target moves
here. Grep your consumer repos forcolormath:review-ticketwhen you take
this release; product copy that tells users to run it is the likely hit.
Added
-
AGENTS.colormath.md— a third vendored file, alongside
Makefile.colormathandeslint.config.colormath.mjsand refreshed by the
samemake colormath-update. It carries the half of a consumer's agent docs
that is identical in every colormath app: what being a colormath app means,
the sixteen gates with their localmakemirrors and how to pass each, the
preflight rhythm, the/colormath:shippipeline, the layering and
design-token and docstring conventions, the ADR practice, and the guardrails
(never hand-edit the vendored files, never push to the default branch, never
touch.env, justify every CVE ignore).The problem it solves is drift, and the drift was already there. Surveying
four consumers, the gate suite was documented four ways — sixteen gates, nine
gates, nine gates, seven gates — and one repo's doc named two disabled gates
that were not the two itsgates.ymlactually disables. Each copy was right
when written, and nothing marks the ones that stopped being right.Consumers keep an
AGENTS.mdof their own and import this one from it
(@AGENTS.colormath.md), so app-specific facts stay app-specific. Two things
deliberately do not move here: which gates a consumer disables — that is
enable-<gate>: falseplusCOLORMATH_PREFLIGHT_SKIP, and prose about them
is exactly what went stale — and where a consumer's design tokens live. The
shared file states the rule and points at the local file for the value.Additive: nothing reads it in CI and no gate, input or target changes. A
consumer that never vendors it is unaffected; one that does gets the file on
its nextcolormath-update, and wires up the import in that same PR.
example/carries the wiring as the reference. -
/colormath:refine-initiative(plugin/skills/refine-initiative/) — the
layer aboverefine-ticket. Takes an initiative, reads its feature
definitions and the tickets already under it, investigates the architecture
and decision records those features land in, interviews the filer in batched
concrete rounds, then rewrites the initiative's description and every feature
so a team could build from them.It is deliberately bounded at both ends. It stops short of code: no
file-by-file steps, no signatures, no DDL — that altitude belongs to
refine-ticket, per ticket, later. And it never starts building, because
that transition is one-way, locks the feature list and cuts a ticket per
feature; there is no MCP tool for it and the skill hands back instead of
asking for one.Contract surfaces it depends on (Abacus MCP):
get_ticketreturning
type,initiative_status,featuresandchildren;update_ticket;
add_feature,update_feature,move_feature;add_comment. A rename of
any of them must ship with a skill update in the same release. It also relies
on two current asymmetries, and names both rather than working around them:
there is no delete tool for feature definitions, andupdate_feature
replaces both fields.Second skill to require the Abacus MCP server, after
refine-ticket. -
/colormath:plan-initiative(plugin/skills/plan-initiative/) — runs
refine-ticketover every ticket in an initiative, one at a time, in build
order, injecting what that skill cannot see on its own: the initiative and its
settled decisions, the ticket's position in the sequence, what came before it
and what those plans decided, and what comes after it.The reason is the seams. Run by hand seven times,
refine-ticketgrooms seven
strangers — r...
v3.0.0
MAJOR — the gates could not fail. They can now. Every gate command is
gate-cmd 2>&1 | tee /tmp/x.log, and GitHub's implicit shell for a run:
step is bash -e {0} — without pipefail. The pipeline's exit status was
therefore tee's, always 0, and every gate failure in every consumer was
silently swallowed. This release makes the suite enforcing for the first time
since the | tee summaries landed. Expect red gates on your bump PR: they are
findings that were always there, not new rules.
Fixed
-
| teeno longer masks gate exit codes (all 22 gate steps). The
workflow now declaresdefaults.run.shell: bash, which selects
bash --noprofile --norc -eo pipefail {0}, so the gate command's status is
the step's status. Jobs set onlydefaults.run.working-directory, which
merges with the workflow-levelshellper-key rather than replacing it.Observed in the wild before the fix: an intendent run where
pip-audit
reported 10 unignored CVEs and exited 1, while the job reported success. The
file header claimed "bash runs with -o pipefail, so tee never masks a real
failure" — that assumption was simply wrong, and is now enforced instead of
asserted.
Added
-
deps-skip-unchangedinput (boolean, defaulttrue). On
pull_requestruns, the pip-audit scan is skipped when the PR changes none
ofpoetry.lock,pyproject.toml, or.colormath/audit.conf(compared
three-dot against the base branch, so base-side churn doesn't count). The
scan is ~200 sequential per-package PyPI lookups — 6m15s of a 6m30s job in
intendent, against a suite where every other gate finishes under 2m30s — and
its verdict is a pure function of the locked set, the allowlist, and the
upstream advisory databases. A PR that moves neither of the first two can
only differ by the third, which the base branch re-checks on every merge.Non-PR events always run the full scan, so merges to the default branch
still catch newly-published advisories. A consumer wanting a nightly
re-check just adds aschedule:trigger to its caller — no further config.
Setdeps-skip-unchanged: falseto scan on every run.The
depsjob now checks out withfetch-depth: 0(merge-base needs
history), matchingmigrationsanddiff-coverage.make auditis
unchanged and always performs a full scan.
Changed
-
The review workflow's default models and effort now target cost
(.github/workflows/review.yml). Measured on a real intendent PR, the two
agents cost $1.00 (review, 18 turns) and $0.53 (test-plan, 14 turns)
— $1.54 per pull request. Two defaults change, and consumers get both
without editing anything:test-plan-modelnow defaults toclaude-haiku-4-5(previously it
inheritedmodel, i.e. Sonnet 4.6). The test-plan agent does read-only
analysis and emits a checklist — work well inside Haiku's range — at $1/$5
per MTok against Sonnet's $3/$15. The review agent deliberately does
not move: adversarial review is where model tier buys real findings.review-effortnow defaults tomedium, below the model's own
high. Fewer thinking tokens and usually fewer turns, for a modest cost
in review depth.
Four new inputs make both tunable per agent:
review-model,
test-plan-model,review-effort,test-plan-effort. An empty model falls
back tomodel; an empty effort passes no flag at all, leaving the model's
own default.effortis rejected by Haiku 4.5 and Sonnet 4.5, sotest-plan-effort
defaults to empty by necessity rather than preference — setting it while
test-plan-modelis on the default Haiku will fail the request. Move
test-plan-modelto a 4.6+ model first.If your caller sets
model:, it no longer reaches both agents:
test-plan-model's non-empty default takes precedence over the fallback.
Settest-plan-modelexplicitly to keep one model everywhere.
Upgrade notes
Paste into Claude Code in each consumer repo:
Bump colormath from v2.x to v3.0.0: update the
@v2.x.xpins in
.github/workflows/gates.ymland.github/workflows/review.yamlto
@v3.0.0, and runmake colormath-update REF=v3.0.0so the vendored
Makefile.colormathstays in lockstep with the pins.v3.0.0 fixes a bug where
| teemasked every gate's exit code, so gates
that were silently passing may now fail. Runmake preflightbefore
opening the bump PR — it mirrors CI and will surface the backlog locally.
Triage what it finds: fix the real findings, and for anything you are not
ready to burn down, set that gate'senable-<gate>: falsein the caller
(keepingCOLORMATH_PREFLIGHT_SKIPinMakefile.colormathin lockstep) and
file yourself an issue. Do not merge a bump PR with a gate disabled and no
issue filed.Known for intendent specifically: the
depsgate will go red on 10
unignoredonnxadvisories and 3nltkadvisories (CVE-2026-12075 /
-12061 / -12074, fixed in nltk 3.10.0). Bump nltk; for onnx, either bump to
1.22.0 or add the ids to.colormath/audit.confwith a justification and a
revisit trigger.No input was renamed or removed and no gate default flipped, so the gate
half of the bump is mechanical — that work is entirely in the findings it
uncovers.The review workflow does change behavior on this bump, with no action
needed: the test-plan agent moves to Haiku and the reviewer drops to
mediumeffort, roughly halving review spend per PR. Two cases need a
caller edit — if you setmodel:and want it to apply to both agents, add a
matchingtest-plan-model:; and if you want the test-plan agent at a
non-default effort, movetest-plan-model:to a 4.6+ model first, because
Haiku 4.5 rejects the effort parameter.
v2.4.0
MINOR — new /colormath:review-ticket skill; no consumer CI changes. Purely
additive: a new plugin skill, which LIFECYCLE classifies as MINOR. Nothing a
consumer runs changes behavior, and the existing skills are untouched.
Added
-
/colormath:review-ticket— groom a ticket until it can be worked
(plugin/skills/review-ticket/). Fills the gap before the pipeline starts:
bugfixturns a reported defect into a fix,qasweeps for unknown ones, and
shipcarries a finished branch through the PR — but none of them help with a
one-line ticket nobody can act on. The skill reads the ticket and its full
comment thread, does a real code pass before asking anything, then asks the
few concrete questions whose answers change the outcome, and writes back a
standalone description, an implementation plan whose every step names a real
path, and a QA plan whose every item a person could execute without asking
what it meant.Two failure modes drove the wording. Fluent restatement — expanding a
title into confident prose and generic steps ("update the relevant service")
that could have been written without opening the repo — is countered by
ordering investigation ahead of questions and by the rule that a plan step
naming no file is a wish. Interrogation — a dozen open-ended questions
that cost the filer more than writing the ticket themselves — is countered by
the one-batch cap (two rounds absolute maximum), concrete multiple choice via
AskUserQuestion, and the instruction to take a conventional default and
record it as a stated assumption rather than spend a question on it.Step 2 hunts specifically for what makes a ticket non-trivial, because the
expensive miss is a one-line code change whose real work sits outside the diff
— a DNS record, a verified vendor identity, a migration ordering constraint, a
deploy that must reach two services. It also checks the repo's recorded
decisions, so a ticket asking for something an ADR explicitly rejected gets
caught during grooming rather than during review.The skill grooms and stops: no branches, no code edits, and no creating,
splitting, moving or re-typing tickets as a side effect — an oversized ticket
gets a recommended split and the call stays with the user. When investigation
shows the ticket shouldn't be done at all, that finding is the deliverable
instead of a plan for work nobody needs.
Contract surfaces
- New dependency: the Abacus MCP server — the plugin's first tracker
dependency.review-ticketkeys onmcp__abacus__get_ticket,
update_ticket,add_comment,list_boardsandlist_tickets, on ticket
keys (CM-00001) as the identifier, and on Abacus's split of
description,planandqa_planinto three separate fields — the skill
writes all three, because Abacus marks a story ready only once bothplan
andqa_planare set, so folding QA into the plan field leaves the ticket
looking unready. A rename of any of those tools or fields must ship with a
skill update in the same release. No other skill touches Abacus, and no
consumer CI does.
v2.3.0
MINOR — /colormath:ship no longer re-triggers the review; adds an explicit
merge-decision gate. No consumer CI changes. Only skill instructions change;
nothing a consumer runs behaves differently.
Fixed
-
shipcould loop forever between review and QA. Step 5 told the agent to
comment@claudefor a fresh review whenever its fixes were "substantive".
But the review workflow runs the test-plan agent alongside the reviewer, so
every re-review produced a new test plan, which demanded a new full round
of UI + API QA, whose fixes again looked substantive. Observed in practice on
a real PR: the agent completed a full browser QA pass, posted its checklist,
requested a re-review, and was immediately asked for another QA round. The
"cap this at one re-trigger round" wording was too weak to stop it.There is now exactly one review per ship run. Step 5 states the failure
mode explicitly and forbids@claudere-triggers, and a new Rules entry
("One review per run") repeats it. Findings added later by a human reviewer
are still read and addressed — the rule only bars summoning another automated
review.
Changed
- Step 7 is now "Merge decision", not "Final review" — it judges what the
run already produced (the single review, the Addressed/Not-changed response,
the QA results, the gates on the latest commit) rather than implying another
review pass. Three explicit gates:- the thermonuclear review raised no Blockers — if it did, a human signs
off, even when the agent already fixed it; - every finding is fixed or dismissed with a written reason, gates green;
- QA ran with no
❌standing, and every⚠️carries a stated, sound
reason.
- the thermonuclear review raised no Blockers — if it did, a human signs
- Explainable QA gaps no longer block the merge. The old gate failed on any
⚠️, which punished correct judgment — e.g. skippingmake cleanprecisely
because it would destroy the working dev database. A⚠️now passes when
the reason is stated and sound, and still blocks when it is "didn't get to
it", an undrivable UI item, or a stack that wouldn't come up.
v2.2.0
MINOR — new /colormath:bugfix skill; no consumer CI changes. Purely
additive: a new plugin skill, which LIFECYCLE classifies as MINOR. Nothing a
consumer runs changes behavior, and the existing skills are untouched.
Added
-
/colormath:bugfix— a bug report, all the way to a merged fix
(plugin/skills/bugfix/). Fills the gap on the front of the pipeline:
/colormath:qasweeps a feature area for unknown problems andshiptakes
an already-fixed branch through the PR, but neither turns a specific
reported defect into a fix. The skill reads the report (prose, a pasted
stack trace or log excerpt, or a path to a written-up report file), then
works six steps — establish the facts the report omitted (environment,
surface, literal repro, privilege tier, blast radius) via one batched round
of concreteAskUserQuestionoptions informed by a fast code pass first;
reproduce against the running stack at the reporter's surface and tier;
diagnose to the layer the invariant belongs at, enumerating sibling entry
paths; fix with a regression test whose fail-then-pass ordering is actually
verified; remediate already-corrupted stored data as an idempotent migration
in the same PR (including the constraint-trap check, where a new
NOT NULL/CHECK/unique index meets existing violating rows); then commit
tofix/<slug>, runmake preflight, and hand off to/colormath:ship.Two refusals are deliberate and load-bearing: it never modifies
production — production reports are reproduced locally by constructing data
in the shape the report implies, and remediation lands through the PR — and
when it cannot reproduce, it stops and reports what it tried, ruled out,
and needs, rather than shipping a fix that's reasoned instead of observed.
This mirrors ship's "when in doubt, hold".Contract surfaces it depends on — a rename of any must ship with a matching
skill update: the siblingqaskill'sreferences/recon.md(step 2's stack
bring-up + credential tiers), themake preflightendpoint, and the
/colormath:shipskill name for the step-6 handoff. -
Plugin bumped to
2.2.0; marketplace blurb notes the new skill.
v2.1.0
MINOR — /colormath:ship gains test-plan execution and can now auto-merge;
no consumer CI changes. Nothing a consumer runs turns red without them
editing anything (the test-plan job already ships in the review workflow
since v2.0.0). But note the behavior change: where ship previously stopped
at a merge recommendation and never merged, it now fixes findings itself
(blockers included) and, when the PR is genuinely clean, merges it. Repos
without the review workflow are unaffected — the skill detects its absence,
skips QA, and its final review holds rather than merging a PR it couldn't
verify.
Changed
-
/colormath:shipnow executes the PR's test plan (plugin/skills/ship/).
A new step 4 waits for thereview / test-plancheck, reads the## Test Plancomment and its machine-readable verdict (qa_depth/requires_ui_qa
/requires_api_qa), runs the UI/API QA checklists against the running
stack — reusing theqaskill's recon + verify discipline, scaling effort by
qa_depth, driving a browser for UI items when one is reachable and marking
them⚠️unverified when not — then posts a## Test Plan · Resultscomment
checking off each item with the evidence it observed. A new step 6 restores
any local state the run mutated. The skill'sallowed-toolswiden to broad
Bash+Write+Skill+AskUserQuestion(step 4 drives the local stack,
which no narrowgh/gitallowlist covers);modelis unchanged. -
/colormath:shipnow fixes findings automatically and gates the merge, not
the fix. The old SMALL/LARGE triage is gone. Step 5 fixes every review and
❌QA finding it can — Blockers included, without asking — in iterative
rounds (re-verify each repro against the running system; re-trigger the review
with@claudeonce when fixes are substantive), deferring only findings that
genuinely need a human (a design decision, or aCHANGES_REQUESTEDstructural
call). A new step 7 "final review" then decides: if no blockers stand
and QA was performed and passed, it posts a comment and auto-merges
(gh pr merge, honoring the repo's merge convention); if either is false —
a standing blocker, a finding deferred to a human, or QA that failed or
couldn't run (e.g. a required UI item with no browser) — it posts why it
held off and stops, leaving the merge to a human. The former hard rule "never
merge" is replaced by this gated auto-merge; "never push to the default
branch" still holds (agh pr mergeis not a push tomain).Contract surfaces it depends on — a rename of any must ship with a matching
skill update: the review workflow'sreview / test-plancheck name, the
## Test Plancomment marker with its<!-- colormath-test-plan -->/
<!-- testplan … -->hidden markers and theqa_depth/requires_ui_qa/
requires_api_qaverdict fields, the siblingqaskill'sreferences/
(recon + probe catalogs), and — as before — thegates / *check names, the
review / reviewcheck, and the## Thermonuclear Reviewmarker. -
Plugin bumped to
2.1.0; marketplace blurb notes/colormath:shipnow
executes the generated test plan and auto-merges when clean.
v2.0.0
MAJOR — two formerly opt-in gates flip default-on. migrations (shipped
opt-in in v1.1.0) and import-linter (added and promoted in this same
release) now default enable-*: true. A consumer that never set these flags
gets both gates on its next bump: migrations just works wherever
migrations-path (alembic/versions) exists; import-linter fails until the
repo has [tool.importlinter] contracts, so disable it and burn down on your
schedule. This is the promotion the v1.1.0 changelog and the new-gate rollout
rule pointed at.
Added
-
import-lintergate (import-linter): enforces the project's import
architecture — layered dependency order, forbidden edges, module
independence — vialint-imports. Pure static analysis (grimp builds the
import graph without executing code), so no project deps are installed,
mirroringdocstrings/templates. The contracts are entirely
project-specific and come from[tool.importlinter]in the consumer's
pyproject (or a.importlinterfile), whichlint-importsauto-discovers.
New inputs:import-linter-spec("import-linter>=2,<3") and
enable-import-linter(defaulttrue). Flat-layout note:root_packages
takes packages (dirs with__init__.py), so to forbid a top-level single-file
module (e.g. ashared.py) setinclude_external_packages = Trueand name it
inforbidden_modules. -
Makefile.colormath:import-lintertarget with a
COLORMATH_IMPORT_LINTER_SPECknob, now part ofpreflight. -
example/: anexample_pkg(service + web layers) with a[tool.importlinter]
forbiddencontract (service must not import web) and a covering test;
colormath's self-test runs the gate default-on. -
/colormath:qaplugin skill (plugin/skills/qa/): QAs a focus area
against the running stack, then hands the fixes to/colormath:ship.
Recon (bring the stack up without disturbing existing state; collect
credentials at several privilege tiers) → probe → verify → ranked findings
the user selects from → fix → restore → ship. Three probe catalogs live in
references/:security.md(authorization matrix, confused-deputy,
stored content served back, input validation, disclosure),correctness.md
(cross-surface consistency, write round-trips, boundaries, contract drift),
accessibility.md(keyboard, semantics, dynamic state, contrast).Contract surfaces it depends on — a rename of any must ship with a matching
skill update: theshipskill (step 7 handoff),make preflightand the
gates / *check names (step 4, telling a real finding from local
tool-version drift), and thea11ygate as the documented floor that the
accessibility catalog deliberately goes beyond. It also assumes the
consumer documents run commands and ports inAGENTS.md/CLAUDE.md.
Changed
enable-migrationsandenable-import-linternow defaulttrue. These
were the last two opt-in gates; every gate is now default-on. Disable any
that are red for your repo (enable-<gate>: false) and burn down on your
own schedule.Makefile.colormath:migrationsandimport-linterjoinpreflight, so
the local mirror matches the default-on CI suite. Keep
COLORMATH_PREFLIGHT_SKIPin lockstep with your caller'senable-*: false.- Plugin metadata aligned to the release.
plugin/.claude-plugin/plugin.json
bumps1.0.0→2.0.0(it was last set at the v1.0.0 release and missed the
qa-skill bump), and the marketplace blurb now names/colormath:qa. The
plugin marketplace tracks the default branch, so this ships to installs on the
next auto-update — no consumer action.
Upgrade notes
Paste into Claude Code in each consumer repo:
Bump colormath to v2.0.0: update the
gates.ymluses:pin (and
review.yml/review.yamlif present) to@v2.0.0and run
make colormath-update REF=v2.0.0(refreshes Makefile.colormath AND
eslint.config.colormath.mjs). Two gates flip default-on at v2.0.0:
migrationsandimport-linter. If this repo uses alembic (has
alembic/versions), leavemigrationson — it needs no config; otherwise
addenable-migrations: false. Unless this repo already has a
[tool.importlinter]contract, addenable-import-linter: falseand file
yourself an issue to write import contracts later. Mirror every newly
disabled gate inCOLORMATH_PREFLIGHT_SKIPin the root Makefile (before the
include) somake preflightmatches CI. Verify locally:make preflight
(or the individualmake migrations/make import-lintertargets),
poetry check --lock, then open the bump PR.
v1.1.0
MINOR: new gate, shipped opt-in per the new-gate rollout rule — existing
consumers see a skipped migrations job until they set
enable-migrations: true; promotion to default-on comes with the next MAJOR.
Added
migrationsgate (scripts/migrations-sync.sh, pure git — no project
deps): fails when the base branch has alembic migration changes the PR
branch predates, before they merge into multiple alembic heads. Diffs the
base branch against its merge-base with the PR head (checked out at the
real head SHA — the ephemeral merge commit would hide divergence), scoped
to the migrations directory, and tells the author to
git pull --rebase origin <branch>. The base branch is the
default-branchinput when set, else the repo's default branch (main and
master both work); local runs discover it fromorigin/HEAD. New inputs:
migrations-path("alembic/versions") andenable-migrations
(defaultfalse).Makefile.colormath:migrationstarget (fetch-then-run of the shared
script) with aCOLORMATH_MIGRATIONS_PATHknob. Not inpreflightwhile
the CI gate is opt-in.example/: referencealembic/versions/directory (excluded from
interrogate + coverage, mirroring real consumers); the self-test runs with
enable-migrations: true.
v1.0.0
MAJOR — pins are now contractual. Two breaking changes: every gate now
defaults on (the five formerly-opt-in gates — docstrings, jslint,
templates, js-deps, dockerfile — flip to enable-*: true), and the
jslint contract now expects the vendored shared eslint base.
Added
eslint.config.colormath.mjs: the shared eslint base, vendored by
consumers alongsideMakefile.colormath(both refreshed by
make colormath-update). Exposes acolormathConfig()factory —
files,testFiles,cdnGlobals,rules— with appended flat-config
blocks as the escape hatch (later entries win) and ejecting as sanctioned
divergence. Consumereslint.config.jsbecomes a thin caller; devDeps
contract:eslint,@eslint/js,globals.Makefile.colormath:COLORMATH_PREFLIGHT_SKIPknob —preflightnow
runs the full fourteen-gate mirror minus the listed targets; keep it in
lockstep with your caller'senable-*: falseflags.
Changed
- All
enable-<gate>inputs defaulttrue. A consumer that never set the
opt-in flags gets five new gates on its next bump — disable any that are
red and burn down on your schedule. colormath-updaterefreshes both vendored files.
Upgrade notes
Paste into Claude Code in each consumer repo:
Bump colormath to v1.0.0: update the
gates.ymluses:pin (and
review.yamlif present) to@v1.0.0and run
make colormath-update REF=v1.0.0(now refreshes Makefile.colormath AND
eslint.config.colormath.mjs). All gates default on at v1.0.0: delete any
now-redundantenable-*: truelines, and add an explicit
enable-<gate>: falsefor every gate this repo isn't ready for (check the
caller's comments for the current burn-down list — at minimumtemplates
everywhere, plusstyles/a11yin runwayz). Set
COLORMATH_PREFLIGHT_SKIPin the root Makefile (before the include) to
the same list somake preflightmirrors CI. Rewriteeslint.config.js
as a thin caller of the vendored base per its header — move this repo's
CDN globals intocdnGlobals, file globs intofiles, and keep any
special-file blocks (e.g. worklet globals) as appended entries; ensure
devDepseslint,@eslint/js,globals. Verify locally:
npm run jslint,poetry check --lock,make audit, then open the bump
PR.
v0.6.0
MINOR: four new gates, all shipped opt-in per the new-gate rollout rule —
existing consumers see skipped jobs until they set the enable-* inputs;
promotion to default-on comes with the next MAJOR. One caveat below on the
tests gate (allowed while on 0.x).
Added
jslintgate (eslint): lints hand-written JS via a consumer-defined
jslintnpm script — closes the asymmetry where Python gets ruff and CSS
gets stylelint but JS only gets tests. New input:enable-jslint
(defaultfalse). Consumer contract: npm scriptjslint+
eslint.config.js(reference inexample/).templatesgate (djlint): lints the Jinja templates themselves —
unbalanced tags, malformed syntax — complementinga11y, which validates
the HTML but not the Jinja. Pure scan, no project deps; profile and rule
ignores come from[tool.djlint]in the consumer's pyproject. New inputs:
djlint-spec("djlint>=1.36,<2"),djlint-paths("templates/"), and
enable-templates(defaultfalse).js-depsgate (npm audit): the JS half of "audit what ships" — audits
package-lock.jsonwith--omit=dev, so dev tooling never fails the gate.
Skips quietly when the workdir has no lockfile. New inputs:
npm-audit-level("high") andenable-js-deps(defaultfalse).dockerfilegate (hadolint): static Dockerfile lint, pinned binary
(same install pattern as gitleaks). New inputs:hadolint-version
("2.14.0"),hadolint-dockerfiles("Dockerfile"), and
enable-dockerfile(defaultfalse).Makefile.colormath: mirror targetsjslint,templates,js-audit,
dockerfile(out ofpreflightwhile their gates are opt-in) and
lock-check(inpreflight), withCOLORMATH_DJLINT_SPEC/
COLORMATH_DJLINT_PATHS/COLORMATH_NPM_AUDIT_LEVEL/
COLORMATH_HADOLINT_DOCKERFILESknobs.example/:eslint.config.js+jslintnpm script,[tool.djlint]
(jinja profile, H030/H031 ignored), and a hadolint-cleanDockerfile;
colormath's self-test enables all four new gates.
Changed
testsgate now runspoetry check --lockfirst, so pyproject/lock
drift fails up front with a legible message instead of deep inside a
poetry install. Strictly this can turn a drifted consumer red without a
consumer edit (MAJOR territory) — landed on0.xwhere breaking changes
are allowed; the fix ispoetry lockin the consumer repo.