Declaring links
Load this card when a task makes a displayed value or an editor field open a
request: Link (ordinary or stacked), LinkOnly, a link target's
construction and its reply, including a link return into the editor. Its
rules are generated from the link contract,
which owns them. The target's own presentation follows the
actions card; a link return that writes a calculation's
destination or a constrained-choice source also follows the
calculation card.
Order of work
- Place each link and name its one target (
links.declaration,links.placement,links.direct-declaration,links.rows-only-declared). - Map the target's construction from the invoking node
(
links.direction.*,links.typed-selector-binding,links.editor.*). - Design the target's reply: an ordinary reply, a link return
(
links.return.*) or a stacked page (links.stacked.*). - Keep a row-only request off the Actions surface with
LinkOnly(links.link-only.*), then compile with Studio and fix every reported rule.
Rules to apply
Studio does not check these rules. Each is quoted from its owner.
Protocol roles
Owner: protocols.md#protocol-roles
links.editor.source-order: A link declared in a request editor probes the bound current editor first, then the reference objects in the interaction context's order. Within each source, it tries the declared paths in order, using the shared copy availability and valid-null rules. A valid editor match wins. Parent-relative paths retain the host field's editor ancestry, and a return applies to the bound current-request destination. These logical contexts need not use separate stored objects: a received EditRequest may be both the current request and its reference object. PrevRequest construction retains its current-editor source under the path context rules.
Links
Owner: protocols.md#links
-
links.direct-declaration: As for inline choices, each declaration is read directly, not through effective-attribute resolution: a link on either the field or its type enables it, a field cannot switch off its type's link, and when both declare a link the field's complete declaration, including its stacking option, applies to that occurrence. -
links.rows-only-declared: Rows carry actions only through declared links. No row action is inferred from CopyFrom: a Request whose CopyFrom resolves to a displayed value is not thereby offered on that value's row. Each occurrence has one target, so a row never opens a request chooser. The Actions surface keeps offering the displayed value's filtered NextRequest set, link targets included, except link-only requests and, when the page supplies an explicitActionsarray, requests outside it (named action controls). -
links.direction.displayed: On a displayed Response or SubscriptionData occurrence, including inline endpoint-list data, the tapped node is the invoking node. The client constructs the target through ordinary CopyFrom with the displayed value's interaction reference context. -
links.direction.editor: In a request editor, the host field is the invoking node. The client constructs the target from the current editor first, then the editor's reference objects in context order. The draft is read, never transmitted; only the constructed target is sent, and it must be complete and valid even while the editor is not. A transient request composer is a request editor for this purpose: a link on one of its fields is offered; an ordinary link's plain reply is a return into the composer, and its transitioning reply replaces the composer. A stacked link follows the display-only rules below. -
links.typed-selector-binding: Typed selectors in the target's CopyFrom bind the nearest self-or-ancestor occurrence of the named type on the invoking node's path, at any depth and across nested Lists, under the bound-occurrence rule. From Actions, a menu, automatic singleton selection, or any other entry with no tap, the selector is unbound and the next alternative applies. The binding belongs to that one construction and its reply; it is never stored or transmitted, and a later construction does not inherit it. -
links.ordinary-reply.plain:Reply Composer above a Response or SubscriptionData screen Composer above a stable editor Plain Response: neither NextRequest nor WorkflowRequest The non-transition acknowledgement: the composer closes, the screen stays, and the Response becomes its reference context's acknowledgement object. Its CopyTo is not applied. A return into the originating editor.
Link return
Owner: protocols.md#link-return
links.return.apply: Link return. A plain Response replying to an ordinary link sent from a stable editor assigns its CopyTo fields into that editor exactly as a calculation result does: the complete mapping is checked first and applied atomically; destinations resolve against the host field's bound occurrence, retained for the transaction; an unavailable destination rejects the whole update; and assigned values are input changes that trigger dependent calculations. The editor stays, awaits its own submission, and its reference context is not replaced; nothing navigates. A return that cannot be applied is a refused reply: the composer stays, and the editor and its draft are unchanged.
Links
Owner: protocols.md#links
links.stacked.declaration: Stacked links. This is an upcoming capability. Author@LibertasLink("ViewRecord", stacked=true)in TypeScript or#[libertas_link("ViewRecord", stacked = true)]in Rust to display the reply on a read-only page above the sending surface. Availability requires stacked-link support in Studio and the client. The optionalstackedargument is Boolean and defaults tofalse. Omission or explicitfalseemits exactly{"Name":"Link","Value":"ViewRecord"}, preserving existing ordinary-link behavior and schema digests.trueemits exactly{"Name":"Link","Value":"ViewRecord,stacked=true"}; this is one target with an option, not a target list. A client that cannot resolve this form must display the row without a link. The option is valid wherever Link is valid.links.stacked.not-offered: Stacked targets are never offered through Actions, menus, landings, or automatic selection. A tap uses the ordinary construction, validation and RequestConfirmation rules and, with no editable input, sends at once after any required confirmation.links.stacked.close: Every stacked page has a platform-owned Close at the leading edge of its heading. It is not an App Request and sends nothing. Neither schema nor Display Template rules may suppress or remove it; the template'sbutton_labels.closemay change only its caption, with the ordinary platform-caption fallback.links.stacked.subscription-target: A SubscriptionRequest target starts demand through the normal client subscription lifecycle on opening. Its completing Response is the first state of that page's own SubscriptionData snapshot; compatible reports and reduced reports update that snapshot under the existing rules. Updates never navigate or overwrite the underlying editor or another stacked page. Closing or releasing the page ends its demand through the existing subscription release lifecycle, including that lifecycle's Hub-version and endpoint-wide release scope. Subscription ownership and demand remain runtime-only, are never persisted, and are rebuilt only through normal subscription lifecycle.links.stacked.navigate-away: Installing a non-stacked destination from any stacked page navigates away: an ordinary Link or selected NextRequest action that opens a non-stacked working editor or page releases every stacked page and their underlying editor or displayed working value. A transitioning Response, WorkflowRequest or EditRequest reply does the same. Place the destination by the pane rules. A transient input chooser or composer alone is not a working destination; cancellation before navigation, including RequestConfirmation cancellation, keeps the current surface under the existing rules. The initial stacked reply's NextRequest supplies page actions and does not itself navigate away; live reports likewise do not navigate. Ordinary acknowledgements retain their existing non-transition behavior. Departing drafts follow the existing draft lifetime and editor-state rules: a transitioning link reply retains an unsubmitted CacheIdentity draft for recovery; without CacheIdentity unfinished edits are discarded. No new prompt is added.links.offered-when-eligible: On a Response or SubscriptionData, a link row is offered only while its target is in the displayed value's filtered action set. In a request editor the link is offered only when the target passes the same access and ExcludeUI filtering; an editor has no ConstrainedBy set. Visible regular-response content, including link rows, remains displayed even when only one action is eligible; the person opens that action through Actions.links.migrate-copyfrom-rows: Existing Apps that relied on rows tappable through CopyFrom alone must declare links.
Link only
Owner: protocols.md#link-only
links.link-only.declaration: Link-only requests.@LibertasLinkOnlyand#[libertas_link_only]take no argument and emit{"Name":"LinkOnly","Value":"true"}on a protocol Request variant, or a SubscriptionRequest variant targeted by a stacked link.links.link-only.offered-only-by-links: A link-only target is offered only through declared links. Every Response or SubscriptionData that lists it in NextRequest still governs its availability through the filtered action set, so a link row targeting it is offered only while it is in that set. The Actions surface, menus, endpoint landings, and automatic singleton selection never offer it, and it is not counted when deciding whether a constrained set has exactly one action. A filtered set holding only link-only requests leaves the Actions surface with no actions while its link rows remain. Use it for a request that exists only to be opened from rows, such as a fieldless request a row tap sends at once, so that it does not also appear as a second Actions entry. A link-only target cannot be DefaultRequest, and it cannot carry ExcludeUI, which would also hide its links.links.link-only.older-clients: Clients that predate the marker ignore it and keep offering the request in Actions; the request remains valid there, so adding the marker changes presentation only.
Rules Studio enforces
Studio rejects a source that breaks one of these rules. The owner holds the full rule.
Links
Owner: protocols.md#links
links.declaration: A Link targets exactly one Request variant native name of the same protocol; a stacked link may target a SubscriptionRequestlinks.placement: A Link goes on any field or on a Table, Union or Enumeration type declaration
Link return
Owner: protocols.md#link-return
links.return.no-transition-copyto: A link target NextResponse names no Response declaring CopyTo with NextRequest or WorkflowRequest
Links
Owner: protocols.md#links
links.stacked.target-constructible: A stacked target is LinkOnly and fully constructible with no editable inputlinks.stacked.display-only-reply: A stacked target declares NextResponse whose non-Error replies are plain display Responseslinks.studio.next-request-lists-target: On a displayed occurrence the containing variant NextRequest lists the link targetlinks.studio.return-copyto: An editor link target plain-reply CopyTo is valid against the originating request; every direct field has CopyTo or is Hiddenlinks.studio.displayable-host: A link host is visible and its variant is displayable as rows, never an Error or WorkflowRequest Response
Link only
Owner: protocols.md#link-only
links.link-only.studio: LinkOnly needs a link targeting it and excludes DefaultRequest and ExcludeUI