Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Sections dated before 2026-09-19 predate the cycle and stay as they are.

## Unreleased

- new(gate-check, interaction-model): **Stage 3 now fails on an interaction model that leaves out a user task or a wireframe's page parameter, and asks about BRD actors with no home** ([#291](https://github.com/MendixMau/mxcli-project-toolkit/issues/291), part 2). Once `design/interaction-model.md` exists, `bin/lib/interaction-model-check.sh` (called by `gate-check.sh`) lists one FAIL per BRD approval/review/sign-off/user-task use case not named on any line (row or bullet) under an Inbox heading, and per `design/wireframes/*.html` without a `Page parameter:` line (`Module.Entity` or `none — <reason>`) in its `wf-contract` block; any of them fails Stage 3. A BRD actor with no "Role homes" row is MANUAL (confirm it is a user role, or add a `none — <reason>` row), never FAIL. Findings are waived by hand in the register (`Waived artifact interaction-model/<unit>: reason`); a project adopted above stage 3 reports ADOPTED. A new `artifact-manifest.tsv` row reports the artifact PENDING when it is absent, which does not block. `checkpoint-design.md` asks "who lands where, and how does each role find its work" next to the layout question; `brd-to-build-plan.md` Step 7 reads wire points from the artifact. Fixture `tests/wave2/test-interaction-model-gate.sh` (synthetic trees only) — field retro (#291)
- feat(lint): **Lint Batch 4 — SEC014 external role can write state, audit or ownership members; CONV009 reads its `maxObjects` option.** SEC014 ([#268](https://github.com/MendixMau/mxcli-project-toolkit/issues/268)): a partner role with `create, read *, write *` on self-owned entities passed every stock rule, so it could set its own record's status through the client API. Lint showed only CONV006 +4, which was re-baselined, and a manual review found it five days later. The new rule flags an external role (named in `lint-config.yaml` as `external_roles: "A, B"`, or `"none"`) that writes all members, a `Status`/`State`/`Result`/`Stage`/`Phase` enumeration, an `…At`/`…By` attribute, or the association its own XPath uses for ownership. It is a warning in the security tier. A `_rule` finding marks the rule blind when the option is blank or given as a YAML list (mxcli passes a list as `None`), when a role name does not exist, or when the catalog is not FULL. Each message is under 160 characters and names the place to fix it, because lint-gate prints only that much. An **absent** option is not a `_rule` finding, so it does not block every exec (the #229 pattern). Instead `bin/sync-project.sh` prints a NOTE on every sync through new `bin/lib/sec014-notice.sh` until the option is set. It is a NOTE rather than a counted warning, so a pristine scaffold still syncs clean under `--strict` (fixture `tests/wave2/test-sec014-notice.sh`, 14 cases; `test-bug12-sync.sh` 61/61). `"none"` switches a security rule off, so it is set only on the user's answer in chat and recorded in `PROJECT.md`. Right after configuring, re-baseline with `lint-gate.sh --update-baseline --reason "SEC014 configured" --approved-in-chat`, because the 0 → N jump would otherwise be blamed on the next script. CONV009 ([#267](https://github.com/MendixMau/mxcli-project-toolkit/issues/267)): the stock rule hard-codes 15, so the `maxObjects: 20` example in the config `mxcli init` writes did nothing. The toolkit copy reads `maxObjects` and `countLoopBodies`, with the **default kept at 15** (raising it is a maintainer call). It replaces the stock file through `STOCK-HASHES.txt`, which gains conv009 hashes for v0.17.0/0.24.0/0.25.0 and a full v0.26.0 set, captured by `mxcli init` from the official release assets; `test-stock-hashes.sh` T4 passes live on all four. `lint-that-actually-runs.md` now says `options:` reaches only rules that call `get_option()`. 32 cases in `tests/lint-rules/test-batch4-rules.sh`; the harness gained `get_option`, `permissions_for`, `attributes_for`, `user_roles` and `role_mappings`. Field run (official mxcli 0.26.0, two model copies): stock CONV009 stayed at 5 with `maxObjects: 20`, while the toolkit copy gave 5 at 15, 10 at 10 and 6 at 20 with loop bodies counted. SEC014 gave 1 true finding with the model's external role configured, and 7 with every role configured, matched by a SQL replay. Both rules load on 0.17.0 and 0.24.0 too. CONV009's "every flow reports 0 objects" self-check now needs `activities_for()` to find a real object in a zero-count flow, so an early greenfield model with empty microflows is not reported blind. `lint-rules/README.md` and the TXT002 header now give the supported way to turn a toolkit rule off. ISO dates in particular are a toolkit opinion, so a project with a non-ISO display standard sets `rules: TXT002: enabled: false` in `lint-config.yaml` and records that in `PROJECT.md`. It must not delete the file, which sync would re-install. On mxcli 0.26.0, a rule with 1 finding gave 0 once disabled. After #308, there is one opt-out story, always the user's decision (asked in chat, recorded in `PROJECT.md`), and it never deletes a `.star` file. `enabled: false` in `lint-config.yaml` stops a rule reporting; this is preferred. `.claude/lint-rules-disabled.txt` with a `# reason:` stops sync installing a toolkit rule, and refuses stock-seeded rules, a list that now includes conv009. `rules: SEC014: enabled: false` is the same decision as `external_roles: "none"`. While SEC014 is undecided, `project-bin/lint-gate.sh` also prints one uncounted `note: SEC014 not configured …` line on stderr on every run (no exit-code change, no lint row). Both it and the sync NOTE now count `external_roles` only when it sits under the `SEC014` key. `test-sec014-notice.sh` has 24 cases. — MendixMau
- fix(lint-gate): **rules that `mxcli init` reverted to stock now stop the lint run as BLIND instead of showing up as a rise on the next script** ([#264](https://github.com/MendixMau/mxcli-project-toolkit/issues/264)). `bin/lint-gate.sh` reads `.claude/.mxtk-lint-receipt` (written by `bin/lib/install-lint-rules.sh`) before linting: a stock-seeded rule (the file names `mxcli init` writes, now marked `stock` in the receipt) whose md5 no longer matches and whose `# mxtk-lint-rule:` header is gone, or any receipt-listed rule that is missing, is named, the remedy `bin/sync-project.sh <project>` (fallback `--upgrade-lint-rules all`) is printed, and the run exits 1 with `BLIND — lint rules reverted to stock` (exec.sh logs `LINT BLIND`, not `LINT ROSE`); `--update-baseline` is refused over reverted rules, also under the `LINT_RULES_REVERTED_OK=1` look-anyway override. Field cases: CONV010 26 -> 169 blamed on a pages-only exec, and a re-seed that sat five days while the baseline absorbed stock counts. So the remedy always clears it, `bin/lib/install-lint-rules.sh` restores such a stock-seeded rule even when its body matches no known stock hash (a newer mxcli's seed, or a hand edit that also lost the header: it was reported LOCALLY MODIFIED and never written), labelled `header missing — restored from toolkit, your copy kept as <rule>.reseeded-<ts>` with the newest 3 copies kept; a headerless toolkit-only or configurable rule is still a local edit, never overwritten. The installer also writes each receipt row's kind, prunes rows for rules no longer shipped, and honours `.claude/lint-rules-disabled.txt`: one line per rule with `# reason: …` (`#\s*reason:` in both tools), a user decision recorded in `PROJECT.md`; a line with no reason, or naming a stock-seeded rule, is ignored with a warning (`lint-rules/README.md` had told projects to just delete txt002). Fixture `tests/wave2/test-lint-receipt-revert.sh` builds its receipt with the real installer and uses a genuine v0.25.0 stock body: 55/55 here, 28 fail on master's lint-gate and installer. — AlvinnYeo
- learn(learned-mdl-preflight): **a microflow change near a `return` in a dense flow is planned as drop + create after `show access`; `check -p --references` does not run exec's free-room test** ([#263](https://github.com/MendixMau/mxcli-project-toolkit/issues/263)). STOP row 28 and BUG-156: on v0.25.0 `check` printed "no flow change exec would refuse" and exec then refused "there is no free room for the fragment". Upstream issue drafted, not filed. — AlvinnYeo
Expand Down
27 changes: 27 additions & 0 deletions bin/gate-check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1159,6 +1159,13 @@ check_stage_3() {
return
fi
done
# The interaction-model facts (#291), computed once before the stage loop and printed under
# "Interaction model (stage 3 facts)". PENDING (no artifact yet — the Artifacts block reports
# that) and ADOPTED/WAIVED leave this verdict alone; a finding is something examined and wrong.
case "${MXTK_IM_STATUS:-}" in
FAIL) echo "FAIL|interaction model: ${MXTK_IM_NOTE} — each finding is listed under 'Interaction model (stage 3 facts)' (skills/interaction-model.md)"; return ;;
MANUAL) echo "MANUAL|interaction model: ${MXTK_IM_NOTE}"; return ;;
esac
if reg_unavailable; then reg_unavailable_note; return; fi
if has_confirmed_decision 3; then
echo "PASS|fit-gap, blueprint render, design system, wireframes present and a Stage-3 CONFIRMED decision is in $REGISTER"
Expand Down Expand Up @@ -2544,6 +2551,26 @@ else
printf 'Artifacts: FAULT — bin/lib/artifact-check.sh missing from %s; no artifact was checked\n' "$TOOLKIT_DIR"
fi

# ---------------------------------------------------------------------------
# Interaction model — the Stage-3 facts over design/interaction-model.md (#291).
#
# The artifact check above only notices the file is absent. Once it exists, three facts are
# read from it: every BRD role has a home row, every BRD approval/user-task use case has an
# inbox row (or a stated reason), every wireframe carries its `Page parameter:` line. Each
# finding prints here; check_stage_3() turns any un-waived one into a Stage-3 FAIL. Judgement
# (is that the right home?) stays in skills/interaction-model.md. Waivers and adoption: see the
# header of bin/lib/interaction-model-check.sh.
MXTK_IM_STATUS=""; MXTK_IM_NOTE=""
if [ -r "$TOOLKIT_DIR/bin/lib/interaction-model-check.sh" ] && [ -r "$TOOLKIT_DIR/bin/lib/artifact-check.sh" ]; then
# shellcheck source=lib/interaction-model-check.sh
. "$TOOLKIT_DIR/bin/lib/interaction-model-check.sh"
mxtk_interaction_model_check "$PROJECT_DIR" "$REGISTER" "$ANALYSIS_BASE"
else
MXTK_IM_STATUS="MANUAL"
MXTK_IM_NOTE="bin/lib/interaction-model-check.sh missing from $TOOLKIT_DIR — cannot evaluate, which is not a pass"
printf 'Interaction model (stage 3 facts): MANUAL — %s\n' "$MXTK_IM_NOTE"
fi

# ---------------------------------------------------------------------------
# Source sufficiency — was the source actually characterised, or did Stage 0
# pass on nobody having looked?
Expand Down
1 change: 1 addition & 0 deletions bin/lib/artifact-manifest.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ blueprint-html 3 architecture/blueprint.html skills/architecture-blueprint.md St
design-system 3 design/design-system.html skills/design-artifacts.md:79 (annotated showcase linking ds.css) bin/gate-check.sh:913 (check_stage_3), bin/gate-check.sh:1135 (build-ready) migration,requirements,existing-app-change gate
ds-css 3 design/ds.css skills/design-artifacts.md:75 (the physical token/component source) skills/design-artifacts.md:68 (two-files rule — every wireframe links it), bin/gate-check.sh:1135 (build-ready accepts either file) migration,requirements,existing-app-change report
wireframes 3 design/wireframes/*.html skills/design-artifacts.md Step 3, one per screen bin/gate-check.sh:923 (check_stage_3), bin/gate-check.sh:1129 (build-ready), skills/ui-preflight-pages.md (mdl-agent pre-flight input) migration,requirements,existing-app-change gate
interaction-model 3 design/interaction-model.md skills/interaction-model.md (Stage 3, after the role model is CONFIRMED and before the first wireframe — design-artifacts.md Step 2b) skills/design-artifacts.md Step 3 (every wireframe's wf-contract block is copied from its row), skills/brd-to-build-plan.md Step 7 Navigation Wiring (home per role, wire points, page parameters), bin/lib/interaction-model-check.sh (the Stage-3 facts check_stage_3 fails on) migration,requirements,existing-app-change report
module-definition 3 architecture/modules/*/definition.md skills/architecture-blueprint.md Step 1 (:60 — the directory shape, one doc per module) THIS ROW IS ITS FIRST MECHANICAL CONSUMER — as of 2026-08-22 nothing executable read definition.md (improvement-plan P7 residue, the newest instance of the pattern this table exists to stop); prose consumers: skills/architecture-blueprint.md:43, skills/coverage-ledger.md:27 migration,requirements,existing-app-change report
build-plan 4 architecture/build-plan.md skills/brd-to-build-plan.md bin/gate-check.sh:955 (check_stage_4), project-bin/build-plan-status.sh (progress board), project-bin/coverage-preflight.sh:238 (the claims join) migration,requirements,existing-app-change gate
build-plan-html 4 architecture/build-plan.html project-bin/build-plan-status.sh --html (skills/brd-to-build-plan.md:47 — derived view, never hand-edited; refreshed by exec.sh and full gate-check.sh runs via --refresh) conversion-runbook.md §2 Stage-4 Surface row, parsed at bin/gate-check.sh:1284 migration,requirements,existing-app-change report
Expand Down
Loading