Skip to content

[major] Make the shared vocabulary drive C# emission, not just check it - #222

Merged
matt-edmondson merged 3 commits into
mainfrom
claude/bold-planck-mxarux
Sep 12, 2026
Merged

matt-edmondson merged 3 commits into
mainfrom
claude/bold-planck-mxarux

Conversation

@matt-edmondson

@matt-edmondson matt-edmondson commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

QuantityVocabulary has resolved dimensions.json into the quantities and operators it describes since #219, refusing the relationships whose exponents contradict their declared result. The C++ projection emitted from that resolution. The C# generator did not: it walked the metadata a second time to build its own type and operator lists, and called the vocabulary only to report SEM008 — against operators it then emitted anyway.

QuantitiesGenerator now emits from vocabulary.Types and vocabulary.Relationships. One physics model, literally: which classes exist, what each one is and how it is bounded, and which operators relate them, decided once and read by both targets.

Two things stay on the C# side, and both are the language rather than the physics. A relationship becomes four C# operators — as declared, commuted, and the two divisions that undo it — because a caller who writes duration * velocity is not making a different claim about physics; that expansion is CollectOperators. And an overload's relationships (the Diameter.ToRadius() pairs) are C# expressions written in the metadata and pasted through, so there is nothing in them for a language-agnostic model to hold.

Rebased on #221. That PR fixed the Sensitivity dimension, which was one of the five relationships this originally removed — the formula said amperes per newton while the unit beside it said VoltPerPascal and the relationship agreed with the unit, so there was no physics call to make after all. Sensitivity<T> * Pressure<T> -> VoltageMagnitude<T> is dimensionally true now and is generated. The removal below is four relationships rather than five.

Breaking change

Twelve operators and three methods are gone, being the four refused relationships in each direction C# spelled them:

Removed Why
TorqueMagnitude<T> * Angle<T> -> Energy<T> the r × F versus τ · θ contradiction
MomentOfInertia<T> * AngularSpeed<T> -> AngularMomentumMagnitude<T> the same contradiction
MomentOfInertia<T> * AngularAccelerationMagnitude<T> -> TorqueMagnitude<T> the same contradiction
Force{2,3,4}D<T>.Dot(Displacement{2,3,4}D<T>) -> Energy<T> signed value, magnitude result

That is the whole of the generated diff: 81 deletions across 10 files and not one line added. No type was added, removed or renamed, so the storage-type alias packages are untouched.

docs/migration-guide-5.0.md says what to write instead of each. The first three are provably unfixable by choosing angle exponents — Torque * AngularDisplacement -> Energy forces torque's angle exponent to −1 and Force × Length -> Torque forces it to 0 — and the fourth is fixable, by a vector1 form on Energy for a signed result to land in, which the diagnostic names rather than guesses at.

The dot product is worth a second look: its exponents agree (LMT⁻² · L is L²MT⁻², which is what Energy is), and the method would still have thrown ArgumentException on an ordinary input. A force opposing a displacement does negative work, Energy declares only a magnitude form, and Vector0Guards.EnsureNonNegative runs on every factory. Anyone who was calling it was relying on never braking.

SEM008 still reports each refusal and now says no operator is generated. UnkeepableRelationshipTests asserts their absence from the compiled surface by reflection — and asserts that Displacement3D.Cross(Force3D), which is keepable, is still there, so the test says what was removed rather than only that something was.

Also in here

  • VocabularyIssue carries an IssueSite — the names, the kind and the form — so SEM001, SEM002, SEM003 and SEM008 are all reported from the vocabulary's refusals, each keeping the location it pointed at before, rather than from a second walk that reported what it dropped.
  • Missing checks each participant at the form it actually has to supply. The right operand of a product is the magnitude the vector is scaled by, so what it needs is a vector0 however many components the relationship asks for; checking it at the declared form could name no participant at all.
  • The SEM003 fixtures declare real physics (Length × Force → Torque). They multiplied two lengths into a length, which the vocabulary refuses for its exponents before it reaches the question of forms — so they would have reported SEM008 and never SEM003. They now assert which participant the message names, not merely that the name appears somewhere in it.
  • Both readers derive the strict-positive floor from the constraint's value rather than from the presence of the object holding it, so a constraint of another kind will not silently turn it on.

Verification

  • Full solution builds clean; Semantics.Test 1129/1129 and Semantics.Cpp.Test 27/27 pass, after the merge.
  • Sonar analyzers run locally over the whole solution report the same findings as main and no new ones.
  • The generated output under Semantics.Quantities/Generated/ was diffed line by line: every removal is one of the four relationships above.

🤖 Generated with Claude Code

https://claude.ai/code/session_01QGCMUrT3jBgmANHNHcPmBf

`QuantityVocabulary` has resolved the metadata into the quantities and
operators it describes since #219, and refused the relationships whose
exponents contradict their declared result. The C++ projection emitted from
that resolution. The C# generator did not: it built its own type and operator
lists by walking `dimensions.json` a second time, and called the vocabulary
only to report SEM008 against operators it then emitted anyway.

So `QuantitiesGenerator` now emits from `vocabulary.Types` and
`vocabulary.Relationships`. One physics model, literally: which classes exist,
what each one is and how it is bounded, and which operators relate them, all
decided once and read by both targets.

Two things stay on this side, and both are the language rather than the
physics. A relationship becomes four C# operators — as declared, commuted, and
the two divisions that undo it — because a caller who writes
`duration * velocity` is not making a different claim; that expansion is
`CollectOperators`. And an overload's `relationships` are C# expressions
written in the metadata and pasted through, so there is nothing in them for a
shared model to hold.

**Breaking.** Sixteen operators and three methods are gone, being the five
refused relationships in each direction C# spelled them:

  Sensitivity * Pressure -> VoltageMagnitude
  TorqueMagnitude * Angle -> Energy
  MomentOfInertia * AngularSpeed -> AngularMomentumMagnitude
  MomentOfInertia * AngularAccelerationMagnitude -> TorqueMagnitude
  Force{2,3,4}D.Dot(Displacement{2,3,4}D) -> Energy

That is the whole of the generated diff: 105 deletions across 13 files and not
one line added. `docs/migration-guide-5.0.md` says what to write instead of
each, and why the fix is a physics call rather than a spelling one in all five
cases. SEM008 still reports them and now says no operator is generated;
`UnkeepableRelationshipTests` asserts their absence from the compiled surface
by reflection, because a call that does not compile cannot be written down.

Along the way:

- `VocabularyIssue` carries an `IssueSite` — the names, the kind and the form —
  so SEM001, SEM002, SEM003 and SEM008 are all reported from the vocabulary's
  refusals, each with the location it had before, rather than from a second
  walk that reported what it dropped.
- `Missing` checks each participant at the form it actually has to supply. The
  right operand of a product is the magnitude the vector is scaled by, so what
  it needs is a `vector0` however many components the relationship asks for;
  checking it at the declared form could name no participant at all.
- The SEM003 fixtures declare real physics (`Length x Force -> Torque`). They
  multiplied two lengths into a length, which the vocabulary refuses for its
  exponents before it ever reaches the question of forms — so they would have
  reported SEM008 and never SEM003. They now assert which participant the
  message names, not merely that the name appears in it.
- Both readers derive the strict-positive floor from the constraint's value
  rather than from the presence of the object holding it, so a constraint of
  another kind will not silently turn it on.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QGCMUrT3jBgmANHNHcPmBf
#221 fixed the Sensitivity dimension and moved the cross product to Length,
which lands squarely on this branch: it was one of the five relationships whose
operators this removes. The dimension said amperes per newton while the unit
beside it said VoltPerPascal and the relationship agreed with the unit, so the
formula was the one thing that was wrong and there was no physics call to make
after all.

So the removal is four relationships rather than five, and twelve operators and
three methods rather than sixteen and three. Sensitivity * Pressure ->
VoltageMagnitude is dimensionally true now and is generated, along with its
commutation and both inverses. The migration guide says so under "what this does
not change", because a reader coming from 4.3 will have seen SEM008 name it.

The generated diff is again purely subtractive: 81 deletions across 10 files and
not one line added.

The conflicts were all the same one thing — main's text saying the four remaining
refusals are still emitted, against this branch's saying the five are not — and
each is resolved to the two facts that are both true: four remain, and none is
emitted. The Force3D conflict was generated output either way and is regenerated.
UnkeepableRelationshipTests keeps main's TheSensitivityRelationshipIsNoLongerRefused
and gains, beside the dot product it asserts is gone, the cross product between
the same two types asserted to still be there — so the reflective test says what
was removed rather than only that something was.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QGCMUrT3jBgmANHNHcPmBf
@sonarqubecloud

Copy link
Copy Markdown

@matt-edmondson
matt-edmondson merged commit cc7d4b2 into main Sep 12, 2026
13 checks passed
@matt-edmondson
matt-edmondson deleted the claude/bold-planck-mxarux branch September 12, 2026 12:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants