Repository navigation
release: promote release/v1.0 to master for the 1.0 tag - #466
Merged
Merged
Conversation
…urrent (#437) fix(build-plan-status) # PR 437 review r1 (self-review, small delta) Verdict: APPROVE - --refresh path: parse into TMP_JSON as before; only when json already exists and --json not given, compare minus generatedAt, cp on difference. Never creates. Correct. - Same parser as --json (parse_plan), so content identical to an explicit run. - Test T11 covers flip, no-rewrite, no-create. 70/70 pass. - Docs/routing surfaces in sync (render-routing --check).
…rom #309 by @AlvinnYeo) (#450) Lands #309 by @AlvinnYeo. Independent review: APPROVE. # PR 450 (lands #309 by @AlvinnYeo) review r1 (delta self-review) Verdict: APPROVE - w1 must-fix 1 done: every #244 status now says closed 2026-10-14 by #322, upstream root cause remains (gh issue view 244 confirms). No STOP-table wording left in the note. - w1 must-fix 2 done: body drops the stale byte-identical claim. - Inbox lane only (contrib/inbox + one CHANGELOG line, credit " — AlvinnYeo"); nothing loaded or routed; zero behaviour risk. - Optional word limits not enforced (inbox notes, maintainer call).
… language, a switch a normal role can use (from #387 by @CarlesZ) (#449) Lands #387 by @CarlesZ. Independent review: APPROVE. # PR 449 (lands #387 by @CarlesZ) review r1 (delta self-review) Verdict: APPROVE - Content identical to #387 head reviewed APPROVE in 387-w1.md; delta = merge from release/v1.0, routing re-render (--check in sync), CHANGELOG credit " — CarlesZ" and roles "architect, mdl + review". - Squash message scoped; no "not grantable"/"cannot be granted". - Leak guard, private citations, PR discipline clean.
Process map in the BRD report. Independent review r2: APPROVE. Verdict: APPROVE PR #438 re-review at 98f452f (base release/v1.0). Earlier review: 438.md at 11cfe06 (CHANGES, 7 must-fix). Checked in a detached worktree /tmp/rv438, now removed. Tests: test-process-map-field 52 ok, test-process-map 74 ok, test-brd-report-process-map 73 ok, test-interview-gate 11, test-interview-mode 76, all 0 FAIL. (The tests live in tests/wave2/; there is no tests/test-process-map*.sh.) check-portability clean (280 files). check-pr-discipline origin/release/v1.0 clean. No client, brand or project names in the added lines. ## Earlier must-fix items 1. Map clipped on the right: FIXED. fit() now scales every board in embed mode (template `scalable = long || EMBED`), the frame gets a computed first height, the host ignores a height post under 80 px, and a new render check passes: `FIT:inside:905/970:zoom0.83`. 2. "Scroll sideways" hint on a scaled long process: FIXED. The hint is built per case: "scaled to fit" when zoom < 1, "split it in two" above 12 steps, "scroll sideways" only when it still overflows. The embed path gets its own hint, hidden unless it overflows. 3. Stage 0/1 gate with only sme-questions.md got no report: FIXED. brd-report counts collector questions when there are no BRDs and renders a questions-only page ("Open questions, stage N view"); exit 3 stays for no BRDs and no questions. Tests cover the gate at stage 1 writing the file, the question in it, the header, and the exit-3 case. 4. Migrate lost judged placements on a collision: FIXED. An old judged entry replaces a current derived one and prints a note. When both are judged and differ, the current one is kept and a note names the .bak. Test "the judged placement won" (judged P1-07). 5. Judged entries with no target: FIXED. anchorEntries leaves them out and lists them, and counts entries with no id. anchor now warns on a judged entry with empty targets and does not count it, so it ends up derived or unanchored, never silently "global". 6. Stale questions-report.sh in docs/pipeline-walks.html: FIXED (now bin/brd-report.sh). 7. Migrate tests: FIXED. New cases: old shape to targets (step/page/bucket/string target, compared as objects), the .bak to .bak.1 path, the collision, the empty-target and no-id entries, a second migrate that leaves the files byte-identical. Also the whole-app collapse above 8, --stage passthrough, and the CAUSE line for page names. Nice-to-haves also done: the duplicate I6 line, .bak.N, write order (anchors file, then model key, then rename, each write atomic through tmp+rename), only the model key is patched, PAGE-MAP precedence matching the docs plus a warning for a row naming a missing file, ResizeObserver on wrap, "Questions shown: up to stage N" header, the CAC-3 line in checkpoint-architecture. Not done: a staleness FAIL for the embedded map in brd-report (fine as a follow-up). ## New must-fix items None. ## Follow-ups (not blocking) - Model key patch can hit the wrong key. process-map.js migrate replaces the FIRST `"anchors": "<string>"` anywhere in the model text. Reproduced: a model with `{"meta":{"anchors":"notes.txt"}, ..., "anchors":"design/old/a.json"}` gets meta rewritten, the top-level key still names the old file, which was just moved to .bak. The output still says "only that key was changed". The schema has the key only at top level, so normal models never hit this. Fix: after the replace, `JSON.parse(newText).anchors === want`, else fall back to the re-serialize branch. - `"targets": "P1-02"` (a string, not an array) in an old file crashes migrate with a TypeError stack trace at process-map.js:89. Nothing is written, so no data is lost. Wrap a non-array value in an array, or die() with a clear message. - Entries in the CURRENT anchors file with empty targets (an `anchor --json` skeleton pasted in but not filled) are dropped silently on migrate (haveE.noTarget/noId are never printed). anchor lists them as unanchored again, so nothing is lost, but print them for symmetry. - brd-report runs open-questions.sh one extra time to count when there are no BRDs. Harmless.
Follow-up to #438 review. Review: APPROVE. # PR 453 (process-map migrate follow-ups to #438 r2) review r1 (delta self-review) Verdict: APPROVE - Nested-key: regex result is kept only if JSON.parse(patched).anchors === want; otherwise full re-write. Output claim now matches what happened. Test with a nested meta.anchors look-alike plus a plain-model control. - String targets: one line in anchorEntries wraps a non-array in a list; test checks it becomes one step target and rc 0. - Unfilled current entries: listed with ids in the output; test names F001:Q77. - 6 of 7 new checks fail on the pre-fix code (worker report). Field 59, process-map 74, brd-report 73, all 0 FAIL; portability and discipline clean. - No behaviour change outside migrate; no names in the diff.
…434) Progress report. Independent review r6: APPROVE. Verdict: APPROVE # PR #434 progress report, round 6 review at 8977271 (base release/v1.0 32266fa; delta 0a908a3..8977271, one commit) 1. Gitignore the cache: met. init (grep on the exact line), doctor (check-ignore, same shape as .doctor-receipt/toolkit.env), sync-project (WIRED + .git + check-ignore, --dry-run prints "Would add" and writes nothing). New test-progress-report-gitignore.sh 6/0. My own runs: pristine git dir gets no .gitignore; wired dir with --dry-run gets none; two real syncs leave exactly 1 line. 2. Cache header carries realpath root and rules (share=0.25;calls=20); mismatch reads cold: met. F15 covers a cp -R copy (cold, lists no sessions), the original stays warm, a changed rules field rebuilds. 3. Cold skips recorded as "no" with mtime and size: met. PROGRESS_REPORT_SCAN_MAX_FILES override added. F16: cap 2, cold reads 2, warm reads <= 2, files past the cap stored as no. 4. Ledger decides the switch: met. stage_switch() drops the session exec.sh and build-skill candidates when switch_from_runs() returns a row; timed sign-off stays; mdlsource only without rows. F17 plus a no-ledger control. Footer: met. "5 = a build skill" replaced by the ledger/sign-off rule, session signals only with no exec log rows. Changed assertion 11:50 -> 12:00: intended, not weakened. Fixture has BUILD-LOG rows (first 12 Oct 10:00) and a timed sign-off 10 Oct 12:00; the session exec.sh at 11:50 is now ignored (item 4), so earliest of first row and timed sign-off = 12:00. ## Tests on the head (detached worktree) test-progress-report 101/0, test-progress-report-field-fixes 101/0 (F1-F17), test-progress-report-gitignore 6/0, test-build-plan-json 70/0, test-build-plan-status-args 6/0 (both given the absolute build-plan-status.sh path). ## Author's claim about pre-existing failures: confirmed, PR makes nothing worse test-bug12-sync: base 55/6, head 55/6, the same six FAILs (T0 pristine WRITTEN to, T0 false alarm exit 3, intake rewrite, second sync rewrote, duplicated CLAUDE.md, pointer-only CLAUDE.md). Normalized ok/FAIL lists are identical, and the T0 detail differs only in timestamps and worktree paths (no .gitignore write in T0). test-doctor-gate-selftest: base 26/2, head 26/2, same two (case 7 no-mxbuild: NOT RUN expected). ## New must-fix None. ## Notes (not blocking) 1. Tracked cache gives duplicate lines. If .claude/progress-report-sessions.txt was already committed or staged (possible on projects that ran an earlier PR head), `git check-ignore` reports tracked files as not ignored. Reproduced: sync-project then appends the block on every run (3 lines after 2 extra syncs) and counts a change each time. doctor has the same pre-existing flaw for .doctor-receipt/toolkit.env. Fix: `check-ignore -q --no-index`, and tell the user to `git rm --cached` the file. 2. doctor writes the line into any directory with .git, wired or not. This matches the existing receipt/toolkit.env lines, so it is not new behaviour. 3. Earlier r5 notes 1 (ancestor-slug folders not share-ruled) and 3 (no timing budget test) still stand.
#454) Opus review 454-r1: APPROVE # PR #454 review, round 1 PR: fix(progress-report): polish counts, stage 0 pending, calm no-log line Base: release/v1.0, head 98649f9 (branch fix/progress-report-polish). Up to date with base, merges clean. Verdict: APPROVE ## Must-fix None. ## What I checked 1. **"done" rows count as built (build-plan-status.sh).** Both `classify()` copies (the plan-table rollup at line 303 and the HTML tally at line 559) now use `startswith(("built", "done"))`, with "not built" still checked first. progress-report.py `classify_state` already treats `done` as built, so the two "done" readings now match. `**done** 2026-...` and `Done` both count in both parsers (probe run). `not done` does not match `done` in either. 2. **Future-stage placeholder FAIL shows PENDING (progress-report.py ~1475).** It only relabels when all of these hold: status is `FAIL`, the detail contains "placeholder", `info["current"]` is a known stage, and the stage index is strictly greater than the current one. The current stage and earlier stages keep FAIL. With an unknown current stage (no or unparseable PROJECT.md) nothing is relabelled, which is the safe default. Only two gate-check.sh FAIL messages contain "placeholder" (lines 827 and 835, both the `Confirmed by:` sign-off check), so a substantive failure on a later stage stays FAIL unless its detail happens to say "placeholder". The stage badge for a later stage is already "pending" (`stage_state` comes from PROJECT.md), so the sub line now agrees with the badge. 3. **Calm no-log line.** The orange `note-card` box is replaced by `<p class=nolog>`. The looked-in counts and the regenerate hint move into the `How this was measured` details. That block always exists when there is no log, so the line's pointer to it never dangles. `--no-log` drops the "Looked in" text and the regenerate hint. The ledger card still renders under the second `if not L:`. 4. **Test-moments wording.** Every change = "Gate check plus only the tests that touch the change" (impacted only). Build complete = "Everything, once". Before every later deploy = "Everything". Module done now names the per-flow unit tests chosen at CAC-5, listed in the module brief. That matches the rule (impacted-only per change, full run at build complete and before each deploy, unit-test choice at CAC-5) and the `Unit flows:` brief list from #417 and its follow-up. 5. **Prose and hygiene.** No em or en dashes in added prose. The only one is the CHANGELOG attribution separator ` — field review of the #434 report`, which follows the existing entry convention. No private or client names: the fixtures use Orders, Billing and Kickoff. No conflict markers. The CHANGELOG entry sits under Unreleased, is accurate and is credited, and check-pr-discipline is clean. ## Tests run (detached worktree at 98649f9, removed afterwards) | Test | Result | |---|---| | tests/wave2/test-progress-report-polish.sh (new) | 20 / 0 | | tests/wave2/test-progress-report.sh | 101 / 0 | | tests/wave2/test-progress-report-field-fixes.sh | 101 / 0 | | tests/wave2/test-progress-report-gitignore.sh | 6 / 0 | | tests/wave2/test-build-plan-json.sh | 70 / 0 | | tests/wave2/test-build-plan-status-args.sh | 6 / 0 | | bin/check-portability.sh, bin/check-pr-discipline.sh | clean | The new test against the base scripts (project-bin from release/v1.0): 12 of 20 fail. So the test pins the new behaviour. The 8 that pass on base are the pre-existing parts: report written, Stage 5 card count, Phase 2 count, and the `--no-log` "never claims it looked" checks. Discovery is already off under `--no-log`, which makes the new `and not args.no_log` guard defensive only. ## Nice-to-haves 1. **"One plan count" holds for `done`, not for the rest of the vocabulary.** progress-report.py also reads `complete`, `verified`, `proven` and `passed` as built, and `partial`, `in progress`, `started` and `wip` as partial. build-plan-status.sh reads all of these as unknown. That includes `partial` and `blocked`, which brd-to-build-plan.md line 333 lists as legal State values. Probe: one plan with rows `complete (...)`, `**done** ...` and `Done` gives "3 of 3 rows built" on the card. The plan header shows "2 built, 1 with a State cell this page cannot read" and the phase is labelled "unknown". This disagreement predates the PR, but the PR body promises the card, header and badge agree. Follow-up: share one vocabulary, or at least add `partial`/`blocked` to the sh classifier as non-built. Alternatively, reword the CHANGELOG to say "done rows" only. 2. **Narrow the PENDING relabel to `states[s] == "pending"`.** Today the relabel also applies to a later stage that PROJECT.md lists as passed, when the gate page is not stale. This happens when the current stage moves back during a change round. A real conflict, "passed but the sign-off is still a placeholder", would then read PENDING. Adding the state check costs one condition. 3. **Test gaps for item 2.** Add one test where a non-placeholder FAIL on a later stage stays FAIL, and one where an unknown current stage (no stage line) keeps FAIL. 4. **Module done wording is a little narrow.** Unit tests can also be switched on per module (`Unit modules:` in PROJECT.md, `Unit tests: yes` in the brief) or per project (`Unit tests default: yes`). "listed in the module brief" covers the per-flow default only. A shorter alternative: "plus the unit tests the CAC-5 test strategy selects for the module". 5. **Reading order.** The calm line renders after the collapsed details block, so the pointer "details in How this was measured" points upward. Consider writing the line first. 6. **Pre-existing, outside this PR's scope.** `build-plan-status.sh` called by a relative path (`bash project-bin/build-plan-status.sh <proj> --html`) says "progress-report.py is not next to this script". GEN is `$(dirname "${BASH_SOURCE[0]}")/...`, which probably breaks after the script changes directory (cause not confirmed). An absolute path works.
…gitignore) (#455) Opus review 455-r1 + self r2: APPROVE # PR #455 review round 2 (self, small delta on Opus r1) Verdict: APPROVE Opus r1 (455-r1.md) approved everything except one must-fix: the em dash on bin/lib/skill-routing.tsv:164 is the When-cell trigger separator that the agent stubs cut on, not prose. Removing it put the whole tail into three agent stubs. Delta (5f512a0, cea8e85): - Separator restored on the multilingual row; bin/render-routing.sh re-run. README.md, ROUTING.md and the three agent tables are now identical to release/v1.0 (diff empty), so the PR no longer changes generated content. - CHANGELOG entry reworded: the skill loses its dashes, the routing row keeps its trigger separator; the internal review file name is gone. - render-routing --check 0, check-skill-routing 0, check-pr-discipline clean, client-data scan clean. Doctor stale-hook check, gitignore worktree/tracked handling and tests were verified in r1 and are unchanged. Follow-ups (post-release): stale-hook regex should match only $TOOLKIT/ and ${TOOLKIT}/; hide the empty section header; doctor still tests -d .git for the doctor-receipt and toolkit.env lines (same -e fix).
… the phase card (#456) Opus review 456-r1: APPROVE # PR #456 review, round 1 PR: fix(build-plan-status): read every skill-listed State like the phase card Base release/v1.0, head 89a21a7 (branch fix/plan-state-values, one commit, base is an ancestor, no conflicts) Verdict: APPROVE ## Must-fix None. ## What was checked **Classifiers agree.** I ran 48 State values through `classify_state` (progress-report.py) and both copies of `classify` (build-plan-status.sh) and mapped the buckets: plan built/partial/skipped to card built/partial/skip; plan not built/blocked/pending a person to card todo. - Every prefix value gives the same bucket: built, done, done <date>, complete(d), verified, proven, passed, partial(ly built), in progress, started, wip, not built, not done, not started, planned, to do, todo, open, pending, failed, blocked (also "blocked (waiting)"), not needed, n/a, dropped, skipped, waived, deferred. - Case is lowered before matching. `**bold**` is stripped. "built (partial)" is built in both. - Order is safe. "not built", "not done" and "not started" are matched before "built", "done" and "started". "pending a person" is matched before "pending". - The only differences are deliberate: an unreadable value (leading date such as "2026-10-01 built", backticked `` `built` ``, an emoji prefix, an empty cell) is todo on the card and None (reported as unreadable) on the plan page. The PR says it does this. - One trivial edge: "** built**". The card strips spaces before removing the `**`, so it reads todo; the plan reads built. The card's own md parser (`_cells`) already strips `**`, so this can only happen on the JSON path. Not worth a change. **Blocked.** The card puts blocked in todo. The plan counts blocked as not built in both the header and the rollup, and names it with "(N of them blocked)". This matches. **Nothing that counted as built drops out.** Before the PR, built meant the lowered value starts with "built" or "done". None of the new earlier checks (pending a person, blocked, the not-built list, the skip list) can match a value that starts with "built" or "done". The new code also strips whitespace and `**`, so it can only count more rows as built, never fewer. **Relabel.** `stage_state` already returns "pending" for any stage after the current one that is not in the passed list. So the new `states[s] == "pending"` guard changes one case only: a later stage that PROJECT.md lists as passed while gate-check shows a placeholder FAIL. That case now stays FAIL, which is right. A WAIVED stage never has a FAIL status, so it cannot reach this branch. gate-check.sh's verdict words (PASS/FAIL/MANUAL/PENDING/WAIVED) are unchanged. Narrow and harmless. **Downstream.** No script in bin/ or project-bin/ reads the phase `state` from build-plan.json to make a decision. gate-check.sh and exec.sh only call `--refresh`, and progress-report.py reads the row States, not the phase rollup. test-build-plan-json passes. **Tests.** - I ran P5 against the base scripts: 7 of its 8 checks fail, as the PR says, and the P1 header check also fails because the header format changed. - The P2b checks pass on base. The PR body says this honestly: they guard behaviour the base already had. - All suites pass on head: progress-report 101/0, field-fixes 101/0, gitignore 15/0, polish 32/0, build-plan-json 70/0, build-plan-status-args 6/0. - check-portability is clean (285 files). check-pr-discipline against origin/release/v1.0 is clean. **Hygiene.** No em or en dashes in new prose. The only one is the repo's usual "— MendixMau" credit separator in the CHANGELOG. No private names, no conflict markers. The CHANGELOG entry is accurate but long. **Screens** (/tmp/states-out/before.png, after.png): - Before: "1 built, 2 not built, 5 with a State cell this page cannot read", and every phase badge says unknown. - After: "3 built, 1 partial, 4 not built (2 of them blocked)" with built, in progress and not built badges. This matches the card's "3 of 8 rows built, 1 partial". ## Nice-to-haves (follow-up, not blocking) 1. **"Not needed" rows.** The per-phase line on the plan page ("N of M rows built") and the header row count both include not-needed rows. The card leaves them out (`n = len(steps) - skip`). A phase with one not-needed row reads "3 of 4" on the plan page and "3 of 3" on the card. Excluding skipped rows from M would make the "one count" claim hold here too. 2. **"0 partial" is always printed.** The header now always shows it (see the P1 assertion). The card adds partial only when it is non-zero. Printing it only when non-zero would match the card and keep the header calmer. 3. **No test for the real narrowing.** Nothing exercises the case the relabel change actually affects: a later stage marked passed in PROJECT.md with a placeholder FAIL on the gate page. Add one P2b case. 4. **P5 coverage gaps.** P5 does not cover `not needed`, `in progress`, `**built**` (bold) or `done`. Adding one row each would pin the skip rollup and bold stripping. 5. **Drift note misses blocked and partial rows.** The drift note fires only for "not built". A blocked or partial row whose scripts are all `done-` gets no note. This is minor and existed before the PR. 6. **Stale card stamp.** The card's stamp still reads "anything else = not built" and does not mention not needed rows. The wording predates this PR. 7. **Duplicate classifier.** The two `classify` copies in build-plan-status.sh are now 8 lines each and must change together. One shared helper would remove that drift risk. Fine to leave for after v1.0.
…#457) fix(progress-report): fold the full plan tables under the phase cards Verdict: APPROVE PR #457 fix(progress-report): fold the full plan tables under the phase cards Branch fix/progress-report-collapse-plan-tables at b1c7921, base release/v1.0. GitHub reports MERGEABLE. ## Must-fix None. ## Checked and fine - HTML of the fold. The fragment sits in `<details id=plan-tables class='card frag'>` with one summary and one closing `</details>` (progress-report.py lines 1612 to 1615). On the real render (/tmp/do-copy/architecture/progress-report.html) there are 33 `<details>` and 33 `</details>`, and an HTML parser walk from the plan-tables tag to `<footer>` leaves nothing unclosed and no mismatched end tags. - No empty card. With no plan fragment the branch writes nothing: I rendered a bare project with `--no-log` and got 0 hits for `plan-tables`, "No plan to summarise yet" still shows, and details stay balanced (9/9). Before this PR an empty `<div id=plan-tables>` was written. - Jump link. "Jump to the plan tables" (`href='#plan-tables'`, line 1593) only renders inside the plan-present branch. `openTarget()` sets `open` on a targeted DETAILS element, so the link opens the fold and lands on it. The autoreload remembers `details[id]`, and plan-tables has an id, so an opened fold stays open across the 30 s refresh. - Dark mode. The card uses the same `--card`/`--line`/`--ink` tokens as every other card, and the existing `.frag ...` rules still apply because the class is kept. Nothing colour-specific was added. - Print. The report has no print stylesheet, so print renders the same as the screen: the fold is closed and prints as one summary line. The phase cards were already folded `<details>` before this PR, so print stays consistent with them. (See nice-to-have.) - Screenshot /tmp/do-457.png: the 14 phase cards, then one folded line "Full plan tables and views A and B" with its subline, then the footer "How these numbers are derived" right below. The page is short now and the footer can be reached. - CHANGELOG line. Accurate: a closed `<details>` card below the phase cards, nothing written without a fragment, test P6 named, credited "MendixMau". The only dash in it is the house credit separator before "MendixMau" that every entry uses. - Test P6 checks the details wrapper, that it is closed by default, that the jump link is still there, and that the old `<div>` wrapper is gone. - Hygiene. No client, brand or app names in the diff, no personal paths, no conflict markers. `bin/check-pr-discipline.sh origin/release/v1.0`: clean. ## Tests Run in a detached worktree with the stub `gh` first on PATH. | Test | Result | |---|---| | tests/wave2/test-progress-report.sh (arg: project-bin/build-plan-status.sh) | PASS=101 FAIL=0 | | tests/wave2/test-progress-report-polish.sh | PASS=36 FAIL=0 (P6 included) | | tests/wave2/test-progress-report-field-fixes.sh | PASS=101 FAIL=0 | | tests/wave2/test-progress-report-gitignore.sh | PASS=15 FAIL=0 | | tests/wave2/test-build-plan-status-args.sh (arg: build-plan-status.sh) | 6 passed, 0 failed | | tests/*/test-build-plan-json.sh | PASS=70 FAIL=0 | | Manual: render without fragment | no plan-tables, no jump link, details 9/9 | | bin/check-pr-discipline.sh origin/release/v1.0 | clean | ## Nice-to-have 1. P6 does not test the no-fragment case. Add `hasnt "$R_nofrag" "plan-tables"` on a project without build-plan.md so the "nothing is written" claim is pinned down. 2. Edge case: if progress-report.py is called directly with `--plan-json` and no `--fragment`, the jump link points at an id that does not exist (before, it pointed at an empty div). build-plan-status.sh always passes both, so this does not happen in normal use. Hiding the link when `args.fragment` is empty would close it. 3. Print: a reader who prints the report now gets no plan tables. If printing the full plan matters, a small `@media print{details#plan-tables>*{display:block}}` or a `beforeprint` handler that opens it would restore that. Optional. 4. `<div class=sub>` inside `<summary>` is not strictly valid content for a summary (phrasing content only). The phase cards already do the same and browsers render it fine, so this is a consistency note only.
…ocal runtime (#439) learn(load-test): the 6-session cap and 2-4 h stop of an unlicensed local runtime Verdict: APPROVE r2 on head 6bf0b6b. The r1 must-fix is done: the skill header now says every trap was hit in the 16 load runs except trap 9, which comes from parallel journey runs against local apps. The rest of r1 stands: the traps run 1 to 11 with no gaps, there are no stale references to the old numbers, the added prose has no dashes, and check-pr-discipline is clean.
…n an infra exit (#451) fix(exec): pause the project watcher during the gate and retry once on an infra exit Verdict: APPROVE r3 on head fd90628. The r2 must-fix is done (f4cc5ae): the -p value is no longer cut at the first space; the longest run of words that exists on disk is kept, so a duplicated "<proj> copy" watcher is not matched, and this project's own watcher under a path with a space is. The r2 nice-to-have 1 is also done: the pause record is written before the SIGSTOP loop. Test D now runs a "<proj> copy" sibling with its own .mpr; it fails on 0c14d17 (22/24) and passes with the fix (24/24). lock-watch 84/84, crash-rollback 13/13, check-scripts, check-portability and check-pr-discipline clean. Base release/v1.0 merged (CHANGELOG keep-both). Everything else in r2 stands.
fix(exec): keep the gate's full mxbuild output in a per-run log. Independent review: APPROVE. Verdict: APPROVE r3 on head after merging the fixed #451 (f4cc5ae, fd90628) into fix/gate-full-log; the merge was clean. r2 APPROVE stands. gate-log 24/24 and gate-retry 24/24 on the merged head, check-pr-discipline clean. Merges after #451.
…seed instead of ending BLIND (#463) fix(exec): restore toolkit lint rules after mxcli's session-start re-seed (#461). Independent review: APPROVE; follow-up 1 (installer output trimmed after capture) fixed in 99faa67. # PR #463 review, round 1 fix(exec): restore toolkit lint rules after mxcli's session-start re-seed instead of ending BLIND (fixes #461) - Branch `fix/lint-rules-revert`, head `4fbb0692`, merge base `02e4366e` (= current `origin/release/v1.0`) - Files: `project-bin/exec.sh` (+24), `tests/wave2/test-exec-lint-autorestore.sh` (new, 91 lines), `CHANGELOG.md` (+1) - Reviewed in a detached worktree at /tmp/rv463, base at /tmp/rvbase, `/tmp/ghstub` first on PATH, macOS bash 3.2.57 ## What it does When `bin/lint-gate.sh` exits 1 with its "BLIND, lint rules reverted to stock" line, `run_lint_gate` finds the toolkit root (`find_toolkit_root`: `$MXTK_ROOT` or the CLAUDE.local.md row), runs `mxtk_install_lint_rules <tk> <project> 0 ""` in a subshell, shows up to 12 lines of its output, and runs lint-gate once more. If the rerun is not BLIND, the BUILD-LOG detail gets `; toolkit lint rules auto-restored after a stock re-seed (<names>)`. `MXTK_NO_LINT_RESTORE=1` skips the whole block. No toolkit root found: unchanged BLIND. ## Tests | Test | head | base | |---|---|---| | test-exec-lint-autorestore.sh (new) | 17 pass, 0 fail | 7 pass, **10 fail** (run against base exec.sh: T2, T3, T6 fail as expected; T1, T4, T5 pass, correctly, they assert unchanged behaviour) | | test-lint-receipt-revert.sh | 54 pass, 1 fail | 54 pass, 1 fail | | test-exec-lock-watch.sh | 84 / 0 | 84 / 0 | | test-lint-gate-accept.sh | 20 / 0 | 20 / 0 | | test-lint-gate-new-findings.sh | 9 / 0 | 9 / 0 | | test-sec014-notice.sh | 24 / 0 | 24 / 0 | **Claim verified:** the single test-lint-receipt-revert.sh failure (`FAIL no remedy command`, line 78) fails identically on base. It is pre-existing and unrelated to this PR (the remedy line is printed; the test's `$P` match misses on this macOS temp path). `bash -n` clean on bash 3.2. I also ran the extracted function under `bash -ec` (exec.sh runs under `set -e`; the shipped test uses plain `bash -c`): restore and rerun complete, no abort. `bin/check-pr-discipline.sh origin/release/v1.0`: clean. ## Safety: can the restore overwrite a hand-edited rule? The restore delegates entirely to the installer's existing classification, which is what `sync-project.sh` already runs: | Rule state on disk | Installer action | |---|---| | matches a STOCK-HASHES entry | replaced with the toolkit version | | matches the receipt (ours, unedited) | refreshed if the toolkit changed it | | toolkit body without header | header re-added (identical body) | | hand edit, header kept | **reported, never written** (T3 proves this) | | stock-kind rule, header gone, hash unknown | replaced, **copy kept** as `.reseeded-<ts>` (newest 3) | | configurable (conv020) edited | left alone | So a hand edit is never lost. The one write over non-toolkit content is the last-but-one row, and it keeps a backup; that branch already existed for `sync-project.sh`. I checked whether that branch would fire on every compaction with the field mxcli: `mxcli-v0.27.0 init` in a scratch dir seeds the four stock rules with hashes that **are** in STOCK-HASHES.txt, so the field case takes the plain stock branch and no backups churn. Minor wording point: the CHANGELOG and the code comment say "a hand edit is never written". Strictly, a header-less edit of a stock-kind rule is replaced (with a backup). "never lost" would be exact. ## Findings ### MUST-FIX None. ### Follow-ups (not blocking v1.0) 1. **`| head -12` can kill the installer mid-run (SIGPIPE).** `( . install-lint-rules.sh; mxtk_install_lint_rules ... ) 2>&1 | head -12 | sed ...`: once head has its 12 lines it exits and the installer dies on its next echo. Reproduced at /tmp/probe463: four stock rules re-seeded plus the eleven toolkit-only rules deleted (what a full `mxcli init` does) gives 15 output lines; the installer stopped after line 12, `ui001` and `sec014` were not restored, `_mxtk_lint_receipt_finalize` never ran, and the run still ended BLIND (rc 3). The next exec.sh restored the rest. Not a data risk (each rule's cp and receipt write happen before its echo) and not the #461 hook case (`--sync-skills` only rewrites the four stock rules, which come first in the manifest, so 4 lines). Fix: write the installer output to a temp file, then `head -12` the file. 2. **The restore also refreshes rules the toolkit has moved on.** With `upgrade_spec ""` the installer applies every safe change, not only the reverted rules: receipt-matching rules whose toolkit copy changed are refreshed, newly shipped rules are installed. If the user pulled the toolkit (for example the v1.0 release) without running `sync-project.sh`, exec.sh now switches those rules mid-Stage-5, and the rerun may show a ROSE that the BUILD-LOG row attributes to the script just applied. The row's "auto-restored (names)" lists only the reverted rules. Options: restrict to the names lint-gate reported, or detect skew (toolkit md5 differs from the receipt for any non-reverted rule) and stay BLIND with the sync hint in that case. 3. Wording: "a hand edit is never written" to "a hand edit is never lost" (CHANGELOG + comment), see above. 4. The new test runs the function under `bash -c`, not `set -e` like exec.sh. A `-e` variant would guard future edits of this block. ## Portability
…r unattended install (#464) fix(doctor): pick the Windows ARM64 mxcli build under Git Bash (#459). Independent review: APPROVE. # PR #464 review, round 1 fix(doctor): pick the arm64 mxcli on Windows ARM64; document --yes for unattended install (fixes #459) - Branch `fix/doctor-windows-arm64`, head `b943890d`, merge base `02e4366e` (= current `origin/release/v1.0`) - Files: `bin/doctor.sh` (+22/-2), `tests/wave2/test-doctor-mxcli-arch.sh` (new), `CHANGELOG.md`, `README.md`, `project-bin/_common.sh`, `project-bin/lint-gate.sh` (message text only), three skills (doc text only) - Reviewed in a detached worktree at /tmp/rv464, base at /tmp/rvbase, `/tmp/ghstub` first on PATH, macOS bash 3.2.57 ## What it does 1. After the `uname -m` mapping, and only when `PLATFORM=gitbash`, `MXCLI_ARCH` becomes arm64 if `uname -s` ends in `-ARM64`, or `PROCESSOR_ARCHITEW6432=ARM64`, or `PROCESSOR_ARCHITECTURE=ARM64`. 2. New table row `gitbash-arm64` picks `mxcli-windows-arm64.exe` with `MXCLI_FALLBACK_ASSET=mxcli-windows-amd64.exe`. If the first fetch fails and a fallback exists, it warns and fetches the amd64 .exe from the same release. 3. Success is now judged by `[ -s dest ] && chmod +x && probe_runs` instead of the fetch exit code. 4. Docs and two script messages say `--install --yes` for shells with no terminal. ## Tests | Test | head | base | |---|---|---| | test-doctor-mxcli-arch.sh (new) | 10 pass, 0 fail | 7 pass, **3 fail** (the three ARM64 cases; x86, macOS, Linux cases pass on both, as they should) | | test-doctor-gate-selftest.sh | 26 pass, 2 fail | 26 pass, 2 fail | | test-doctor-docker-probe.sh | 18 / 0 | 18 / 0 | | test-doctor-stale-hooks.sh | 7 / 0 | 7 / 0 | **Gate self-test confirmed:** both failures are the known no-mxbuild case (`reported as NOT RUN, not FAIL` and `no gate-self-test FAIL line`), identical on base and head. Not introduced here. Fallback path (not covered by the shipped test) probed by hand at /tmp/probe464: fake `uname` (`...-ARM64`, `x86_64`), fake `curl` that 404s the arm64 asset and serves a runnable amd64 stub, `MXCLI_VERSION=v0.20.0 --install --yes`. Result: plan shows the arm64 URL, `WARN mxcli-windows-arm64.exe is not published ...; trying mxcli-windows-amd64.exe`, then `ok mxcli fetched`. Works. No test greps the changed lint-gate or `_common.sh` message text. `bash -n` clean on bash 3.2; the new test runs under bash 3.2. `bin/check-pr-discipline.sh origin/release/v1.0`: clean. ## Safety: can arch detection misfire? - **macOS, Linux, WSL:** the override sits inside `if [ "$PLATFORM" = gitbash ]`, so `PROCESSOR_*` variables leaking into a Unix env cannot change anything. The test covers `Darwin` with both vars set to ARM64: still darwin-amd64. - **x86-64 Windows:** `uname -s` has no `-ARM64` suffix; `PROCESSOR_ARCHITECTURE` is `AMD64` or `x86`; `PROCESSOR_ARCHITEW6432` is unset or `AMD64` (WOW64). None match the exact `ARM64` patterns. Covered by two test cases. - **Windows ARM64, 32-bit or x64 emulated shell:** any of the three signals is enough, and all three describe the hardware, so a mixed reading still lands on arm64. - **Asset exists:** `mxcli-windows-arm64.exe` is published in v0.24.0, v0.25.0, v0.26.0 and v0.27.0 (latest), checked with `gh release view`. The fallback only runs for older pins. - **Failure handling:** a failed fetch with no fallback still ends in the existing "fetching mxcli failed" branch (curl `-f` leaves no file, wget leaves an empty one, `-s` rejects both). The `rm -f` on failure is unchanged from base and only runs when `NEED_MXCLI=1`, which means no runnable project mxcli existed. ## Findings ### MUST-FIX None. ### Follow-ups (not blocking v1.0) 1. **Fallback warning blames the release for any fetch failure.** It fires on a proxy or network error too and says "is not published for this mxcli version", then the second fetch fails the same way. Suggest "could not fetch $MXCLI_ASSET (not published for this version, or network); trying ...". 2. **16 bare `doctor.sh --install` mentions remain**, 7 of them in doctor.sh's own output (lines 704, 851, 861, 894, 949, 1208, 1213). Agents read doctor output and will run the bare form. It self-corrects (the no-terminal warning prints the `--yes` command), but #459 point 2 is only partly closed. 3. **No test for the fallback branch.** The probe above shows it works; a fake-`curl` case in test-doctor-mxcli-arch.sh would lock it in. 4. CHANGELOG entry does not name the test and says `(#459)` where sibling entries use `Test: ...` and `(Fixes #...)`. Cosmetic. 5. The doctor.sh header comment at line 60 is now one very long line; rewrap. ## CHANGELOG Present under Unreleased, describes both halves, credited to MendixMau. See follow-up 4. Verdict: APPROVE
…l open (#458) (#465) fix(process-map): count only open questions; show answers and decisions (#458). Independent review: CHANGES, must-fix (UNCONFIRMED read as decided) fixed in ee05a22 with tests. # Review r1: PR #465 (fixes #458), process map counts answered and assumed questions as open - Head: 3c53696 (committed 2026-10-18 22:00 +0800). Base: origin/release/v1.0 @ 02e4366. - Read-only review in a detached worktree at /tmp/rv465 (and /tmp/rv465base for base). Nothing pushed, merged or commented. - My renders: ~/Mendix/night-desk/night-2026-10-17/shots/458/review-r1/ (idx.png, idx-open.png, drawer.png, drawer-open.png), built from the T11 fixture at head. ## Verdict up front The fix does what #458 asks. Answers, consent, options and recommendations now reach `D.items`, the header counts only open items, the drawer shows Answer / Status / Answered by / Consent by, and "Open only" works. One must-fix: the CONFIRMED test is a substring match, so `UNCONFIRMED` and `ASSUMED, NOT CONFIRMED` rows show as green "Decided (confirmed)". That is the same bug `gate-check.sh` already fixed for the hard gate, and it is a one-line change. ## Test runs | Suite | head | base | |---|---|---| | tests/wave2/test-process-map.sh | 83 ok, 0 FAIL | 74 ok, 0 FAIL | | tests/wave2/test-process-map-field.sh | 59 ok, 0 FAIL | 59 ok, 0 FAIL | | tests/wave2/test-brd-report-process-map.sh | 73 ok, 0 FAIL | 73 ok, 0 FAIL | | head's test-process-map.sh run against base code | | 77 ok, 6 FAIL | - The new T11 fails on base as it should: 6 FAIL (state counts and fields, answer text, CONFIRMED row shown, Open only filter, legend, consolidated state). The 2 that pass on base ("CONFIRMED rows are not pinned or anchored", "not in the anchors file") are regression guards. They pass trivially on base because base drops CONFIRMED rows. - `bin/check-pr-discipline.sh origin/release/v1.0` with LEAKGUARD_DENY set: clean, rc 0. - `bin/check-portability.sh`: clean (287 files). `bin/check-scripts.sh`: 118/118 shell, 25/25 Node parse. - macOS bash 3.2.57: the test parses under `/bin/bash -n` and runs 83 ok, 0 FAIL under `/bin/bash`. `sed -i.bak` is the portable BSD/GNU form. The template keeps to ES5 (`var`, `function`). The builder uses spread and `Set`, which is fine for Node. ## Field case: every question ANSWERED or ASSUMED with consent I built a fixture with all 4 BRD questions settled (Q01 is `blocking: true` and ANSWERED, Q02 and Q04 are ASSUMED with consentBy/consentAt, Q03 is ANSWERED). Result from the embedded data, using the header's own filters: **open 0, block 0**, answered or decided 5, assumptions to confirm 2. The 2 are the two register rows whose status is plain `ASSUMED` in PROJECT.md. That is correct: the register really says ASSUMED. In the #458 run, "40 assumptions to confirm" will therefore not drop to 0 unless the register rows record consent (`/consent/i` in Status). That is expected, but worth saying in the release note. ## MUST-FIX **M1. `/CONFIRMED/` matches UNCONFIRMED and NOT CONFIRMED** (`project-bin/process-map.js`, register loop, `const confirmed = /CONFIRMED/.test(status)`). Reproduced at head. Register rows `ASSUMED, NOT CONFIRMED` and `UNCONFIRMED` both come out as `state: answered`, are labelled "Decided (confirmed)" in green, and are taken out of the anchors (never pinned). On base the `ASSUMED, NOT CONFIRMED` row was an assumed decision pinned to its step, so this is a regression for that row: an explicitly unconfirmed decision now reads as settled. `bin/gate-check.sh` (around L943-951) documents the identical bug in the gate and fixed it by requiring an exact CONFIRMED field. Fix: match a status that starts with CONFIRMED (the fixture uses `CONFIRMED 2026-09-02`) and has no ASSUMED in it, for example `/^\s*CONFIRMED\b/i.test(status) && !/ASSUMED/i.test(status)`, plus one T11 line with an `UNCONFIRMED` row and an `ASSUMED, NOT CONFIRMED` row. The same loop's `/consent/i` test, which decides whether a register row is "consented", has the mirror risk ("ASSUMED, no consent yet" reads as consented). Tightening it in the same change is cheap but optional. ## Your four observations, checked against the code **1. "Not tied to one step (3)" with four groups.** Not a count bug. `offStep` holds 3 items in 3 groups (Decided in the project register, Money, Not placed yet). The fourth row, "Use cases that are not part of a process (1)", is a separate `<details>` that `renderIndex` appends from `D.standalone`. It is not part of `offStep`, and on base it already rendered there with the same styling and no heading of its own. The PR did not cause it, but the new group makes the mismatch easier to see. **Follow-up:** give the standalone use cases their own `h2`, or move them above the section. **2. C3-01 "Decided (confirmed)" with subtitle "Assumption" and "not placed yet".** - Subtitle: your screenshots (21:54) were taken before the head commit (22:00). At head, `qrow` shows "Decided in the project register" when text equals title, and my render (idx.png) shows exactly that. The "Assumption" subtitle is already fixed. - The right-side "not placed yet" is still there at head: `whereLabel` returns "not placed yet" for any item with no targets, and CONFIRMED rows never have any by design. That reads as contradictory. **Follow-up:** say "project-wide" or leave it empty for `kind==='decision' && state==='answered'`. - The red counter is **not** inflated by C3-01. `stats.unanchored` counts `ITEMS` only, and the CONFIRMED rows sit in `DECIDED`, which is concatenated only into the payload. The 1 in your shot is D3-02, the hosting row. T11's sed lengthens its text, so its fingerprint changes and its judged anchor drops. That is the real "Not placed yet" item. With the untouched fixture the counter is 0 and the tile does not render. **3. Process card pins red 1, purple 1, green 2 merge teal into green.** Confirmed. `counts()` maps both `answered` and `cons` into `ans`, and the card and board pins draw `ans` with the green `answered` dot. The legend, rows and drawer cards do use teal. **Follow-up:** give teal its own `pc.cons` / `pin.cons` count, or say in the legend that pins merge both settled states. Nothing settled is shown as open, so this is cosmetic and not a must-fix. **4. Red "not placed yet" header counter.** Pre-existing. The line `if (D.stats.unanchored) st.appendChild(h('div',{class:'stat warn'}...` is unchanged context in the diff and comes from #429. Unanchored items fail only `validate --strict`, not the build, so by the red-only-for-severe rule this should be neutral or amber. **Follow-up**, outside #458's scope. ## Rendered check (headless Chrome, 1440x2400, T11 fixture at head) - idx.png: the header reads 1 open, 1 blocks, 3 to confirm, 3 answered or decided, 1 not placed yet. The legend has teal and green entries. C3-01 is green with subtitle "Decided in the project register". - idx-open.png ("Open only" ticked through the real checkbox): the green pin disappears from the process card. The C3-01 row is hidden, but its group header "Decided in the project register (1)" stays as an empty expander, and the section title still says (3). - drawer.png (step 3 "Open the order", clicked through the real step card): two cards. D3-01 is purple with Status "Assumed, waiting for confirmation (ASSUMED)". F001:Q02 is green with Question, We assume, **Answer**, **Status: Answered**, **Answered by: owner, 2026-10-17**. The process list shows the consented Q03 in teal with "Assumed, consented by owner, 2026-10-17". - drawer-open.png (Open only plus the drawer): the answered Q02 card is hidden and only D3-01 remains. The process list below hides Q02 and Q03. So the drawer rows exist and "Open only" hides answered and consented items, in the drawer, in the lists and on the pins. ## Follow-ups (not blocking the release) 1. "Use cases not part of a process" gets its own heading (obs. 1). 2. No "not placed yet" label on CONFIRMED register rows (obs. 2).
…ort and the progress report (#462) fix(windows): MSYS paths and Claude Code session logs on Windows (#460). Independent review: CHANGES, three must-fixes fixed in cdb03d1. Confirmed on a Windows Git Bash machine: report finds the project's session log (2 sessions, 51 sub-agents, cost shown) where the release found 1 session and 0 sub-agents. # PR #462 review, round 1 PR: fix(windows): Git Bash MSYS paths and Windows session logs in brd-report and the progress report Base: release/v1.0, head 4f91d06 (fix/windows-msys-paths), fixes #460. Reviewed in a detached worktree (/tmp/rv462), nothing pushed. ## Verdict: CHANGES (small) The PR fixes the likely field cause of "No Claude Code session log was found". Three small items should land before 1.0; all are one to five lines. ### Root cause, as traced - `build-plan-status.sh` passes `$ROOT` (Git Bash `pwd`, so `/c/work/proj`) as an ARGUMENT, which MSYS rewrites to `C:/work/proj`. The old code then built `abspath -> C:\work\proj -> slug C--work-proj`, so the slug was most likely already right in the field. - The real miss is `session_in_project`: it compared the raw JSON text of `"cwd"` (`C:\\work\\proj`, escaped) with `C:\work\proj`. That never matches, so every own-folder log was rejected. The PR unescapes with `json.loads` and compares through `path_key` (slashes folded, case folded on Windows). This is the fix that matters, and it is correct. - The byte needle (doubled backslashes, `re.I` on Windows), the case-insensitive `own_dir` slug match (VS Code launches often record `c:\...` and slug `c--...`), the cache root, the warm/cold paths and `touches` (via `under`) are all covered. `fix_msys` on argv is defensive only (useful under `MSYS_NO_PATHCONV=1`). - Likely correction to the issue and the portable.sh comment: MSYS2 (and Git for Windows) DOES convert environment variables whose value starts like a POSIX path, but only the leading path. The issue's own error names `F002.brd.json`, not F001: the first line of the newline-joined `BRD_FILES` got converted, the second did not. So the risky shape is a newline-joined path list (or a path inside a string, or read back from a pipe), not "any env var". ## Must-fix 1. `project-bin/progress-report.py:367` `root_forms`: on Windows add the MSYS spelling of each drive root (`C:\work\proj` -> `/c/work/proj`). Claude Code on Windows runs its Bash tool through Git Bash, so `cd /c/work/proj && ...` is common in commands. Today `needle_for` and the Bash regex in `touches` miss it (verified: `touches("Bash", {"command": "cd /c/work/demo && ls"}, None, roots, True)` returns False, needle does not match). Sessions started in another folder that work on the project through Bash are not found. The needle and the touches regex pick the new form up automatically. Add one check to the test. 2. `bin/extraction-report.sh:114`: same shape as the brd-report bug. `KB_DIRS=$(discover_kbs "$PROJECT_DIR")` is a newline-joined list handed to Python in an env var; with more than one knowledge base the second and later dirs stay `/c/...`. Add `PROJECT_DIR="$(native_path "$PROJECT_DIR")"` (and `HTML_OUT`) after the directory check, as in brd-report.sh. Stage 1/2 gate artifact (gate-check.sh line 859). 3. `bin/facts-lock.sh:62`: same shape (`KBS` newline list in env, read with `splitlines()`), Stage 2 checklist item in `skills/brd-generation.md:353`. Not in the PR's call-site list. One line `PROJECT_DIR="$(native_path "$PROJECT_DIR")"` before line 62; portable.sh is already sourced. If items 2 and 3 are kept out of this PR to stay narrow, they need their own PR merged into release/v1.0 before the tag; they are the same bug a Windows user hits in Stage 2. ## Checked and fine - macOS/Linux: `IS_WIN = os.name == "nt"`; every new helper takes `win=None -> IS_WIN`, so non-Windows behaviour is the old one: no `re.I`, no case fold, no extra root forms. `real_path` only short-circuits a drive path on a non-nt host (test hook), so realpath still runs for POSIX paths. Symlinked `/tmp -> /private/tmp`: slug still from realpath as before, found a log under `-private-tmp-...` (checked). Only theoretical change: `path_key` turns `\` into `/` on POSIX too, harmless for real paths. - brd-report.sh: conversion happens before `discover_brds`, so the discovered file list is already `C:/...`; `--html` too. Off Windows `native_path` is a no-op (test asserts cygpath is never called). - native_path duplication: `bin/lib/portable.sh` (uname) vs `project-bin/_common.sh` (mxtk_platform: OSTYPE then uname). Same output, both fall back to the input when cygpath is missing or fails. Duplication is justified (portable.sh is toolkit-side, never copied into projects; _common.sh is the project twin). doctor.sh sources both; the later definition wins and the behaviour is equivalent. Without cygpath the path stays `/c/...`, so it fails as before but does not crash; Git Bash always ships cygpath. - `cygpath -m -- "$1"`: `--` is accepted by cygpath's getopt. - `TOOLKIT_BIN="$HERE"` in brd-report.sh is a single path in env, so MSYS converts it; not a bug. - Style: the only em dash in added lines is the CHANGELOG `— MendixMau` credit. No client or project names (leakguard clean with the repo denylist). Issue text is neutral. - PR body is honest about what cannot be verified without a Windows machine. ## Nice-to-haves - `project-bin/progress-report.py:496`: Python's realpath on Windows turns a mapped network drive (or a subst drive) into a UNC path, while Claude Code slugs the drive path it was launched in. Match `own_dir` against `slug(real_path(proj))` AND `slug(abspath(proj))`. Common on managed corporate machines (H: home drives). - `project-bin/progress-report.py:405/428`: `tool_share` does not take or pass `win`, so the "started elsewhere" path cannot be tested with Windows inputs on macOS. Thread `win` through and add one test log in another slug folder with a Windows `file_path`. - MSYS2's own Python (`sys.platform` "msys"/"cygwin", `os.name` "posix") would get `IS_WIN = False` and slug `-c-work-proj`. Rare (Git for Windows ships no Python, resolve_py finds the native one), but `IS_WIN = os.name == "nt" or sys.platform in ("msys", "cygwin")` plus a cygpath call is the complete form. - `real_path` takes `win` and ignores it; drop the parameter or use it. - `--rates` is not in the `fix_msys` argv list (harmless: MSYS converts argv anyway; just for consistency). - Reword the portable.sh comment (line 66) and the PR body's call-site paragraph: single-path env vars are converted by MSYS; the risk is newline-joined lists, paths embedded in strings and paths read back from a pipe. Re-triage the listed call sites with that rule; most of the listed ones pass a single path and are likely fine. ## Tests (macOS, /tmp/ghstub first on PATH) | Test | Result | |---|---| | tests/wave2/test-windows-msys-paths.sh | pass 7, fail 0 (1 s) | | tests/wave2/test-progress-report.sh project-bin/build-plan-status.sh | PASS=101 FAIL=0 | | tests/wave2/test-progress-report-field-fixes.sh | PASS=101 FAIL=0 | | tests/wave2/test-progress-report-gitignore.sh | PASS=15 FAIL=0 | | tests/wave2/test-progress-report-polish.sh | PASS=36 FAIL=0 | | tests/wave2/test-brd-report.sh | 55 passed, 0 failed | | tests/wave2/test-brd-report-process-map.sh | 73 ok, 0 FAIL | | bin/check-portability.sh (changed .sh files) | clean, 3 files | | bin/check-scripts.sh | 118/118 shell, 25/25 node parse, rc 0 | | bin/check-pr-discipline.sh origin/release/v1.0 | clean, rc 0 | | bin/check-no-client-data.sh (repo denylist via LEAKGUARD_DENY) | clean, 1125 files | | Ad hoc: MSYS form in a Bash command (win=True) | touches False, needle no match (must-fix 1) |
The 1.0 release notes, rewritten performance first: speed and measured effect come first, followed by the settings you control, context packs, reports, the process map, learnings, fixes and known gaps. Review: independent review MERGE-AFTER-FIX, then a re-review MERGE at 3ef0c37. - All 73 cited PRs are merged. - The with/without-toolkit figures were re-derived from tests/behaviour/results/2026-10-15-*.tsv, and the #395 bug-lookup rates match its body. - Fixed in 3ef0c37: - dropped the withdrawn context-pack token claim (771eb97); - moved the A/B paragraph to "Does the toolkit help?"; - led the bug-lookup bullet with held-out; - replaced the unsourced "sixty questions" and "almost 9 times"; - nine reports from Joshua, not ten; - cited #385 and #183; - added the progress-report and permission-profile settings rows; - added #220, #292, #391, #271, #265 and #407 as highlights; - kept the "review of PR #434" CHANGELOG credit. - The #462 figure traces to #462's squash commit 2d80287. - Added-line client-name scan: 0 hits. Leak guard, routing and pr-discipline are clean.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Promotes the
release/v1.0integration branch to master: 17 squash-merged PRs plus the release notes (#428), each independently reviewed and green againstrelease/v1.0. After this merges, the maintainer cuts the release withbin/cut-release.sh(tagv2026.10.19), and the GitHub Release titled "1.0" is created fromdocs/releases/v1.0.md.Merge with a merge commit, not a squash, so the PR commits keep their own history and credits on master.
release/v1.0is 0 behind master, so there are no conflicts; it will be 18 ahead once #428 is in.Merge #428 (the 1.0 release notes) into
release/v1.0first. The notes ride along in this PR.What's in it
build-plan.html)--refreshkeepsbuild-plan.jsoncurrent; every State read the same wayThe CR flow (#306, #430), the decomposition pipeline (#341, #342, #344–#347), mxpatch (#374), the ranked bug lookup (#395) and the behaviour suite (#389) are already on master.
Direct lane
build-plan.htmlas the Stage 4–5 surface, and the BRD report gains the process map.project-bin/exec.sh(#451, #452, #463) andbin/doctor.sh(#464). After this lands, the two other open PRs that touch exec, #384 and #379, conflict inproject-bin/exec.sh(checked withgit merge-treeagainstrelease/v1.0). Each needs a master merge with a hand-resolvedexec.shbefore it lands. Windows paths are probed under Git Bash (#462).bin/render-routing.sh --checkonorigin/masterandorigin/release/v1.0.release/v1.0; see each PR. After the 17 PRs, the only new agent-read text is the one row #428 adds to the refusal table initerative-build-loop.md. That file is on demand, so its context cost is 0. Behaviour: NOT RUN, because the row restates whatexec.shalready prints.Note: the 856127d (#465) commit message carries a local maintainer path (not client data); it lands in master history with the merge commit.
Not included, shipping after 1.0: #384 exec-on-copy (needs a Mac re-time) and #379 lock reap (needs a Git Bash run).
https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw
Generated by Claude Code