From 13accfcae5ebaf36082dc61a32e8b4cb57ab11ee Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 14 Sep 2026 11:34:50 +0000 Subject: [PATCH 01/24] docs(wiki): re-sync describe-round-trip-gaps after #923 closed out MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Triggered by the irreducible-graph work landing (named join points, then Mode 2 and Mode 3 describe). Sources gained the two new describe-side files, the graph classifier and the proposal; the existing three kept. Three changes, grounded in the 13 mdl/executor findings since the last sync. A FIFTH SHAPE at the end of the survival ordering: "means something else". The other four all leave evidence — output that will not parse, a missing property, an invented clause, vanished structure. This one parses, executes, yields a valid model, drops nothing and invents nothing, and denotes a different program. No automated signal can see it; only comparing behaviour finds it. SOME DOCUMENTS CANNOT BE DESCRIBED FAITHFULLY AT ALL, which is a vocabulary limit rather than a careless describer: MDL's `if` is single-entry/single-exit against an arbitrary digraph, so the remedy is to extend the language, and per Boehm-Jacopini one sub-class has no faithful rendering at any effort. The beat earns its place by telling a reader whether to reach for a fix or a proposal. VERIFICATION rewritten around the trap that let shape five survive: a round trip that is a fixed point on the corpus you have is not evidence it is faithful, because the describer's inference rule was reverse-engineered from that corpus and agrees with it by construction — the disagreeing case has to be constructed. Plus three measurement rules that each hid a defect until applied: compare identities rather than counts; diff the whole corpus against a baseline binary (a refactor must be byte-identical, a feature must move only its targets); algebra is not behaviour. Also removes the quoted "83 of 248" count — the area has since more than doubled to 615, so the figure was simply wrong. The Do-not-duplicate line now says counts are computed, per CLAUDE.md's "coverage is computed, never quoted". Separately, three findings written 2026-09-13/14 used the shard filename (mdl-executor) as their `area` where the other 613 use the path (mdl/executor), so digest-status bucketed them into "(32 areas < 5)". Corrected in place before using those counts to choose this run's scope. This is the THIRD occurrence — the same correction was made on 2026-09-12 — and the repetition is the tell that correcting is not fixing: check-findings validates that the field exists, not that its value is in the vocabulary. Two SYNC_LOG rows appended, one for the correction and one for the page. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_018hifgRSawfaRWXS44YKtSJ --- .../fix-issue/findings/mdl-executor.jsonl | 6 +- docs-wiki/SYNC_LOG.md | 2 + .../bug-patterns/describe-round-trip-gaps.md | 81 +++++++++++++++---- 3 files changed, 69 insertions(+), 20 deletions(-) diff --git a/.claude/skills/fix-issue/findings/mdl-executor.jsonl b/.claude/skills/fix-issue/findings/mdl-executor.jsonl index d490dadc9b..13ad0d576c 100644 --- a/.claude/skills/fix-issue/findings/mdl-executor.jsonl +++ b/.claude/skills/fix-issue/findings/mdl-executor.jsonl @@ -607,9 +607,9 @@ {"area": "mdl/executor", "date": "2026-09-13", "symptom": "A workflow with `boundary event timer '\u2026'` (no interrupting / non interrupting) passes check and builds at 0 errors; the runtime then fails to start: `Class 'Workflows$TimerBoundaryEvent' could not be found`", "cause": "The bare form maps to `Workflows$TimerBoundaryEvent`, which exists in no cached 11.x runtime (only Interrupting/NonInterruptingTimerBoundaryEvent). mxbuild tolerates the unknown type. It was the documented syntax example", "file": "`mdl/executor/validate_workflow_refs.go` (`bareTimerBoundaryEventErrors`, MDL-WF07), `cmd/mxcli/syntax/features_workflow.go`", "insight": "**A type mxbuild accepts is not a type the runtime has.** Found only because a verification boot of an unrelated fix loaded it. When a grammar has a default branch that maps to a storage type, check that type against the runtime's class list, not against `mx check`", "fix": "Refuse the bare form on 11+ at check and exec, CREATE and every ALTER op that can carry a boundary event; update syntax help, skill table and the ako/mxcli#415 bug-test script to name the kind"} {"area": "mdl/executor", "date": "2026-09-13", "symptom": "A view entity whose association column is also declared as an attribute (`MeterRef: Trends.Meter` or `MeterRef: Trends.Meter.ID` beside `select m.ID as MeterRef`) passes `mxcli check`; `check -p` says 'OQL select has 1 columns but 2 attributes declared'; exec writes `Enumeration(Trends.Meter)` and mx check reports CE1613, or throws 'An error occurred when trying to set the Enumeration property' for the three-part form", "cause": "A bare qualified name parses as TypeEnumeration (the entity/enum ambiguity), and execCreateViewEntity converted it with convertDataType without asking what it names. The alias-to-attribute alignment skips association columns, so the declared attribute had no column and was compared against the next one", "file": "`mdl/executor/oql_view_associations.go` (`ValidateViewAttributeDeclarations` MDL080, `viewAttributeEntityTypeErrors`), `mdl/executor/cmd_entities.go` (`execCreateViewEntity`), `mdl/executor/validate.go`, `mdl/executor/validate_program.go`, `cmd/mxcli/lsp_diagnostics.go`", "insight": "**The TypeEnumeration/TypeEntity ambiguity has a consumer wherever a data type becomes a stored type, and view entity attributes were one nobody had listed.** Split the refusal by what it needs: an association column's alias and a three-part name are decidable from the script, so they belong in the no-project phase that exec's pre-check also runs; entity-vs-enum needs the project, so it goes in check -p AND the handler, because exec --no-check skips both phases. Verify the handler refusal by counting changed files, not by the error text", "fix": "Refuse in ValidateProgram/LSP (MDL080) and at the top of execCreateViewEntity before any backend call; report an attribute once"} {"area": "mdl/executor", "date": "2026-09-13", "symptom": "Three new MDL-WIDGET27 tests passed locally and failed in CI on the same commit: two reported the fallback remedy (\"move the entries into the widget body as container blocks\") instead of naming the container keyword, and the third found 0 violations where it wanted 1", "cause": "The tests resolved the widget through `LoadWidgetRegistry(fixtureProject(t))`, which reads `.def.json` files from `testdata/expr-checker/.mxcli/widgets/`. That directory is GITIGNORED — the definitions are derived, not tracked — so they exist for any developer who has ever run `mxcli widget docs` against the fixture (I generated them earlier in the same session, while investigating) and never exist on the runner. With no definition, `containerKeyword` returns \"\" and the two definition-dependent branches degrade exactly as designed: fallback wording, and silence for the scalar case", "file": "`mdl/executor/validate_widget_object_property_test.go` (`fixtureProjectWithDefs`)", "insight": "**A gitignored fixture makes a test environment-dependent in the one direction nobody checks** — the developer's tree is a superset of the runner's, so the test is green exactly where it is not being tested. The fix is to DERIVE the artifact from tracked inputs inside the test (`RefreshWidgetDefinitions` over the fixture's tracked `.mpk` files, into a temp copy, after removing any `.mxcli` the developer's tree carries), so local and CI see identical inputs. Reproduce by moving the gitignored directory aside before believing any diagnosis. **The sibling lesson is why this was not caught by the existing suite**: #999's test asserted `strings.Contains(msg, \"attribute\")` on a widget whose property is named `attributes`, so the property name alone satisfied it and the assertion passed with NO definition loaded — a substring assertion whose needle is a substring of the data it is meant to distinguish from proves nothing. Tightened to the remedy shape (`` `attribute (…)` blocks ``) and verified with the derivation stubbed: all four then fail, where before only the three new ones did", "refs": []} -{"area": "mdl-executor", "date": "2026-09-13", "symptom": "DESCRIBE silently deletes an ExclusiveMerge: describe -> exec leaves the microflow with fewer merge nodes than the stored graph, with no warning, no MDL-FLOW01 and mx check clean", "cause": "The nested describer walks straight through a merge with a single incoming path without emitting anything for it, so the rebuild has no reason to create it. Only two merge shapes were represented: a split's join point (rendered by `end if`) and a labelled error rejoin (`merge