12 KiB
Independent chain-assembly expressivity review
Conclusion and scope
The gap is real: Studio cannot currently author a connection to one particular interior contribution of one particular reused process/chain. This loses a useful OA/SA modeling relationship, rather than merely using different Capella class names. It should block a claim of the requested fully feature-complete OA/SA release until implemented or explicitly accepted as a narrower product scope by the user. It does not invalidate the already demonstrated procedural-reference workflows, and it does not justify implementing Logical/Physical Architecture or the entire Capella metamodel.
This is a read-only conceptual/code review, independent of the initial scope reviewer. No production edits, Cargo, native input or new runtime probe occurred. Existing source and regression tests establish current admission limits; the counterexamples below are reasoning about representable facts, not claims of a new executable behavior engine.
Primary evidence
The official Creation of Functional Chains and Physical Paths — Assembling Functional Chains describes connecting involved functions inside referenced chains, including functions that are not at a chain boundary. It also describes connecting two involvements of the same function, nested composite chains, and promoting interior link anchors to a folded chain's visible container. Its sequencing section distinguishes contextual precedence from functional-exchange involvement. This supports contextual endpoint addressing and distinct connection purposes. It does not establish that common-function assembly merges two executions into one; no such runtime meaning should be invented from the class name or illustration.
Current Studio evidence:
docs/language.rst:497–534: distinct occurrence identity, reference reuse, same-owner direct SequenceLinks, explicit topology, and separate recursive-reference rejection.model.rs:528:SequenceReferencecontains only a sequence identity;SequenceLinkendpoints are plain occurrence IDs. No caller/reference-instance path accompanies an interior endpoint.validation.rs:2321–2369: endpoints must have the same immediate Process/FunctionalChain parent; work/exchange adjacency also checks semantic endpoint correspondence.tests/release_topology.rs:240: direct foreign-owner links are intentionally refused by bothcan_connectand command execution.behavior/topology.rs:238–252andtopology/direct.rs:383: the graphical contribution and connection predicates share those restrictions. Expanded reuse content is a source preview (topology.rs:858–886,1140–1185), not a caller-specific endpoint surface.harness/release-process-editing.md:185–188already records cross-reference inner-chain linking as unresolved. The recent behavior-transition navigation fix does not alter this boundary.
Counterexample and attempted substitutes
Shared procedure P contains distinct contributions a → b → c, involving work A, B and C. Outer process O owns two references r1 → P and r2 → P, plus contribution x involving X. In O only, X supplies a condition/input required by b inside r1; a inside r1 need not wait for X. r2 remains unaffected. P must remain independently reusable, and later edits to P must be reflected through both references.
The required target is the address (O, [r1], b). (O, [r2], b) is a different contextual contribution even though it has the same leaf occurrence and engineering-work identity. The stronger nested case (O, [r1, nested_reference], b) prevents a one-level workaround from silently becoming the contract.
| Attempt with current semantics | Why it does not preserve the stated facts |
|---|---|
| Link x directly to b | Rejected by the same-parent invariant. Merely relaxing that check leaves r1 versus r2 unspecified. |
| Link x to r1 | Identifies the reference as a whole, not its interior b. It cannot assert the desired interior connection. Treating it as a call-entry predecessor also introduces an unwanted dependency on a; the current specification does not define a special selective-entry interpretation that would fix this. |
| Add outer contribution b2 involving B and link x to b2 | b2 is a new occurrence. Shared work identity does not make it the b occurrence inside r1, and there is no typed binding identifying them. |
| Put x/the connection in P | Changes the shared procedure for every reuse and its standalone use, contradicting caller-local scope. |
| Split P into smaller procedures or flatten it into O | Can approximate selected traces, but changes the reusable procedure boundary or duplicates contextual topology. Subsequent shared edits do not preserve the originally requested single P contract. This is a model redesign, not an equivalent encoding of the same model. |
| Add a Constraint with prose naming r1 and b | Retains explanatory text, but supplies neither a resolved endpoint nor graph validation, projection or typed transition/review behavior. It cannot satisfy graphical/source/API parity for the relationship. |
A second counterexample uses r1(P) and s1(Q), each with an interior contribution involving B. An explicit common-work connection must identify those two contextual contributions without fabricating a B→B global exchange. Two ordinary work occurrences sharing B do not express that assembly relation by themselves. Keep the precise common-work meaning at the modeling level until a stronger formal interpretation is justified.
Smallest coherent approach
Prefer one shared contextual occurrence address: an ordered vector of SequenceReference occurrence IDs, followed by the leaf occurrence ID, resolved from an explicit owning Process/FunctionalChain. The empty vector means a direct contribution. Address by reference occurrence, never by referenced chain ID or work ID: both can repeat.
Keep existing direct SequenceLink records and their old serialized meaning unchanged. Add a typed caller-owned contextual link record for the missing relationships, with source/target addresses and an explicit purpose: precedence with guard, assembly through an existing same-layer Interaction/FunctionalExchange, or assembly through existing common work. The exact public names are a design decision; a closed purpose enum avoids conflating a shared-work connection with an invented global exchange or silently treating every assembly edge as execution precedence. Both old and new records should resolve into one internal topology-edge representation used by validation, authoring and rendering. A disconnected new renderer or an unvalidated JSON field is not a solution.
For exchange assembly, resolve both leaves to Action contributions and require their referenced work to equal the exchange's source/target. For common-work assembly, both resolved Action contributions must reference exactly the same existing work. A precedence link may use the applicable work/control/exchange contribution kinds; its graph rules must remain distinct from assembly incidence. Do not automatically identify or collapse separate occurrences merely because they share work.
This adds a real persistent contract. New fields/variants must have explicit source/interchange admission and migration policy; do not loosen frozen source 1–6 admission to smuggle them into old versions. An initial implementation can avoid replacing the existing direct-link DTO while sharing its resolved topology machinery.
Occurrence-context invariants
- The owner exists and is the correct same-layer Process/FunctionalChain kind. Every path component is a direct SequenceReference of the current resolved owner; its referenced sequence becomes the next owner. The leaf belongs directly to the final owner and is a valid contribution. Missing, mistyped, wrong-layer, skipped and stale components reject with a located endpoint error.
- Full addresses determine identity and equality. Repeating the same referenced chain or work elsewhere never aliases its occurrences. A self-link check compares full addresses, not just leaf IDs.
- Reuse remains acyclic. Contextual precedence may still express the permitted topology loops; those are a different graph. Define one bounded, shared contextual graph/index so nested repeated reuse cannot cause uncontrolled expansion in every render or edge check.
- Caller-owned links cannot mutate source-chain containment or its links. Changing a reference target must invalidate incompatible paths rather than redirect them by name or leaf UUID coincidence. Deletion/reidentification must visit every path component and leaf; deleting a component must never turn a path into an empty/direct path.
- Fold/expand changes anchors only. The complete authored address remains in the link, source and inspector. Each expanded reference exposes the contribution in its caller context; Open source remains a distinct guarded action.
Alternatives and tradeoffs
An exposed-gate/port model can be coherent if gates map explicitly to these same interior addresses. It is useful for deliberate public procedure interfaces, but requires extra declarations and caller bindings and cannot fully cover arbitrary interior assembly without eventual contextual resolution. It is a larger initial authoring burden for this task.
Changing SequenceLink directly to addressed endpoints may reduce the number of record variants, but touches every source/target-ID consumer and requires a careful distinction between precedence and common-work/exchange assembly. A compatibility adapter plus the new contextual record is the smaller initial migration surface. Either representation must use the same semantic resolver; choosing the record spelling cannot avoid that work.
Flattening with generated provenance still duplicates presentation instances or semantic records and needs synchronization/conflict rules. A global cross-owner link without a caller path is smaller code but wrong for repeated reuse. Neither is recommended.
Bounded acceptance and integration consequences
| Gate | Required observable result |
|---|---|
| Core falsifier | Both OA and SA represent the r1-only interior connection; r2 and the complete shared P record remain unchanged. Repeat with two nesting levels and repeated work occurrences inside P. |
| Common-work case | Typed link identifies the exact two contextual B contributions; different-work candidates reject; no new global work/exchange is created and no unsupported execution-merge claim is made. |
| Lifecycle | Reidentify each path component/leaf, retarget a reference, delete an interior contribution and restore Undo. Invalid paths reject atomically; no widening, name lookup or accidental r2 redirection. |
| Graphical authoring | Expand r1, draw/select/reconnect its interior connection, compare r2, fold and re-expand with exact address retained. Picker/inspector show the caller/reference breadcrumb. Pending forms and stale gestures remain guarded. |
| Text and REST | Canonical source/JSON round-trip and schema admission; shared command/source updater; real HTTP creation/update/invalid-address rejection with unchanged revision and source on refusal. Publish OpenAPI examples for both empty and nested paths and enforce declared address/expansion limits. |
| OA↔SA/review | Required/Subtree closure includes the owner, each reference occurrence, referenced chain, leaf and selected assembly fact. Map every typed path component; preserve raw drafts and exact addressed facet review, source mutation and single Undo/Redo. Never map by display name or assume one occurrence per work. |
| Coverage/presentation | Audit core reachability, source references/deletion, transitions, semantic exact slots, topology scene/context handling and selected behavior navigation against the shared resolver. Coverage must not confuse one caller's contextual edge with a global modification to P. |
The implementation should be accepted on these finite engineering cases rather than an open-ended “all Capella” checklist. Ordinary direct links and existing procedure reuse must retain their current behavior. A new schema, an endpoint picker alone, or successful compilation alone would not close this release gap.