Repository navigation
fix(exec): warn when replaying an older owning script reverts newer work (#269) - #498
Merged
Merged
Conversation
A script restated from a stale DESCRIBE (#261), or a re-run of an older script that owns the same document (#269), silently reverted newer work: exit 0, every gate green, no warning. - project-bin/script-owners.sh: which mdlsource/ scripts create or restate each microflow/nanoflow/page/snippet; -- supersedes: / -- superseded-by: headers; a last-writer ledger exec.sh appends after every kept write, so an in-order rebuild stays quiet without asking mxcli. - project-bin/describe-baseline.sh: stamp -- baseline: <Doc> sha256=... at draft time, verify at exec (opt-in: only stamped scripts DESCRIBE). - exec.sh: WARN lines + BUILD-LOG detail; never a refusal, exit codes unchanged. Happy path: one grep over mdlsource/, zero mxcli calls. - gate-check.sh: co-owned count (info only). - learned-mdl-preflight.md: STOP 33 and 34. - Fixture tests/wave2/test-restatement-guard.sh; not-found DESCRIBE output captured from mxcli v0.26.0. Rebuilt from master; supersedes draft #311 without its merged #315 and crash-rollback (#424) hunks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw
…d compaction (#269) Review of #498 (MERGE-AFTER-FIXES). This PR is now the #269 part only. - script-owners.sh: run order pads digit runs per path component (2-core before 11-fixes, 9-a before 10-b); a script that has never run, over an owner that never ran either, is new (no message, no DESCRIBE). - A new owner without a supersedes header gets one NOTE line on its first run, out of BUILD-LOG; WARN only when a newer owner's write is live. - restore-mpr.sh appends a RESTORE row; rows before it are ignored, so a replay after a restore asks the model again. exec.sh's own rollback does not mark. - record compacts the ledger (last row per script and document, last RESTORE) through a temp file and mv; the folder self-gitignores. - At most 5 DESCRIBE calls per check; the rest print CONFLICT (not checked). The DESCRIBE existence check now lives in script-owners.sh (live). - Removed: describe-baseline.sh, its exec.sh verify block and the preflight rows 33/34 (the #261 part moves to its own PR; STOP 34 dropped). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw
This was referenced Oct 21, 2026
Re-review of #498. The ledger is machine-local, so in a fresh clone or cloud session "neither script has a ledger row" read as "S is new" and a replay over committed newer work was silent. - has-run now also counts a done- file name and a docs/BUILD-LOG.md row naming the script in backticks whose result says applied (the done-drift-check.sh pattern); no mxcli call. Draft-ahead stays quiet. - Header: the ledger cannot see Studio Pro edits or a git pull/checkout of the .mpr. - Fixture: three fresh-clone cases (done-09, BUILD-LOG applied row, never run) and a rolled-back row; exec.sh's own rollback writes no RESTORE row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw
MendixMau
pushed a commit
that referenced
this pull request
Oct 21, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw
…-v (#269) check-portability flagged a backslash in an awk -v value (gawk and mawk differ on escapes). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw
MendixMau
pushed a commit
that referenced
this pull request
Oct 21, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw
MendixMau
pushed a commit
that referenced
this pull request
Oct 21, 2026
#498 landed as a squash, so its commits on this branch conflicted with master in bin/lib/install-manifest.sh and project-bin/exec.sh. Kept this branch's side of both hunks (describe-baseline.sh in the manifest; the #261 stale-draft block and the shared note comment). The diff against master is now exactly this PR's own change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw
MendixMau
added a commit
that referenced
this pull request
Oct 21, 2026
…ow 21 (#503) Fixes #261. Supersedes #311 (with #498). - project-bin/describe-baseline.sh: opt-in `-- baseline:` header. `stamp` records a DESCRIBE of each restated document plus the mxcli version; `verify` compares with the live model. - exec.sh: for a script with that header, warns on a stale draft (DRIFT into the BUILD-LOG detail; STALEHEADER warns; RESTAMP after an mxcli upgrade instead of drift). Warn-only; exit codes unchanged; no mxcli call for a script without the header. - skills/learned-mdl-preflight.md row 21: trigger names `create or modify` / `create or replace`; Rule gains "A new owner adds `-- supersedes: <script>`; drafting ahead of unrun scripts, `bin/describe-baseline.sh stamp <script>` after the DESCRIBE (exec.sh only warns)." (+24 always-loaded words; baseline 73828/80000). Review: independent review of 8431714, then 1858281, stacked on #498; after #498's squash the merge desk merged master in (780f0ad), keeping this branch's side of the two install-manifest/exec.sh hunks so the diff against master is exactly this PR. On the merged tree: test-describe-baseline 33/0 (3 cases fail against #498's exec.sh), test-restatement-guard 58/0, test-exec-gate-retry 52/0; render-routing in sync, baseline 73828 words; check-pr-discipline clean; client grep 0. Behaviour A/B on row 21 (66 sonnet reps, M = master c2b9bec, T = 1858281, packs differing only in row 21): the planted STOP 21 case flagged 10/10 in both arms, and all 10 T mf reps now name `-- supersedes:` and `describe-baseline.sh stamp` (0/10 in M). The first-pass `quoted` dip (two T reps at 55/61%) is a scorer artifact (it counts unquoted names inside the new header comments); on MDL code lines only, M and T both have median 91% and 8/10 reps >= 91%. Probes and all other metrics equal or within one rep. Verdict: T no worse. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw
5 of 7 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Direct lane
What changed and why (one paragraph):
When a document is created by several
mdlsource/scripts, re-running an older one silently reverted the newer one's work: exit 0, every gate green (#269). Three field apps had 99/579, 130/357 and 38/159 such documents. Newproject-bin/script-owners.shmaps each microflow, nanoflow, page and snippet to the scripts that create or restate it, in run order, and reads-- supersedes:/-- superseded-by:headers. Run order compares digit runs as numbers, so2-core/runs before11-fixes/and9-abefore10-b; a leadingdone-is ignored.exec.shrecords every kept write in a machine-local last-writer ledger, andrestore-mpr.shmarks the ledger when it restores. Before a write,exec.shprints:-- supersedes:header;It never refuses, and the exit codes (0/1/2/4/5) do not change.
gate-check.shprints the co-owned count when it is above 0. The #261 part (stale-DESCRIBE baseline) is a separate PR that lands after this one; together they supersede draft #311.Fixes #269
Behaviour change to check in your project:
exec.shoutput can carryWARN: restatement: …andNOTE: new owner of …lines, and the BUILD-LOG detail cellWARN reverts: <docs>. SetMXTK_RESTATE_GUARD=offto skip the check. A new machine-local file appears:.claude/loop/script-owners/written.tsv. It is not committed, because that folder carries its own*.gitignore(the same pattern asapp-restart.sh's.claude/loop/app-restart/).How "is the newer owner's write live?" is decided, cheapest first:
If neither script has run, the script is new: no message and no DESCRIBE. A script has run when any of these holds:
done-prefix;docs/BUILD-LOG.mdhas a row naming it in backticks whose result says applied (thedone-drift-check.shpattern).The last two are committed, so a fresh clone or cloud session (empty ledger) still WARNs on a replay over committed newer work. A script nobody has run yet (draft-ahead) stays quiet. None of this calls mxcli.
Otherwise read the ledger's last writer of the document, ignoring rows before the last
RESTORErow. If it is this script, or a script that runs before it, nothing would be reverted.Otherwise run one DESCRIBE. If the document is absent (a fresh model), there is nothing to revert.
With no
./mxclito ask, it is a WARN.DESCRIBE is capped at 5 calls per exec; any further documents print as
CONFLICT (not checked).recordcompacts the ledger to the last row per (script, document) plus the lastRESTORErow, through a temp file andmv, whileexec.shholds the exec lock.exec.sh's own rollback passesMXTK_RESTORE_SAME_LEDGER=1, because its snapshot predates nothing recorded; every other restore writes the marker.Impact
-- supersedes:). gate-check gains one information line when the co-owned count is above 0.project-bin/exec.sh(one block after the pre-exec check, onerecordcall after LOOK owed, one cell in the BUILD-LOG detail, one env var on its rollback),project-bin/restore-mpr.sh(the RESTORE row),bin/gate-check.sh, andbin/lib/install-manifest.sh(new installed script). New artifact: the ledger, machine-local and not committed (self-gitignored;init-project.shdoes not ignore.claude/loop/, so the folder ignores itself, which also covers existing projects). The ledger's producers areexec.shrecord(mandatory, after every kept write) andrestore-mpr.sh; its consumer isscript-owners.sh check.checkalso readsdocs/BUILD-LOG.md(producer:exec.sh, every run) and thedone-names (producer: theiterative-build-loop.mdrename). The ledger cannot see Studio Pro edits or agit pull/git checkoutof the.mpr; the header says so, and a post-merge/post-checkout hook is the follow-up. Branched from 88e9848; master has since moved to 9f3948c (#497) and the branch merges cleanly (git merge-tree). No new tools: bash 3.2, awk, grep, sed.render-routing.sh --check:Baseline: 73804 words (budget 80000), base and head).exec.shon a sole-owner script): 20 scripts: base 1406-1458 ms, head 1495-1523 ms. 600 scripts: base 1382-1443 ms, head 1485-1525 ms. That is about +90 ms per exec, with zero DESCRIBE calls (fixture-asserted). gate-checkcounttakes 345 ms on 600 scripts, once per gate run.Field evidence: no client project was run. Before the split,
exec.shran with real mxcli v0.26.0 (check, exec, DESCRIBE) on a scratch copy of mxcli's publictestdata/pedappmodel. Replaying 01 over 02 printedWARN: restatement … newer owner mdlsource/02-A.mdl — replaying this script reverts it. A fresh-model rebuild in run order printed 0 WARN lines, including with a stale ledger, because DESCRIBE answered not-found. The not-found output (microflow, nanoflow, page, snippet) and two found documents are captured verbatim from that model intests/wave2/fixtures/restatement-guard/. A field run on a real project is still owed.Fails on master, passes on head:
tests/wave2/test-restatement-guard.shexec.shrestore-mpr.sh(control)On 2a0886b, the 2 failures are the new fresh-clone cases: empty ledger plus
done-09(which supersedes 05), and empty ledger plus a BUILD-LOG applied row for 09; replaying 05 must WARN in both. Also new: 09 never run, and a rolled-back BUILD-LOG row, both stay quiet. exec.sh's own rollback must write no RESTORE row; a control with that flag removed fails exactly that assertion. The 28 failures on 8ad0d77 are every review finding: the run-order cases, NOTE vs WARN, new-script quiet, compaction, the RESTORE marker, the DESCRIBE cap, andlive. The control's 2 failures are exactly the RESTORE-marker case: 05, then 09 (supersedes 05), replay 05, restore, replay 05 must WARN.Re-review checks (head 292ded7):
test-restatement-guard58/0,test-exec-gate-retry47/0,test-restore-mpr-arg10/0,test-exec-gate-log24/0. One batch run showed 23/1, the same intermittent failure as before; it did not reproduce in 3 serial reruns or 12 runs in parallel with master.check-scripts,check-portability(an awk-vbackslash it flagged is fixed in 292ded7),render-routing --check(73804, unchanged), the leak guard,check-pr-discipline: all clean. The client grep is 0.Scoped checks (head 2a0886b):
Fixtures:
test-restore-mpr-arg10/0test-exec-change-class21/0test-exec-check-refusal10/0test-exec-cr-brief-warn19/0test-exec-crash-rollback13/0test-exec-gate-log24/0 (one run showed 23/1, three reruns 24/0)test-exec-gate-retry47/0test-exec-hash-verify43/0test-exec-lock-watch84/0test-exec-step-times7/0test-exec-subjects6/0test-exec-lint-autorestore17/0test-bug07-0845/0test-sync-only-bin40/0test-bug12-sync62/0test-bug03-gates47/0test-wrong-verdicts20/0test-manifest-clean-checkout1/0Guards, all clean:
check-scripts,check-portability,render-routing --check, the leak guard,check-pr-discipline. The client-name grep of added lines returns 0.No client data anywhere in the diff
Size cap: 17 files, but 10 are captures or fixtures. Non-fixture lines: 350 (+347 −3).
Test tier reached: T1/T2. A stub-mxcli fixture, plus real mxcli v0.26.0 on a public model before the split. No T3 field project.
For a new/changed instrument:
app/mdlsource/is asserted.sort -V,grep -rlF -f,awk ENVIRON. Checked by inspection; not run on macOS or Git Bash.For a new skill: n/a
CHANGELOG.mdline, credited to MendixMau (One document, several owning scripts: re-running an older mdlsource script silently reverts later changes #269)New bug entries: none
https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw
Generated by Claude Code