diff --git a/CHANGELOG.md b/CHANGELOG.md index 5842181a..43dfd52e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ Sections dated before 2026-09-19 predate the cycle and stay as they are. Named m ## Unreleased +- fix(progress-report): **`progress-report.py` no longer crashes with `TypeError: '<' not supported between instances of 'dict' and 'dict'` when two exec'd scripts share a leading number** (#513). `script_phases()` sorted `(number, phase dict)` pairs, so `10-data.mdl` and `10b-data-flows.mdl` (or one script logged under two paths), both matched to a plan phase, compared the dicts and every `exec.sh` run ended with `WARN: progress-report.html not updated`. It now sorts on (number, script name) only; an unmatched numbered script still takes the phase of the nearest lower-numbered matched one, ties broken by name. Fixture: `test-progress-report.sh` T17 (fails 3/3 on master). — jopterhorst (#513) - new(canvas): **`project-bin/mf-canvas-facts.py` prints how a microflow or nanoflow lies on the Studio Pro canvas: labels on top of each other, lines through objects, note lines leaving the side that faces away, flows that run right-to-left or curl, captions wider than their shape, and decision captions that ask the opposite of their true branch (SENSE).** It prints facts, one line each, and no verdicts; read-only (it reads units through `mxpatch.py`), exit 2 when a flow could not be read. `verify-module.sh` runs it per module as an info rung (1c, log `14-canvas-facts.log`; a missing script, Python or `.mpr`, an exit 2 or a timeout is an INFO row reading NOT MEASURED, never a gate). Fixture `tests/wave2/test-mf-canvas-facts.sh` runs on a model mxcli v0.27.0 wrote (units and `.mpr` captured); `test-verify-module-446.sh` gains V8 for the rung. Calibration is from Studio Pro 10.24 screenshots, not verified on Studio Pro 11. Field evidence, measured 2026-10-21 on one Mendix 10.24 app: 1,588 flows read in one pass; on the 36 flows an mxcli-built branch added or changed in one module, OVERLAP 19, LINE 27, NOTE-SIDE 17, LEFTWARD 3, TURN 4, SENSE 9 before repair — saingge - learn(preflight, teamserver-alignment): **a flow's return-type change and its callers' update go in one script (STOP row 33), and Studio Pro 10.24.18's "Merge feature branch" merges `origin/`, not the local branch.** On a copy of a Mendix 10.24.18 app with mxcli v0.27.0, the first script alone left `mx check` with CE0112 at a caller stored the way Studio Pro stores a call, while `check` and `exec` stayed silent; Studio Pro's own git log shows the merge of the remote branch. Text only; baseline-routed, +101 words. — saingge - fix(doctor, preflight, platform-link): **a clean `fix property-sets --dry-run` on Mendix below 10.24.24 is no longer reported as "every unit has the property names Studio Pro writes".** mxcli's property table starts at 10.24.24, so on 10.24.18 (mxcli v0.27.0) the dry-run printed "Every unit already has the property names Studio Pro writes" while Mendix's `mx diff` exited 129 on a unit mxcli had written (mendixlabs/mxcli#1595). `doctor.sh --property-sets`, `--if-stale` and `--quick` now WARN "NOT PROOF" and name `mx diff .mpr .mpr out.json` (exit 0) as the proof; the receipt records `unproven`; exit stays 0. `learned-detection-gaps.md` and `platform-link.md` said v0.26.0 closed the gap. Fixture P2b (11 cases). Baseline-routed, +18 words. — saingge diff --git a/project-bin/progress-report.py b/project-bin/progress-report.py index 8583b530..9c2440d4 100755 --- a/project-bin/progress-report.py +++ b/project-bin/progress-report.py @@ -781,7 +781,8 @@ def script_phases(runs, phases): direct[sc] = phase_for_script(sc, phases) m = re.match(r"0*(\d+)", os.path.splitext(os.path.basename(sc.strip().split()[-1]) if sc.strip() else "")[0]) num[sc] = int(m.group(1)) if m else None - known = sorted((num[k], direct[k]) for k in direct if direct[k] and num[k] is not None) + # sort on (number, script name) only: two scripts can share a leading number (10-a, 10b-b) and dicts do not compare (#513) + known = [(n_, pp) for n_, _k, pp in sorted(((num[k], k, direct[k]) for k in direct if direct[k] and num[k] is not None), key=lambda t: (t[0], t[1]))] out = {} for k, p in direct.items(): if not p and num[k] is not None: diff --git a/tests/wave2/test-progress-report.sh b/tests/wave2/test-progress-report.sh index 119fe4fc..852a0ff8 100755 --- a/tests/wave2/test-progress-report.sh +++ b/tests/wave2/test-progress-report.sh @@ -20,6 +20,8 @@ # T13 context windows are 200k or 1M only; "window reached" only at 98% of the real window. # T14 --session-log overrides discovery and is recorded in the footer; --help lists it and the rates file. # T15 --open matches this project's exact file URL (osascript stubbed); a generator crash leaves one WARN. +# T17 two scripts sharing a leading number (10-data.mdl, 10b-data-flows.mdl), each matched to a phase: +# the page is still written, no TypeError (#513); the unmatched 11- script takes the later 10 phase. # # Usage: bash tests/wave2/test-progress-report.sh /path/to/build-plan-status.sh @@ -314,6 +316,42 @@ CLAUDE_CONFIG_DIR="$CFG2" bash "$SCRIPT" "$P5" --html >/dev/null 2>&1 has "$P5/architecture/progress-report.html" "location.reload" "reload script present while active" hasnt "$R2" "location.reload" "no reload script when idle" +echo "T17 scripts sharing a leading number (#513)" +# BUILD-LOG rows: the exec.sh row captured in fixtures/verify-446/BUILD-LOG.captured.md, time and script replaced. +CAP="$ROOT/tests/wave2/fixtures/verify-446/BUILD-LOG.captured.md" +P6="$WORK/p6"; mkdir -p "$P6/architecture" "$P6/docs" +cat > "$P6/architecture/build-plan.md" <<'MD' +# Build plan + +### Phase 1 - Data + +| # | Kind | Step | Produces / Proves | Depends on | Skills | State | +|---|---|---|---|---|---|---| +| 1 | DATA | Create entities: `mdlsource/10-data.mdl` | Order | none | none | built | + +### Phase 2 - Flows + +| # | Kind | Step | Produces / Proves | Depends on | Skills | State | +|---|---|---|---|---|---|---| +| 2 | LOGIC | Create flows: `mdlsource/10b-data-flows.mdl` | ACT_Order | 1 | none | built | +MD +{ sed -n '1,/^|---|/p' "$CAP" + for x in "2026-10-19T09:00:00+0000 10-data.mdl" "2026-10-19T10:00:00+0000 10b-data-flows.mdl" "2026-10-19T11:00:00+0000 11-misc.mdl"; do + set -- $x; grep '^| 20' "$CAP" | head -1 | sed -e "s/^| [^|]* | \`[^\`]*\` |/| $1 | \`$2\` |/" + done; } > "$P6/docs/BUILD-LOG.md" +out6="$(CLAUDE_CONFIG_DIR="$EMPTY" bash "$SCRIPT" "$P6" --html 2>&1)" +printf '%s' "$out6" | grep -q "generator failed\|not updated\|TypeError" && bad "generator does not fail on a shared leading number" || ok "generator does not fail on a shared leading number" +printf '%s' "$out6" | grep -q "wrote architecture/progress-report.html" && ok "page written with two scripts numbered 10" || bad "page written with two scripts numbered 10" +IFS= read -r -d '' SP_PY <<'PYEOF' || true +import sys, importlib.util +spec = importlib.util.spec_from_file_location("pr", sys.argv[1] + "/progress-report.py"); m = importlib.util.module_from_spec(spec); spec.loader.exec_module(m) +ph = [{"id": "1", "name": "Data", "steps": [{"step": "mdlsource/10-data.mdl"}]}, {"id": "2", "name": "Flows", "steps": [{"step": "mdlsource/10b-data-flows.mdl"}]}] +runs = [{"script": s} for s in ("10b-data-flows.mdl", "10-data.mdl", "11-misc.mdl")] +print(m.script_phases(runs, ph)["11-misc.mdl"]["name"]) +PYEOF +SP="$("$PY" -c "$SP_PY" "$ROOT/project-bin" "$P6")" +[ "$SP" = "Flows" ] && ok "11- script takes the phase of the later-named 10 script (order by number, then name)" || bad "11- script takes the phase of the later-named 10 script (got: $SP)" + echo "T16 narrow-width CSS (phone)" GEN="$ROOT/project-bin/progress-report.py" grep -q "@media(max-width:520px){code,pre,.stage dd" "$GEN" && ok "narrow rules wrap code and values" || bad "narrow rules wrap code and values"