Skip to content

fix(tools): score the pilot's JVM-dependent api-json values in a pilot-nondeterministic column - #1555

Merged
HuiJun merged 2 commits into
developfrom
fix/apijson-featuring-type-order
Oct 27, 2026
Merged

HuiJun merged 2 commits into
developfrom
fix/apijson-featuring-type-order

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

What and why

develop's scheduled Oracle-reproduction workflow fails on go run -C tools ./cmd/pilot-apijson-diff -check: scorecard.FeatureChainExpression.featuringType[2]: 287 -> 284, [4]: 227 -> 230. Nothing in this repository moved; the pinned pilot's api-json export of that property is not reproducible across JVMs, so re-recording the baseline only chases the flip. This PR proves where the flip comes from, scores the property in a new, visible pilot-nondeterministic scorecard column so the baseline reproduces on every JVM, and re-records the baseline once. Comparison code, the baseline and its docs only.

The nondeterminism, proved

The pinned exporter (scripts/download-pilot-json-exporter.sh, pin 2026-08, jupyter-sysml-kernel-0.62.0) was run over all nine corpus roots four times on one checkout and every keyed element's every property compared across runs (ids mapped through the bridge's key table):

run JVM checkout path result against run 1
1, 2 OpenJDK 25 same identical: no element, property or key differs — the export is deterministic on one JVM
3 OpenJDK 21 (the workflow's setup-java) same exactly 9 values differ, all FeatureChainExpression.featuringType, nothing else
4 OpenJDK 25 a copy of the corpora under another path identical to run 1 — the checkout path is not the cause

The nine values are three elements written into three documents each (the pilot writes an element into every file whose traversal reaches it; the copies collapse to three matched elements = the 287/284 flip), the FeatureChainExpressions valuing the three event occurrences of ServerSequenceModelOutside::PubSubSequenceOutside:

element (stable key) notation documents
…::server::publish_target_event/FeatureChainExpression[0] then event occurrence :>> publish_target_event = publish_message.done; examples/pilot-corpora/sysml-examples/Interaction Sequencing Examples/ServerSequenceModelOutside.sysml, re-exported by ServerSequenceOutsideRealization-2.sysml and ServerSequenceOutsideRealization-3.sysml (they import it)
…::server::deliver_source_event/FeatureChainExpression[0] then event occurrence :>> deliver_source_event = deliver_message.start; same three
…::consumer::deliver_target_event/FeatureChainExpression[0] then event occurrence :>> deliver_target_event = deliver_message.done; same three

Shape of the difference — not an ordering of a multi-valued reference and not a moving @id, but presence: under OpenJDK 21 each has "featuringType": []; under OpenJDK 25 each has "featuringType": [{"@id": <x>}] where x is the defaultFeaturingType of the enclosing SuccessionAsUsage (ConnectorAsUsage::defaultFeaturingType), an element the pilot neither exports nor keys in any document (hence the row's single unkeyed reference). Mechanism, from the pilot's org.omg.sysml.logic at the pinned commit: an expression's featuringType is derived from its adapter's implicit featuring types, which FeatureAdapter.addImplicitFeaturingTypesIfNecessary fills while the owner is transformed and FeatureUtil.insertImplicitTypeFeaturings materializes as implied TypeFeaturings — the result depends on transformation order, not on the model. Both answers are legitimate JSON shapes, so a single comparison cannot tell a stable value from an unstable one; sorting would not help (the difference is presence, not order).

The fix, at the comparison

  • tools/referee/apijson/nondeterminism.go: a declared list pilotNondeterminism of (metaclass, property) rows with the evidence as text; one entry, FeatureChainExpression.featuringType. The list is written into the report and the baseline (pilotNondeterministic), so a declaration change is itself a baseline movement -check shows.
  • compare.go: a seventh bucket pilot-nondeterministic. Every comparison of a declared row is scored there and in no other bucket, never recorded as an example; undeclared properties — the same metaclass's included — are scored exactly as before. Nothing is dropped and no tolerance is widened: the row and the headline show the 514 comparisons the oracle cannot answer reproducibly.
  • report.go: the scorecard is [7]int (present-equal, present-different, ours-missing, pilot-missing, absent-both, indeterminate, pilot-nondeterministic); the headline and the text report carry the column.
  • Docs: docs/project/api-json-differential.md documents the column semantics, the evidence and the declared list (new section "Declared pilot nondeterminism"), and its headline is the re-recorded one; docs/project/pilot-differential.md's oracle-reproduction bullet names the third kind of -check failure (the reference disagreeing with itself) and what to do; the api-json row of docs/project/spec-compliance.md states the declared row and its metamodel basis.

Baseline movement (-update once, on the fixed comparison)

11730 rows before and after; one row moved, nothing else (totals, element counts, every other row and the provenance block are byte-identical in meaning):

row develop this PR
FeatureChainExpression.featuringType [0, 0, 287, 0, 227, 0] [0, 0, 0, 0, 0, 0, 514]

Totals: ours-missing 1076508 → 1076221 (−287), absent-both 1978076 → 1977849 (−227), pilot-nondeterministic 0 → 514; present-equal, present-different, pilot-missing and indeterminate unchanged. totals.unkeyedReferences 112272 → 112263: the declared row's values are not compared, so the 9 unkeyed references they carried (3 of them the JVM-dependent ones — under OpenJDK 21 the old count came out 112269, which is how the first round of this PR still failed -check across JVMs; caught by Devin Review) are no longer counted; the record's stale figure for this count is corrected alongside. Per root the 514 are training 62, pilot-examples 172, pilot-validation 66, kerml-examples 9, spec-examples 7, examples 162, manual-examples 36 (testdata and probes 0). Which side is right: the metamodel (KerML 1.1 Beta 2 §8.3.3.3.4, deriveFeatureFeaturingType) derives featuringType from the feature's typeFeaturings and its first chaining feature, which these expressions have neither of, so the stable answer is none — what this repository's writer states; the pilot's other answer is a different run, not a different fact, and the oracle is not the authority.

Specification basis

Tooling change; no runtime behaviour moves. The adjudication above relies on KerML 1.1 Beta 2 §8.3.3.3.4 (Feature::featuringType : Type [0..*] {ordered}, deriveFeatureFeaturingType) and §8.3.4.8.4 (FeatureChainExpression). The api-json row of docs/project/spec-compliance.md keeps its ✅ status with the declared row added to its text.

How it was verified

  • TestDeclaredNondeterminismScoresTheWholeRow: a declared row takes every comparison (pilot states / ours lacks, both state none, the two state different values) in the seventh column only, never an example; an undeclared property of the same metaclass and the same property on another metaclass are scored as before.
  • TestDeclaredNondeterminismScoresTheWholeRow also asserts unkeyedReferences stays 0 for every pilot value shape of a declared row.
  • TestDeclaredNondeterminismIsEvidencedAndRecorded: the committed baseline's declared list is the comparison's, every declared row carries evidence and sits wholly in the seventh column, the headline total is the sum of the declared rows, and no undeclared row has a count there.
  • TestRecordStatesTheBaselinesFigures ties the seven-figure headline in the docs to the baseline.
  • Reproducibility: go run -C tools ./cmd/pilot-apijson-diff -check three times on the final tree — run 1 under OpenJDK 25, run 2 under OpenJDK 21, run 3 under OpenJDK 25: run 1 ✅ reproduces, run 2 (OpenJDK 21) ✅ reproduces, run 3 ✅ reproduces — the committed baseline reproduces on both JVMs (before the second commit the OpenJDK 21 run stopped at totals.unkeyedReferences: 112272 -> 112269; with it, every total and row holds)
  • Gates: go build ./... ✅, go vet ./... ✅, gofmt -l . empty ✅, go test ./... with OPENSYSML_REQUIRE_TRAINING_CORPUS=1 OPENSYSML_REQUIRE_PILOT_CORPORA=1 OPENSYSML_REQUIRE_SPEC_EXAMPLES=1: every package ✅ except two that hit the 10-minute default timeout while two pilot exports were running beside them (cmd/sysml in TestExploreTablesTheSpacecraftRaceWithinItsBudget, tests/codegen in TestCompiledStepBudgetMatchesInterpreter, both budgeted perf tests, neither touching this change); re-run alone, cmd/sysml ✅ 308s and tests/codegen ✅ 340s; go test -C tools ./... ✅ on the final head; python3 scripts/changelog.py check ✅; mkdocs build --strict ✅; make docs-counts ✅ ("Documentation counts and refereed figures are current").

Coordination with #1529 (pilot pin 2026-09)

Dry-merged both commits onto #1529's current head 9ca600a59 in a detached worktree: the comparison code, its tests, the changelog fragment, pilot-differential.md and spec-compliance.md merge clean and go build, go vet and the comparison tests pass on the merged tree; docs/project/api-json-differential-baseline.json (both PRs re-record every row) and two adjacent hunks of docs/project/api-json-differential.md (the headline, and the Results heading's pin beside the new section) conflict, as expected of two re-records. Against origin/develop (0286c9845) the branch is a fast-forward. Landing order: this PR first, then #1529 re-merges develop and re-records once on the seven-column scorecard at its pin (the #1529 session reproduced the same flip with the 0.63.0 exporter, so the declared row stays; its evidence text names the pin it was proved at). If #1529 lands first, this PR re-records once more on top of it instead.

Checklist

  • make test and make lint pass locally (see gates above)
  • Tests added or updated for the change
  • Documentation extended where it already covers the surface (see CONTRIBUTING.md)
  • Changelog entry added as changes/unreleased/<slug>.<section>.md, not as an edit to CHANGELOG.md
  • baselines regenerated and make docs-counts run if a gate count moved (compliance rows need nothing: the census is counted at docs build)
  • No internal work-item labels (waves, slices, F4, K5) in the body, docs, or changelog

…t-nondeterministic column

pilot-apijson-diff -check failed on develop because the pinned pilot exports
FeatureChainExpression.featuringType differently from one JVM to the next:
three expressions in its ServerSequenceModelOutside example gain or lose a
reference to an element the pilot never exports. The comparison now carries a
declared list of such properties with the evidence, scores every comparison of
a declared row in a seventh scorecard column and nowhere else, writes the list
into the report and the baseline, and the baseline is re-recorded once on the
seven-column scorecard; only that row moved.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

@devin-ai-integration
devin-ai-integration Bot marked this pull request as ready for review October 26, 2026 22:44
devin-ai-integration[bot]

This comment was marked as resolved.

A pilot-nondeterministic comparison scores no value, so the references it
mentions are not compared and are not counted unkeyed either; the count
otherwise moved by three between OpenJDK 21 and 25 while every bucket held.
Baseline re-recorded on the fixed count: unkeyedReferences 112272 -> 112263,
nothing else moved.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
@HuiJun
HuiJun merged commit 2c0f175 into develop Oct 27, 2026
30 checks passed
@HuiJun
HuiJun deleted the fix/apijson-featuring-type-order branch October 27, 2026 00:38
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.

1 participant