From c78212999f1f8589243ea5114e562243f4df3092 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 13 Oct 2026 15:07:49 +0000 Subject: [PATCH 1/3] Promote 2026-09-29 inbox notes: exec.sh refusal rule, UI fix rounds, transcript usage, autocompact advisory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - skills/iterative-build-loop.md: every model write via exec.sh; on a guard refusal commit (--commit-first), wait (lock) or stop — never FORCE_EXEC on own initiative; MCP writes accepted as unrecorded and recorded by an "MCP:" commit; phase-close count (model commits = exec rows + MCP: commits); save-then-check; no batched-gate mode, do not imitate one (#69, #171, #173). - skills/upstream-feedback.md: Studio Pro MCP server asks as a third destination, with the evidence rows from the field comparison (#173). - skills/learned-page-patterns.md: UI fix rounds — SCSS first, apply directly, re-look only changed pages; N1–N4 pre-apply checks (#170). - skills/measured-claims.md §7: read each message's LAST usage record; re-measured on Claude Code 2.1.296 transcripts (sub-agents up to 7x low on the first record; main loop exact) (#171). - bin/init-project.sh: auto-compact advisory, writes nothing, never fails (#167); fixture tests/wave2/test-init-autocompact.sh (9 checks, 4 fail on master). Field run: scaffolded four scratch projects with no setting, env 1M, settings 250000 and settings 1000000. - Routing triggers for three on-demand rows extended so the refusal, UI round and token-count cases reach the stubs; baseline unchanged at 79,997 words. - contrib/inbox: mcp-build-observations deleted (fully promoted); ui-loop-efficiency, parallel-build-hygiene, subagent-context-cost trimmed to what stays open. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw --- CHANGELOG.md | 3 + README.md | 6 +- ROUTING.md | 6 +- agents/architect-agent.md | 2 +- agents/ba-agent.md | 2 +- agents/gate-agent.md | 4 +- agents/mdl-agent.md | 6 +- agents/review-agent.md | 2 +- agents/test-agent.md | 2 +- bin/init-project.sh | 44 +++++++++++ bin/lib/skill-routing.tsv | 6 +- .../2026-09-29-mcp-build-observations.md | 25 ------- .../2026-09-29-parallel-build-hygiene.md | 13 ++-- .../inbox/2026-09-29-subagent-context-cost.md | 7 +- .../inbox/2026-09-29-ui-loop-efficiency.md | 13 ++-- skills/iterative-build-loop.md | 58 +++++++++++++++ skills/learned-page-patterns.md | 37 ++++++++++ skills/measured-claims.md | 30 ++++++++ skills/upstream-feedback.md | 19 +++++ tests/wave2/test-init-autocompact.sh | 73 +++++++++++++++++++ 20 files changed, 301 insertions(+), 57 deletions(-) delete mode 100644 contrib/inbox/2026-09-29-mcp-build-observations.md create mode 100755 tests/wave2/test-init-autocompact.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 20342404..ad407609 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,9 @@ Sections dated before 2026-09-19 predate the cycle and stay as they are. ## Unreleased +- learn(iterative-build-loop, upstream-feedback): **every model write goes through `exec.sh`; on a refusal the move is commit (`--commit-first`), wait (exec lock) or stop and report — never `FORCE_EXEC` on the agent's own initiative; MCP writes stay allowed and are accepted as unrecorded, recorded by an `MCP:` commit; the gate checks the saved file (save, then check); no batched-gate mode exists, so do not imitate one with overrides; Studio Pro MCP server asks get a third destination in `upstream-feedback.md` with their evidence rows** ([#69](https://github.com/MendixMau/mxcli-project-toolkit/issues/69), [#171](https://github.com/MendixMau/mxcli-project-toolkit/issues/171), [#173](https://github.com/MendixMau/mxcli-project-toolkit/issues/173)). The phase-close check is a count: model commits = exec rows + `MCP:` commits. Field evidence: a 2026-09-29 two-build comparison (229 errors hidden by a stale disk copy; per-script apply 22–30 s growing to 55–65 s). Routing triggers for the build loop, page patterns and measured claims now name the refusal, the UI round and transcript token counts (stubs re-rendered; baseline unchanged at 79,997 words) — MendixMau +- learn(learned-page-patterns, measured-claims): **UI fix rounds: SCSS first, apply directly through `exec.sh`, re-look only changed pages (N of N), and check four regressions before applying (N1 page-column on the layout root, N2 `.d-none` losing to a `display` class, N3 Data Grid 2 `autoFit` columns at 32 px, N4 focus ring); transcript usage is read from each message's LAST record** ([#170](https://github.com/MendixMau/mxcli-project-toolkit/issues/170), [#171](https://github.com/MendixMau/mxcli-project-toolkit/issues/171)). The round took 1,507 calls against 515 for a comparable build; re-measured on Claude Code 2.1.296 transcripts, sub-agent output read from the first record was up to 7x low while the main loop's was exact — MendixMau +- new(init-project): **the scaffold prints an auto-compact advisory: a warning when `CLAUDE_CODE_AUTO_COMPACT_WINDOW` or a settings `autoCompactWindow` is above 250k, otherwise a `/autocompact 250k` recommendation; it writes no setting and never fails the scaffold** ([#167](https://github.com/MendixMau/mxcli-project-toolkit/issues/167)). A lead session at a 1M window averaged 363–466k context per call. Fixture `tests/wave2/test-init-autocompact.sh` (9 checks; 4 fail on master) — MendixMau - learn(testing-shape, test-result-audit): **three test-strength rules, each with a denominator: refusals probed on the server, every undeclared 4xx a failure, one clean-database final run** ([#172](https://github.com/MendixMau/mxcli-project-toolkit/issues/172)). A field comparison of two builds against one frozen spec found the toolkit build checked permission refusals only in the client (a hidden menu item), logged unexpected 400/422 responses as information, and reached 20/20 by stitching three runs. `testing-shape.md` §7 gains three report lines (`N of N restricted pairs probed server-side`, `undeclared 4xx/5xx: 0 of M requests — each fails its step`, `one clean-DB run : N of N scenarios incl. timers, not stitched`); `test-result-audit.md` gains check 2e, which carries the reasons and the field evidence and asks for those numbers. Both baseline skills stay inside the 80,000-word baseline budget (79,997): the reasons live in the on-demand audit skill, and two redundant sentences in `testing-shape.md` §4's verdict paragraphs were folded (the empty-set corollary is stated in the Trace row, `harness-architecture.md` and `report-schema.md`). The backing inbox note is trimmed to its unpromoted "Keep" items. — MendixMau - learn(learned-detection-gaps, module-review): **two UI defects every model gate passes: a lazy-loaded combo box filter hides its preset, and a theme that pins row heights by position lets the next row cover an inserted widget** ([#299](https://github.com/MendixMau/mxcli-project-toolkit/issues/299)). Both passed check, mxbuild, lint and the scripted checks on a 127-module production app and were found only in the running app. Two register rows with the first oracle and the pre-build check (pluggable properties that change *when* data loads; grep the theme for fixed `height` on positional row classes before an insert), and two LOOK lines in `module-review.md` §4c with denominators (`N of N filters show their preset on first load`, `N of N widgets inserted into an existing container fully visible`). — jopterhorst - fix(app-restart, test-stack-up): **a port held by another process before `run --local` is named and reported as "instrument blocked" (exit 4), never killed** ([#262](https://github.com/MendixMau/mxcli-project-toolkit/issues/262)). Studio Pro had started its own runtime on the app/admin ports a restart needed; `mxcli run --local` then failed with "Authentication failed" (the admin API answering was Studio Pro's), the next journey ran against Studio Pro's runtime, and four more runs were NOT RUN. `app-restart.sh` now checks the app port and the admin port (record's `adminPort`, else app + 10) before stopping anything — a holder outside the recorded process tree exits 4 with its name, pid and parent — and again after the stop, where anything still holding a port is by definition not ours. `test-stack-up.sh` runs the same check wherever it would tell you to start `run --local` (`--no-docker`, no Docker daemon, `--check` with a leftover record), on the record's ports, `RUN_LOCAL_APP_PORT`/`RUN_LOCAL_ADMIN_PORT`, or mxcli's defaults 8080/8090. Holder lookup: lsof, then ss, then netstat + tasklist on Windows; unnamed holders still block when nothing of ours is running. Fixtures: `test-app-restart.sh` +2 cases (14 assertions, all fail on the old script), `test-stack-up-local.sh` T7 (4 of 6 fail on the old script). — AlvinnYeo diff --git a/README.md b/README.md index 50f41d55..0c7bcc37 100644 --- a/README.md +++ b/README.md @@ -525,7 +525,7 @@ Every mxcli project has a `.ai-context/skills/` directory (bundled by `mxcli ini | Changing an EXISTING Mendix app — adding a feature, altering a flow, restructuring a module — when it has no BRDs, no architecture doc and no wireframes: the knowledge base comes from the live model (Path D), stages 2–4 run over what changes plus what it touches, nothing more, and the Track B regression baseline is the precondition; audit-only stays in existing-app-assurance | `skills/existing-app-change.md` | | Setting up or wiring a COMPANY BRAIN — the private tier between the toolkit and a project for own skills, conventions, lint rules, MDL snippets and approved MPKs; and deciding whether something goes to the toolkit, the company brain or docs/brain/ | `skills/company-brain.md` | | Cutover and retrospective — promoting proven patterns back into the toolkit | `skills/close-the-loop.md` | -| Before citing ANY behavioural claim about the harness, the Mendix runtime or a test tool as evidence — a claim not in the register may not be cited | `skills/measured-claims.md` | +| Before citing ANY behavioural claim about the harness, the Mendix runtime or a test tool as evidence, or counting tokens from transcripts — a claim not in the register may not be cited | `skills/measured-claims.md` | | Any review pass that runs more than once — module-review, coherence, monkey, wiring-sweep: findings accumulate across runs, a per-run report cannot show a trend | `skills/improvement-register.md` | **Source — reading a legacy system (migration entry mode)** @@ -587,7 +587,7 @@ Every mxcli project has a `.ai-context/skills/` directory (bundled by `mxcli ini | Task | Skill to load | |---|---| | Dispatching a build step to a helper — generate its one-file pack (the brief's Build steps row, mxcli brain brief, live DESCRIBEs, example, folder) and hand over the path instead of a reading list. Measured: 25-38% fewer tool calls and 30-41% less time, same quality; the token saving is not proven | `project-bin/context-pack.sh` | -| Building a module with mxcli — verified, iterative, coverage-checklist gated | `skills/iterative-build-loop.md` | +| Building a module with mxcli, or an exec.sh guard just refused a write — verified, iterative, coverage-checklist gated; on a refusal: commit, wait or stop, never FORCE_EXEC | `skills/iterative-build-loop.md` | | After marking a module done, or any time "how much is built vs proven" is asked — renders build-plan.html from done- prefixes and verify-module.sh/improvement-register.md, kept as two honestly separate views; --json writes architecture/build-plan.json parsed from build-plan.md's Phase headings (a plan with no Phase headings gets no file) | `project-bin/build-plan-status.sh` | | Turning a client-derived Mendix app into a clean, shareable demo with zero client fingerprint — branding, data, custom widgets | `skills/anonymize-client-app-for-demo.md` | | Handing a headless-built model to a person — opening it in Studio Pro, a free sandbox, or a colleague's machine: the model travels, the demo data and runtime config (keys, an agent's bound model) do not, and each needs its own re-establish step | `skills/handoff-to-studio-pro.md` | @@ -610,7 +610,7 @@ Every mxcli project has a `.ai-context/skills/` directory (bundled by `mxcli ini | Task | Skill to load | |---|---| -| Building and auditing Mendix pages — widget patterns, datasource shapes | `skills/learned-page-patterns.md` | +| Building and auditing Mendix pages, or starting a UI fix round across many pages — widget patterns, datasource shapes; for a round: SCSS first, four regressions to check | `skills/learned-page-patterns.md` | | Generating a whole page tree in one script — the structure patterns that survive it | `skills/oneshot-page-structure-patterns.md` | | Building or auditing a collapsible sidebar nav — Atlas Core's collapsed state needs icons assigned per menu item or it silently clips label text | `skills/learned-sidebar-collapse-icons.md` | | Building or altering any data grid — native DATAGRID vs pluggable DG2 decision rule, the ALTER PAGE INSERT corruption, sort-by and filter-binding traps | `skills/learned-dg2-patterns.md` | diff --git a/ROUTING.md b/ROUTING.md index 2c55707c..3e0b4548 100644 --- a/ROUTING.md +++ b/ROUTING.md @@ -55,7 +55,7 @@ picks the row up. That is the whole procedure — there is no second list to rem | Changing an EXISTING Mendix app — adding a feature, altering a flow, restructuring a module — when it has no BRDs, no architecture doc and no wireframes: the knowledge base comes from the live model (Path D), stages 2–4 run over what changes plus what it touches, nothing more, and the Track B regression baseline is the precondition; audit-only stays in existing-app-assurance | `skills/existing-app-change.md` | ba,architect | P,0 | ondemand | | Setting up or wiring a COMPANY BRAIN — the private tier between the toolkit and a project for own skills, conventions, lint rules, MDL snippets and approved MPKs; and deciding whether something goes to the toolkit, the company brain or docs/brain/ | `skills/company-brain.md` | all | - | ondemand | | Cutover and retrospective — promoting proven patterns back into the toolkit | `skills/close-the-loop.md` | all | 7 | ondemand | -| Before citing ANY behavioural claim about the harness, the Mendix runtime or a test tool as evidence — a claim not in the register may not be cited | `skills/measured-claims.md` | all | - | ondemand | +| Before citing ANY behavioural claim about the harness, the Mendix runtime or a test tool as evidence, or counting tokens from transcripts — a claim not in the register may not be cited | `skills/measured-claims.md` | all | - | ondemand | | Any review pass that runs more than once — module-review, coherence, monkey, wiring-sweep: findings accumulate across runs, a per-run report cannot show a trend | `skills/improvement-register.md` | mdl,gate,test,review | 5,6 | ondemand | | The first command of every session, and any time someone asks "where are we" or "what next" — one screen: stage, done/overdue, the ONE next action, from the instruments, never from memory | `bin/status.sh` | all | P,0,1,2,3,4,5,6,7 | baseline | | Before obeying any learned-* STOP or workaround that costs a detour — probe the binary you actually have, then stamp the verdict back into the rule | `skills/retesting-learned-rules.md` | all | - | baseline | @@ -136,7 +136,7 @@ picks the row up. That is the whole procedure — there is no second list to rem | Reviewing any module before calling it done — the ONE pass: build, gate, prove, LOOK (is it logical, does it look right, does it match our design, over every page not just the tested ones), confirm with the denominator stated | `skills/module-review.md` | mdl,review,test | 5,6 | baseline | | Before any mxcli exec / exec.sh / --mcp write — ask or run? the knob decides | `bin/exec-approval.sh` | mdl,gate | 5,6 | baseline | | Dispatching a build step to a helper — generate its one-file pack (the brief's Build steps row, mxcli brain brief, live DESCRIBEs, example, folder) and hand over the path instead of a reading list. Measured: 25-38% fewer tool calls and 30-41% less time, same quality; the token saving is not proven | `project-bin/context-pack.sh` | mdl | 5 | ondemand | -| Building a module with mxcli — verified, iterative, coverage-checklist gated | `skills/iterative-build-loop.md` | mdl,gate | 5 | ondemand | +| Building a module with mxcli, or an exec.sh guard just refused a write — verified, iterative, coverage-checklist gated; on a refusal: commit, wait or stop, never FORCE_EXEC | `skills/iterative-build-loop.md` | mdl,gate | 5 | ondemand | | After marking a module done, or any time "how much is built vs proven" is asked — renders build-plan.html from done- prefixes and verify-module.sh/improvement-register.md, kept as two honestly separate views; --json writes architecture/build-plan.json parsed from build-plan.md's Phase headings (a plan with no Phase headings gets no file) | `project-bin/build-plan-status.sh` | architect,gate,review | 4,5,6 | ondemand | | Turning a client-derived Mendix app into a clean, shareable demo with zero client fingerprint — branding, data, custom widgets | `skills/anonymize-client-app-for-demo.md` | mdl,review | 6 | ondemand | | Handing a headless-built model to a person — opening it in Studio Pro, a free sandbox, or a colleague's machine: the model travels, the demo data and runtime config (keys, an agent's bound model) do not, and each needs its own re-establish step | `skills/handoff-to-studio-pro.md` | test,review | 6,7 | ondemand | @@ -164,7 +164,7 @@ picks the row up. That is the whole procedure — there is no second list to rem | Always relevant for | Load this | Agent(s) | Stage(s) | Tier | |---|---|---|---|---| -| Building and auditing Mendix pages — widget patterns, datasource shapes | `skills/learned-page-patterns.md` | mdl | 5 | ondemand | +| Building and auditing Mendix pages, or starting a UI fix round across many pages — widget patterns, datasource shapes; for a round: SCSS first, four regressions to check | `skills/learned-page-patterns.md` | mdl | 5 | ondemand | | Generating a whole page tree in one script — the structure patterns that survive it | `skills/oneshot-page-structure-patterns.md` | mdl | 5 | ondemand | | Building or auditing a collapsible sidebar nav — Atlas Core's collapsed state needs icons assigned per menu item or it silently clips label text | `skills/learned-sidebar-collapse-icons.md` | mdl | 5 | ondemand | | Building or altering any data grid — native DATAGRID vs pluggable DG2 decision rule, the ALTER PAGE INSERT corruption, sort-by and filter-binding traps | `skills/learned-dg2-patterns.md` | mdl | 5 | ondemand | diff --git a/agents/architect-agent.md b/agents/architect-agent.md index 5e6927a3..2ef72c7b 100644 --- a/agents/architect-agent.md +++ b/agents/architect-agent.md @@ -71,7 +71,7 @@ This table is your whole list. The baseline table in the project's CLAUDE.local. | `skills/mendix-agents.md` | Building a Mendix AI agent | | `skills/company-brain.md` | Setting up or wiring a COMPANY BRAIN | | `skills/close-the-loop.md` | Cutover and retrospective | -| `skills/measured-claims.md` | Before citing ANY behavioural claim about the harness, the Mendix runtime or a test tool as evidence | +| `skills/measured-claims.md` | Before citing ANY behavioural claim about the harness, the Mendix runtime or a test tool as evidence, or counting tokens from transcripts | | `skills/agent-permission-friction.md` | Any refused, denied or blocked command | | `project-bin/build-plan-status.sh` | After marking a module done, or any time "how much is built vs proven" is asked | | `skills/workflow-structure-rules.md` | Designing or reviewing a Workflow's SHAPE before or after the MDL | diff --git a/agents/ba-agent.md b/agents/ba-agent.md index 92ef81ee..4176190a 100644 --- a/agents/ba-agent.md +++ b/agents/ba-agent.md @@ -87,7 +87,7 @@ This table is your whole list. The baseline table in the project's CLAUDE.local. | `skills/brd-validation.md` | Validating BRDs against the code and document KB | | `skills/company-brain.md` | Setting up or wiring a COMPANY BRAIN | | `skills/close-the-loop.md` | Cutover and retrospective | -| `skills/measured-claims.md` | Before citing ANY behavioural claim about the harness, the Mendix runtime or a test tool as evidence | +| `skills/measured-claims.md` | Before citing ANY behavioural claim about the harness, the Mendix runtime or a test tool as evidence, or counting tokens from transcripts | | `skills/agent-permission-friction.md` | Any refused, denied or blocked command | | `skills/mendix-epics-api.md` | Working with the Mendix Epics board programmatically | | `skills/upstream-feedback.md` | About to open an issue, PR or discussion against mxcli or the toolkit | diff --git a/agents/gate-agent.md b/agents/gate-agent.md index 32fa7df8..505e5241 100644 --- a/agents/gate-agent.md +++ b/agents/gate-agent.md @@ -56,14 +56,14 @@ This table is your whole list. The baseline table in the project's CLAUDE.local. | `skills/cloud-dev-environment.md` | Setting up or resuming an mxcli project in a cloud/ephemeral container | | `skills/coverage-ledger.md` | Building the Stage 4 coverage ledger | | `bin/coverage-check.sh` | Checking a coverage ledger against its BRD | -| `skills/iterative-build-loop.md` | Building a module with mxcli | +| `skills/iterative-build-loop.md` | Building a module with mxcli, or an exec.sh guard just refused a write | | `project-bin/conformance-check.sh` | Running the ledger rung alone | | `project-bin/graph-sweep.sh` | Running the wiring rung alone | | `project-bin/test-stack-up.sh` | Before any runtime test | | `skills/company-brain.md` | Setting up or wiring a COMPANY BRAIN | | `skills/close-the-loop.md` | Cutover and retrospective | | `skills/report-schema.md` | Writing or reading docs/report.json | -| `skills/measured-claims.md` | Before citing ANY behavioural claim about the harness, the Mendix runtime or a test tool as evidence | +| `skills/measured-claims.md` | Before citing ANY behavioural claim about the harness, the Mendix runtime or a test tool as evidence, or counting tokens from transcripts | | `skills/agent-permission-friction.md` | Any refused, denied or blocked command | | `project-bin/coherence-cadence.sh` | After every module's CONFIRM stage | | `project-bin/build-plan-status.sh` | After marking a module done, or any time "how much is built vs proven" is asked | diff --git a/agents/mdl-agent.md b/agents/mdl-agent.md index a4b47bc4..a9e28c34 100644 --- a/agents/mdl-agent.md +++ b/agents/mdl-agent.md @@ -71,17 +71,17 @@ This table is your whole list. The baseline table in the project's CLAUDE.local. | `bug-logs/mxcli-bugs.md` | Reading a whole class of tool defects (a retest, a new mxcli release, an audit) | | `skills/cloud-dev-environment.md` | Setting up or resuming an mxcli project in a cloud/ephemeral container | | `skills/microflow-loop-antipatterns.md` | Reading what loop bodies do (LOOP_TQ, deferred commit, nested loop, REST in loop, transaction control per item, scheduled-event reachability) from described MDL; the catalog holds top-level activities only and cannot see inside a loop | -| `skills/iterative-build-loop.md` | Building a module with mxcli | +| `skills/iterative-build-loop.md` | Building a module with mxcli, or an exec.sh guard just refused a write | | `skills/mdl-cookbook-microflows.md` | Writing MDL microflow scripts | | `skills/build/mdl/oneshot-mdl-method.md` | Writing a single MDL script that takes a project from nothing to a working vertical slice | -| `skills/learned-page-patterns.md` | Building and auditing Mendix pages | +| `skills/learned-page-patterns.md` | Building and auditing Mendix pages, or starting a UI fix round across many pages | | `skills/oneshot-page-structure-patterns.md` | Generating a whole page tree in one script | | `skills/mendix-agents.md` | Building a Mendix AI agent | | `skills/mendix-agent-ui.md` | Embedding a copilot chat panel | | `skills/mendix-agent-setup.md` | Standing up a project's GenAI agents in any environment | | `skills/company-brain.md` | Setting up or wiring a COMPANY BRAIN | | `skills/close-the-loop.md` | Cutover and retrospective | -| `skills/measured-claims.md` | Before citing ANY behavioural claim about the harness, the Mendix runtime or a test tool as evidence | +| `skills/measured-claims.md` | Before citing ANY behavioural claim about the harness, the Mendix runtime or a test tool as evidence, or counting tokens from transcripts | | `skills/agent-permission-friction.md` | Any refused, denied or blocked command | | `project-bin/coherence-cadence.sh` | After every module's CONFIRM stage | | `skills/mpr-corruption-and-sp-load-errors.md` | Studio Pro will not load the project, or the .mpr looks gutted | diff --git a/agents/review-agent.md b/agents/review-agent.md index b925be50..63a7beac 100644 --- a/agents/review-agent.md +++ b/agents/review-agent.md @@ -78,7 +78,7 @@ This table is your whole list. The baseline table in the project's CLAUDE.local. | `skills/close-the-loop.md` | Cutover and retrospective | | `skills/report-schema.md` | Writing or reading docs/report.json | | `skills/harness-architecture.md` | Installing, extending, debugging or porting the verification harness | -| `skills/measured-claims.md` | Before citing ANY behavioural claim about the harness, the Mendix runtime or a test tool as evidence | +| `skills/measured-claims.md` | Before citing ANY behavioural claim about the harness, the Mendix runtime or a test tool as evidence, or counting tokens from transcripts | | `skills/agent-permission-friction.md` | Any refused, denied or blocked command | | `skills/process-coherence-pass.md` | Checking whether the whole journey hangs together rather than each piece | | `project-bin/coherence-cadence.sh` | After every module's CONFIRM stage | diff --git a/agents/test-agent.md b/agents/test-agent.md index 83ad1670..0ff1dd2a 100644 --- a/agents/test-agent.md +++ b/agents/test-agent.md @@ -57,7 +57,7 @@ This table is your whole list. The baseline table in the project's CLAUDE.local. | `skills/close-the-loop.md` | Cutover and retrospective | | `skills/report-schema.md` | Writing or reading docs/report.json | | `skills/harness-architecture.md` | Installing, extending, debugging or porting the verification harness | -| `skills/measured-claims.md` | Before citing ANY behavioural claim about the harness, the Mendix runtime or a test tool as evidence | +| `skills/measured-claims.md` | Before citing ANY behavioural claim about the harness, the Mendix runtime or a test tool as evidence, or counting tokens from transcripts | | `skills/agent-permission-friction.md` | Any refused, denied or blocked command | | `skills/e2e-evidence-report.md` | Turning an already-rigorous run into a narrated proof a stakeholder can trust without running anything | | `skills/record-demo-video.md` | Recording a narrated screen-capture demo of a running app for a human to watch | diff --git a/bin/init-project.sh b/bin/init-project.sh index 243ea49e..7f04a17a 100755 --- a/bin/init-project.sh +++ b/bin/init-project.sh @@ -724,6 +724,50 @@ echo " - Optional, NOT installed by this script: the context-cost hooks. They a echo " they fire in every repo on this machine — so installing them is your explicit choice." echo " Read README.md \"Context cost\" first (the numbers are the point), then:" echo " bin/install-claude-hooks.sh # prints the tier table, installs nothing" + +# Auto-compact window advisory (#167). A WARNING, never a written setting: the value is user-global +# or per-model, and an override the person did not choose is worse than one line of text. Field +# evidence (contrib-promoted, 2026-09-29): a lead session left at a 1M auto-compact window averaged +# 363-466k context per call against ~128k for a comparable build — the cost gap was context re-read +# per call, not call count; re-estimated at a 250k window the build cost fell by about a fifth. +# Reads only what Claude Code itself reads (CLAUDE_CODE_AUTO_COMPACT_WINDOW, then an +# "autoCompactWindow" number in the user or project settings.json; names checked against the +# 2.1.x CLI) and never fails the scaffold. +mxtk_compact_tokens() { # "250k" | "1M" | "250000" | "auto" -> integer tokens, or empty + local v n mult=1 + v="$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]')" + case "$v" in + *k) n="${v%k}"; mult=1000 ;; + *m) n="${v%m}"; mult=1000000 ;; + *) n="$v" ;; + esac + case "$n" in ''|*[!0-9]*) return 0 ;; esac # "auto", "1.5M", garbage: unknown, say nothing about it + printf '%s' "$(( 10#$n * mult ))" +} +_ac_limit=250000; _ac_val=""; _ac_from="" +if [ -n "${CLAUDE_CODE_AUTO_COMPACT_WINDOW:-}" ]; then + _ac_val="$(mxtk_compact_tokens "$CLAUDE_CODE_AUTO_COMPACT_WINDOW")" + _ac_from="CLAUDE_CODE_AUTO_COMPACT_WINDOW=$CLAUDE_CODE_AUTO_COMPACT_WINDOW" +else + for _ac_f in "$PROJECT_DIR/.claude/settings.local.json" "$PROJECT_DIR/.claude/settings.json" \ + "${CLAUDE_CONFIG_DIR:-${HOME:-}/.claude}/settings.json"; do + [ -f "$_ac_f" ] || continue + # Largest number found wins: a per-model block may list several, and the warning is for the worst. + _ac_n="$( (grep -oE '"autoCompactWindow"[[:space:]]*:[[:space:]]*[0-9]+' "$_ac_f" 2>/dev/null || true) \ + | grep -oE '[0-9]+$' | sort -n | tail -1 || true)" + if [ -n "$_ac_n" ]; then _ac_val="$_ac_n"; _ac_from="autoCompactWindow=$_ac_n in $_ac_f"; break; fi + done +fi +if [ -n "$_ac_val" ] && [ "$_ac_val" -gt "$_ac_limit" ]; then + echo " - WARNING: auto-compact window is above 250k ($_ac_from). A field build at a 1M window" + echo " averaged 363-466k context per call — every token re-read on every call. Lower it with" + echo " /autocompact 250k (or CLAUDE_CODE_AUTO_COMPACT_WINDOW=250k). Nothing was changed." +elif [ -n "$_ac_val" ]; then + echo " - Auto-compact window: $_ac_from (at or below 250k — ok)." +else + echo " - Auto-compact window: none set (\"auto\"), which on a 1M-context model compacts near 1M." + echo " Recommended: /autocompact 250k — a field lead at 1M averaged 363-466k per call. Not set by this script." +fi echo "" "$SCRIPT_DIR/gate-check.sh" "$PROJECT_DIR" || true diff --git a/bin/lib/skill-routing.tsv b/bin/lib/skill-routing.tsv index 200b80e4..dc144e7f 100644 --- a/bin/lib/skill-routing.tsv +++ b/bin/lib/skill-routing.tsv @@ -146,10 +146,10 @@ design-artifacts skills/design-artifacts.md Designing the brand and ONE ANNOTATE brd-to-build-plan skills/brd-to-build-plan.md Turning BRDs plus architecture into a numbered, dependency-ordered build plan architect 3,4 ondemand requirements coverage-ledger skills/coverage-ledger.md Building the Stage 4 coverage ledger — every requirement either claimed by a build-plan row or catalogued with a reason, never invisible architect,gate 4 ondemand requirements coverage-check bin/coverage-check.sh Checking a coverage ledger against its BRD — every scalar leaf CLAIMED, LEDGERED, UNCLAIMED, PHANTOM or DOUBLE-CLAIMED, so coverage is measured rather than remembered architect,gate 4 ondemand requirements -iterative-build-loop skills/iterative-build-loop.md Building a module with mxcli — verified, iterative, coverage-checklist gated mdl,gate 5 ondemand build +iterative-build-loop skills/iterative-build-loop.md Building a module with mxcli, or an exec.sh guard just refused a write — verified, iterative, coverage-checklist gated; on a refusal: commit, wait or stop, never FORCE_EXEC mdl,gate 5 ondemand build mdl-cookbook-microflows skills/mdl-cookbook-microflows.md Writing MDL microflow scripts — worked recipes mdl 5 ondemand build/mdl oneshot-mdl-method skills/build/mdl/oneshot-mdl-method.md Writing a single MDL script that takes a project from nothing to a working vertical slice — execution order, why it is deliberately non-idempotent, the instrument hierarchy, and the silent failures that pass every check mdl 5 ondemand build/mdl -learned-page-patterns skills/learned-page-patterns.md Building and auditing Mendix pages — widget patterns, datasource shapes mdl 5 ondemand build/pages +learned-page-patterns skills/learned-page-patterns.md Building and auditing Mendix pages, or starting a UI fix round across many pages — widget patterns, datasource shapes; for a round: SCSS first, four regressions to check mdl 5 ondemand build/pages oneshot-page-structure-patterns skills/oneshot-page-structure-patterns.md Generating a whole page tree in one script — the structure patterns that survive it mdl 5 ondemand build/pages mendix-agents skills/mendix-agents.md Building a Mendix AI agent — the agent is runtime data not a model document, so JSON import, tool microflows, knowledge base chunk loading and the runtime wiring all sit outside MDL, and mxbuild stays green when they are wrong mdl,architect 5 ondemand build/agents mendix-agent-ui skills/mendix-agent-ui.md Embedding a copilot chat panel — the frame is yours, ConversationalUI owns the conversation; wireframe to tokens to snippet to page placement mdl 5 ondemand build/agents @@ -170,7 +170,7 @@ company-brain skills/company-brain.md Setting up or wiring a COMPANY BRAIN — t close-the-loop skills/close-the-loop.md Cutover and retrospective — promoting proven patterns back into the toolkit all 7 ondemand spine report-schema skills/report-schema.md Writing or reading docs/report.json — the append-only contract every instrument writes to and every renderer reads; open BEFORE building a new instrument or a second renderer test,review,gate 5,6 ondemand verify harness-architecture skills/harness-architecture.md Installing, extending, debugging or porting the verification harness — which part owns what, which parts run standalone, and what a missing part must report test,review 5,6 ondemand verify -measured-claims skills/measured-claims.md Before citing ANY behavioural claim about the harness, the Mendix runtime or a test tool as evidence — a claim not in the register may not be cited all - ondemand spine +measured-claims skills/measured-claims.md Before citing ANY behavioural claim about the harness, the Mendix runtime or a test tool as evidence, or counting tokens from transcripts — a claim not in the register may not be cited all - ondemand spine tool-output-is-not-ground-truth skills/tool-output-is-not-ground-truth.md Any time an exit code, a tool's output or a subagent's report is about to become a stated finding — verify before you conclude all - baseline diagnose agent-permission-friction skills/agent-permission-friction.md Any refused, denied or blocked command — BEFORE rewriting a permission rule and before telling the user a tool is blocked. A rule matches the START of the command line, so an allowlisted tool prefixed with cd matches nothing all - ondemand diagnose process-coherence-pass skills/process-coherence-pass.md Checking whether the whole journey hangs together rather than each piece — finds correctly-built components nothing reaches, which per-element conformance and UI tests both miss review 5,6 ondemand verify diff --git a/contrib/inbox/2026-09-29-mcp-build-observations.md b/contrib/inbox/2026-09-29-mcp-build-observations.md deleted file mode 100644 index 2fa4a2c9..00000000 --- a/contrib/inbox/2026-09-29-mcp-build-observations.md +++ /dev/null @@ -1,25 +0,0 @@ -# What a Studio Pro MCP build teaches (and needs) — for the toolkit and for the MCP owners - -**From:** a field comparison (mxcli + toolkit build vs a Studio Pro MCP build, same frozen spec) -**Date:** 2026-09-29 -**Kind:** learning -**Field evidence:** The MCP build's METRICS/RETRO-LOG/TIME-LOG and the comparison tool's own tool journal: 406 screen-driving calls (22% of build calls) clustered on screens the MCP cannot write; those rows ran 12–21 min each vs 2.6–3.5 min/row for domain and logic; the person did 9 manual steps. -**Proposed target:** `skills/upstream-feedback.md` (route the MCP items), `skills/iterative-build-loop.md` (the two adoptable habits) - ---- - -Adoptable by the toolkit: -- **Save-then-check**: the MCP build lost time to checks against a stale disk copy (229 errors - hidden until a real save). The toolkit's exec gate already checks the saved file — keep it - that way, and say so in the build loop. -- **Visual check per page during the build**, not after the person complains (the The MCP build's UI - work started only after "the UI looks quite bad"). - -For the MCP/comparison-tool owners (not toolkit work): -- Write support for security, user roles, role home pages, snippets, scheduled events, - import/export mappings, published REST, pluggable-widget property schema. -- Atomic writes with fixed batch order (a reverse-order batch miswired 16 associations). -- Auto-flush/save before mx check; reload instead of full restart (23 restarts). -- Flag a missing end-of-parallel-path element in consistency check. -- Parallel drafting sub-agents with the lead as the single writer. -- Testing: commit scripts and results, add unit tests, make timers and ERP failure testable. diff --git a/contrib/inbox/2026-09-29-parallel-build-hygiene.md b/contrib/inbox/2026-09-29-parallel-build-hygiene.md index 2bfc62c8..b800b6d4 100644 --- a/contrib/inbox/2026-09-29-parallel-build-hygiene.md +++ b/contrib/inbox/2026-09-29-parallel-build-hygiene.md @@ -10,9 +10,10 @@ Parallel drafting is the toolkit build's speed advantage (2.6 vs 5.5 min/row; equal calls per row), so keep it and remove its friction: -- **Lock/queue mxbuild** across agents (exec.sh lock file or scratch-clone check). [measured: 2 collisions] -- **Batch the gate**: one mxbuild per 3–5 consecutive clean scripts; per-script only after a - failure. [hypothesis: 10–20 min wall time saved in slice 2] -- **Usage measurement**: streamed transcript lines carry partial usage; the measure script must - take each message's final usage record — sub-agent output was undercounted in both builds, - which briefly produced a false "MCP build writes 2.4x more output" conclusion. +- ~~Lock/queue mxbuild~~ — done: per-project exec lock in `project-bin/exec.sh` (#304). +- ~~Usage measurement~~ — promoted 2026-10-14 to `skills/measured-claims.md` §7 (re-measured: + sub-agent transcripts carry partial output_tokens on early records, up to 7x low). +- **Still open — batch the gate**: one mxbuild per 3–5 consecutive clean scripts; per-script + only after a failure. [hypothesis: 10–20 min wall time saved in slice 2]. Needs an `exec.sh` + mode (it has none today; `skills/iterative-build-loop.md` → "Gate cost as the model grows" + says not to imitate one with overrides). diff --git a/contrib/inbox/2026-09-29-subagent-context-cost.md b/contrib/inbox/2026-09-29-subagent-context-cost.md index ce993fcf..f616d5c0 100644 --- a/contrib/inbox/2026-09-29-subagent-context-cost.md +++ b/contrib/inbox/2026-09-29-subagent-context-cost.md @@ -40,7 +40,8 @@ Calls per row are the same. The whole cost gap is **context re-read per call**: Output tokens and effort level are not the driver: lead-to-lead output per call is equal (~630 vs ~610 tokens). The recorded sub-agent output is undercounted in both builds -(streamed usage records; `measure-usage.py` should read each message's final usage record). +(streamed usage records; each message's final usage record is the right one — promoted to +`skills/measured-claims.md` §7). ## Proposed improvements (hypotheses until re-measured) @@ -49,8 +50,8 @@ Output tokens and effort level are not the driver: lead-to-lead output per call Target: sub-agent mean context ≤ ~130k (the MCP build's level). - **Pass the slice, not the spec.** A build sub-agent gets its rows + the module's BRD, not the whole frozen spec. -- **Autocompact default in what `init-project.sh` writes** (or a preflight warning when a - session runs with autocompact > 250k). +- ~~Autocompact default~~ — done 2026-10-14 as an advisory: `bin/init-project.sh` warns when + the auto-compact window is above 250k and recommends `/autocompact 250k`; it writes nothing. - **Cap agent bundles / add autocompact for sub-agents** so a long-running drafter compacts instead of growing to 500k+. - **Measure it.** Add per-role mean-context-per-call to the time log so the next run shows diff --git a/contrib/inbox/2026-09-29-ui-loop-efficiency.md b/contrib/inbox/2026-09-29-ui-loop-efficiency.md index 6c347e34..3e9bb1f5 100644 --- a/contrib/inbox/2026-09-29-ui-loop-efficiency.md +++ b/contrib/inbox/2026-09-29-ui-loop-efficiency.md @@ -16,8 +16,11 @@ Regressions round 1 introduced (N1–N4 are lintable): N1 `page-column` class landing on the layout root (shell detached); N2 Atlas `.d-none` loses to `.pill`; N3 Data Grid 2 autoFit starving columns to 32 px; N4 dashed focus ring on the rail. -Proposed (hypotheses): -- UI agents apply directly, one mx check per batch — roughly halves calls. -- Cross-cutting fixes first, as SCSS, once; then re-review only changed pages. -- N1–N4 as a pre-apply lint in check-page-shell / learned-css-that-never-applied. -- Autocompact/bundle cap for UI drafters (see 2026-09-29-subagent-context-cost.md). +Promoted 2026-10-14: the round order (SCSS first, apply directly, re-look only changed pages) +and the N1–N4 pre-apply checks are in `skills/learned-page-patterns.md` → "UI fix rounds"; +N4's fix was already in `learned-css-that-never-applied.md`. + +Still open (not promoted): +- N1–N3 as a mechanical pre-apply check in `project-bin/check-page-shell.sh` (today a prose + checklist only); needs captured page MDL / SCSS showing each regression as golden input. +- Bundle cap for UI drafters (see 2026-09-29-subagent-context-cost.md). diff --git a/skills/iterative-build-loop.md b/skills/iterative-build-loop.md index 8e64563f..c2a2591f 100644 --- a/skills/iterative-build-loop.md +++ b/skills/iterative-build-loop.md @@ -316,6 +316,64 @@ rm -f "$MPR.lock" echo "✓ SP restarting — click Run Locally when it finishes loading." ``` +### Every model write goes through `bin/exec.sh` — and what to do when a guard refuses + +Every MDL script, and every one-off model patch (`--patch`), is applied with `./bin/exec.sh`. +A raw `./mxcli exec` writes no BUILD-LOG row, takes no snapshot and runs no gate, so the change +shows as "not recorded" to everything that reads the build log (status, done-drift, proof views). + +When a guard refuses, nothing in the model changed (the uncommitted-model and `--commit-first` +refusals also write a `🚫 blocked` row; the Studio Pro and lock refusals only print). Your move +is one of three — **commit, wait, or stop and report** — never an override: + +| Refusal | Do this | +|---|---| +| Uncommitted model changes | Re-run with `--commit-first` (commits the `.mpr` + `mprcontents/` only), or commit them yourself, then re-run | +| Studio Pro holds the model (also refuses `--commit-first`) | Stop. Ask the person to save (Cmd+S) and close the project; never kill Studio Pro | +| Another session holds this project's exec lock | Wait for that run to finish, then re-run. A dead holder is taken over automatically — never `rm` the lock | +| The mxbuild gate cannot run, even after its own download | Stop and report; run `bin/doctor.sh`. `ALLOW_UNVERIFIED=1` only when the person says so | +| Pre-exec check or a NEW mxbuild error | Fix the script. That is the gate working | + +**Never set `FORCE_EXEC=1` on your own initiative** — only when the person tells you to, naming +the risk. The failure it causes: an agent hits the uncommitted-model guard mid-loop, sets +`FORCE_EXEC=1` to keep going, the next script's gate fails, and the auto-restore silently drops +every MCP change made since the last commit (problem 4 above). The guard is the only thing +between those two events. + +**The MCP write path stays allowed** where `learned-mdl-preflight.md` prescribes it (its +Step 0 write-mode choice), and it is **accepted as unrecorded**: no exec row, no exec gate. Its record +is the commit you make before the next exec (the pre-exec sequence below), with a message that +starts `MCP:` and names what changed. + +**Check before closing a phase:** every model-changing commit since the phase began is either +covered by a BUILD-LOG row or is an `MCP:` commit. Count both: commits = exec rows + `MCP:` +commits. A commit that is neither is a raw `mxcli exec` — re-run that script through exec.sh, +or record why it cannot be. + +### The gate checks the saved file — save, then check + +`exec.sh`'s mxbuild gate reads the `.mpr` on disk, never an editor's in-memory model, so after +any Studio Pro or MCP edit, **save first, then check**. The failure this prevents (field +comparison, 2026-09-29): an MCP build ran `mx check` against a stale disk copy and 229 errors +stayed hidden until a real save. Step 1 of the sequence below is that save; exec.sh's +Studio-Pro-holds-the-model refusal exists for the same reason. + +### Gate cost as the model grows — what to batch, and what not to + +The per-script apply grows with the model (field run, 2026-09-29: 22–30 s per script in the +first slice, 55–65 s in the second, almost all of it mxbuild). What the gate already saves: +the pre-flight baseline mxbuild is skipped when the verification stamp says the model is +unchanged since its last clean build, so a run of clean applies costs **one** mxbuild each, not +two. Concurrent applies from parallel agents queue on the exec lock instead of colliding (two of +eleven failed applies in that run were concurrent mxbuild collisions, from before the lock). + +`exec.sh` has **no** batched-gate mode. Do not imitate one with `SKIP_CHECK`, `SKIP_BASELINE`, +`FORCE_EXEC` or raw `mxcli exec` runs between gates — each of those is an unrecorded or +unverified write. If gate time dominates, reduce the number of applies at the granularity level +(see "Iterative Granularity": layer scripts rather than per-page ones where partial recovery is +unlikely, fewer stub-then-fill passes), keep parallel **drafting**, and let the exec lock +serialise the applies. + ### Pre-exec sequence when MCP work was done Before running exec.sh after any MCP session: diff --git a/skills/learned-page-patterns.md b/skills/learned-page-patterns.md index bfa188c5..cc9813a6 100644 --- a/skills/learned-page-patterns.md +++ b/skills/learned-page-patterns.md @@ -349,3 +349,40 @@ nothing). Making the input itself editable does nothing. Set `Editable: true` on between the page and the input. Lint MDL-WIDGET31 checks only native inputs, so it misses a pluggable one such as a Switch (BUG-149). (Mendix 11.13.0, mxcli v0.24.0, the same build, 2026-09-26: fixed only when both the outer and the inner list view were editable.) + +## UI fix rounds — fix once in SCSS, apply directly, re-look only what changed + +A round of UI fixes across many pages (after a review, a design port, or "the UI looks bad") is +where calls multiply and regressions sneak in. Field comparison, 2026-09-29: one UI round took +1,507 calls against 515 for a comparable build in the same ~3 h. Seven drafting agents each ran +check → exec → mx check → page-fidelity on a scratch copy for every script, and the lead then +applied each script again: about 25 calls per applied fix. Round 1 also introduced four +regressions, and about a fifth of the UI tokens went to round 2 repairing them. + +**Order of work for a round:** + +1. **Cross-cutting fixes first, once, as SCSS** — a token, a spacing rule, a class every page + shares. One theme change fixes N pages; N page scripts fix N pages and drift. +2. **Then the page scripts.** A drafter that has run `mxcli check` and `page-fidelity.js` on its + script hands it over for applying with `./bin/exec.sh`. Do not pre-apply to a scratch copy + "to be sure": exec.sh's gate is that check, and its lock serialises parallel applies. +3. **Re-look only the pages the round changed** — the list is the round's BUILD-LOG rows plus + the pages an SCSS change reaches. Done when every page on that list has a fresh look + (`ui-loop.md`), N of N. + +(Expected effect: roughly half the calls per round. That is a hypothesis from one run, not a +measurement.) + +**Before applying, check the round's scripts and SCSS for these four regressions** — each one +broke previously correct pages in that round, and every one is visible in the source before it is +visible on screen: + +| # | Regression | What it looked like | Check before applying | +|---|---|---|---| +| N1 | The page-column class lands on the layout root, not the page body | the app shell (sidebar, top bar) detaches and squeezes into the page column | the page-column class sits on a container **inside** the page's content placeholder; no layout or layout-root selector carries it | +| N2 | Atlas `.d-none` loses to a project class that sets `display` | an element meant to be hidden shows (seen: a `.pill` badge) | hide with Mendix conditional visibility, not a hiding class; if a class must hide, grep the theme for any rule on the same widget that sets `display` | +| N3 | Data Grid 2 columns left on `autoFit` | columns starve to 32 px, text cut to an ellipsis | every text column has an explicit width (`autoFill` or `manual`); `autoFit` only for icon or button columns | +| N4 | A `:focus-visible` ring on navigation | one tab looks selected on every page landing | `learned-css-that-never-applied.md` → "a focus ring that paints on every page landing" | + +Denominator: the round's scripts and SCSS files checked against all four rows, M of M, before the +first apply. diff --git a/skills/measured-claims.md b/skills/measured-claims.md index ba530ff4..a478bcb5 100644 --- a/skills/measured-claims.md +++ b/skills/measured-claims.md @@ -235,6 +235,36 @@ mocked integration render empty" as a suspected instrument fault, not a finding. --- +## 7. A transcript's first usage record for a message is not its usage — read the last + +**Verdict: CONFIRMED for sub-agent transcripts; not seen in a main-loop transcript.** Measured +2026-10-13 on Claude Code 2.1.296 transcripts (`~/.claude/projects//…`), grouping +`assistant` records by `message.id` and comparing `usage.output_tokens` on the first and the last +record of each message: + +| Transcript | Messages | Messages whose records disagree | Output, first record | Output, last record | +|---|---:|---:|---:|---:| +| main loop | 941 | **0** | 631,486 | 631,486 | +| sub-agent a | 133 | 2 | 1,652 | 2,837 | +| sub-agent b | 109 | 2 | 1,152 | 6,402 | +| sub-agent c | 93 | 3 | 1,151 | **8,241** | +| sub-agent d | 98 | 0 | 1,709 | 1,709 | + +The main loop is the control that did not move; the sub-agents moved by up to 7x. A streamed +message is written as several records carrying one `message.id`, and in sub-agent transcripts +the early ones carry a partial `output_tokens` (e.g. `[4, 2631]` — first record 4, final 2,631). + +**Rule: sum usage once per `message.id`, from that message's LAST record.** Deduplicating by id +is right (summing every record double-counts); keeping the *first* record is the undercount. + +**Cost:** a field comparison of two builds (2026-09-29) briefly concluded "the other build writes +2.4x more output" — an artefact of reading partial records in both builds' sub-agents. A +measurement that compares per-role or sub-agent output is wrong in the direction of whichever +side uses more sub-agents. Replicated on one machine only; the main-loop arm shows the effect is +sub-agent-specific, not universal — re-measure on your harness version before citing a ratio. + +--- + ## Adding an entry An entry earns its place only if it carries all four parts: the claim, the verdict, the diff --git a/skills/upstream-feedback.md b/skills/upstream-feedback.md index 4d2aef7b..640c9edf 100644 --- a/skills/upstream-feedback.md +++ b/skills/upstream-feedback.md @@ -26,6 +26,25 @@ mxcli. No → toolkit. A finding that is really both ("`check` passed a construc `learned-detection-gaps.md` did not list it") is **two** submissions, one per repo, each citing the other's number once it exists. +**A third owner, outside both repos: the Studio Pro MCP server.** A gap in what the MCP path can +write or check (a model element it cannot create, a batch it applies out of order, a check that +reads an unsaved model) is neither an mxcli defect nor a toolkit one — the test above answers +"no" for mxcli and "yes, still wrong without the toolkit". Do not file it in either repo and do +not paper over it with a toolkit rule; draft it locally (§4 shape, §5 scrub) for the person to +pass to that server's owners, and note the draft's path in `PROJECT.md`. Failure it prevents: a +2026-09-29 field comparison logged its MCP asks only in a toolkit inbox note, where no MCP owner +would read them. The asks from that run, with what was measured: + +| Ask | Evidence from the run | +|---|---| +| Write support for security, user roles, role home pages, snippets, scheduled events, import/export mappings, published REST, pluggable-widget property schema | 406 screen-driving calls (22% of the build) on screens the MCP path could not write; those rows ran 12–21 min each vs 2.6–3.5 min for domain and logic; 9 manual steps by the person | +| Atomic writes with a fixed batch order | a reverse-order batch miswired 16 associations | +| Save before `mx check`; reload instead of full restart | 229 errors hidden until a real save; 23 restarts | +| Flag a missing end-of-parallel-path element in the consistency check | observed in that run; no count recorded | + +Done when each ask is either in a local draft with its evidence row, or recorded as dropped with +a reason — none left only in a chat or an inbox note. + ## 2. Before anything else: is it already written down? > **A gap that reads as "the toolkit already said this and it was not followed" is not a toolkit diff --git a/tests/wave2/test-init-autocompact.sh b/tests/wave2/test-init-autocompact.sh new file mode 100755 index 00000000..958e0a79 --- /dev/null +++ b/tests/wave2/test-init-autocompact.sh @@ -0,0 +1,73 @@ +#!/bin/bash +# test-init-autocompact.sh — init-project.sh must warn about a large auto-compact window (#167). +# +# Field finding (2026-09-29): a lead session left at a 1M auto-compact window averaged 363-466k +# context per call; the cost gap to a comparable build was context re-read per call, not call +# count. The fix is an advisory line, never a written setting. This fixture checks the four +# states by execution: nothing set, env var 1M, settings.json 250000 (ok), settings.json 1000000. +# Setting names (CLAUDE_CODE_AUTO_COMPACT_WINDOW, "autoCompactWindow", CLAUDE_CONFIG_DIR) were +# read out of the Claude Code 2.1.x CLI binary, and the per-model settings shape below is the +# shape a real ~/.claude/settings.json carried — not invented. +# +# Usage: bash test-init-autocompact.sh [path-to-init-project.sh] +# Fails against master's init-project.sh (no auto-compact line at all). + +export MXTK_LEAKGUARD_DENYFILE="${TMPDIR:-/tmp}/mxtk-fixture-denylist.$$" +export MXTK_NO_GUIDE=1 +SUBJECT="${1:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../../bin" && pwd)/init-project.sh}" +PASS=0; FAIL=0 +ok() { PASS=$((PASS+1)); echo " ok — $1"; } +bad() { FAIL=$((FAIL+1)); echo " FAIL — $1"; } + +WORK=$(mktemp -d "${TMPDIR:-/tmp}/initac.XXXXXX") || exit 2 +trap 'rm -rf "$WORK" "$MXTK_LEAKGUARD_DENYFILE"' EXIT + +run() { # $1 = case name, $2 = env value for CLAUDE_CODE_AUTO_COMPACT_WINDOW ("" = unset) + local c="$1" envv="$2" + mkdir -p "$WORK/home-$c/.claude" "$WORK/proj-$c" + if [ -n "$envv" ]; then + env CLAUDE_CODE_AUTO_COMPACT_WINDOW="$envv" CLAUDE_CONFIG_DIR="$WORK/home-$c/.claude" \ + bash "$SUBJECT" "$WORK/proj-$c" "$WORK/out-$c" 2>&1 + else + env -u CLAUDE_CODE_AUTO_COMPACT_WINDOW CLAUDE_CONFIG_DIR="$WORK/home-$c/.claude" \ + bash "$SUBJECT" "$WORK/proj-$c" "$WORK/out-$c" 2>&1 + fi + echo $? +} + +echo "== subject: $SUBJECT" + +rc=$(run none "") +[ "$rc" -eq 0 ] && ok "no setting: scaffold exits 0" || bad "no setting: scaffold exited $rc" +grep -q 'Recommended: /autocompact 250k' "$WORK/out-none" \ + && ok "no setting: recommends /autocompact 250k" || bad "no setting: no auto-compact recommendation" + +rc=$(run env1m "1M") +[ "$rc" -eq 0 ] && ok "env 1M: scaffold still exits 0 (advice, not a gate)" || bad "env 1M: scaffold exited $rc" +grep -q 'WARNING: auto-compact window is above 250k (CLAUDE_CODE_AUTO_COMPACT_WINDOW=1M)' "$WORK/out-env1m" \ + && ok "env 1M: warns and names the source" || bad "env 1M: no warning naming the env var" + +mkdir -p "$WORK/home-ok/.claude" +printf '{\n "modelSettings": {\n "some-model": {\n "autoCompactWindow": 250000\n }\n }\n}\n' \ + > "$WORK/home-ok/.claude/settings.json" +run ok "" >/dev/null +grep -q 'autoCompactWindow=250000 .*at or below 250k' "$WORK/out-ok" \ + && ok "settings 250000: reported ok" || bad "settings 250000: not reported as ok" +grep -q 'WARNING: auto-compact' "$WORK/out-ok" && bad "settings 250000: warned anyway" || ok "settings 250000: no warning" + +mkdir -p "$WORK/home-big/.claude" +printf '{"autoCompactWindow":1000000}\n' > "$WORK/home-big/.claude/settings.json" +run big "" >/dev/null +grep -q 'WARNING: auto-compact window is above 250k (autoCompactWindow=1000000' "$WORK/out-big" \ + && ok "settings 1000000: warns" || bad "settings 1000000: no warning" + +rc=$(run garbage "1.5M") +[ "$rc" -eq 0 ] && ok "unparseable env value: scaffold exits 0" || bad "unparseable env value: scaffold exited $rc (must never fail on advice)" + +# Never writes a setting. +if grep -rqs 'autoCompactWindow' "$WORK"/proj-*/.claude 2>/dev/null; then + bad "init wrote an autoCompactWindow setting into the project — it must only advise" +else ok "nothing written: no autoCompactWindow in any scaffolded .claude/"; fi + +echo "== $PASS passed, $FAIL failed" +[ "$FAIL" -eq 0 ] From 2d9d6b88614cb002aa6fcd6cb47dedb0a881ca6c Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 13 Oct 2026 15:27:55 +0000 Subject: [PATCH 2/3] Review fixes for #323: coverage check, lock refuses, PRE-EXISTING row, dropped MCP asks, stricter autocompact advisory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - iterative-build-loop: phase-close check is coverage (applied exec row, --commit-first commit, or MCP: commit), N of N, not an equality; pre-exec step 2 commits as "MCP: "; the exec lock refuses (no queue); refusal table gains PRE-EXISTING and "any other refusal: read the printed remedy"; the rows each refusal writes are named. - learned-page-patterns + inbox: the lock refuses, it does not serialise/queue. - upstream-feedback: the two dropped MCP-build items recorded with why. - routing: the exec trigger leads with "An exec.sh guard refused a write (not a permission prompt)"; stubs re-rendered; baseline unchanged at 79,997 words. - init-project advisory: reads env AND every autoCompactWindow in project and user settings, warns if any is above 250k; string values ("1M") parsed; unreadable values printed as "unparseable: " instead of "none set"; values over 12 digits never reach arithmetic. Fixture grows to 15 checks (all pass). - measured-claims §7: notes token-burn.sh keeps the first record today. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw --- CHANGELOG.md | 4 +- README.md | 2 +- ROUTING.md | 2 +- agents/gate-agent.md | 2 +- agents/mdl-agent.md | 2 +- bin/init-project.sh | 73 +++++++++++-------- bin/lib/skill-routing.tsv | 2 +- .../2026-09-29-parallel-build-hygiene.md | 3 +- skills/iterative-build-loop.md | 35 +++++---- skills/learned-page-patterns.md | 3 +- skills/measured-claims.md | 2 + skills/upstream-feedback.md | 6 ++ tests/wave2/test-init-autocompact.sh | 38 ++++++++-- 13 files changed, 117 insertions(+), 57 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ad407609..073dcd4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,9 +17,9 @@ Sections dated before 2026-09-19 predate the cycle and stay as they are. ## Unreleased -- learn(iterative-build-loop, upstream-feedback): **every model write goes through `exec.sh`; on a refusal the move is commit (`--commit-first`), wait (exec lock) or stop and report — never `FORCE_EXEC` on the agent's own initiative; MCP writes stay allowed and are accepted as unrecorded, recorded by an `MCP:` commit; the gate checks the saved file (save, then check); no batched-gate mode exists, so do not imitate one with overrides; Studio Pro MCP server asks get a third destination in `upstream-feedback.md` with their evidence rows** ([#69](https://github.com/MendixMau/mxcli-project-toolkit/issues/69), [#171](https://github.com/MendixMau/mxcli-project-toolkit/issues/171), [#173](https://github.com/MendixMau/mxcli-project-toolkit/issues/173)). The phase-close check is a count: model commits = exec rows + `MCP:` commits. Field evidence: a 2026-09-29 two-build comparison (229 errors hidden by a stale disk copy; per-script apply 22–30 s growing to 55–65 s). Routing triggers for the build loop, page patterns and measured claims now name the refusal, the UI round and transcript token counts (stubs re-rendered; baseline unchanged at 79,997 words) — MendixMau +- learn(iterative-build-loop, upstream-feedback): **every model write goes through `exec.sh`; on a refusal the move is commit (`--commit-first`), wait (exec lock) or stop and report — never `FORCE_EXEC` on the agent's own initiative; MCP writes stay allowed and are accepted as unrecorded, recorded by an `MCP:` commit; the gate checks the saved file (save, then check); no batched-gate mode exists, so do not imitate one with overrides; Studio Pro MCP server asks get a third destination in `upstream-feedback.md` with their evidence rows** ([#69](https://github.com/MendixMau/mxcli-project-toolkit/issues/69), [#171](https://github.com/MendixMau/mxcli-project-toolkit/issues/171), [#173](https://github.com/MendixMau/mxcli-project-toolkit/issues/173)). The phase-close check is coverage, N of N: every model commit is covered by an applied exec row, a `--commit-first` commit or an `MCP:` commit (the pre-exec sequence now commits as `MCP: …`); the refusal table includes PRE-EXISTING errors and ends with "read the printed remedy"; the exec lock refuses rather than queues, and the text says so. Field evidence: a 2026-09-29 two-build comparison (229 errors hidden by a stale disk copy; per-script apply 22–30 s growing to 55–65 s). Routing triggers for the build loop, page patterns and measured claims now name the refusal, the UI round and transcript token counts (stubs re-rendered; baseline unchanged at 79,997 words) — MendixMau - learn(learned-page-patterns, measured-claims): **UI fix rounds: SCSS first, apply directly through `exec.sh`, re-look only changed pages (N of N), and check four regressions before applying (N1 page-column on the layout root, N2 `.d-none` losing to a `display` class, N3 Data Grid 2 `autoFit` columns at 32 px, N4 focus ring); transcript usage is read from each message's LAST record** ([#170](https://github.com/MendixMau/mxcli-project-toolkit/issues/170), [#171](https://github.com/MendixMau/mxcli-project-toolkit/issues/171)). The round took 1,507 calls against 515 for a comparable build; re-measured on Claude Code 2.1.296 transcripts, sub-agent output read from the first record was up to 7x low while the main loop's was exact — MendixMau -- new(init-project): **the scaffold prints an auto-compact advisory: a warning when `CLAUDE_CODE_AUTO_COMPACT_WINDOW` or a settings `autoCompactWindow` is above 250k, otherwise a `/autocompact 250k` recommendation; it writes no setting and never fails the scaffold** ([#167](https://github.com/MendixMau/mxcli-project-toolkit/issues/167)). A lead session at a 1M window averaged 363–466k context per call. Fixture `tests/wave2/test-init-autocompact.sh` (9 checks; 4 fail on master) — MendixMau +- new(init-project): **the scaffold prints an auto-compact advisory: a warning when `CLAUDE_CODE_AUTO_COMPACT_WINDOW` or any `autoCompactWindow` in the project or user settings is above 250k, an "unparseable" line for a value it cannot read, otherwise a `/autocompact 250k` recommendation; it writes no setting and never fails the scaffold** ([#167](https://github.com/MendixMau/mxcli-project-toolkit/issues/167)). A lead session at a 1M window averaged 363–466k context per call. Fixture `tests/wave2/test-init-autocompact.sh` (15 checks; fails on master) — MendixMau - learn(testing-shape, test-result-audit): **three test-strength rules, each with a denominator: refusals probed on the server, every undeclared 4xx a failure, one clean-database final run** ([#172](https://github.com/MendixMau/mxcli-project-toolkit/issues/172)). A field comparison of two builds against one frozen spec found the toolkit build checked permission refusals only in the client (a hidden menu item), logged unexpected 400/422 responses as information, and reached 20/20 by stitching three runs. `testing-shape.md` §7 gains three report lines (`N of N restricted pairs probed server-side`, `undeclared 4xx/5xx: 0 of M requests — each fails its step`, `one clean-DB run : N of N scenarios incl. timers, not stitched`); `test-result-audit.md` gains check 2e, which carries the reasons and the field evidence and asks for those numbers. Both baseline skills stay inside the 80,000-word baseline budget (79,997): the reasons live in the on-demand audit skill, and two redundant sentences in `testing-shape.md` §4's verdict paragraphs were folded (the empty-set corollary is stated in the Trace row, `harness-architecture.md` and `report-schema.md`). The backing inbox note is trimmed to its unpromoted "Keep" items. — MendixMau - learn(learned-detection-gaps, module-review): **two UI defects every model gate passes: a lazy-loaded combo box filter hides its preset, and a theme that pins row heights by position lets the next row cover an inserted widget** ([#299](https://github.com/MendixMau/mxcli-project-toolkit/issues/299)). Both passed check, mxbuild, lint and the scripted checks on a 127-module production app and were found only in the running app. Two register rows with the first oracle and the pre-build check (pluggable properties that change *when* data loads; grep the theme for fixed `height` on positional row classes before an insert), and two LOOK lines in `module-review.md` §4c with denominators (`N of N filters show their preset on first load`, `N of N widgets inserted into an existing container fully visible`). — jopterhorst - fix(app-restart, test-stack-up): **a port held by another process before `run --local` is named and reported as "instrument blocked" (exit 4), never killed** ([#262](https://github.com/MendixMau/mxcli-project-toolkit/issues/262)). Studio Pro had started its own runtime on the app/admin ports a restart needed; `mxcli run --local` then failed with "Authentication failed" (the admin API answering was Studio Pro's), the next journey ran against Studio Pro's runtime, and four more runs were NOT RUN. `app-restart.sh` now checks the app port and the admin port (record's `adminPort`, else app + 10) before stopping anything — a holder outside the recorded process tree exits 4 with its name, pid and parent — and again after the stop, where anything still holding a port is by definition not ours. `test-stack-up.sh` runs the same check wherever it would tell you to start `run --local` (`--no-docker`, no Docker daemon, `--check` with a leftover record), on the record's ports, `RUN_LOCAL_APP_PORT`/`RUN_LOCAL_ADMIN_PORT`, or mxcli's defaults 8080/8090. Holder lookup: lsof, then ss, then netstat + tasklist on Windows; unnamed holders still block when nothing of ours is running. Fixtures: `test-app-restart.sh` +2 cases (14 assertions, all fail on the old script), `test-stack-up-local.sh` T7 (4 of 6 fail on the old script). — AlvinnYeo diff --git a/README.md b/README.md index 0c7bcc37..651b9787 100644 --- a/README.md +++ b/README.md @@ -587,7 +587,7 @@ Every mxcli project has a `.ai-context/skills/` directory (bundled by `mxcli ini | Task | Skill to load | |---|---| | Dispatching a build step to a helper — generate its one-file pack (the brief's Build steps row, mxcli brain brief, live DESCRIBEs, example, folder) and hand over the path instead of a reading list. Measured: 25-38% fewer tool calls and 30-41% less time, same quality; the token saving is not proven | `project-bin/context-pack.sh` | -| Building a module with mxcli, or an exec.sh guard just refused a write — verified, iterative, coverage-checklist gated; on a refusal: commit, wait or stop, never FORCE_EXEC | `skills/iterative-build-loop.md` | +| An exec.sh guard refused a write (not a permission prompt), or building a module with mxcli — verified, iterative, coverage-checklist gated; on a refusal: commit, wait or stop, never FORCE_EXEC | `skills/iterative-build-loop.md` | | After marking a module done, or any time "how much is built vs proven" is asked — renders build-plan.html from done- prefixes and verify-module.sh/improvement-register.md, kept as two honestly separate views; --json writes architecture/build-plan.json parsed from build-plan.md's Phase headings (a plan with no Phase headings gets no file) | `project-bin/build-plan-status.sh` | | Turning a client-derived Mendix app into a clean, shareable demo with zero client fingerprint — branding, data, custom widgets | `skills/anonymize-client-app-for-demo.md` | | Handing a headless-built model to a person — opening it in Studio Pro, a free sandbox, or a colleague's machine: the model travels, the demo data and runtime config (keys, an agent's bound model) do not, and each needs its own re-establish step | `skills/handoff-to-studio-pro.md` | diff --git a/ROUTING.md b/ROUTING.md index 3e0b4548..758c6cf8 100644 --- a/ROUTING.md +++ b/ROUTING.md @@ -136,7 +136,7 @@ picks the row up. That is the whole procedure — there is no second list to rem | Reviewing any module before calling it done — the ONE pass: build, gate, prove, LOOK (is it logical, does it look right, does it match our design, over every page not just the tested ones), confirm with the denominator stated | `skills/module-review.md` | mdl,review,test | 5,6 | baseline | | Before any mxcli exec / exec.sh / --mcp write — ask or run? the knob decides | `bin/exec-approval.sh` | mdl,gate | 5,6 | baseline | | Dispatching a build step to a helper — generate its one-file pack (the brief's Build steps row, mxcli brain brief, live DESCRIBEs, example, folder) and hand over the path instead of a reading list. Measured: 25-38% fewer tool calls and 30-41% less time, same quality; the token saving is not proven | `project-bin/context-pack.sh` | mdl | 5 | ondemand | -| Building a module with mxcli, or an exec.sh guard just refused a write — verified, iterative, coverage-checklist gated; on a refusal: commit, wait or stop, never FORCE_EXEC | `skills/iterative-build-loop.md` | mdl,gate | 5 | ondemand | +| An exec.sh guard refused a write (not a permission prompt), or building a module with mxcli — verified, iterative, coverage-checklist gated; on a refusal: commit, wait or stop, never FORCE_EXEC | `skills/iterative-build-loop.md` | mdl,gate | 5 | ondemand | | After marking a module done, or any time "how much is built vs proven" is asked — renders build-plan.html from done- prefixes and verify-module.sh/improvement-register.md, kept as two honestly separate views; --json writes architecture/build-plan.json parsed from build-plan.md's Phase headings (a plan with no Phase headings gets no file) | `project-bin/build-plan-status.sh` | architect,gate,review | 4,5,6 | ondemand | | Turning a client-derived Mendix app into a clean, shareable demo with zero client fingerprint — branding, data, custom widgets | `skills/anonymize-client-app-for-demo.md` | mdl,review | 6 | ondemand | | Handing a headless-built model to a person — opening it in Studio Pro, a free sandbox, or a colleague's machine: the model travels, the demo data and runtime config (keys, an agent's bound model) do not, and each needs its own re-establish step | `skills/handoff-to-studio-pro.md` | test,review | 6,7 | ondemand | diff --git a/agents/gate-agent.md b/agents/gate-agent.md index 505e5241..3e7c647b 100644 --- a/agents/gate-agent.md +++ b/agents/gate-agent.md @@ -56,7 +56,7 @@ This table is your whole list. The baseline table in the project's CLAUDE.local. | `skills/cloud-dev-environment.md` | Setting up or resuming an mxcli project in a cloud/ephemeral container | | `skills/coverage-ledger.md` | Building the Stage 4 coverage ledger | | `bin/coverage-check.sh` | Checking a coverage ledger against its BRD | -| `skills/iterative-build-loop.md` | Building a module with mxcli, or an exec.sh guard just refused a write | +| `skills/iterative-build-loop.md` | An exec.sh guard refused a write (not a permission prompt), or building a module with mxcli | | `project-bin/conformance-check.sh` | Running the ledger rung alone | | `project-bin/graph-sweep.sh` | Running the wiring rung alone | | `project-bin/test-stack-up.sh` | Before any runtime test | diff --git a/agents/mdl-agent.md b/agents/mdl-agent.md index a9e28c34..bb0e7ff9 100644 --- a/agents/mdl-agent.md +++ b/agents/mdl-agent.md @@ -71,7 +71,7 @@ This table is your whole list. The baseline table in the project's CLAUDE.local. | `bug-logs/mxcli-bugs.md` | Reading a whole class of tool defects (a retest, a new mxcli release, an audit) | | `skills/cloud-dev-environment.md` | Setting up or resuming an mxcli project in a cloud/ephemeral container | | `skills/microflow-loop-antipatterns.md` | Reading what loop bodies do (LOOP_TQ, deferred commit, nested loop, REST in loop, transaction control per item, scheduled-event reachability) from described MDL; the catalog holds top-level activities only and cannot see inside a loop | -| `skills/iterative-build-loop.md` | Building a module with mxcli, or an exec.sh guard just refused a write | +| `skills/iterative-build-loop.md` | An exec.sh guard refused a write (not a permission prompt), or building a module with mxcli | | `skills/mdl-cookbook-microflows.md` | Writing MDL microflow scripts | | `skills/build/mdl/oneshot-mdl-method.md` | Writing a single MDL script that takes a project from nothing to a working vertical slice | | `skills/learned-page-patterns.md` | Building and auditing Mendix pages, or starting a UI fix round across many pages | diff --git a/bin/init-project.sh b/bin/init-project.sh index 7f04a17a..6523aabc 100755 --- a/bin/init-project.sh +++ b/bin/init-project.sh @@ -730,43 +730,58 @@ echo " bin/install-claude-hooks.sh # prints the tier table, instal # evidence (contrib-promoted, 2026-09-29): a lead session left at a 1M auto-compact window averaged # 363-466k context per call against ~128k for a comparable build — the cost gap was context re-read # per call, not call count; re-estimated at a 250k window the build cost fell by about a fifth. -# Reads only what Claude Code itself reads (CLAUDE_CODE_AUTO_COMPACT_WINDOW, then an -# "autoCompactWindow" number in the user or project settings.json; names checked against the -# 2.1.x CLI) and never fails the scaffold. -mxtk_compact_tokens() { # "250k" | "1M" | "250000" | "auto" -> integer tokens, or empty +# Reads what Claude Code reads — CLAUDE_CODE_AUTO_COMPACT_WINDOW, and every "autoCompactWindow" +# value (number or string) in the project's settings.local.json / settings.json AND the user's +# settings.json; names checked against the 2.1.x CLI — and warns if ANY of them is above 250k. +# A value it cannot read is printed as "unparseable", never as "none set". Never fails the scaffold. +mxtk_compact_tokens() { # "250k" | "1M" | "250000" -> integer tokens; "auto"/garbage -> empty local v n mult=1 - v="$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]')" + v="$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]"')" case "$v" in *k) n="${v%k}"; mult=1000 ;; *m) n="${v%m}"; mult=1000000 ;; *) n="$v" ;; esac - case "$n" in ''|*[!0-9]*) return 0 ;; esac # "auto", "1.5M", garbage: unknown, say nothing about it - printf '%s' "$(( 10#$n * mult ))" + case "$n" in ''|*[!0-9]*) return 0 ;; esac + n="$(printf '%s' "$n" | sed 's/^0*//')"; [ -n "$n" ] || n=0 + # Beyond 12 digits shell arithmetic can overflow; anything that long is far above 250k anyway. + if [ "${#n}" -gt 12 ]; then printf '999999999999'; return 0; fi + printf '%s' "$(( n * mult ))" } -_ac_limit=250000; _ac_val=""; _ac_from="" -if [ -n "${CLAUDE_CODE_AUTO_COMPACT_WINDOW:-}" ]; then - _ac_val="$(mxtk_compact_tokens "$CLAUDE_CODE_AUTO_COMPACT_WINDOW")" - _ac_from="CLAUDE_CODE_AUTO_COMPACT_WINDOW=$CLAUDE_CODE_AUTO_COMPACT_WINDOW" -else - for _ac_f in "$PROJECT_DIR/.claude/settings.local.json" "$PROJECT_DIR/.claude/settings.json" \ - "${CLAUDE_CONFIG_DIR:-${HOME:-}/.claude}/settings.json"; do - [ -f "$_ac_f" ] || continue - # Largest number found wins: a per-model block may list several, and the warning is for the worst. - _ac_n="$( (grep -oE '"autoCompactWindow"[[:space:]]*:[[:space:]]*[0-9]+' "$_ac_f" 2>/dev/null || true) \ - | grep -oE '[0-9]+$' | sort -n | tail -1 || true)" - if [ -n "$_ac_n" ]; then _ac_val="$_ac_n"; _ac_from="autoCompactWindow=$_ac_n in $_ac_f"; break; fi - done +_ac_limit=250000; _ac_over=""; _ac_ok=""; _ac_bad=""; _ac_auto="" +mxtk_compact_note() { # $1 raw value, $2 where it came from + local t + case "$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]"')" in + auto) _ac_auto="$_ac_auto; $2"; return 0 ;; + esac + t="$(mxtk_compact_tokens "$1")" + if [ -z "$t" ]; then _ac_bad="$_ac_bad; unparseable: $1 ($2)" + elif [ "$t" -gt "$_ac_limit" ]; then _ac_over="$_ac_over; $1 ($2)" + else _ac_ok="$_ac_ok; $1 ($2)"; fi +} +[ -z "${CLAUDE_CODE_AUTO_COMPACT_WINDOW:-}" ] || \ + mxtk_compact_note "$CLAUDE_CODE_AUTO_COMPACT_WINDOW" "CLAUDE_CODE_AUTO_COMPACT_WINDOW" +for _ac_f in "$PROJECT_DIR/.claude/settings.local.json" "$PROJECT_DIR/.claude/settings.json" \ + "${CLAUDE_CONFIG_DIR:-${HOME:-}/.claude}/settings.json"; do + [ -f "$_ac_f" ] || continue + while IFS= read -r _ac_v; do + [ -n "$_ac_v" ] && mxtk_compact_note "$_ac_v" "autoCompactWindow in $_ac_f" + done <<< "$( (grep -oE '"autoCompactWindow"[[:space:]]*:[[:space:]]*("[^"]*"|[^,}[:space:]]+)' "$_ac_f" 2>/dev/null || true) \ + | sed -E 's/^"autoCompactWindow"[[:space:]]*:[[:space:]]*//')" +done +if [ -n "$_ac_over" ]; then + echo " - WARNING: auto-compact window is above 250k: ${_ac_over#; }." + echo " A field build at a 1M window averaged 363-466k context per call — every token re-read on" + echo " every call. Lower it with /autocompact 250k (or CLAUDE_CODE_AUTO_COMPACT_WINDOW=250k). Nothing was changed." fi -if [ -n "$_ac_val" ] && [ "$_ac_val" -gt "$_ac_limit" ]; then - echo " - WARNING: auto-compact window is above 250k ($_ac_from). A field build at a 1M window" - echo " averaged 363-466k context per call — every token re-read on every call. Lower it with" - echo " /autocompact 250k (or CLAUDE_CODE_AUTO_COMPACT_WINDOW=250k). Nothing was changed." -elif [ -n "$_ac_val" ]; then - echo " - Auto-compact window: $_ac_from (at or below 250k — ok)." -else - echo " - Auto-compact window: none set (\"auto\"), which on a 1M-context model compacts near 1M." - echo " Recommended: /autocompact 250k — a field lead at 1M averaged 363-466k per call. Not set by this script." +[ -z "$_ac_bad" ] || echo " - Auto-compact window ${_ac_bad#; } — could not read it; check it is 250k or lower." +if [ -z "$_ac_over" ] && [ -z "$_ac_bad" ]; then + if [ -n "$_ac_ok" ]; then + echo " - Auto-compact window: ${_ac_ok#; } (at or below 250k — ok)." + else + echo " - Auto-compact window: none set, or \"auto\" — which on a 1M-context model compacts near 1M." + echo " Recommended: /autocompact 250k — a field lead at 1M averaged 363-466k per call. Not set by this script." + fi fi echo "" diff --git a/bin/lib/skill-routing.tsv b/bin/lib/skill-routing.tsv index dc144e7f..0ab9a4a3 100644 --- a/bin/lib/skill-routing.tsv +++ b/bin/lib/skill-routing.tsv @@ -146,7 +146,7 @@ design-artifacts skills/design-artifacts.md Designing the brand and ONE ANNOTATE brd-to-build-plan skills/brd-to-build-plan.md Turning BRDs plus architecture into a numbered, dependency-ordered build plan architect 3,4 ondemand requirements coverage-ledger skills/coverage-ledger.md Building the Stage 4 coverage ledger — every requirement either claimed by a build-plan row or catalogued with a reason, never invisible architect,gate 4 ondemand requirements coverage-check bin/coverage-check.sh Checking a coverage ledger against its BRD — every scalar leaf CLAIMED, LEDGERED, UNCLAIMED, PHANTOM or DOUBLE-CLAIMED, so coverage is measured rather than remembered architect,gate 4 ondemand requirements -iterative-build-loop skills/iterative-build-loop.md Building a module with mxcli, or an exec.sh guard just refused a write — verified, iterative, coverage-checklist gated; on a refusal: commit, wait or stop, never FORCE_EXEC mdl,gate 5 ondemand build +iterative-build-loop skills/iterative-build-loop.md An exec.sh guard refused a write (not a permission prompt), or building a module with mxcli — verified, iterative, coverage-checklist gated; on a refusal: commit, wait or stop, never FORCE_EXEC mdl,gate 5 ondemand build mdl-cookbook-microflows skills/mdl-cookbook-microflows.md Writing MDL microflow scripts — worked recipes mdl 5 ondemand build/mdl oneshot-mdl-method skills/build/mdl/oneshot-mdl-method.md Writing a single MDL script that takes a project from nothing to a working vertical slice — execution order, why it is deliberately non-idempotent, the instrument hierarchy, and the silent failures that pass every check mdl 5 ondemand build/mdl learned-page-patterns skills/learned-page-patterns.md Building and auditing Mendix pages, or starting a UI fix round across many pages — widget patterns, datasource shapes; for a round: SCSS first, four regressions to check mdl 5 ondemand build/pages diff --git a/contrib/inbox/2026-09-29-parallel-build-hygiene.md b/contrib/inbox/2026-09-29-parallel-build-hygiene.md index b800b6d4..735ec333 100644 --- a/contrib/inbox/2026-09-29-parallel-build-hygiene.md +++ b/contrib/inbox/2026-09-29-parallel-build-hygiene.md @@ -10,7 +10,8 @@ Parallel drafting is the toolkit build's speed advantage (2.6 vs 5.5 min/row; equal calls per row), so keep it and remove its friction: -- ~~Lock/queue mxbuild~~ — done: per-project exec lock in `project-bin/exec.sh` (#304). +- Lock/queue mxbuild — half done: the per-project exec lock in `project-bin/exec.sh` (#304) + refuses a second apply at once (no collision), but does not queue it; the agent re-runs. - ~~Usage measurement~~ — promoted 2026-10-14 to `skills/measured-claims.md` §7 (re-measured: sub-agent transcripts carry partial output_tokens on early records, up to 7x low). - **Still open — batch the gate**: one mxbuild per 3–5 consecutive clean scripts; per-script diff --git a/skills/iterative-build-loop.md b/skills/iterative-build-loop.md index c2a2591f..5cf1d88a 100644 --- a/skills/iterative-build-loop.md +++ b/skills/iterative-build-loop.md @@ -322,17 +322,20 @@ Every MDL script, and every one-off model patch (`--patch`), is applied with `./ A raw `./mxcli exec` writes no BUILD-LOG row, takes no snapshot and runs no gate, so the change shows as "not recorded" to everything that reads the build log (status, done-drift, proof views). -When a guard refuses, nothing in the model changed (the uncommitted-model and `--commit-first` -refusals also write a `🚫 blocked` row; the Studio Pro and lock refusals only print). Your move -is one of three — **commit, wait, or stop and report** — never an override: +When a guard refuses, the script's change is not in the model. Most refusals also write a +BUILD-LOG row (uncommitted model, `--commit-first`, a failed pre-exec check, a gate that could +not run, PRE-EXISTING errors, a NEW gate error); the Studio Pro and lock refusals only print. +Your move is one of three — **commit, wait, or stop and report** — never an override: | Refusal | Do this | |---|---| | Uncommitted model changes | Re-run with `--commit-first` (commits the `.mpr` + `mprcontents/` only), or commit them yourself, then re-run | | Studio Pro holds the model (also refuses `--commit-first`) | Stop. Ask the person to save (Cmd+S) and close the project; never kill Studio Pro | -| Another session holds this project's exec lock | Wait for that run to finish, then re-run. A dead holder is taken over automatically — never `rm` the lock | +| Another session holds this project's exec lock | The lock refuses at once; it does not queue. Re-run after the other apply finishes. A dead holder is taken over automatically — never `rm` the lock | | The mxbuild gate cannot run, even after its own download | Stop and report; run `bin/doctor.sh`. `ALLOW_UNVERIFIED=1` only when the person says so | | Pre-exec check or a NEW mxbuild error | Fix the script. That is the gate working | +| `PRE-EXISTING ` — errors already in the model before this script | Not the script's cause. Stop and report: it needs Studio Pro (e.g. "Update security") or a separate fix script, not a retry | +| Any other refusal | Read the remedy exec.sh prints and do that; the table above is not the full list of guards | **Never set `FORCE_EXEC=1` on your own initiative** — only when the person tells you to, naming the risk. The failure it causes: an agent hits the uncommitted-model guard mid-loop, sets @@ -342,13 +345,16 @@ between those two events. **The MCP write path stays allowed** where `learned-mdl-preflight.md` prescribes it (its Step 0 write-mode choice), and it is **accepted as unrecorded**: no exec row, no exec gate. Its record -is the commit you make before the next exec (the pre-exec sequence below), with a message that -starts `MCP:` and names what changed. +is the commit you make before the next exec (the pre-exec sequence below, step 2), with a +message that starts `MCP:` and names what changed. -**Check before closing a phase:** every model-changing commit since the phase began is either -covered by a BUILD-LOG row or is an `MCP:` commit. Count both: commits = exec rows + `MCP:` -commits. A commit that is neither is a raw `mxcli exec` — re-run that script through exec.sh, -or record why it cannot be. +**Check before closing a phase:** every model-changing commit since the phase began is covered +by one of three things — an `✅`/`⚠️ applied` BUILD-LOG row for the script it commits, a +`--commit-first` commit (message `model: commit pending model changes before exec.sh …`), or an +`MCP:` commit. Blocked and failed rows have no commit, so this is a coverage check, not an +equality between counts. Done when all N model commits are covered, N of N. A commit covered by +none of the three is a raw `mxcli exec` — re-run that script through exec.sh, or record why it +cannot be. ### The gate checks the saved file — save, then check @@ -364,15 +370,16 @@ The per-script apply grows with the model (field run, 2026-09-29: 22–30 s per first slice, 55–65 s in the second, almost all of it mxbuild). What the gate already saves: the pre-flight baseline mxbuild is skipped when the verification stamp says the model is unchanged since its last clean build, so a run of clean applies costs **one** mxbuild each, not -two. Concurrent applies from parallel agents queue on the exec lock instead of colliding (two of -eleven failed applies in that run were concurrent mxbuild collisions, from before the lock). +two. A second apply while one is running is refused by the exec lock instead of colliding (two +of eleven failed applies in that run were concurrent mxbuild collisions, from before the lock); +re-run it after the other apply finishes. `exec.sh` has **no** batched-gate mode. Do not imitate one with `SKIP_CHECK`, `SKIP_BASELINE`, `FORCE_EXEC` or raw `mxcli exec` runs between gates — each of those is an unrecorded or unverified write. If gate time dominates, reduce the number of applies at the granularity level (see "Iterative Granularity": layer scripts rather than per-page ones where partial recovery is unlikely, fewer stub-then-fill passes), keep parallel **drafting**, and let the exec lock -serialise the applies. +refuse overlapping applies — re-run a refused one after the other finishes. ### Pre-exec sequence when MCP work was done @@ -380,7 +387,7 @@ Before running exec.sh after any MCP session: ``` 1. Cmd+S in Studio Pro (or run save-sp.sh) -2. git add MyProject.mpr mprcontents/ && git commit -m "Commit MCP changes before exec" +2. git add MyProject.mpr mprcontents/ && git commit -m "MCP: " 3. Close the project in Studio Pro 4. ./bin/exec.sh mdlsource/my-script.mdl ``` diff --git a/skills/learned-page-patterns.md b/skills/learned-page-patterns.md index cc9813a6..a48ebe01 100644 --- a/skills/learned-page-patterns.md +++ b/skills/learned-page-patterns.md @@ -365,7 +365,8 @@ regressions, and about a fifth of the UI tokens went to round 2 repairing them. shares. One theme change fixes N pages; N page scripts fix N pages and drift. 2. **Then the page scripts.** A drafter that has run `mxcli check` and `page-fidelity.js` on its script hands it over for applying with `./bin/exec.sh`. Do not pre-apply to a scratch copy - "to be sure": exec.sh's gate is that check, and its lock serialises parallel applies. + "to be sure": exec.sh's gate is that check. Its lock refuses a second apply while one runs — re-run after + the other apply finishes. 3. **Re-look only the pages the round changed** — the list is the round's BUILD-LOG rows plus the pages an SCSS change reaches. Done when every page on that list has a fresh look (`ui-loop.md`), N of N. diff --git a/skills/measured-claims.md b/skills/measured-claims.md index a478bcb5..5047588b 100644 --- a/skills/measured-claims.md +++ b/skills/measured-claims.md @@ -256,6 +256,8 @@ the early ones carry a partial `output_tokens` (e.g. `[4, 2631]` — first recor **Rule: sum usage once per `message.id`, from that message's LAST record.** Deduplicating by id is right (summing every record double-counts); keeping the *first* record is the undercount. +`bin/token-burn.sh` keeps the first record per id as of 2026-10-13, so its sub-agent output is +low until that is fixed (a fix is in flight) — check its header before citing its output totals. **Cost:** a field comparison of two builds (2026-09-29) briefly concluded "the other build writes 2.4x more output" — an artefact of reading partial records in both builds' sub-agents. A diff --git a/skills/upstream-feedback.md b/skills/upstream-feedback.md index 640c9edf..a030bdbc 100644 --- a/skills/upstream-feedback.md +++ b/skills/upstream-feedback.md @@ -42,6 +42,12 @@ would read them. The asks from that run, with what was measured: | Save before `mx check`; reload instead of full restart | 229 errors hidden until a real save; 23 restarts | | Flag a missing end-of-parallel-path element in the consistency check | observed in that run; no count recorded | +Two more items from that run were dropped from the table on purpose. Both are advice to whoever +runs an MCP build, not asks of the server, so no owner can act on them: (a) parallel drafting +sub-agents with the lead as the single writer, which the toolkit already does; (b) commit scripts +and results, add unit tests, and make timers and external-system failures testable, which the +toolkit's own test skills cover. + Done when each ask is either in a local draft with its evidence row, or recorded as dropped with a reason — none left only in a chat or an inbox note. diff --git a/tests/wave2/test-init-autocompact.sh b/tests/wave2/test-init-autocompact.sh index 958e0a79..1a683aae 100755 --- a/tests/wave2/test-init-autocompact.sh +++ b/tests/wave2/test-init-autocompact.sh @@ -11,6 +11,7 @@ # # Usage: bash test-init-autocompact.sh [path-to-init-project.sh] # Fails against master's init-project.sh (no auto-compact line at all). +# Cases 5-8 (unparseable, string, 20-digit, project+user) were added after review of the first cut. export MXTK_LEAKGUARD_DENYFILE="${TMPDIR:-/tmp}/mxtk-fixture-denylist.$$" export MXTK_NO_GUIDE=1 @@ -24,7 +25,7 @@ trap 'rm -rf "$WORK" "$MXTK_LEAKGUARD_DENYFILE"' EXIT run() { # $1 = case name, $2 = env value for CLAUDE_CODE_AUTO_COMPACT_WINDOW ("" = unset) local c="$1" envv="$2" - mkdir -p "$WORK/home-$c/.claude" "$WORK/proj-$c" + mkdir -p "$WORK/home-$c/.claude" "$WORK/proj-$c" # pre-made dirs/files are kept if [ -n "$envv" ]; then env CLAUDE_CODE_AUTO_COMPACT_WINDOW="$envv" CLAUDE_CONFIG_DIR="$WORK/home-$c/.claude" \ bash "$SUBJECT" "$WORK/proj-$c" "$WORK/out-$c" 2>&1 @@ -44,28 +45,55 @@ grep -q 'Recommended: /autocompact 250k' "$WORK/out-none" \ rc=$(run env1m "1M") [ "$rc" -eq 0 ] && ok "env 1M: scaffold still exits 0 (advice, not a gate)" || bad "env 1M: scaffold exited $rc" -grep -q 'WARNING: auto-compact window is above 250k (CLAUDE_CODE_AUTO_COMPACT_WINDOW=1M)' "$WORK/out-env1m" \ +grep -q 'WARNING: auto-compact window is above 250k: 1M (CLAUDE_CODE_AUTO_COMPACT_WINDOW)' "$WORK/out-env1m" \ && ok "env 1M: warns and names the source" || bad "env 1M: no warning naming the env var" mkdir -p "$WORK/home-ok/.claude" printf '{\n "modelSettings": {\n "some-model": {\n "autoCompactWindow": 250000\n }\n }\n}\n' \ > "$WORK/home-ok/.claude/settings.json" run ok "" >/dev/null -grep -q 'autoCompactWindow=250000 .*at or below 250k' "$WORK/out-ok" \ +grep -q 'Auto-compact window: 250000 (autoCompactWindow in .*at or below 250k' "$WORK/out-ok" \ && ok "settings 250000: reported ok" || bad "settings 250000: not reported as ok" grep -q 'WARNING: auto-compact' "$WORK/out-ok" && bad "settings 250000: warned anyway" || ok "settings 250000: no warning" mkdir -p "$WORK/home-big/.claude" printf '{"autoCompactWindow":1000000}\n' > "$WORK/home-big/.claude/settings.json" run big "" >/dev/null -grep -q 'WARNING: auto-compact window is above 250k (autoCompactWindow=1000000' "$WORK/out-big" \ +grep -q 'WARNING: auto-compact window is above 250k: 1000000 (autoCompactWindow in' "$WORK/out-big" \ && ok "settings 1000000: warns" || bad "settings 1000000: no warning" rc=$(run garbage "1.5M") [ "$rc" -eq 0 ] && ok "unparseable env value: scaffold exits 0" || bad "unparseable env value: scaffold exited $rc (must never fail on advice)" +grep -q 'unparseable: 1.5M' "$WORK/out-garbage" \ + && ok "unparseable env value: reported as unparseable" || bad "unparseable env value: not reported (silence reads as 'none set')" +grep -q 'none set' "$WORK/out-garbage" && bad "unparseable env value: printed 'none set' — false" || ok "unparseable env value: no false 'none set'" + +# A string value in settings ("1M") is a value, not an absence. +mkdir -p "$WORK/home-str/.claude" +printf '{"autoCompactWindow": "1M"}\n' > "$WORK/home-str/.claude/settings.json" +run str "" >/dev/null +grep -q 'WARNING: auto-compact window is above 250k: "1M"' "$WORK/out-str" \ + && ok "settings string \"1M\": warns" || bad "settings string \"1M\": no warning" + +# A 20-digit number must not reach shell arithmetic (stderr noise / overflow). +mkdir -p "$WORK/home-huge/.claude" +printf '{"autoCompactWindow": 99999999999999999999}\n' > "$WORK/home-huge/.claude/settings.json" +run huge "" >/dev/null +grep -q 'WARNING: auto-compact window is above 250k: 99999999999999999999' "$WORK/out-huge" \ + && ok "20-digit value: warns" || bad "20-digit value: no warning" +grep -qiE 'value too great|syntax error|integer expression' "$WORK/out-huge" \ + && bad "20-digit value: arithmetic error leaked to output" || ok "20-digit value: no arithmetic error" + +# Project settings ok, user settings 1M: BOTH are read, and the worse one warns. +mkdir -p "$WORK/home-both/.claude" "$WORK/proj-both/.claude" +printf '{"autoCompactWindow":250000}\n' > "$WORK/proj-both/.claude/settings.json" +printf '{"autoCompactWindow":1000000}\n' > "$WORK/home-both/.claude/settings.json" +run both "" >/dev/null +grep -q 'WARNING: auto-compact window is above 250k: 1000000' "$WORK/out-both" \ + && ok "project 250k + user 1M: user value still warns" || bad "project 250k + user 1M: user value not read once the project had one" # Never writes a setting. -if grep -rqs 'autoCompactWindow' "$WORK"/proj-*/.claude 2>/dev/null; then +if grep -rqs 'autoCompactWindow' $(ls -d "$WORK"/proj-*/.claude 2>/dev/null | grep -v '/proj-both/') 2>/dev/null; then bad "init wrote an autoCompactWindow setting into the project — it must only advise" else ok "nothing written: no autoCompactWindow in any scaffolded .claude/"; fi From 5bf8048f9b6fe38c4de6897034795cd742f7256e Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 13 Oct 2026 15:34:18 +0000 Subject: [PATCH 3/3] learn(build-loop): kept writes and PARTIAL rows in the refusal and phase-close text Confirmation review of #323: "the change is not in the model" was false for LINT ROSE/BLIND and PARTIAL (the write is kept) and could lead an agent to re-apply a non-idempotent script; PARTIAL rows now count as coverage and are finished, not re-run; the print-only list is exact. Wording as the reviewer specified. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw --- skills/iterative-build-loop.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/skills/iterative-build-loop.md b/skills/iterative-build-loop.md index 5cf1d88a..07cf98e7 100644 --- a/skills/iterative-build-loop.md +++ b/skills/iterative-build-loop.md @@ -322,9 +322,9 @@ Every MDL script, and every one-off model patch (`--patch`), is applied with `./ A raw `./mxcli exec` writes no BUILD-LOG row, takes no snapshot and runs no gate, so the change shows as "not recorded" to everything that reads the build log (status, done-drift, proof views). -When a guard refuses, the script's change is not in the model. Most refusals also write a +When a guard refuses, the script's change is not in the model, unless exec.sh says the write is kept (LINT ROSE/BLIND, ⚠️ PARTIAL) — then do not re-apply it. Most refusals also write a BUILD-LOG row (uncommitted model, `--commit-first`, a failed pre-exec check, a gate that could -not run, PRE-EXISTING errors, a NEW gate error); the Studio Pro and lock refusals only print. +not run, PRE-EXISTING errors, a NEW gate error); the plain Studio Pro refusal, the lock refusal and argument errors only print. Your move is one of three — **commit, wait, or stop and report** — never an override: | Refusal | Do this | @@ -349,12 +349,12 @@ is the commit you make before the next exec (the pre-exec sequence below, step 2 message that starts `MCP:` and names what changed. **Check before closing a phase:** every model-changing commit since the phase began is covered -by one of three things — an `✅`/`⚠️ applied` BUILD-LOG row for the script it commits, a +by one of three things — an `✅`/`⚠️ applied`/`⚠️ PARTIAL` BUILD-LOG row for the script it commits, a `--commit-first` commit (message `model: commit pending model changes before exec.sh …`), or an `MCP:` commit. Blocked and failed rows have no commit, so this is a coverage check, not an equality between counts. Done when all N model commits are covered, N of N. A commit covered by none of the three is a raw `mxcli exec` — re-run that script through exec.sh, or record why it -cannot be. +cannot be. After a `⚠️ PARTIAL` row, finish the script's remaining statements; never re-run the whole script. ### The gate checks the saved file — save, then check