Skip to content

Releases: ColorMath/ci

v3.1.0

Choose a tag to compare

@craigmbooth craigmbooth released this 04 Aug 13:30

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-diff did not source .colormath/ci.env
    (Makefile.colormath). CI's diff-coverage gate 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 preflight is only useful if it runs what CI runs.

    Found while bumping intendent to v3.0.0: make test had been fixed to
    mirror CI, and coverage-diff was the one remaining target still running
    pytest with the wrong environment. The fix uses the same if [ -f … ]; then set -a; . …; set +a; fi form 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 .env is the consumer's business: a .env holding
    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's colormath-ref
    default is how CI fetches its gate scripts; Makefile.colormath's
    COLORMATH_REF is how make preflight fetches the same ones. The release
    checklist stamped only the first, so COLORMATH_REF sat at v2.0.0 while
    CI moved to v3.0.0 — preflight fetching scripts from a tag three releases
    behind the workflow.

    Harmless so far purely by luck: audit-deps.sh, migrations-sync.sh and
    diff-coverage.sh are byte-identical between v2.0.0 and v3.0.0. The
    first script change would have made local and CI disagree with no signal.

    The refs-lockstep job 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 stamped v3.1.0 before the tag
    existed, it passed while every consumer's make preflight would 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.0 through v2.4.0 each ship a
    gates.yml that fetches its gate scripts from v2.0.0, and v3.0.0's
    Makefile.colormath points three releases back. This release was itself
    stamped into main and written up here without ever being tagged, leaving
    main advertising 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 single git push --atomic — git
    updates both refs or neither, so the window is gone rather than merely
    shortened. In steady state main is stamped at the last released tag and
    every ref in it resolves.

    release/cut.sh is the one gesture: it refuses to start unless the tree is
    clean, synced with origin/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.sh replaces refs-lockstep in CI and is a strict superset:
    it covers plugin.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-all produced 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.0 and
    the two workflow usage-comments said @v1.1.0 and @v1.0.0, while the repo
    was on v3.1.0. They now read @vX.Y.Z and point at
    /releases/latest, and verify.sh fails 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.sh created them retroactively from
    this file, and cut.sh creates them going forward, so
    /releases/latest is now a real answer to "what should I pin to?"

Changed

  • /colormath:review-ticket is 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-ticket for a ticket, refine-initiative for
    the initiative above it. "Review" also collided with the other review in
    this plugin — the Thermonuclear Review that ship waits 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-ticket stops 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 for colormath:review-ticket when 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.colormath and eslint.config.colormath.mjs and refreshed by the
    same make 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 local make mirrors and how to pass each, the
    preflight rhythm, the /colormath:ship pipeline, 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 its gates.yml actually disables. Each copy was right
    when written, and nothing marks the ones that stopped being right.

    Consumers keep an AGENTS.md of 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>: false plus COLORMATH_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 next colormath-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 above refine-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_ticket returning
    type, initiative_status, features and children; 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, and update_feature
    replaces both fields.

    Second skill to require the Abacus MCP server, after refine-ticket.

  • /colormath:plan-initiative (plugin/skills/plan-initiative/) — runs
    refine-ticket over 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-ticket grooms seven
    strangers — r...

Read more

v3.0.0

Choose a tag to compare

@craigmbooth craigmbooth released this 04 Aug 13:29
ec9441a

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

  • | tee no longer masks gate exit codes (all 22 gate steps). The
    workflow now declares defaults.run.shell: bash, which selects
    bash --noprofile --norc -eo pipefail {0}, so the gate command's status is
    the step's status. Jobs set only defaults.run.working-directory, which
    merges with the workflow-level shell per-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-unchanged input (boolean, default true). On
    pull_request runs, the pip-audit scan is skipped when the PR changes none
    of poetry.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 a schedule: trigger to its caller — no further config.
    Set deps-skip-unchanged: false to scan on every run.

    The deps job now checks out with fetch-depth: 0 (merge-base needs
    history), matching migrations and diff-coverage. make audit is
    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-model now defaults to claude-haiku-4-5 (previously it
      inherited model, 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-effort now defaults to medium, 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 to model; an empty effort passes no flag at all, leaving the model's
    own default.

    effort is rejected by Haiku 4.5 and Sonnet 4.5, so test-plan-effort
    defaults to empty by necessity rather than preference — setting it while
    test-plan-model is on the default Haiku will fail the request. Move
    test-plan-model to 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.
    Set test-plan-model explicitly 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.x pins in
.github/workflows/gates.yml and .github/workflows/review.yaml to
@v3.0.0, and run make colormath-update REF=v3.0.0 so the vendored
Makefile.colormath stays in lockstep with the pins.

v3.0.0 fixes a bug where | tee masked every gate's exit code, so gates
that were silently passing may now fail. Run make preflight before
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's enable-<gate>: false in the caller
(keeping COLORMATH_PREFLIGHT_SKIP in Makefile.colormath in 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 deps gate will go red on 10
unignored onnx advisories and 3 nltk advisories (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.conf with 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
medium effort, roughly halving review spend per PR. Two cases need a
caller edit — if you set model: and want it to apply to both agents, add a
matching test-plan-model:; and if you want the test-plan agent at a
non-default effort, move test-plan-model: to a 4.6+ model first, because
Haiku 4.5 rejects the effort parameter.

v2.4.0

Choose a tag to compare

@craigmbooth craigmbooth released this 04 Aug 13:29
0f33095

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:
    bugfix turns a reported defect into a fix, qa sweeps for unknown ones, and
    ship carries 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-ticket keys on mcp__abacus__get_ticket,
    update_ticket, add_comment, list_boards and list_tickets, on ticket
    keys (CM-00001) as the identifier, and on Abacus's split of
    description, plan and qa_plan into three separate fields — the skill
    writes all three, because Abacus marks a story ready only once both plan
    and qa_plan are 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

Choose a tag to compare

@craigmbooth craigmbooth released this 04 Aug 13:29
47ebd6b

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

  • ship could loop forever between review and QA. Step 5 told the agent to
    comment @claude for 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 @claude re-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:
    1. the thermonuclear review raised no Blockers — if it did, a human signs
      off, even when the agent already fixed it;
    2. every finding is fixed or dismissed with a written reason, gates green;
    3. QA ran with no standing, and every ⚠️ carries a stated, sound
      reason
      .
  • Explainable QA gaps no longer block the merge. The old gate failed on any
    ⚠️, which punished correct judgment — e.g. skipping make clean precisely
    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

Choose a tag to compare

@craigmbooth craigmbooth released this 04 Aug 13:29
82dee4c

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:qa sweeps a feature area for unknown problems and ship takes
    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 concrete AskUserQuestion options 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
    to fix/<slug>, run make 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 sibling qa skill's references/recon.md (step 2's stack
    bring-up + credential tiers), the make preflight endpoint, and the
    /colormath:ship skill name for the step-6 handoff.

  • Plugin bumped to 2.2.0; marketplace blurb notes the new skill.

v2.1.0

Choose a tag to compare

@craigmbooth craigmbooth released this 04 Aug 13:29
1844337

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:ship now executes the PR's test plan (plugin/skills/ship/).
    A new step 4 waits for the review / test-plan check, reads the ## Test Plan comment and its machine-readable verdict (qa_depth / requires_ui_qa
    / requires_api_qa), runs the UI/API QA checklists against the running
    stack — reusing the qa skill'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 · Results comment
    checking off each item with the evidence it observed. A new step 6 restores
    any local state the run mutated. The skill's allowed-tools widen to broad
    Bash + Write + Skill + AskUserQuestion (step 4 drives the local stack,
    which no narrow gh/git allowlist covers); model is unchanged.

  • /colormath:ship now 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 @claude once when fixes are substantive), deferring only findings that
    genuinely need a human (a design decision, or a CHANGES_REQUESTED structural
    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 (a gh pr merge is not a push to main).

    Contract surfaces it depends on — a rename of any must ship with a matching
    skill update: the review workflow's review / test-plan check name, the
    ## Test Plan comment marker with its <!-- colormath-test-plan --> /
    <!-- testplan … --> hidden markers and the qa_depth / requires_ui_qa /
    requires_api_qa verdict fields, the sibling qa skill's references/
    (recon + probe catalogs), and — as before — the gates / * check names, the
    review / review check, and the ## Thermonuclear Review marker.

  • Plugin bumped to 2.1.0; marketplace blurb notes /colormath:ship now
    executes the generated test plan and auto-merges when clean.

v2.0.0

Choose a tag to compare

@craigmbooth craigmbooth released this 04 Aug 13:29

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-linter gate (import-linter): enforces the project's import
    architecture — layered dependency order, forbidden edges, module
    independence — via lint-imports. Pure static analysis (grimp builds the
    import graph without executing code), so no project deps are installed,
    mirroring docstrings/templates. The contracts are entirely
    project-specific and come from [tool.importlinter] in the consumer's
    pyproject (or a .importlinter file), which lint-imports auto-discovers.
    New inputs: import-linter-spec ("import-linter>=2,<3") and
    enable-import-linter (default true). Flat-layout note: root_packages
    takes packages (dirs with __init__.py), so to forbid a top-level single-file
    module (e.g. a shared.py) set include_external_packages = True and name it
    in forbidden_modules.

  • Makefile.colormath: import-linter target with a
    COLORMATH_IMPORT_LINTER_SPEC knob, now part of preflight.

  • example/: an example_pkg (service + web layers) with a [tool.importlinter]
    forbidden contract (service must not import web) and a covering test;
    colormath's self-test runs the gate default-on.

  • /colormath:qa plugin 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: the ship skill (step 7 handoff), make preflight and the
    gates / * check names (step 4, telling a real finding from local
    tool-version drift), and the a11y gate as the documented floor that the
    accessibility catalog deliberately goes beyond. It also assumes the
    consumer documents run commands and ports in AGENTS.md / CLAUDE.md.

Changed

  • enable-migrations and enable-import-linter now default true. 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: migrations and import-linter join preflight, so
    the local mirror matches the default-on CI suite. Keep
    COLORMATH_PREFLIGHT_SKIP in lockstep with your caller's enable-*: false.
  • Plugin metadata aligned to the release. plugin/.claude-plugin/plugin.json
    bumps 1.0.02.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.yml uses: pin (and
review.yml/review.yaml if present) to @v2.0.0 and 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:
migrations and import-linter. If this repo uses alembic (has
alembic/versions), leave migrations on — it needs no config; otherwise
add enable-migrations: false. Unless this repo already has a
[tool.importlinter] contract, add enable-import-linter: false and file
yourself an issue to write import contracts later. Mirror every newly
disabled gate in COLORMATH_PREFLIGHT_SKIP in the root Makefile (before the
include) so make preflight matches CI. Verify locally: make preflight
(or the individual make migrations / make import-linter targets),
poetry check --lock, then open the bump PR.

v1.1.0

Choose a tag to compare

@craigmbooth craigmbooth released this 04 Aug 13:29
bb20ce0

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

  • migrations gate (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-branch input when set, else the repo's default branch (main and
    master both work); local runs discover it from origin/HEAD. New inputs:
    migrations-path ("alembic/versions") and enable-migrations
    (default false).
  • Makefile.colormath: migrations target (fetch-then-run of the shared
    script) with a COLORMATH_MIGRATIONS_PATH knob. Not in preflight while
    the CI gate is opt-in.
  • example/: reference alembic/versions/ directory (excluded from
    interrogate + coverage, mirroring real consumers); the self-test runs with
    enable-migrations: true.

v1.0.0

Choose a tag to compare

@craigmbooth craigmbooth released this 04 Aug 13:29

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 alongside Makefile.colormath (both refreshed by
    make colormath-update). Exposes a colormathConfig() factory —
    files, testFiles, cdnGlobals, rules — with appended flat-config
    blocks as the escape hatch (later entries win) and ejecting as sanctioned
    divergence. Consumer eslint.config.js becomes a thin caller; devDeps
    contract: eslint, @eslint/js, globals.
  • Makefile.colormath: COLORMATH_PREFLIGHT_SKIP knob — preflight now
    runs the full fourteen-gate mirror minus the listed targets; keep it in
    lockstep with your caller's enable-*: false flags.

Changed

  • All enable-<gate> inputs default true. 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-update refreshes both vendored files.

Upgrade notes

Paste into Claude Code in each consumer repo:

Bump colormath to v1.0.0: update the gates.yml uses: pin (and
review.yaml if present) to @v1.0.0 and 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-redundant enable-*: true lines, and add an explicit
enable-<gate>: false for every gate this repo isn't ready for (check the
caller's comments for the current burn-down list — at minimum templates
everywhere, plus styles/a11y in runwayz). Set
COLORMATH_PREFLIGHT_SKIP in the root Makefile (before the include) to
the same list so make preflight mirrors CI. Rewrite eslint.config.js
as a thin caller of the vendored base per its header — move this repo's
CDN globals into cdnGlobals, file globs into files, and keep any
special-file blocks (e.g. worklet globals) as appended entries; ensure
devDeps eslint, @eslint/js, globals. Verify locally:
npm run jslint, poetry check --lock, make audit, then open the bump
PR.

v0.6.0

v0.6.0 Pre-release
Pre-release

Choose a tag to compare

@craigmbooth craigmbooth released this 04 Aug 13:29
d9c1802

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

  • jslint gate (eslint): lints hand-written JS via a consumer-defined
    jslint npm script — closes the asymmetry where Python gets ruff and CSS
    gets stylelint but JS only gets tests. New input: enable-jslint
    (default false). Consumer contract: npm script jslint +
    eslint.config.js (reference in example/).
  • templates gate (djlint): lints the Jinja templates themselves —
    unbalanced tags, malformed syntax — complementing a11y, 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 (default false).
  • js-deps gate (npm audit): the JS half of "audit what ships" — audits
    package-lock.json with --omit=dev, so dev tooling never fails the gate.
    Skips quietly when the workdir has no lockfile. New inputs:
    npm-audit-level ("high") and enable-js-deps (default false).
  • dockerfile gate (hadolint): static Dockerfile lint, pinned binary
    (same install pattern as gitleaks). New inputs: hadolint-version
    ("2.14.0"), hadolint-dockerfiles ("Dockerfile"), and
    enable-dockerfile (default false).
  • Makefile.colormath: mirror targets jslint, templates, js-audit,
    dockerfile (out of preflight while their gates are opt-in) and
    lock-check (in preflight), with COLORMATH_DJLINT_SPEC /
    COLORMATH_DJLINT_PATHS / COLORMATH_NPM_AUDIT_LEVEL /
    COLORMATH_HADOLINT_DOCKERFILES knobs.
  • example/: eslint.config.js + jslint npm script, [tool.djlint]
    (jinja profile, H030/H031 ignored), and a hadolint-clean Dockerfile;
    colormath's self-test enables all four new gates.

Changed

  • tests gate now runs poetry check --lock first, 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 on 0.x where breaking changes
    are allowed; the fix is poetry lock in the consumer repo.