Skip to content

Support the Behavior Annex port updated value 🤖 - #3216

Merged
lwrage merged 2 commits into
masterfrom
3171_support_port_updated_value
Sep 11, 2026
Merged

Support the Behavior Annex port updated value 🤖#3216
lwrage merged 2 commits into
masterfrom
3171_support_port_updated_value

Conversation

@lwrage

@lwrage lwrage commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Fixes #3171

Cause and correction

AS5506/3 Rev. A lists 'updated among the D.5 port services and admits it in the D.7 value_variable production as port_name ' updated, beside 'count and 'fresh. The grammar spelled only the count and fresh suffixes, in both ReferenceExpression and UnindexedReferenceExpression, so 'updated was a syntax error at the suffix and the rest of the transition was read as something else entirely: in the regression model the action of the second transition lost its assignment and its names were taken for source states of the next transition.

  • Grammar: both rules gained the suffix. Adding it to UnindexedReferenceExpression as well keeps the two rules' suffix sets equal; that rule stands for a property array index, where the translator drops the suffix today for count and fresh alike.
  • Strict model: a new PortUpdatedValue, an ActualPortHolder like PortCountValue and PortFreshValue, so the port the user named has somewhere to go and a value reached through a feature group keeps its group holder beside it. The Ecore addition and the code generated from it come from the model generators.
  • Type rules: a universal Boolean, which is what D.5 gives it and what PortFreshValue already establishes — data representation Boolean, data classifier null. D.5 makes the value equivalent to the core Updated runtime service, true when new values were received in the port since its last freeze and without freezing it, so there is no data classifier to carry. The null classifier is also what lets the value apply to an event port that carries no data at all, because nothing then asks that port for a classifier it does not have.
  • Translator: creates the new holder, and excludes the value where it already excludes count, fresh, and dequeue — an actual parameter in a writable position becomes a Target only when it is a plain reference, and a port value is a value rather than something a subprogram can assign to.
  • Editor and serializer: support follows from the generated sequencers and proposal provider, so the value round-trips.

BehaviorAnnexConformanceTest now requires acceptance of PortUpdated.aadl instead of recording its rejection, which closes conformance finding G07. That fixture needed no correction.

No legality check

The production spells port_name rather than the incoming_port_name it uses for a plain port read, so it admits an outgoing port, and neither the D.5 nor the D.7 legality rules narrow that — even though D.5 describes all three port services only in terms of input freeze: 'updated reports values "received … since the last freeze", 'count counts "frozen elements", and 'fresh reports a value "received … by the previous dispatch or freeze". There is therefore no stated rule to enforce here, and the existing behavior for the two older services is the same: the covering-syntax corpus reads 'count and 'fresh off an outgoing port with no diagnostic. The G07 row of ba/doc/conformance.md records that tension rather than resolving it, and the regression model names incoming ports only. Deciding a house interpretation and enforcing it for all three services is tracked separately.

Regression model and assertions

ba/org.osate.ba.tests/models/issue3171/Issue3171.aadl writes the value on an incoming data port in an execute condition, on an incoming event data port in a condition with the value of a data-less incoming event port in the action beside it, and on a port reached through a feature group. Issue3171Test pins the three things the value needs beyond parsing:

  • portUpdatedResolvesToAPortValueOfItsOwn — the strict metaclass and the port each value holds, in the order the annex writes them, with the feature group kept beside its port. The metaclass is read by name so that a run without the support reports the representation it did build rather than failing to compile. The action block is collected separately from the condition, because the strict model keeps every block under the annex and has the transition refer to it rather than contain it.
  • portUpdatedIsAUniversalBooleanAadlBaUtils.getTypeHolder returns Boolean with no data classifier for all four values, including the data-less event port.
  • portUpdatedRoundTripsThroughTheUnparser — the annex serializes with the suffix and reparses to the same strict shape. It serializes a copy, because the registered unparser returns the source text of anything that still has a node model, and the copy joins the parsed resource for the duration so that the unit and classifier cross-references have the scope they need.

All three failed on no viable alternative at input 'updated' before the fix, and the second and third would still have failed on the type and on the serialized text if only the grammar had been extended. The characterization goldens in the first commit record that rejection, so the second commit shows the complete change. Every diagnostic in the golden generated without the BA validator comes from the unparsable suffix itself, so the model carries no unrelated AADL error.

'updated joins 'count and 'fresh as a word the annex reserves. No model in the corpus used it as an identifier: regenerating the characterization goldens changed only the two fixtures that write the value.

Validation

Focused regression, from the repository root:

mvn -o -s releng/osate.releng/settings.xml -Plocal -pl :org.osate.ba.tests \
  -Dtycho.localArtifacts=default -Dpr.build=true -Dsign=false \
  -Dspotbugs=false -Dcodecoverage=false -Djavadoc=false \
  -Dtest=Issue3171Test -DfailIfNoTests=false clean install

Tests run: 3, Failures: 0, Errors: 0, Skipped: 0

All nine Behavior Annex bundles with their tests: Tests run: 128, Failures: 0, Errors: 0, Skipped: 4 in org.osate.ba.tests and Tests run: 15, Failures: 0, Errors: 0, Skipped: 0 in org.osate.xtext.aadl2.ba.tests. BehaviorAnnexConformanceTest reports Tests run: 12, Failures: 0, Skipped: 4; the skips are the four conformance findings still open, one fewer than before, and everyStandardExpectationNamesItsTrackingIssue enforces that TRACKED_SKIPS agrees.

Clean root-reactor build:

mvn -s releng/osate.releng/settings.xml -Plocal -T5 \
  -Dtycho.localArtifacts=ignore \
  -Dpr.build=true -Dsign=false \
  -Dspotbugs=false -Dcodecoverage=false -Djavadoc=false \
  -DfailIfNoTests=false clean install

BUILD SUCCESS, 3176 tests run, 0 failures, 0 errors, 8 skipped.

Dependencies

None. The branch is based on master at 817cb70 and contains the regression commit followed by the production-fix commit.

Residual risk

  • The generated code was produced by running the EMF and Xtext model generators headlessly rather than from the IDE. A control run against the unmodified models isolated the semantic delta from formatter drift, and only that delta was transferred onto the committed files, so the generated diff stays reviewable; the ANTLR parser and lexer still change broadly because adding a keyword shifts token numbers. All nine bundles compile and the full reactor is green.
  • Adding an EClass shifts the AadlBaPackage classifier IDs of the enums and data types that follow it. The new genClasses entry is appended last so the shift stays confined to those constants, but any consumer that hard-codes a classifier ID rather than using the generated constant would be affected.
  • 'updated is now a reserved word in the annex, as 'count and 'fresh already are. A pre-existing model that uses updated as an identifier inside a behavior_specification would stop parsing. No model in the 100-fixture corpus does.
  • The value's applicability to outgoing ports is left exactly as it is for 'count and 'fresh: accepted, undiagnosed, and unresolved by the standard.

🤖 Generated with Claude Code

sei-lwrage and others added 2 commits September 11, 2026 15:22
AS5506/3 Rev. A lists 'updated among the D.5 port services and admits it in the
D.7 value_variable production as "port_name ' updated", beside 'count and
'fresh. The BA grammar spells only the count and fresh suffixes, in both
ReferenceExpression and UnindexedReferenceExpression, so 'updated is a syntax
error: "no viable alternative at input 'updated'". The error is not confined to
the suffix. In Issue3171.aadl the action of the second transition loses its
assignment and its names are read as source states of the next transition.

Issue3171.aadl writes the value on an incoming data port in an execute
condition, on an incoming event data port in a condition with the value of a
data-less incoming event port in the action beside it, and on a port reached
through a feature group. Its three assertions pin the three things the value
needs beyond parsing:

- The strict model gives it a port value of its own metaclass holding the named
  port, with the feature group kept beside the port the way every other port
  holder keeps it. The metaclass is read by name so that a run without the
  support reports the representation it did build rather than failing to
  compile. The action block is collected separately from the condition, because
  the strict model keeps every block under the annex and has the transition
  refer to it rather than contain it.
- The value types as a universal Boolean with no data classifier. D.5 makes it
  equivalent to the core Updated runtime service - true when new values were
  received in the port since its last freeze, without freezing it - so there is
  no data classifier to carry, and that is also what lets it apply to an event
  port that carries no data at all, the way a port fresh value already does.
- The value round-trips through the registered unparser, because the generated
  sequencers have to emit the suffix as well as the parser having to read it. It
  serializes a copy: the registered unparser returns the source text of anything
  that still has a node model. The copy joins the parsed resource for the
  duration, because serializing the unit and classifier cross-references needs
  the scope that resource provides.

Every port the model names is incoming, which is what D.5's description of the
value is about. The production spells "port_name" rather than the
"incoming_port_name" it uses for a plain port read, so it admits an outgoing
port too, but neither the D.5 nor the D.7 legality rules say what the value
would mean there, so the model takes no position on that case.

All three assertions currently fail on the syntax error, which is neither the
rule nor the location a user needs, and the second and third would still fail on
the type and on the serialized text if only the grammar were extended. The
characterization goldens record that rejection so the fix commit shows the
complete change. The model project is self-contained: every diagnostic in the
golden generated without the Behavior Annex validator comes from the unparsable
suffix itself, so the model carries no unrelated AADL error.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
AS5506/3 Rev. A lists 'updated among the D.5 port services and admits it in the
D.7 value_variable production as "port_name ' updated", beside 'count and
'fresh. The grammar spelled only the count and fresh suffixes, in both
ReferenceExpression and UnindexedReferenceExpression, so 'updated was a syntax
error at the suffix and the rest of the transition was read as something else
entirely: in Issue3171.aadl the action of the second transition lost its
assignment and its names were taken for source states of the next transition.

Add the suffix to both rules and a PortUpdatedValue to the strict model. It is
an ActualPortHolder like PortCountValue and PortFreshValue, so the port the user
named has somewhere to go and a value reached through a feature group keeps its
group holder beside it, the way every other port holder does. Adding it to
UnindexedReferenceExpression as well keeps the two rules' suffix sets equal; that
rule stands for a property array index, where the translator drops the suffix
today for count and fresh alike. The Ecore addition and the code generated from
it come from the model generators.

The type rules give it a universal Boolean, which is what D.5 gives it and what
PortFreshValue already establishes: the data representation is Boolean and the
data classifier stays null. D.5 makes the value equivalent to the core Updated
runtime service - true when new values were received in the port since its last
freeze, and it does not freeze the port - so there is no data classifier to
carry. The null classifier is not incidental either. It is what lets the value
apply to an event port that carries no data at all, because nothing then asks
that port for a classifier it does not have.

The translator also excludes the value where it excludes count, fresh, and
dequeue: an actual parameter in a writable position becomes a Target only when it
is a plain reference, and a port value is a value rather than something a
subprogram can assign to. Serializer and content-assist support follow from the
generated sequencers and proposal provider, so the value round-trips.

No legality check comes with this. The production spells "port_name" rather than
the "incoming_port_name" it uses for a plain port read, so it admits an outgoing
port, and neither the D.5 nor the D.7 legality rules narrow that - even though
D.5 describes all three port services only in terms of input freeze: 'updated
reports values "received ... since the last freeze", 'count counts "frozen
elements", and 'fresh reports a value "received ... by the previous dispatch or
freeze". There is therefore no stated rule to enforce, and the existing behavior
for the two older services is the same: the covering-syntax corpus reads 'count
and 'fresh off an outgoing port with no diagnostic. The G07 row of the
conformance document records that tension rather than resolving it, and the
regression model names incoming ports only.

'updated joins 'count and 'fresh as a word the annex reserves. No model in the
corpus used it as an identifier: regenerating the characterization goldens
changed only the two fixtures that write the value.

BehaviorAnnexConformanceTest now requires acceptance of PortUpdated.aadl instead
of recording its rejection, which closes conformance finding G07. That fixture
needed no correction.

Fixes #3171

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@lwrage
lwrage merged commit 04bec93 into master Sep 11, 2026
1 check passed
@lwrage
lwrage deleted the 3171_support_port_updated_value branch September 11, 2026 22:24
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.

Support the Behavior Annex port updated value 🤖

2 participants