Skip to content

docs(skills): verify a fix at the layer its symptom lives in - #70

Merged
ako merged 2 commits into
mainfrom
claude/verify-in-runtime-skill
Aug 1, 2026
Merged

docs(skills): verify a fix at the layer its symptom lives in#70
ako merged 2 commits into
mainfrom
claude/verify-in-runtime-skill

Conversation

@ako

@ako ako commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Docs only. Adds .claude/skills/verify-in-runtime.md and the checklist rule that routes to it, so browser-level verification is a normal step for the bugs that need it rather than something rediscovered per incident.

Why

Twice this week a green test suite coexisted with a live bug:

A green suite is evidence about the layer it tests, and nothing more.

mendixlabs#812 is the case the new tier exists for: every popup opened by an mxcli-authored button rendered a blank caption, yet the BSON was structurally valid, mx check reported 0 errors, and MxBuild completed. Nothing below the browser could see it — the defect was the rendering. An empty Microflows$TextTemplate is not "no title override", it is an override to the empty string.

The trigger rule — deliberately narrow

Symptom lives in Sufficient proof
parser / grammar unit test
the BSON we write unit test on the encoded document
files on disk after mx runs integration test (-tags integration)
the rendered app's behaviour or appearance the new skill

Of four fixes this week exactly one qualified. mendixlabs#808 (visible on disk), mendixlabs#779 (visible in BSON round-trip) and the mandatory-semicolon change (parser) did not. This is not a blanket mandate — the runtime tier costs ~5 min and 1.4 GB per Mendix version.

What's in the skill

The procedure, and more usefully the traps that cost the most time:

  • cache MxBuild for the version under test explicitly — test what users report against
  • create the scratch project at a short path: Mendix throws PathTooLongException on deep ones, and in Go tests that becomes a silent skip (set TMPDIR)
  • install the Playwright package but never the browsers (/opt/pw-browsers/chromium is pre-installed)
  • A/B against a pre-fix binary — a run that only shows the fixed state demonstrates the app works, not that your change is why
  • restore files with absolute paths after a mutation test; a relative cp after a cd silently fails and leaves reverted code in the tree

The finished shape, from mendixlabs#812:

build TitleOverride headerText caption
fixed null "×\nOrder Details Popup" "Order Details Popup"
pre-fix empty TextTemplate "×" ""

Also adds

A companion checklist item requiring every bug fix to demonstrate its test detects the bug — revert the fix, watch it fail. That is what would have caught both failures above.

Contributor-only: top-level .claude/skills/ are not synced into user projects by mxcli init (only .claude/skills/mendix/ is). Verified — make sync-skills does not pick it up.

CI wiring for a runtime test tier is deliberately not included; worth doing once a second bug actually needs it, rather than building the tier speculatively off a sample of one.

🤖 Generated with Claude Code

https://claude.ai/code/session_012XR649rKk68z6gBpngu6MA


Generated by Claude Code

claude added 2 commits August 1, 2026 04:03
Adds .claude/skills/verify-in-runtime.md and the checklist rule that routes to
it, so browser-level verification is a normal step for the bugs that need it
rather than something rediscovered per incident.

The motivating pattern: twice this week a green test suite coexisted with a live
bug. In mendixlabs#812 a duplicate codec.RegisterTypeDefaults for the same $Type silently
clobbered the fix — registrations overwrite rather than merge and resolve by init
order — so the unit test passed while the emitted BSON stayed wrong. In mendixlabs#808 the
integration test had only ever skipped, for want of an mx binary, and a skip
reads exactly like a pass. A green suite is evidence about the layer it tests and
nothing more.

mendixlabs#812 is the case the new tier exists for: every popup opened by an mxcli-authored
button rendered a blank caption, yet the BSON was structurally valid, mx check
reported 0 errors and MxBuild completed. Nothing below the browser could see it,
because the defect WAS the rendering — an empty Microflows$TextTemplate is not
"no title override", it is an override to the empty string.

The trigger rule is deliberately narrow: use the layer the symptom lives in.
Parser to unit test, BSON to a test on the encoded document, on-disk effects to
an integration test, and only render-time behaviour to the runtime tier. Of four
fixes this week exactly one qualified; mendixlabs#808, mendixlabs#779 and the semicolon change did
not.

The skill records the procedure and, more usefully, the traps that cost the most
time: cache MxBuild for the version under test explicitly; create the scratch
project at a SHORT path, because Mendix throws PathTooLongException on deep ones
and in Go tests that turns into a silent skip (set TMPDIR); install the
Playwright package but never the browsers; and A/B against a pre-fix binary,
since a run that only shows the fixed state demonstrates the app works, not that
the change is why. Also the mundane one that nearly landed in a commit: restore
files with absolute paths after a mutation test, and finish with git status.

Adds a companion checklist item requiring every bug fix to demonstrate its test
detects the bug, by reverting the fix and watching it fail.

Contributor-only: the top-level .claude/skills/ are not synced into user projects
by mxcli init (only .claude/skills/mendix/ is).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012XR649rKk68z6gBpngu6MA
Every bug fix touches .claude/skills/fix-issue.md, and new rows went in directly
under the table header. Two branches fixing unrelated bugs therefore inserted at
the same line, which git cannot merge — a conflict by construction rather than by
bad luck. It cost five separate resolution rounds in one week (#59-#62 three
times, then #68 and #69), and each round risks silently dropping a row: one
earlier round was "resolved" by deleting a PR's rows entirely and restoring them
in a follow-up.

Appending puts each branch's insert at a different offset, which git merges
without help. The table is unordered — it is looked up by matching a symptom, not
read top to bottom — so position carries no meaning and appending costs nothing.

Recorded in both places a contributor might look: the skill's How to Use, with
the reasoning so it is not "fixed" back, and the PR checklist item in CLAUDE.md.

Existing rows are deliberately left where they are. Reordering them would
conflict with every open branch at once, which is the problem this change exists
to avoid.

Folded into this branch rather than opened separately: it edits the same How to
Use block, so a sixth branch would have manufactured exactly the conflict it is
meant to prevent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012XR649rKk68z6gBpngu6MA
ako pushed a commit that referenced this pull request Aug 1, 2026
#68 landed, putting its symptom row at the top of the table — the same line this
branch inserts at, so the conflict recurred immediately after the previous
resolution. That is the pattern, not bad luck.

Resolved by keeping main's rows and moving this branch's row to the END of the
table, which is the convention #70 introduces. Applying it here rather than
waiting: this branch is the exact case it exists for.
@ako
ako merged commit 94dc279 into main Aug 1, 2026
3 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.

2 participants