Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
05fefc8
fix(page-shell): read `create page if not exists` declarations; repor…
claude Oct 26, 2026
cff3154
fix(doctor): gate self-test leaves Studio Pro's .mpr.lock out of the …
claude Oct 26, 2026
a5173d7
fix(preflight, exec): mxcli v0.27+ exec is all or nothing; stop calli…
claude Oct 26, 2026
7cdbd9c
fix(platform-link): Deploy API v1 creates the first Free App environment
claude Oct 26, 2026
d87e629
fix(monkey): cut a typed payload to the input's maxLength (#544)
claude Oct 26, 2026
271759c
learn(brd-to-build-plan): widget-only packages never appear in SHOW M…
claude Oct 26, 2026
5fa2277
fix(monkey): a long spinner is a slow finding, not crash-class (#562)
claude Oct 26, 2026
90bcbb9
fix(monkey): a target navTo cannot find is not reached, never fuzzed …
claude Oct 26, 2026
36a1839
learn(mendix-agent-ui): stock ConversationalUI popup traps
claude Oct 26, 2026
7780bf3
learn(microflow-patterns): separate-transaction commit leaves the req…
claude Oct 26, 2026
566b847
fix(doctor): say why mxbuild stopped; warn on a JDK that does not mat…
claude Oct 26, 2026
2c36b12
learn(page-patterns, file-upload-widget): last unpromoted 2026-10-12 …
claude Oct 26, 2026
db8c06e
fix(lint): CONV010 allows NanoflowCallAction in ACT_ nanoflows
claude Oct 26, 2026
d002efe
fix(design-audit): a11y findings name the failing element (#561)
claude Oct 26, 2026
7d9742c
fix(gate-check): warn on multi-line placeholders left in agent stubs
claude Oct 26, 2026
0229c5f
fix(journeys): outcome.label reaches the outcome check name (#538)
claude Oct 26, 2026
2dbbed8
fix(verify-module): page-scope budget override; kill the timed-out tree
claude Oct 26, 2026
700e227
fix(journeys): a mutant that never ran reads INVALID, not "went green…
claude Oct 26, 2026
d71d7b0
fix(verify-module): walk journeys/<Module>/*.journey.json when no sin…
claude Oct 26, 2026
a90b552
fix(e2e): actions wait for visibility instead of isVisible({timeout})…
claude Oct 26, 2026
9926b91
fix(status): WATCH when the lint baseline, acceptances or local rules…
claude Oct 26, 2026
7f5cf8d
fix(coverage-preflight): scope a per-module ledger file to the BRDs i…
claude Oct 26, 2026
cd2e85e
new(journeys): browser Back action and opt-in ui.no5xx row (#564)
claude Oct 26, 2026
c0616d0
fix(doctor): look-hook check catches one-line settings and says when …
claude Oct 26, 2026
dafdd2b
fix(conformance): accept BRD-prefixed pointers and not built/blocked …
claude Oct 26, 2026
081d65a
fix(exec): prune mxbuild logs by time-stamped name, not mtime
claude Oct 26, 2026
e604a46
fix(facts-lock): read name-keyed identifiers, WARN-only
claude Oct 26, 2026
c1dba09
fix(validate-journeys): back.timeout must be a positive number (#522)
claude Oct 26, 2026
b93a968
test(monkey): write the CJK payload in the oracle fixture as \u escapes
claude Oct 26, 2026
2059e68
fix(restatement-guard, hooks): mark the last-writer ledger on a model…
claude Oct 26, 2026
fe2b143
Merge branch 'fix/docs' into claude/loving-gates-hauc1n
claude Oct 26, 2026
984f274
Merge branch 'fix/doctor' into claude/loving-gates-hauc1n
claude Oct 26, 2026
10f9d04
Merge branch 'fix/gates' into claude/loving-gates-hauc1n
claude Oct 26, 2026
fbe92f8
Merge branch 'fix/runners' into claude/loving-gates-hauc1n
claude Oct 26, 2026
5041738
new(fixture-seeding, e2e): state-fixture template — set and revert on…
claude Oct 26, 2026
30abfed
new(harness-scan): flag test scripts that sign in without the toolkit…
claude Oct 26, 2026
a376f9b
fix(validate-journeys): accept offline/online, check device/viewport/…
claude Oct 26, 2026
22be37f
new(journeys): phone device per run, offline/online steps, GPS fix pe…
claude Oct 26, 2026
273c93f
learn(iterative-build-loop): measure mx check vs the deploy gate (#44…
claude Oct 26, 2026
c5c51cc
fix(lint-gate): a project-edited bundled rule lost to mxcli's session…
claude Oct 26, 2026
08af415
new(journeys): --rerun-failed and --only for fix-loop reruns (#492)
claude Oct 26, 2026
2cb753b
new(journeys): ui.pseudoText asserts CSS-authored ::before/::after te…
claude Oct 26, 2026
ff227eb
new(journeys): row-scoped click — `row` and `in` on click (#148, one …
claude Oct 26, 2026
4eaab08
fix(e2e): the remaining isVisible({timeout}) sites that mean "wait" u…
claude Oct 26, 2026
7f9c146
fix(design-audit): read the a11y widget name from the full tag, not t…
claude Oct 26, 2026
b5a409f
test(journeys): drive back and no5xx in a real Chromium (#564)
claude Oct 26, 2026
379289a
fix(doctor, exec): name the env file JAVA_HOME came from; failed-exec…
claude Oct 26, 2026
4b94318
Merge branch 'fix/runner-features' into claude/loving-gates-hauc1n
claude Oct 26, 2026
69899df
Merge branch 'fix/lint-receipt' into claude/loving-gates-hauc1n
claude Oct 26, 2026
da132fa
Merge branch 'fix/acceptance' into claude/loving-gates-hauc1n
claude Oct 26, 2026
87e3d5c
learn(detection-gaps): separate-transaction commit + browser Back reg…
claude Oct 26, 2026
a7f59c7
learn(testing-shape): returned-defect-string false-green register row…
claude Oct 26, 2026
f7c59d4
fix(verify-module, tests): portability markers on the guarded pgrep; …
claude Oct 26, 2026
c5a6a82
Merge branch 'fix/restatement' into claude/loving-gates-hauc1n
claude Oct 26, 2026
9546745
Merge branch 'fix/fixtures' into claude/loving-gates-hauc1n
claude Oct 26, 2026
0a66f9b
Merge branch 'ab/baseline-adds' into claude/loving-gates-hauc1n
claude Oct 26, 2026
8f40c98
chore(mdl-rules): re-render skills/generated after merging the sweep …
claude Oct 26, 2026
d583780
Merge remote-tracking branch 'origin/master' into claude/loving-gates…
claude Oct 26, 2026
75c38f1
fix(journeys): --rerun-failed reads only never-walked row shapes as j…
claude Oct 26, 2026
94ae58d
fix(journeys): a row-scoped click matches whole words and refuses an …
claude Oct 26, 2026
038011a
fix(validate-journeys): the typo rule allows one edit for short keys;…
claude Oct 26, 2026
ef204de
fix(lint-gate): #524 project rows ignore CRs, cover seeded rules only…
claude Oct 26, 2026
d377ec3
fix(state-fixture): refuse PGHOSTADDR/remote server and a NULL curren…
claude Oct 26, 2026
99900f7
fix(hooks): core.hooksPath advice covers post-merge and post-checkout…
claude Oct 26, 2026
38c8b15
fix(doctor): name the env file JAVA_HOME came from only when it did
claude Oct 26, 2026
abfcd79
test(behaviour): commit the baseline-adds A/B tasks and scorer so the…
claude Oct 26, 2026
5dc2f8a
Merge branch 'fix/p2-runners' into claude/loving-gates-hauc1n
claude Oct 26, 2026
1c2c783
Merge branch 'fix/p2-lint' into claude/loving-gates-hauc1n
claude Oct 26, 2026
821d814
test: leak-guard-clean fixture values (git email, test password default)
claude Oct 26, 2026
1ebfedf
Merge remote-tracking branch 'origin/master' into claude/loving-gates…
claude Oct 26, 2026
b288594
fix(page-shell): here-string instead of printf | grep -q under pipefail
claude Oct 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions CHANGELOG.md

Large diffs are not rendered by default.

89 changes: 83 additions & 6 deletions bin/doctor.sh
Original file line number Diff line number Diff line change
Expand Up @@ -948,6 +948,45 @@ if [ -n "$JAVA_EXE" ] && [ -x "$JAVA_EXE" ]; then
if [ -n "$JV" ]; then
ok "java runs: $JV ($JAVA_EXE)"
[ -z "${_jnote:-}" ] || warn "$_jnote"
# The JDK major must match the model (#576): 11.14+ builds need JDK 25, 11.13 and below
# JDK 21 (the same rule as find_java's _mxtk_java_want, read off the model doctor resolved). A JAVA_HOME exported in the shell
# profile wins over that rule AND over toolkit.env, and the mismatch then surfaces only as
# mxbuild stopping before it checks anything. Mendix 11 only — the rule is documented
# there; Studio Pro's bundled runtime is paired with its mxbuild and never flagged.
JAVA_WANT=""
_mver="$(mxtk_model_version "${MPR:-}" 2>/dev/null || true)"
case "$_mver" in
11.*)
_mmin="${_mver#11.}"; _mmin="${_mmin%%.*}"
JAVA_WANT=21; [ "$_mmin" -ge 14 ] 2>/dev/null && JAVA_WANT=25
_jmaj="$(printf '%s\n' "$JV" | sed -n 's/.*version "\(1\.\)\{0,1\}\([0-9][0-9]*\).*/\2/p' | head -1)"
case "$JAVA_HOME_FOUND" in
"${SP_APP:-<none>}"/*) ;;
*) if [ -n "$_jmaj" ] && [ "$_jmaj" != "$JAVA_WANT" ]; then
warn "Java $_jmaj, but this model (Mendix $_mver) builds with JDK $JAVA_WANT — mxbuild will stop before checking the model"
# Which env file JAVA_HOME came from, in the loader's order (project file, then user
# file; the shell beats both). The first file that sets it decides: its value equal
# to JAVA_HOME means it was loaded from there; otherwise the shell had set it already.
_jenv=""
for _ef in ${MXTK_ENV_LOADED:-}; do
_jl="$(grep -E '^[[:space:]]*(export[[:space:]]+)?JAVA_HOME[[:space:]]*=' "$_ef" 2>/dev/null | head -1)"
[ -n "$_jl" ] || continue
_jv="$(printf '%s' "${_jl#*=}" | tr -d '\r' | sed 's/^[[:space:]]*//; s/[[:space:]]*$//; s/^"\(.*\)"$/\1/; s/^'"'"'\(.*\)'"'"'$/\1/')"
type mxtk_posix_path >/dev/null 2>&1 && _jv="$(mxtk_posix_path "$_jv")"
[ "$_jv" = "${JAVA_HOME:-}" ] && _jenv="$_ef"
break
done
if [ -n "$_jenv" ]; then
note "JAVA_HOME comes from $_jenv: change it there to a JDK $JAVA_WANT folder."
elif [ -n "${JAVA_HOME:-}" ]; then
note "JAVA_HOME is set in your environment, which wins over the toolkit's per-model JDK choice and over toolkit.env."
note "Point it at JDK $JAVA_WANT (macOS: export JAVA_HOME=\$(/usr/libexec/java_home -v $JAVA_WANT)) or unset it in your shell profile."
else
note "Install JDK $JAVA_WANT, or set JAVA_HOME=<a JDK $JAVA_WANT folder> in .claude/toolkit.env."
fi
fi ;;
esac ;;
esac
# mxbuild compiles the model's Java actions, which takes javac — a JRE has none. Studio
# Pro's bundled runtime is a JRE that mxbuild is paired with, so only flag a system Java.
case "$JAVA_HOME_FOUND" in
Expand Down Expand Up @@ -1291,13 +1330,22 @@ if [ -n "$PROJECT_DIR" ]; then
grep -qF 'bin/session-check.sh' "$PSET" 2>/dev/null || HOOK_GAP="session-check (SessionStart)"
grep -qF 'bin/look-ledger.sh seen' "$PSET" 2>/dev/null || HOOK_GAP="${HOOK_GAP:+$HOOK_GAP and }look-ledger seen (PostToolUse Read)"
# #446: the pre-2026-10-21 relative form records nothing from any cwd but the project root.
# Any look hook not in the root-resolving form (one JSON string per line, so per-line is exact).
grep -F 'look-ledger.sh seen' "$PSET" 2>/dev/null | grep -vqF 'CLAUDE_PROJECT_DIR' && HOOK_GAP="${HOOK_GAP:+$HOOK_GAP and }look-ledger seen run from the project root (a look hook that does not cd to \$CLAUDE_PROJECT_DIR first records nothing from any other cwd)"
# Any look hook not in the root-resolving form. Each "command" string is split onto its own
# line first, so a single-line settings.json holding both forms is still caught (#502).
grep -F 'look-ledger.sh seen' "$PSET" 2>/dev/null | awk '{gsub(/"command"/, "\n\"command\""); print}' | grep -F 'look-ledger.sh seen' | grep -vqF 'CLAUDE_PROJECT_DIR' && HOOK_GAP="${HOOK_GAP:+$HOOK_GAP and }look-ledger seen run from the project root (a look hook that does not cd to \$CLAUDE_PROJECT_DIR first records nothing from any other cwd)"
# The installer rewrites only its own old form (`bash bin/look-ledger.sh seen || true`); a
# hand-written variant it leaves alone, so naming --hooks-only for it would be a dead end.
LOOK_CUSTOM=0
case "$HOOK_GAP" in *"project root"*)
grep -F 'look-ledger.sh seen' "$PSET" 2>/dev/null | awk '{gsub(/"command"/, "\n\"command\""); print}' \
| grep -F 'look-ledger.sh seen' | grep -vF 'CLAUDE_PROJECT_DIR' | grep -vqF '"bash bin/look-ledger.sh seen || true"' && LOOK_CUSTOM=1 ;;
esac
if [ -n "$HOOK_GAP" ] && [ -f "$PROJECT_DIR/.mxtk/no-hook-repair" ]; then
ok "hook gap ($HOOK_GAP) accepted: .mxtk/no-hook-repair is set"
elif [ -n "$HOOK_GAP" ]; then
warn "the project's .claude/settings.json has no hook for $HOOK_GAP, so the look ledger will read 0 seen however many screenshots were opened"
note "fix: $TOOLKIT_ROOT/bin/install-claude-permissions.sh $PROJECT_DIR --hooks-only (or bin/sync-project.sh $PROJECT_DIR)"
[ "$LOOK_CUSTOM" = 1 ] && note "the look hook is a hand-written variant the installer will not rewrite: edit it by hand in .claude/settings.json to cd to \$CLAUDE_PROJECT_DIR first (the installed form is in bin/install-claude-permissions.sh, LOOK_HOOK)"
else
ok "session-check and look-ledger hooks are in .claude/settings.json"
fi
Expand Down Expand Up @@ -1403,9 +1451,18 @@ if [ "$QUICK" != 1 ] || [ "$GATE_SELFTEST" = 1 ]; then

head_ "Gate self-test (can the mxbuild gate actually see an error?)"

# A JDK/model mismatch is the commonest reason mxbuild stops before checking (#576): name the
# fix next to mxbuild's own words.
gate_selftest_java_hint() {
case "$1" in
*"release version"*"not supported"*|*"Unsupported class file major version"*)
note "the model needs JDK ${JAVA_WANT:-?}; this run used ${JAVA_HOME_FOUND:-?} — see the Java line under 'Build toolchain'" ;;
esac
}

gate_selftest() {
local scratch scratch_mpr t0 t1 elapsed mdl model_dir base_count bad_count rc timeout_s
local copy_excl copy_ok entry entry_name x copy_size
local copy_excl copy_ok entry entry_name x copy_size why
timeout_s="${DOCTOR_GATE_TIMEOUT:-300}"
t0=$(date +%s)

Expand Down Expand Up @@ -1450,6 +1507,10 @@ gate_selftest() {
for x in $copy_excl; do
[ "$entry_name" = "$x" ] && continue 2
done
# Studio Pro's lock file: copied along, it makes mxcli refuse the known-bad injection
# into the throwaway copy ("Studio Pro has this project open"), the normal case during
# setup (#577). The lock belongs to the real model, never to the scratch copy.
case "$entry_name" in *.mpr.lock) continue ;; esac
cp -R "$entry" "$scratch/" 2>/dev/null || copy_ok=0
done
scratch_mpr="$scratch/$(basename "$MPR")"
Expand All @@ -1459,18 +1520,28 @@ gate_selftest() {
return 0
fi
copy_size="$(du -sh "$scratch" 2>/dev/null | awk '{print $1}')"
note "copied ${copy_size:-?} of project resources into the scratch dir (excluding .git, deployment/, node_modules/, .mpr-snapshots/)"
note "copied ${copy_size:-?} of project resources into the scratch dir (excluding .git, deployment/, node_modules/, .mpr-snapshots/, *.mpr.lock)"

# (a) Baseline: the gate must resolve SOME integer off this model, clean or not — "?" here
# means the gate cannot read mxbuild's own output, which is the original F-042-class defect.
base_count=$(mxtk_mxbuild_error_count "$scratch_mpr" "$timeout_s")
# The errors file is kept so a "?" can say WHY: the count runs in a $(...) subshell, so
# MXTK_MXBUILD_WHY never reaches this function (#576 — "cannot read mxbuild's error file"
# when mxbuild had in fact said "release version 25 not supported").
base_count=$(mxtk_mxbuild_error_count "$scratch_mpr" "$timeout_s" "$scratch/errors-base.json")
rc=$?
if [ "$rc" -eq 3 ]; then
bad "gate self-test: mxbuild did not return within ${timeout_s}s (baseline run)"
GATE_SELFTEST_LINE="fail (timeout)"
return 0
fi
if [ "$base_count" = "?" ]; then
why=$(mxtk_mxbuild_why "$scratch/errors-base.json")
if [ -n "$why" ]; then
bad "gate self-test: mxbuild stopped before checking the model (baseline run): $why"
gate_selftest_java_hint "$why"
GATE_SELFTEST_LINE="fail (mxbuild did not check: ${why:0:80})"
return 0
fi
bad "gate self-test: gate cannot read mxbuild's error file (baseline run) — a real error would go unseen"
GATE_SELFTEST_LINE="fail (unreadable error file)"
return 0
Expand Down Expand Up @@ -1508,14 +1579,20 @@ MDL
return 0
fi

bad_count=$(mxtk_mxbuild_error_count "$scratch_mpr" "$timeout_s")
bad_count=$(mxtk_mxbuild_error_count "$scratch_mpr" "$timeout_s" "$scratch/errors-bad.json")
rc=$?
if [ "$rc" -eq 3 ]; then
bad "gate self-test: mxbuild did not return within ${timeout_s}s (known-bad run)"
GATE_SELFTEST_LINE="fail (timeout)"
return 0
fi
if [ "$bad_count" = "?" ]; then
why=$(mxtk_mxbuild_why "$scratch/errors-bad.json")
if [ -n "$why" ]; then
bad "gate self-test: mxbuild stopped before checking the model (known-bad run): $why"
GATE_SELFTEST_LINE="fail (mxbuild did not check: ${why:0:80})"
return 0
fi
bad "gate self-test: gate cannot read mxbuild's error file (known-bad run) — a real error would go unseen"
GATE_SELFTEST_LINE="fail (unreadable error file)"
return 0
Expand Down
33 changes: 30 additions & 3 deletions bin/facts-lock.sh
Original file line number Diff line number Diff line change
Expand Up @@ -97,17 +97,27 @@ KEYED = {
# in these corpora and the key varies too much to enumerate.
CODESET_RE = re.compile(r"\b[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)*_CODE_SET\b")

def walk(node, key, out, src):
# `name` inside an identifier-bearing array (domainEntities[].name, pages[].name, the shape
# coverage-check.sh enumerates) is read too, but WARN-only (#519): it is collected apart from the
# lock, never frozen, and its disagreements change no exit code, so no project that passes today
# fails after a sync. Whether these become facts is a maintainer call.
NAME_PARENTS = {"domainentities": "entities", "entities": "entities", "pages": "pages",
"microflows": "microflows", "nanoflows": "microflows", "integrations": "operations"}
names = {}

def walk(node, key, out, src, parent=""):
if isinstance(node, dict):
for k, v in node.items():
walk(v, k.lower(), out, src)
walk(v, k.lower(), out, src, key)
elif isinstance(node, list):
for v in node:
walk(v, key, out, src)
walk(v, key, out, src, parent)
elif isinstance(node, str):
s = node.strip()
if not s:
return
if key == "name" and parent in NAME_PARENTS and len(s) <= 80 and " " not in s:
names.setdefault(NAME_PARENTS[parent], {}).setdefault(s.lower(), {}).setdefault(s, set()).add(src)
for ns, keys in KEYED.items():
# Identifiers only: a sentence under an `entity` key is prose, not a name.
if key in keys and len(s) <= 80 and " " not in s:
Expand Down Expand Up @@ -265,6 +275,23 @@ if CMD == "check":
else:
print("facts-lock: %d BRD(s) agree with all %d frozen fact(s)" % (len(brds), total))

# WARN-only: name-keyed spellings that disagree (#519). Same "two mixed-case spellings" test as a
# CONFLICT; reported for build/check/show, never frozen, never counted in the exit code.
name_warns = []
for ns, clusters in sorted(names.items()):
for ck, spellings in sorted(clusters.items()):
if canonical_of(ns, ck) or ck in harvest.get(ns, {}):
continue # already a keyed fact: the lock and the CONFLICT/DEVIATION lines own it
variants = contestable(sorted(spellings.keys()))
if len(variants) > 1:
name_warns.append((ns, ck, {v: sorted(spellings[v]) for v in variants}))
for ns, ck, sp in name_warns:
print(" WARN name-keyed %s/%s — %d spellings (not frozen; WARN only): %s" % (
ns, ck, len(sp), "; ".join("%s (%s)" % (v, ", ".join(srcs)) for v, srcs in sorted(sp.items()))))
if names and not sum(len(v) for v in frozen.values()):
print(" %d identifier(s) found under `name` in %s arrays: read WARN-only, not in the lock"
% (sum(len(c) for c in names.values()), "/".join(sorted(names))))

if unreadable:
sys.exit(1)
if CMD == "check":
Expand Down
16 changes: 15 additions & 1 deletion bin/gate-check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1565,7 +1565,21 @@ check_build_ready() {
echo " ✗ agent(s) still have unfilled placeholders: ${remaining}— complete them (agent-roles.md)"
fails=$((fails+1))
else
echo " ✓ all 6 agents present, no unfilled placeholders"
# A placeholder whose description spans lines (`{{NAME — prose…` on one line, `…}}` on a
# later one — mdl-agent's PROJECT_LOCAL_SOURCES_OF_TRUTH is four lines long) never matched
# the per-line grep above, so a stub with it untouched read "no unfilled placeholders"
# (#509). WARN, not FAIL: every project scaffolded before this check would flip to FAIL on
# a line nobody was ever told about; whether it should block is a maintainer call.
local multi=""
multi=$(sed 's/{{DOUBLE_BRACE}}//g' "$agents_dir"/*.md 2>/dev/null \
| perl -0777 -ne 'while (/\{\{([A-Z][A-Z0-9_]+)[^}\n]*\n.*?\}\}/sg) { print "{{$1…}}\n" }' 2>/dev/null \
| sort -u | tr '\n' ' ')
if [ -n "$multi" ]; then
echo " ✓ all 6 agents present, no single-line placeholders"
echo " ⚠ multi-line placeholder(s) still unfilled: ${multi}— complete them (agent-roles.md); WARN only, not counted as a FAIL"
else
echo " ✓ all 6 agents present, no unfilled placeholders"
fi
fi

# 3b. Crash net present. Stage 5 writes to a binary .mpr that Studio Pro holds
Expand Down
7 changes: 6 additions & 1 deletion bin/lib/install-lint-rules.sh
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ _mxtk_lint_disabled_audit() {
}

# Rewrite the receipt to the current manifest: rows for rules no longer shipped, or validly
# disabled here, are dropped (a stale row would make lint-gate.sh report the rule "gone" on every
# disabled here, are dropped (`project` rows excepted, see below) (a stale row would make lint-gate.sh report the rule "gone" on every
# run, forever), and every kept row carries its kind. Written only when something changed.
_mxtk_lint_receipt_finalize() {
_receipt="$1"; _keep=" $2 "; _dis="$3"
Expand All @@ -185,6 +185,11 @@ _mxtk_lint_receipt_finalize() {
printf '# Do not edit. Deleting this file makes a revert-to-stock look like a first install.\n'
while read -r _n _m _r; do
case "$_n" in ''|'#'*) continue ;; esac
# A `project` row (#524) is lint-gate.sh's, written on --update-baseline for a bundled
# rule the project edited; not a toolkit rule, so the manifest never lists it. Kept as is.
case " $_r " in *" project "*)
case "$_keep" in *" $_n "*) ;; *) printf '%s %s project\n' "$_n" "$_m"; continue ;; esac ;;
esac
case "$_keep" in *" $_n "*) ;; *) _pruned="$_pruned $_n"; continue ;; esac
if _mxtk_lint_is_disabled "$_dis" "$_n"; then _pruned="$_pruned $_n"; continue; fi
printf '%s %s %s\n' "$_n" "$_m" "$(_mxtk_lint_kind "$_n")"
Expand Down
4 changes: 2 additions & 2 deletions bin/lib/install-manifest.sh
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ MXTK_AGENTS_STAGE_BUILD="mdl-agent.md gate-agent.md test-agent.md review-agent.m
# runs it per module (informational; see project-bin/mf-canvas-facts.py header).
# model-scope.py joined on 2026-10-21: it imports mxpatch.py (and through it mxunit_bson.py) from
# its own directory. It puts back units a write changed only in XPath whitespace (mxcli#1596).
MXTK_PROJECT_BIN="process-map.js process-map.template.html app-restart.sh test-scratch.sh script-owners.sh describe-baseline.sh assemble-prototype.js prototype-route.js check-prototype-links.js wf-add-path-terminators.py wf-set-call-captions.py model-scope.py mf-canvas-facts.py mxunit_bson.py mxpatch.py _common.sh app-facts.sh _claims.sh snapshot-mpr.sh restore-mpr.sh exec.sh affected-tests.sh look-ledger.sh save-sp.sh restart-sp.sh check-sp-health.sh verify-module.sh test-stack-up.sh fixture-manifest.sh check-root-clean.sh lint-gate.sh lint-trend.sh metrics-ledger.sh metrics-trend.sh close-task.sh conformance-check.sh coverage-preflight.sh graph-sweep.sh review-module.sh coherence-cadence.sh build-plan-status.sh progress-report.py done-drift-check.sh page-scope.sh render-improvement-register.sh check-design-portability.sh check-design-reaches-app.sh check-page-shell.sh page-fidelity.js model-stamp.sh verify-model.sh install-project-hooks.sh mx-metadata-note.py session-check.sh constants-audit.sh context-pack.sh check-staged-secrets.sh"
MXTK_PROJECT_BIN="process-map.js process-map.template.html app-restart.sh test-scratch.sh script-owners.sh describe-baseline.sh assemble-prototype.js prototype-route.js check-prototype-links.js wf-add-path-terminators.py wf-set-call-captions.py model-scope.py mf-canvas-facts.py mxunit_bson.py mxpatch.py _common.sh app-facts.sh _claims.sh snapshot-mpr.sh restore-mpr.sh exec.sh affected-tests.sh look-ledger.sh save-sp.sh restart-sp.sh check-sp-health.sh verify-module.sh harness-scan.sh test-stack-up.sh fixture-manifest.sh check-root-clean.sh lint-gate.sh lint-trend.sh metrics-ledger.sh metrics-trend.sh close-task.sh conformance-check.sh coverage-preflight.sh graph-sweep.sh review-module.sh coherence-cadence.sh build-plan-status.sh progress-report.py done-drift-check.sh page-scope.sh render-improvement-register.sh check-design-portability.sh check-design-reaches-app.sh check-page-shell.sh page-fidelity.js model-stamp.sh verify-model.sh install-project-hooks.sh mx-metadata-note.py session-check.sh constants-audit.sh context-pack.sh check-staged-secrets.sh"

# Files in project-bin/ that are deliberately NOT installed into projects. The reverse check
# below flags anything named by NEITHER list, so a new file in project-bin/ has to be either
Expand Down Expand Up @@ -196,7 +196,7 @@ MXTK_LINT_RENAMES="conv020_action_user_feedback.star CONV020 mxtk_ux003_action_u
# Files in lint-rules/ deliberately NOT installed. Same contract as MXTK_PROJECT_BIN_NOINSTALL:
# the reverse check below flags anything named by no list, so a new rule has to be either
# delivered or declared undelivered. README.md is documentation, not a rule.
MXTK_LINT_RULES_NOINSTALL="README.md STOCK-HASHES.txt"
MXTK_LINT_RULES_NOINSTALL="README.md STOCK-HASHES.txt STOCK-HASHES-BUNDLED.txt"

# --- verification engine ------------------------------------------------------------------
# Copied from toolkit project-tests/e2e/ into <project>/tests/e2e/. This is the journey-proof
Expand Down
Loading
Loading