148 lines
12 KiB
Markdown
148 lines
12 KiB
Markdown
# Interface-operation message contract — proposal and falsifier, 2026-09-06
|
|
|
|
Status: contract/experiment only. No shared production edit, Cargo, or native input.
|
|
This proposal requires independent critique and root approval before implementation.
|
|
|
|
## Experiment outcome
|
|
|
|
[Standalone existing-core probe](fixtures/interface-operation-probe/README.md)
|
|
builds a valid System/Actor boundary with one provided/required interface and two
|
|
Synchronous Operation items, Reset and ReadStatus. Both have zero parameters. Calls
|
|
with the same participant pair/channel have identical typed bodies despite their
|
|
different names. Item/allocation substitutions for `exchange` reject; an operation
|
|
JSON field rejects. Reset rename/delete does not affect its intended Call body,
|
|
which remains structurally valid. This is a missing maintained semantic reference.
|
|
|
|
A valid alternative adds two Functions, four function ports, two FunctionalExchanges,
|
|
two performer allocations, four port allocations and two exchange allocations.
|
|
Its Calls select distinct FunctionalExchanges while retaining the original boundary.
|
|
That is **six additional elements/ten relationships**, not a different notation for
|
|
the same interface-only model. It is appropriate when the functional refinement is
|
|
part of the engineer's design; it must not be fabricated solely to address an
|
|
interface operation. All three probe models pass complete JSON/source round trips.
|
|
|
|
## Alternatives and selected direction
|
|
|
|
| Choice | Tradeoff |
|
|
|---|---|
|
|
| Name/guard or generic constraint | Can describe intent, but supplies no closed operation-use slot or automatic endpoint/lifecycle validation. Does not close the demonstrated gap. |
|
|
| Select only ExchangeItem | Distinguishes Reset from ReadStatus but loses which interface allocation supplies its protocol/acquisition context; one item may occur in several interfaces. |
|
|
| Select InterfaceItemAllocation | Reuses an existing identified relation, preserving interface, operation item and protocol context. Recommended. |
|
|
| New operation occurrence constructor / whole message-target union | Can support channel-free interface scenarios later, but broadens constructors, old histories and UI beyond this demonstrated shared-channel case. Not required for the first slice. |
|
|
| Infer sole carried Operation item | Ambiguous with multiple or zero-parameter operations and unstable when another item is added. Reject inference even when there happens to be one item. |
|
|
|
|
Proposed persistent field in existing `OccurrenceBody::Exchange`:
|
|
|
|
```rust
|
|
#[serde(default)]
|
|
operation: Option<Id> // InterfaceItemAllocation identity, never an item or port
|
|
```
|
|
|
|
Text: `operation @reset_service_allocation;` or `operation none;`. JSON uses the
|
|
existing externally tagged Exchange record with this optional UUID. No separate
|
|
stored item/interface/provider fields: derive them through the selected allocation
|
|
and validated message endpoints. The field is authored on Call/Asynchronous;
|
|
Reply derives the operation from its exact correlated Call and must store None.
|
|
This prevents two redundant identities disagreeing after edits. Display the
|
|
derived operation on Reply as read-only provenance.
|
|
|
|
## Applicability and validation
|
|
|
|
This bounded slice applies to SA `BoundaryInterface` scenarios with an explicit
|
|
BoundaryExchange channel, participant lifelines and concrete component endpoints.
|
|
Existing operation=None scenarios, including Functional and MixedLegacy, retain
|
|
their prior meaning. New operation selection in MixedLegacy requires explicitly
|
|
choosing BoundaryInterface; it must not silently reinterpret that viewpoint.
|
|
OA participant channels remain operational communication and do not acquire
|
|
invented technical ports/interfaces. Channel-free interface messages are a
|
|
separate future design, not implied by this field.
|
|
|
|
Resolve the actual sender/receiver participant and corresponding boundary ports
|
|
using the existing directional scenario resolver, including reversed traversal of
|
|
an unoriented channel. Never assume stored boundary source is the caller.
|
|
Require:
|
|
|
|
- Existing same-SA-layer InterfaceItemAllocation whose target is an Information
|
|
ExchangeItem with mechanism Operation; the channel carries that item through
|
|
the existing effective-item/subtype rules.
|
|
- Concrete Standard component ports. The actual receiver provides an interface
|
|
exposing that exact allocation; the actual sender requires a compatible
|
|
interface also exposing it. Reuse `effective_interface_allocations` and existing
|
|
`interface_satisfies` inheritance rules. An item present through a different
|
|
allocation is not interchangeable with the selected protocol context.
|
|
- Call selects Synchronous transmission. Asynchronous selects Asynchronous
|
|
transmission and has no correlated Reply. Unspecified transmission remains a
|
|
visible incomplete-contract warning; explicit incompatible transmission rejects.
|
|
Do not invent transport multicast/broadcast call-return semantics in this slice.
|
|
Existing acquisition compatibility remains governed by the allocation validator.
|
|
|
|
Known wrong kind/layer/port/interface/direction/mechanism is a structural error.
|
|
Unfinished sender/receiver or missing operation after an explicit operation-use
|
|
edit is a warning while the candidate is otherwise safe; no automatic endpoint or
|
|
operation choice. The exact warning distinction needs an operation-intent marker
|
|
if None must distinguish ordinary channel calls from cleared operation calls:
|
|
**prefer a closed `OperationSelection::{Unselected, Selected(Id)}` optional field
|
|
if testing confirms the plain Option cannot retain that distinction.** See the
|
|
open decision below; do not silently encode intent in names/history.
|
|
|
|
### Calls, replies and values
|
|
|
|
Existing exact call/reply identity, order, reverse participant and repetition
|
|
checks remain authoritative. Reply derives the operation from its Call; an
|
|
independent Reply selection rejects. Editing Call's operation replans/validates
|
|
all correlated Reply values atomically before admission.
|
|
|
|
Use the selected item's effective data-field paths, not the union of every item
|
|
on the channel. Call values permit In/InOut; Reply permits Out/InOut/Return.
|
|
Unspecified parameter direction warns until clarified. Asynchronous operation
|
|
values use input parameters and cannot carry Return values. Parameterless calls
|
|
need no fabricated field. Keep raw value drafts on operation changes; reject or
|
|
require explicit value removal when a retained field is no longer applicable.
|
|
Never insert defaults or prune authored expressions merely on picker change.
|
|
|
|
## Lifecycle, transition and representation obligations
|
|
|
|
| Surface | Required contract and falsifier |
|
|
|---|---|
|
|
| References and deletion | Visit allocation selection as a typed relation reference. Rename shows the new item/interface label without rewriting message prose. Reidentify allocation/item/interface preserves selection through existing visitors. Deleting allocation/item clears selection to an explicitly unresolved operation use, retains Call and correlated Reply, prunes only proven invalid live field references under normal delete policy, and creates a located warning. One Undo restores all identities/source. Removing provision/channel changes must validate dependents or offer an explicit repair batch; never switch to another matching allocation. |
|
|
| Transition | Preserve source. OA→SA existing operation=None remains None; choosing a technical operation is an explicit SA refinement. SA→OA has no direct operational operation contract in this bounded design: preview must require explicit reviewed omission/abstraction, not silently drop the selection or invent OA ports. Proposed per-occurrence request decision `OmitOperation { source, rationale }` is needed if this transition is allowed; unknown/irrelevant decisions reject and omission is shown as a planned impact. Without that decision, refuse with a precise unsupported-projection message. Keep mapped channel, Call/Reply and values only when independently admissible. Same-layer/review mapping resolves allocation identity through its interface/item counterpart. |
|
|
| Semantic review | Add a closed optional allocation-reference facet, e.g. `OccurrenceFacet::Operation`, to observed/current/history and constructor scope. Compare its actual field slot; preserve invalid candidates and excluded rows. Derived Reply operation has no second independently writable slot. Operation changes plus value edits must compose on one frozen base; conflicting selections remain explicit. Operation omission across layers is not an ordinary mapped Take. |
|
|
| Source/JSON/API | Current source6 rejects new fields; introduce the next schema/source version coherently with any approved S1 change. Old input defaults to ordinary exchange behavior, never guesses an operation. Extend parser/writer/updater/reference spans, strict schemas/OpenAPI examples, migration rejection tests and commands; old historical DTOs remain frozen. Every modified relationship/value and history update must preserve unrelated source/comments and one command boundary. |
|
|
| Desktop | In the existing scenario form, show an optional Operation-use control followed by a named allocation picker (`Interface · operation · protocol`). Filter choices by the proposed actual participant/channel context; no name inference. Preserve a rejected selection visibly when context changes rather than silently dropping it. Call and Reply labels make the selected/derived operation readable, including parameterless messages. Use the existing scenario canvas and draft/recovery machinery. |
|
|
| Recovery and history | Persist exact operation intent/selection and rejected raw drafts; rebuild previews against the recorded base. A source-version migration must upgrade checkpoint accepted bundles/fingerprints through a proved adapter and reset approvals, not merely change schema numbers. Both old-v6 normal restore and new-version exact restore need tests; prior source5 recovery does not automatically prove this. |
|
|
|
|
## One open DTO choice to settle before coding
|
|
|
|
Plain `Option<Id>` is the smallest structural addition but conflates an ordinary
|
|
channel Call with an authored operation Call whose allocation was deleted. To
|
|
preserve explicit unresolved intent without inventing a tombstone, recommend the
|
|
following **final candidate**, replacing the shorthand above:
|
|
|
|
```rust
|
|
operation: Option<OperationUse>
|
|
struct OperationUse { allocation: Option<Id> } // deny_unknown_fields
|
|
```
|
|
|
|
None means ordinary existing channel-message semantics; Some(allocation=None)
|
|
means operation use requiring selection; Some(Some(id)) is a selected allocation.
|
|
Reply still stores None and derives the Call's OperationUse. This is one authored
|
|
semantic slot, with a maintained live reference inside it. Text renders
|
|
`operation { allocation @reset_service_allocation; };` and unresolved
|
|
`operation { allocation none; };`. Migration default is None. Review must preserve
|
|
the outer None versus explicit unresolved shape, as it already does for other
|
|
closed optional/constructor observations. This is the recommendation to critique,
|
|
not a field already added to core.
|
|
|
|
## Decisive next tests
|
|
|
|
Before GUI wiring: same-boundary Reset/ReadStatus selections differ and survive
|
|
rename/reidentify; selected allocation deletion yields unresolved operation intent;
|
|
two interfaces containing the same item retain distinct protocol identities;
|
|
inherited provider/required interfaces resolve the actual allocation; reverse
|
|
unoriented channel uses actual caller/receiver; wrong provider/mechanism/protocol
|
|
rejects unchanged; changing Call operation invalidates incompatible Reply values
|
|
without data loss; explicit SA→OA omission versus missing decision; both semantic
|
|
review directions with mapped/NotApplicable slots; full source/API/history/recovery
|
|
round trips. Include the original parameterless fixture so parameter-value
|
|
inference cannot accidentally substitute for operation identity.
|