Skip to content

Tooling version guard: stamp, warn, keep rule failures out of the score, sync rules and CLAUDE.md (#952) - #955

Merged
ako merged 6 commits into
mainfrom
feat/952-tooling-version-guard
Oct 3, 2026
Merged

ako merged 6 commits into
mainfrom
feat/952-tooling-version-guard

Conversation

@ako

@ako ako commented Oct 3, 2026

Copy link
Copy Markdown
Owner

Closes #952 (all four items).

What changed

1. Version stamp + warning + bootstrap guard

  • mxcli init (every --tool) and every init --sync-skills write .ai-context/mxcli-tooling.json (version, built, written). It's in .ai-context/ because every tool gets that directory, and .claude/ only exists for Claude Code. It is rewritten only when the version changes, so the daily sync doesn't dirty the tree.
  • Any command that opens the project with -p warns once, on stderr when the running binary is provably older than the stamp. The warning names both versions and how to update. Ordering: two releases by number, two nightlies by tag date, a release against a nightly by build date. Dev builds (v0.24.0-914-g…, -dirty, unknown) never warn.
  • init --sync-skills from an older binary refuses to sync, so it can't downgrade the tooling. An explicit init still runs, after the same warning.
  • .claude/bootstrap-mxcli.sh has a POSIX-sh copy of the ordering, tested against the same table as the Go version:
    • it doesn't link an older PATH mxcli and downloads MXCLI_TAG instead;
    • it replaces an older ./mxcli;
    • it downloads to a temp file and then mvs it into place. The old curl -o ./mxcli would have written through a symlinked ./mxcli into the PATH binary.
    • if the download fails, it keeps the old binary and prints a warning.
  • Binaries ≤ v0.24.0 can't warn, because they predate the stamp. Reproduced below. For them the regenerated bootstrap script is the guard. This is documented in docs-site/src/ide/syncing.md.

2. Lint rule failures

  • Reading a missing struct attribute now gives an info-level rule <ID> needs a newer mxcli ("microflow" struct has no .x attribute). Any other failure stays an error, Starlark rule error: ….
  • All failures are marked Violation.RuleFailure. BuildReport partitions them out before computing the score, summary and categories, and lists them separately. That's "Rules That Could Not Run" in markdown and html, and ruleFailures in JSON.
  • A configured rule severity no longer overrides the rule's own failure.
  • A load-time undefined: failure now hints that the rule may need a newer mxcli.

3. Sync

  • init --sync-skills (new alias --sync) also refreshes the bundled lint rules, identified by file name. User rules under other names are never touched, and a missing .claude/lint-rules is not created.
  • It also refreshes the mxcli section of CLAUDE.md / AGENTS.md. Those files are now written between <!-- mxcli:begin … --> / <!-- mxcli:end --> markers by both init and sync, and content outside the markers is preserved, including across a re-run of init.
  • A file without markers (pre-Project tooling newer than the binary: stamp the tooling version, warn, keep rule crashes out of the score, sync lint rules #952) is left alone by the sync, with a note on stderr. Running mxcli init once adopts the markers; that matches its existing regenerate-everything behaviour.
  • The sync doesn't create docs that don't exist, and skips them when there is no .mpr.

4. init and new (including new --skip-init) create mdlsource/README.md.

Reproduction (v0.24.0 built from the tag in a worktree, PedApp copy)

  • init by this branch (built as v0.25.0) stamps v0.25.0. v0.24.0 with -p: no warning at all, so it can't warn. This branch built as v0.24.0 warns once on stderr, and --json stdout stays clean.
  • v0.24.0 report over rules from main: QUAL004 and CUSTOM002 crash on .document_noun_title and score as 2 errors. This branch, with a custom rule reading a nonexistent microflow field: it is an info rule failure, and the score is identical to the control without that rule (98.8 = 98.8).
  • Bootstrap with v0.24.0 on PATH and a fake curl: mxcli on PATH … is v0.24.0, older than this project's tooling (v0.25.0) — not linking it; downloading nightly instead.
  • init --sync-skills from the older binary refuses. From v0.25.0 it refreshes the clobbered bundled rule, keeps ## Our notes below the end marker and the user rule zz_future.star, and the second run is silent.

Test plan

  • go test ./cmd/mxcli/ ./mdl/linter/ — new tests:
    • tooling_stamp_test.go: ordering table, no-churn stamp, warn-once with a same-version control, dev build silent.
    • init_hook_version_test.go: sh version_lt agrees with Go on the shared table, plus run-the-script scenarios: older PATH not linked, newer/no-stamp/dev linked (controls), stale ./mxcli replaced, no write-through to the PATH binary via a symlink.
    • init_tooling_sync_test.go: bundled rules refreshed and user rule untouched, second sync is a no-op, marked section only, unmarked left alone, stamp written, older binary refuses, merge edge cases, init creates mdlsource + stamp + markers and keeps notes on re-run.
    • starlark_rule_failure_test.go: missing field → info, other failure → error, severity override skipped, report not scored (with a control finding that does move the score), all three formats, load hint.
  • make build, make lint, make check-conformance, make check-findings: all pass.
  • Revert checks (fix removed, test fails with the symptom):
    • bootstrap guard → if false: 3 bootstrap tests fail (older binary linked, stale binary ran);
    • temp-file download → direct curl -o ./mxcli: "PATH binary was modified";
    • lint-rule sync stubbed, refusal disabled, unmarked docs overwritten, ensureMdlsourceDir removed: the matching sync/init tests fail;
    • report partition disabled: summary Errors:2, score 98.4 vs 99.4;
    • missing-attr classification disabled: severity error / "Starlark rule error";
    • severity-override guard removed: override test fails.

Notes / follow-up

  • Only CLAUDE.md and AGENTS.md carry markers. Other tools' files (.cursorrules, .windsurfrules, copilot instructions) are still only rewritten by init, not by the sync.
  • An edit to a bundled rule file is overwritten by the sync, the same as a skill. The docs say to copy the rule under a new name or override it in lint-config.yaml.
  • mxcli lint keeps exiting 1 for error-level rule failures, as the issue asks ("stay errors"). Only the report score excludes them.

🤖 Generated with Claude Code

ako and others added 6 commits October 3, 2026 14:39
…race (#951)

SaveToFile now writes to a temp file in the cache's directory (VACUUM INTO,
manual-copy fallback into the same temp file) and renames it over the cache.
buildCatalog and refresh catalog communities no longer remove the cache first.
Opening a cache at the current schema version no longer writes to it: a write
on a file renamed underneath an open connection fails with
SQLITE_READONLY_DBMOVED. File-backed connections get a busy_timeout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Starlark rule reading a struct field this mxcli does not expose is now
reported at info level as "rule <ID> needs a newer mxcli (<detail>)"; other
rule failures stay errors. Every rule failure is marked RuleFailure, kept out
of BuildReport's score, summary and categories, and listed in its own
"Rules That Could Not Run" section (ruleFailures in JSON). A configured rule
severity no longer applies to the rule's own failure, and a rule file failing
to load on an undefined name hints at a newer mxcli.

Part of #952.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sync rules and CLAUDE.md (#952)

- init and every sync write .ai-context/mxcli-tooling.json; any -p command
  warns once on stderr when the binary is provably older (releases by number,
  nightlies by tag date, mixed by build date, dev builds never).
- init --sync-skills (alias --sync) refuses from an older binary instead of
  downgrading, and now also refreshes the bundled lint rules (by file name;
  user rules untouched) and the CLAUDE.md/AGENTS.md section between new
  mxcli:begin/end markers, keeping project notes outside them.
- The bootstrap script compares the PATH binary and ./mxcli with the stamp
  before using them, downloads MXCLI_TAG instead of linking an older binary,
  and downloads through a temp file so a symlinked ./mxcli is never written
  through. Binaries <= v0.24.0 cannot read the stamp; the script is their guard.
- init and new create mdlsource/ with a README.

Closes #952.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docker check ran mx update-widgets on the user's project with only the MPRv2
storage snapshotted, so an MPRv1 .mpr was rewritten permanently, and mx check
itself rewrote theme-cache/ and created deployment/sass/ on both formats, with
or without --no-update-widgets. Both mx steps now run on a temporary copy
(build output, caches and VCS folders skipped), mx output is rewritten to the
project's own paths, and the output says that widgets were normalised on a
copy and what that hides (#568, #646). docker build keeps its snapshot.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nto itself (#951)

Check copies the project's directory, so a missing .mpr (or a path in /tmp)
would copy an unrelated directory: fail early instead, and give the existing
fake-mx tests a project file of their own. With TMPDIR inside the project the
walk met its own copy and recursed until the path was too long; skip it.
Trim the custom-widgets skill back under the 700-line limit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ako
ako merged commit 6e8a2dd into main Oct 3, 2026
33 checks passed
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.

Project tooling newer than the binary: stamp the tooling version, warn, keep rule crashes out of the score, sync lint rules

1 participant