Skip to content

run3-speed: row groups, module-close gate, lint diff, hot reload - #581

Merged
MendixMau merged 13 commits into
masterfrom
perf/run3-speed
Oct 28, 2026
Merged

MendixMau merged 13 commits into
masterfrom
perf/run3-speed

Conversation

@MendixMau

@MendixMau MendixMau commented Oct 27, 2026 •

Copy link
Copy Markdown
Owner

What

Build-loop speed changes from cook-off ProcureFlow run 2/3: fewer agent calls per build-plan row, same gates.

  • Row groups (bin/row-groups.sh): plans one script per run of consecutive low-risk rows (domain, CRUD microflows, pages). Opt-in: the gate checks a script only when it has a -- rows: header or PROJECT.md says Row groups: on. A file-name number alone never triggers it, because the toolkit's documented script numbering (060-crm-page-….mdl under row 21) is not row numbering. When on, exec.sh refuses a script that splits a low-risk group or mixes high-risk rows (security, workflow, agents, REST, Java, Marketplace). "Built" means an explicit done state only (built, done, applied…; never planned or not built).
  • Module-close gate (same opt-in): refuses the first script past a module until docs/progress/module-close/<Module>.md records 0 lint ⚠ for the module, or its Page | Role LOOK table has a filled row. Running bin/module-close.sh or editing the report is never blocked.
  • Lint diff (exec.sh): prints findings this script added, plus earlier ones still unfixed and unaccepted (.claude/loop/lint-unacked.json). Those keep reading "lint rose" until fixed or baselined, so nothing goes silent. It no longer prints the whole backlog (~60 lines per exec).
  • Hot reload (reload.sh): reloads via --direct; finds the port from APP_URL or .docker/.env APP_PORT; works in the two-tree layout.
  • Skill iterative-build-loop.md (on demand): draft the ready set in parallel, apply serially; with row groups on, one script may carry several low-risk rows.

Evidence

Same 137-row ProcureFlow build plan, headless, Opus:

Arm Toolkit Result
C master-era toolkit stopped at row 121 after 650 min
D these changes (pre-rebase) 137/137 built, DONE at 378 min

D: slice 1 5/5 acceptance, 21/21 journeys; slice 2 12/12 acceptance, 23/23 journeys; 66 scripts for 137 rows. n = 1, run before the rebase.

A second A/B (master vs this head, a small purchase-requests app, Sonnet, same machine) is running on 2026-10-27/28 and measures time, tokens, scripts per row and UI quality: page fidelity, design audit, LOOK coverage, journeys.

Impact on existing projects after sync (Row groups off, the default)

  • Three scripts are installed; row-groups.sh check exits 3 ("not checked") for scripts without a -- rows: header, so nothing is refused.
  • The lint output shows new and still-unfixed findings instead of the whole backlog. Exit codes and the "lint rose" classification are unchanged.
  • No page check, LOOK obligation, page fidelity or design-audit step is removed or weakened.
  • Always-loaded skill text: 64,004 words before and after (render-routing.sh --check). iterative-build-loop.md is on demand.

Review

Independent review after the fix round: MERGE, once CI is green. Targeted fixtures pass 72/72:

  • test-row-groups (17), test-module-close (7, both layouts), test-exec-lint-diff (7), lint autorestore (17), lint-gate-accept (24).

Known follow-up, not a regression: a module counts as closed at lint=0 even when pages are still owed a LOOK. The LOOK obligation still reports through gate-check. Also still open on master: the LOOK ledger's seen.tsv hook is not installed by headless runs (0/44 pages seen in arm D).

🤖 Generated with Claude Code

https://claude.ai/code/session_01XM8AzTxUsYcriPXQ58ZPEV

MendixMau and others added 13 commits October 27, 2026 17:47
…d via --direct

Local experiment branch for cook-off run 3 (arm D). Not for the shared toolkit
unless promoted.

- project-bin/reload.sh: mxcli docker reload --direct (no curl in the JRE image),
  --css, --restart; falls back to a restart and says so.
- project-bin/module-close.sh <Module>: lint (module's warnings), score, mx check,
  verify-module --quick, LOOK owed, into docs/progress/module-close/<Module>.md.
- iterative-build-loop.md: low-risk rows of one module may share a script; diff
  only when a landed apply wrote a document the draft also writes.
- install-manifest: both new scripts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XM8AzTxUsYcriPXQ58ZPEV
…plan; exec.sh refuses split low-risk groups and mixed high-risk rows

Enforced in exec.sh, before lock and snapshot, so it holds for every caller, not only agents
that read the prompt. Override MXTK_ROWS_OVERRIDE=<reason>, logged in the BUILD-LOG row.
On ProcureFlow's plan: 58 scripts for 78 BUILD rows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XM8AzTxUsYcriPXQ58ZPEV
… previous exec)

Lint stays per exec (user decision 2026-10-27). The unrefreshable baseline made every later exec
'ROSE' with a ~60-line dump; run 2 arm C logged ROSE on 123/147 rows and nobody read the 6 real
SEC010s. Now: 0 new -> ✅ row, debt named in one line; N new -> those findings only (max 10).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XM8AzTxUsYcriPXQ58ZPEV
…fore CLAUDE.md

Run 2 arm D waited 284 s on localhost:8080 while the app ran on 8084.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XM8AzTxUsYcriPXQ58ZPEV
…dule-close report

Run 2 arm D closed Audit and StyleGallery but entered slice 2 without closing Procurement;
the close was a prompt rule. Tested on a copy of D's plan: row 50 refused (Procurement owed),
passes once module-close/Procurement.md exists; row 30 unaffected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XM8AzTxUsYcriPXQ58ZPEV
check-portability flagged the run-3 code: a hard-coded python3 in exec.sh's
lint diff and in row-groups.sh, and a heredoc inside $(...) (bash 3.2).
Both now resolve the interpreter through _common.sh's resolve_py; the lint
snippet is read into a variable first. No Python: row-groups exits 3
("not checked"), which exec.sh already passes through.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XM8AzTxUsYcriPXQ58ZPEV
…ps: on`, never on a bare file-name number

brd-to-build-plan.md numbers scripts on their own sequence (060-crm-page-... under row 21,
01-{module}-domain.mdl per module). On the real SampleDashboards plan
(tests/wave2/fixtures/build-plan-claims-real.md) 16-cutover.mdl is row 26, and the gate refused
it as row 16 for leaving rows 14 and 15 unbuilt. Every project synced would have hit this.
Now a script is checked when it names its rows in a header, or when PROJECT.md says
`Row groups: on` (then its file name carries the row number); otherwise exit 3, not checked.
The module-close part follows the same switch.

Fixture: tests/wave2/test-row-groups.sh G1-G3 (3 of 6 fail on the previous row-groups.sh).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013jQvaonym9V1wKKfAEPmRr
Any State other than the literal "not built" counted as built, so `planned`, `partial`,
`**blocked**` and an empty State all passed, and the split-group check never fired on a plan
that uses `planned` (the toolkit's own fixtures do). Now the prefixes progress-report.py's
classify_state counts as built (built, done, complete, verified, proven, passed, plus applied).
A BUILD-LOG "applied" row marks rows through the applied script's `-- rows:` header (found
under mdlsource/, done- prefix stripped), or its file name only with `Row groups: on`.

Fixture: test-row-groups.sh S1 (3 of 5 fail on the previous commit).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013jQvaonym9V1wKKfAEPmRr
…report says, not by the file existing

module-close.sh writes its report even when it finds lint findings, and the report's LOOK table
starts empty, so the gate's file-exists test passed a module whose pages nobody opened (run 3
arm D: 0 of 44 pages seen). module-close.sh now writes `<!-- mxtk-module-close: lint=N vm=RC -->`
and row-groups.sh counts a module as closed at lint=0 or with at least one filled row in the
Page | Role table; otherwise it refuses and names both ways out. The remedy (running
module-close.sh, filling the table) is never blocked.

Golden input: tests/wave2/fixtures/module-close/lint-pedapp-v0.28.0.txt, real `mxcli lint` output
from mxcli v0.28.0 on a scratch copy of testdata/pedapp with two bad microflows (3 warnings).
Fixtures: test-row-groups.sh M1 (2 of 6 fail on the previous commit), test-module-close.sh C1
(2 of 4 fail on the PR head's module-close.sh).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013jQvaonym9V1wKKfAEPmRr
….sh; two-tree checkouts work

Both scripts did `cd ..; ls ./*.mpr` and called `./mxcli`, so a two-tree checkout (.mpr under
app/) stopped with "no .mpr here", and a project in that layout could never write the
module-close report the row-group gate asks for. They now source _common.sh (find_mpr,
find_project_mxcli, then mxcli on PATH), like every other project-bin script. reload.sh reads
.docker/ beside the .mpr, where mxcli writes it (cmd/mxcli/docker/build.go: Dir(projectPath)).

Fixture: test-module-close.sh C2 and R1 (both layouts; C2 and R1-app fail on the previous commit).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013jQvaonym9V1wKKfAEPmRr
… fixed or accepted

The per-script diff compared each exec only with the one before it, so a finding an agent
ignored once read "0 new" on every later exec: LINT_RC 0, a clean BUILD-LOG row, and no user
ever accepted the debt (lint-gate --update-baseline is the user's call). Findings reported as
new are now carried in .claude/loop/lint-unacked.json and printed and counted again while they
are still in lint-last.json (fixed or accepted into the baseline = gone). LINT_DETAIL keeps the
"lint rose" wording. A diff that cannot run (no usable Python) reports the rise vs baseline
instead of passing silently.

metrics-ledger.sh classifies from exec's status text, which LINT_RC drives, so a carried
finding keeps its row "applied-lint-rose".

Fixture: tests/wave2/test-exec-lint-diff.sh D1-D6 (D1, D2 x2, D6 fail on the PR head's exec.sh).
Also run: test-exec-lint-autorestore 17/0, test-lint-gate-accept 24/0, test-lint-receipt-revert 79/0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013jQvaonym9V1wKKfAEPmRr
run_lint_gate downgraded a lint-gate ROSE to "0 new this script" when lint-last.json was
missing/unparseable/without new_findings, or itemised nothing with no lint-prev-exec.json.
Now the diff prints nothing in those cases and exec keeps LINT_RC and the "lint rose" wording.
Fixes test-exec-gate-retry I, I2, I2e, I3; adds D7 to test-exec-lint-diff.sh.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013jQvaonym9V1wKKfAEPmRr
Copying lint-last.json to lint-prev-exec.json after a rise the diff could not itemise made
those findings old debt on the next exec ("0 new"). Skip the copy then; D8 covers it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013jQvaonym9V1wKKfAEPmRr
@MendixMau
MendixMau merged commit 8ade505 into master Oct 28, 2026
2 checks passed
MendixMau pushed a commit that referenced this pull request Oct 28, 2026
…s no plan row

In the #581 A/B every script of the Row-groups-on build was numbered on its
own sequence with no `-- rows:` header, so row-groups.sh exited 3 ("not
checked") for all 31 and grouping never ran. Opted in, that case now exits 4
with a hint; exec.sh prints it as a warning and notes it in the BUILD-LOG row.
Never a refusal. Zero always-loaded words.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013jQvaonym9V1wKKfAEPmRr
MendixMau added a commit that referenced this pull request Oct 28, 2026
…ow (#582)

With `Row groups: on`, a script that names no build-plan BUILD row now exits 4 from row-groups.sh check with a `-- rows: N` hint; exec.sh prints it as a warning and notes it in the BUILD-LOG row. Never a refusal; Row groups off is unchanged. In the #581 A/B all 31 scripts of the opted-in build read "not checked", so grouping never ran. Fixture: test-row-groups.sh G4.

Review: MERGE — no findings; exit 4 warns only, never refuses, Row groups off unchanged

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013jQvaonym9V1wKKfAEPmRr
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