Skip to content

ADR-0002: Core–extension interaction contract

Status: Accepted — 2026-09-20. The project owner approved this interaction contract, including the terminal and help refinements of ADR-0001. Acceptance concerns the architecture and the recorded reference checks, not a completed product implementation.

Clarification — 2026-09-22: cancellation now has explicit stable causes, including owner shutdown. The first accepted cause is retained; the invocation deadline remains effective through final commitment, and cancellation never restarts finalization. These rules apply to both process mappings. Historical check results below are unchanged; the additional cases are not claimed as tested.

Refinement — 2026-09-24: shared CLI/MCP and agent-use additions are accepted under ADR-0013 and ADR-0014–0016. The original acceptance date and historical evidence remain unchanged; the new requirements are not claimed implemented or verified.

ADR-0001 establishes a common executor boundary with separate processes as the baseline. The core and extension now need an agreed meaning for a command invocation, its outcome and access to core services. That meaning must survive a change of language, IPC protocol or executor.

The contract must distinguish a command’s failure from a failed invocation, supported operations from granted permissions, and receiving a result from finishing execution. A normal CLI must remain usable without a core-service protocol.

  • One command and authorization identity, independent of aliases and the selected executor.
  • Predictable arguments, terminal behavior, completion and cancellation.
  • Useful help without execution, with an explicitly authorized contextual supplement from the extension.
  • One terminal owner, with ready-made dialogs and managed interfaces available to extensions.
  • Scoped core services with the same checks under local policy and broker integration.
  • Shared logging and owner-supplied integrations without tying extensions to the core’s implementation language.
  • Independent releases through explicit version and feature compatibility.
  • A small common contract that leaves transport, runtime management and isolation to separate decisions.
OptionBenefitsLimits
Only executable, arguments and exit codeSimple integration with existing programsInsufficient for typed invocations, scoped core services and compatible extensions of the interaction
A separate contract per language or executorEach integration can follow its native conventionsDifferent authorization and completion behavior; repeated compatibility work
One semantic contract with executor-specific mappingsShared meaning and checks; supports ordinary processes and future executorsRequires precise mappings and conformance checks for each supported mode

Define one semantic contract owned by the core, with explicit mappings for each executor. The names below describe logical data and behavior, not final API signatures, KCL fields or wire messages.

Terminal refinement of ADR-0001: this decision keeps the executor abstraction and process baseline, but narrows terminal compatibility. Extensions receive data channels and core interaction facilities, never direct ownership of the user’s terminal. Existing programs that require their own TTY or screen-control output need adaptation. ADR-0001’s historical terminal checks do not validate this new presentation model; its recorded measurements and experiments remain unchanged.

Help refinement of ADR-0001: base help, catalogs and static completion remain available without running extension code or preparing its runtime. This decision adds an optional, separately authorized describe invocation for contextual help. That invocation can start extension code; it is an explicit exception to the earlier help rule, not evidence that all help is execution-free. These refinements now govern terminal ownership and help; the execution boundary from ADR-0001 remains in force.

Shared command contract and machine results — 2026-09-24

Section titled “Shared command contract and machine results — 2026-09-24”

ADR-0013 adds a trusted MCP presentation beside CLI. Both select the same canonical command and enter the same semantic validator, compatibility checks, current admission and lifecycle. CLI performs lexical argument parsing first; MCP supplies typed values by parameter ID without shell evaluation or a second argv representation. Task identity and actor context are core-owned under ADR-0014; input data, aliases and client claims cannot change them.

The selected command-result/1 feature adds a bounded, declared text or structured commandResult to managed execution completion, under ADR-0013’s schema and limits. It is required on success when the feature is selected, and is never inferred from stdout, logs or help. It does not replace the typed outcome, process evidence or finalization. Old mappings retain their behavior; required machine support fails before startup if absent, and live confirmation must agree before dispatch.

A host-owned invocation context additionally pins the caller interface, selected interaction availability, task authority reference, enforcement profile and any admitted effect binding. Only the minimum required projection reaches the extension. Transport request IDs do not supply task authority, replay protection or human approval. ADR-0015 determines actual confinement guarantees; ADR-0016 governs protected effect commitment and reconciliation. These checks also apply to ordinary CLI calls when the selected owner profile requires them.

MCP execution is noninteractive in its baseline. Required dialogs/views prevent exposure; optional requests receive their normal unavailable result. The core does not consume the protocol stream as terminal input, invent confirmation, or restart a command to obtain another answer. The two core-owned terminal interaction paths remain available to CLI. A continuation-capable machine adapter requires a separately declared mapping preserving one invocation and effect identity.

The core resolves the public command path against a verified static command tree and pins the extension identity, release and stable command ID for the invocation. Each invocation has one immutable kind: execute runs the command, while describe requests contextual help about it. One invocation dispatches its selected kind at most once and never switches kinds. Background services and extension-to-extension calls are outside this contract.

Names select a command; they do not change its behavior. An alias points directly to a command ID in the same declaration, not to another alias or command-line text. It cannot inject arguments or change defaults, input mode, entry point or permissions. Different behavior requires a separately declared command. The core validates the effective tree, including built-in commands, before activating a configuration: ambiguous paths and collisions between names or aliases reject that configuration, rather than selecting by installation order or the user’s permissions. Authorization and audit use the canonical identity; the spelling used by the caller is only diagnostic context and cannot select another extension handler. Renaming or updating the catalog cannot reroute an invocation already being prepared.

PartMeaning and owner
Invocation IDGenerated by the core for one attempt and used to correlate its events. It is not a permission, a resume token or proof that an external action ran only once.
Invocation kindCore-selected execute or describe, fixed before authorization. Each has its own inputs and required access; neither grants permission for the other.
Command referenceExtension identity, exact selected release and stable command ID, fixed by the core. Display names and mutable release tags are not execution identities.
InputsFor execute, one of the two modes below, fixed by the command declaration and approved mapping. For describe, the help request defined in the next section, without mandatory execution arguments.
ContextCore-selected working directory, permitted environment, locale, deadline, stream routing and permitted interaction facilities. Authoritative subject, release and access scope remain in the core; only necessary information is exposed to the extension.
Contract selectionAgreed interaction version, supported features and declared core-service requirements. Support and authorization are separate checks.

The data model shows the logical parts of one invocation, not language-level API types.

Logical invocation structure
Logical invocation structureOne invocation fixes execute or describe, a command reference, the inputs for that kind, trusted context and contract selection. These are logical parts, not implemented API types.InvocationinvocationIdkindCommandReferenceextensionIdentitypinnedReleasecommandIdInputscommandInputs OR helpRequestContextexecutionSettingscoreOwnedIdentityaccessScopeContractSelectionversionfeaturesserviceRequirementscommandinputscontextcontractLogical invocation structureOne invocation fixes execute or describe, a command reference, the inputs for that kind, trusted context and contract selection. These are logical parts, not implemented API types.InvocationinvocationIdkindCommandReferenceextensionIdentitypinnedReleasecommandIdInputscommandInputs OR helpRequestContextexecutionSettingscoreOwnedIdentityaccessScopeContractSelectionversionfeaturesserviceRequirementscommandinputscontextcontract

Use declared inputs for executing new extensions. Argument passthrough is an explicit compatibility option for existing programs. Every executable command declares exactly one execution input mode; omission, an alias or a user flag cannot select a mode or enable automatic fallback. This keeps detailed input validation and authorization in the core without forcing an existing program to implement the service protocol. The following parsing and runtime-preparation rules concern execute; describe has the separate path below.

Input modeBinding rule
Declared inputsThe core parses flags and positional arguments once against the static declaration. The selected grammar defines types, required parameters, defaults, repetition and --. Unknown parameters, invalid values and undeclared repetition are rejected. Authorization and dispatch use the same typed record keyed by stable parameter IDs, with effective values and the IDs explicitly supplied. Ordered lists and the distinction between an omitted default and an explicitly supplied equal value are preserved. No raw argument alternative is sent for the extension to re-parse.
Argument passthroughThe core fixes the approved entry point, command prefix and boundary where command arguments begin. It passes that remaining list as data, without consuming more core options, expanding variables or evaluating a shell command. The target program parses its own arguments. If it can interpret them as unrestricted subcommands, permission covers the whole program, as required by ADR-0001; a fixed prefix alone does not establish command-level permission.

Preserve the values received by the core. The contract starts with the argument list received by Rukh, after any caller-shell processing; it does not reconstruct the user’s original command-line spelling. In passthrough mode, each argument retains its position and exact supported contents, including empty strings, spaces, quotes and backslashes. No trimming, replacement characters, Unicode normalization or silent truncation is permitted. In declared mode, only conversions specified by the parameter schema are applied, and the executor preserves the resulting types and values.

Each executor mapping declares its supported value representations and limits. Platform encoding is allowed only if the selected target receives the same arguments or typed values. In particular, Windows process creation takes a command-line string: encoding it must match the target’s argument parser, rather than assuming one universal quoting rule. The Microsoft C runtime rules illustrate one such convention. If the approved mapping cannot guarantee preservation, it rejects the invocation. It cannot silently switch to a shell, another input mode or a different target.

Check first, prepare the runtime second, launch last. Using static declarations and trusted executor/provider capabilities, the core checks the chosen inputs, known representation limits, required contract/features and mandatory pre-launch access before requesting runtime preparation. It fixes the command binding, parsed inputs or argument list, and selected mapping for this invocation. A missing installed runtime is not itself an unsupported requirement if the approved RuntimeProvider can supply a matching one. Base help and static completion stop at the declaration and never prepare a runtime or launch extension code.

After preparation, the executor checks the concrete launch settings, including encoding and total size limits, before starting any extension code. The core also ensures mandatory pre-launch authorization remains valid under the selected policy. Preparation cannot change the command binding or weaken requirements. An unrepresentable input, unmet mandatory access or unsupported requirement discovered by these checks yields rejected, with a reason and the affected parameter or requirement, without echoing secret values. This may follow runtime preparation; it is not a promise that no runtime was downloaded. A preparation or OS launch failure instead follows execution_failed; a contradictory live IPC session also follows execution_failed, without command dispatch. Cancellation follows its own rules below. The runtime provider cannot be replaced by an installer supplied in invocation data.

Domain checks requiring extension code and authorization for resources discovered during execution remain later checks. Pre-launch success does not promise their success or grant future service calls. Secrets and the full login context are not implicit environment or invocation fields.

This flow shows static input preparation for execute, before runtime preparation. Concrete launch settings are checked again as described above; live compatibility confirmation follows the version rules below.

Prepare inputs once before runtime preparation
Prepare inputs once before runtime preparationNames and aliases select one canonical command. The declared mode produces typed data or an unchanged argument list. Failed static checks reject the invocation, without a fallback mode or runtime preparation.Name or alias resolves to onecommand and releaseDeclared input mode?Parse once using theparameter schemaTyped values + explicitlysupplied parameter IDsFixed entry point + prefix +argument listWhole-program permissionfor unrestricted argumentsInputs, required support andpre-launch access valid?rejected: no extension codeor runtime preparationFix invocation data, thenprepare runtime if neededDeclaredPassthroughNoYesPrepare inputs once before runtime preparationNames and aliases select one canonical command. The declared mode produces typed data or an unchanged argument list. Failed static checks reject the invocation, without a fallback mode or runtime preparation.Name or alias resolves to onecommand and releaseDeclared input mode?Parse once using theparameter schemaTyped values + explicitlysupplied parameter IDsFixed entry point + prefix +argument listWhole-program permissionfor unrestricted argumentsInputs, required support andpre-launch access valid?rejected: no extension codeor runtime preparationFix invocation data, thenprepare runtime if neededDeclaredPassthroughNoYes

The core builds base help from data; the extension may supply contextual explanations through DescribeCommand. Base help contains purpose, syntax, declared parameters and visible subcommands from the pinned descriptor. It is generated for the current language and visibility policy, not stored as a fixed terminal transcript. Visibility, permission to run describe and permission to execute are separate decisions; none implicitly grants another. When visibility cannot be established, the core must not expose protected command metadata or try another authorization mode.

The core recognizes help before validating required execution arguments. In declared-input mode, --help is reserved in the flag namespace; a parameter name or flag alias that conflicts with it rejects the declaration before activation. It is a help flag only where the grammar permits a flag, never as another parameter’s value or after --. For opaque passthrough, the exact form <command-path> --help is reserved for core help; the core does not search for help flags inside an argument list already being passed through. The explicit boundary in <command-path> -- --help makes the final token command data. A core-owned help <command-path> route provides unambiguous access in either mode and is reserved when validating the command tree. Help resolves the same canonical command as its aliases; it does not run that command with an extra argument or require its missing execution parameters. The initial describe request takes no partial execution-argument record.

Contextual help is optional and must be declared for the command, supported by its executor mapping, enabled by the CLI owner and authorized for this exact release and describe purpose. Shared startup requirements and the requirements of describe must be met; execution-only requirements do not apply. The matching release, runtime and dependency environment must already be installed, verified and ready. Help does not install, repair, update or activate a release, prepare dependencies, refresh credentials or ask for login. It does not open extension dialogs. Missing prerequisites leave base help available. Browsing a catalog, completing a command or displaying parent help never starts child handlers to collect their descriptions. Dynamic completion remains outside this decision.

DescribeCommand is a logical core-to-extension request, not a chosen IPC method name or an extension call to a core service. It has its own invocation ID, immutable kind=describe and short owner-defined deadline. The initial contract does not reuse an executing command’s session for help. Startup, live compatibility confirmation, bounded delivery, cancellation and finalization follow the same executor rules; a session must confirm support before the description request is dispatched.

PartContract
RequestPinned command reference, selected contract and help feature, requested language and the minimum core-approved context, such as a permitted working-directory reference. No raw execution arguments, pipeline input, general credentials or full environment are added for convenience.
ResponseOne bounded structured supplement: actual content language, explanatory sections and examples, with any command or parameter references expressed as known stable IDs. Text is untrusted content rendered by the core; examples are displayed, never evaluated. Unsupported references, oversized or malformed content invalidate the supplement.
AuthorityThe response cannot add commands or parameters, change types, defaults, aliases, permissions, requirements or handlers. References must stay within the visible declared scope; explanations do not change the executable schema.
DeliveryThe response is a result payload bound to the describe invocation and its completion report, not stdout, stderr or a droppable log event. The core validates it and finalizes the invocation before using it. No contextual response is cached across help requests in the initial contract.

Only explicitly allowed read operations may be called through core services during describe, with the same action/resource checks and a scope restricted to this purpose. Core-mediated writes, command dispatch, installation and interactive UI are unavailable. Diagnostic logging and mandatory core audit keep their existing rules. The handler must not perform unrelated actions or disclose secrets. These are contract and service-boundary requirements: starting a native process, including its startup code, does not make it read-only or sandboxed. An owner who cannot trust the handler can leave contextual help disabled and retain base help.

The core closes description stdin and captures any stdout/stderr within bounded diagnostic handling; neither becomes help content or command output. A process-only integration has base help only. It cannot obtain an enrichment by running the wrapped program with --help or parsing its output; that needs an explicitly supported structured mapping. If the requested translation is unavailable, the core uses the declared language fallback and identifies the language actually shown. Rendering uses the same core-owned output rules for terminals and redirected text.

An available base does not turn a failed description into success. If enrichment is not attempted because it is disabled, undeclared or ineligible, the core can successfully show base help and briefly state when a declared supplement was not loaded. Once a describe invocation is created, its result is recorded independently: a successful handler returns valid content and completes cleanup; a handler-reported application error uses command_failed, while protocol, launch or mandatory-finalization failure uses execution_failed. A pre-launch rejection uses rejected; an invocation deadline uses cancelled with deadline_exceeded. On failure or deadline the core may still show base help with a diagnostic, but preserves the failed/cancelled outcome and a non-success CLI exit status. A user_request or owner_shutdown cancellation stops the entire help request and does not start fallback rendering. There is no automatic retry, ordinary-command fallback or fallback that bypasses mandatory audit or cleanup.

This sequence shows one help request and an optional, separate description invocation. Startup or session failure also follows the failure path without dispatching DescribeCommand.

Base help with an optional contextual response
Base help with an optional contextual responseThe core builds visible base help without extension code. An eligible separate describe invocation uses an existing environment and a confirmed session. Only a valid finalized response enriches help. Failures retain their outcome, while user or owner cancellation stops the request.ExtensionRukh coreUserExtensionRukh coreUserNo description dispatchalt[Startup and session succeed][Startup or session fails]alt[Valid result and successful finalization][Failure or deadline][User or owner cancellation]alt[Contextual help is ineligible][Declared, authorized and environment ready]Request command helpResolve identity and build visible base helpBase helpStart describe invocation and confirm sessionDescribeCommand with scoped contextStructured result and completionValidate result and finalize within deadlineBase help and contextual explanationsBase help, diagnostic and non-success statusStop with cancelled outcomeBase help with an optional contextual responseThe core builds visible base help without extension code. An eligible separate describe invocation uses an existing environment and a confirmed session. Only a valid finalized response enriches help. Failures retain their outcome, while user or owner cancellation stops the request.ExtensionRukh coreUserExtensionRukh coreUserNo description dispatchalt[Startup and session succeed][Startup or session fails]alt[Valid result and successful finalization][Failure or deadline][User or owner cancellation]alt[Contextual help is ineligible][Declared, authorized and environment ready]Request command helpResolve identity and build visible base helpBase helpStart describe invocation and confirm sessionDescribeCommand with scoped contextStructured result and completionValidate result and finalize within deadlineBase help and contextual explanationsBase help, diagnostic and non-success statusStop with cancelled outcome

Data streams and interactive presentation are separate concerns. The core decides how each destination is handled; stdout may be a pipe while stderr is a terminal.

PathRequired behavior
Redirected dataOrdered binary command streams, with stdout and stderr kept separate. Pipes and files preserve bytes without terminal-control filtering or reinterpretation as UI messages. EOF closes input without cancelling the command. Buffering and waiting are bounded.
Terminal presentationOnly the core reads interactive input, formats text, moves the cursor and manages terminal state. Captured extension output is presented as data under core policy, with control characters escaped or represented safely. Arbitrary binary output requires redirection or an explicitly supported presentation.
Interaction controlDeclared core operations and input events implement dialogs and managed views. They remain separate from command data, even if one control transport carries invocation, service and UI messages.

Executors capture extension data streams and do not hand it the user’s terminal handles, an interactive console attachment or a raw terminal surface through the Rukh contract. A process-only program can still consume data and produce stdout/stderr, but cannot implement its own prompt or screen interface by writing escape sequences or reading the keyboard. Programs requiring that behavior must be adapted or rejected as unsupported; there is no raw-terminal fallback. These are executor wiring and contract requirements, not an OS sandbox: a native process may attempt direct device access using its OS authority. Preventing such bypass requires separate confinement.

Extension stdin comes from an explicit command-data source. The core never forwards keyboard input to satisfy an undeclared prompt. With no data source, the executor closes stdin rather than waiting for a question hidden in ordinary output.

A closed output consumer is reported through the selected binding; it is neither silently discarded nor interpreted as success. Core terminal formatting applies only to terminal destinations and must not corrupt redirected output. Command data and diagnostics are never parsed as UI declarations, completion messages, structured log records or privilege requests. The original contract introduced no universal structured command-result payload. The accepted 2026-09-24 refinement adds the explicit command-result/1 feature under ADR-0013; without that selected feature the old contract remains unchanged.

The core coordinates captured text, logs, progress displays and active dialogs. It never mixes presentation into command stdout intended for a pipeline. Redirected input is command data, not a source of implicit answers; interactive work alongside a pipeline needs a separately approved core interaction channel. Noninteractive policy forbids opening a hidden terminal or automatically answering questions.

This data-flow view separates transparent data transfer from core-owned terminal presentation. The core handles each output destination independently.

The core owns terminal presentation
The core owns terminal presentationThe core preserves redirected command streams and presents terminal output itself. Extensions interact through data and control channels without direct terminal access.Command input dataExtensionCore routes each destinationPipe or fileCore presentationCore interaction contractUser terminalCore supplies stdinSeparate stdout and stderrRedirected bytesText as dataOperations and UI input eventsDisplay and interactive inputThe core owns terminal presentationThe core preserves redirected command streams and presents terminal output itself. Extensions interact through data and control channels without direct terminal access.Command input dataExtensionCore routes each destinationPipe or fileCore presentationCore interaction contractUser terminalCore supplies stdinSeparate stdout and stderrRedirected bytesText as dataOperations and UI input eventsDisplay and interactive input

Both interaction paths are provided by the core for execute invocations. A describe invocation returns help data and cannot open either kind of interface. An executing extension describes content and behavior using the negotiated facilities; it does not render a screen itself or load rendering code into the core.

PathExtension responsibilityCore responsibility
Ready-made dialogsRequest text, secret input, confirmation, a single choice or multiple choices; process the typed answer.Present the dialog, validate declared constraints, protect secret fields and return the answer or a defined failure.
Managed interfaceCompose supported components, submit state changes and react to input events.Own the component tree, layout, focus, keyboard handling, resizing and rendering throughout the view’s lifetime.

The common facilities must cover text and messages, forms and validation feedback, selectable lists/tables, navigation, progress and status displays, and coordinated updates. These are requirements for the core’s public interaction surface, not a claim that widgets already exist. Full-screen or custom layouts are supported only through advertised core components. Extension content is data, not executable callbacks, terminal escapes or an arbitrary rendering program. Domain validation remains extension code; it can request a new dialog or update a view without injecting a validator into the core.

A dialog request identifies its kind, prompt text, stable field/option IDs, supported validation constraints, allowed defaults, deadline and optional parent view. The core validates the request and returns values by those IDs. Secret fields have no persisted or echoed default. Prompt text and choice labels never become terminal commands. Core-controlled origin indicators identify the requesting extension; extension text cannot replace trusted approval UI.

ResultMeaning
AnswerTyped value, including false, an empty value or an empty selection when allowed. A negative confirmation is an answer, not cancellation.
DismissedThe user closes this dialog without an answer. The extension may recover if the command permits; the invocation is not automatically cancelled.
Input endedThe interaction input closes without an answer. It is distinct from dismissal and does not implicitly cancel command data processing.
Unavailable or unsupportedPolicy, environment or the selected core cannot provide the requested interaction. No implicit answer or fallback to reading command stdin is allowed.
Deadline or cancellationThe request deadline expires or the core cancels the invocation. An expired request and an accepted invocation cancellation remain distinguishable.

These describe operation results, not additional invocation outcome codes. A dialog’s dismissal, timeout or absence does not grant permission to repeat external actions. An accepted global cancellation still determines the invocation outcome according to the existing cancellation rules.

An extension creates a view from supported components, updates it through core operations and closes it when done. The core issues an invocation-bound view handle and identifies component state revisions. Input events carry the view, stable component ID, revision and order assigned by the core. They apply to the state the user saw; an invalidated action cannot be silently applied to another component or a later invocation. The concrete event encoding and push/pull delivery binding remain separate decisions.

Only the core reads the keyboard and owns focus. Dialogs and managed views share one foreground interaction owner per terminal; competing requests are rejected or queued within fixed bounds. A scoped key binding, when supported, receives only input permitted for its active view. It cannot observe other dialogs, secret-field keystrokes or override core-owned global cancellation. Window-size changes are handled by the core and exposed only through supported view events and layout facilities.

A dialog opened from a managed view identifies that parent explicitly. The core suspends the parent’s input while the dialog is active and restores focus only if the parent still exists and is permitted. Nesting is bounded; a child dialog cannot take input from another invocation or leave its parent waiting indefinitely in its own focus queue.

This sequence shows the two paths through the same terminal owner. Input events for a managed view are distinct from lossy diagnostic logs.

Two interaction paths through the core
Two interaction paths through the coreExtensions request either a ready-made dialog or a managed view. The core alone displays content and reads input, returning typed answers or view-scoped events.User terminalCore UIExtensionUser terminalCore UIExtensionloop[While view is active]alt[Ready-made dialog][Managed interface]Dialog kind, fields and deadlinePresent question and read inputAnswer or dismissTyped result or operation errorCreate view from supported componentsInvocation-bound view handleUpdate component stateRender and manage focusInputView and revision-scoped eventClose viewRelease focus and restore presentationTwo interaction paths through the coreExtensions request either a ready-made dialog or a managed view. The core alone displays content and reads input, returning typed answers or view-scoped events.User terminalCore UIExtensionUser terminalCore UIExtensionloop[While view is active]alt[Ready-made dialog][Managed interface]Dialog kind, fields and deadlinePresent question and read inputAnswer or dismissTyped result or operation errorCreate view from supported componentsInvocation-bound view handleUpdate component stateRender and manage focusInputView and revision-scoped eventClose viewRelease focus and restore presentation

Required interaction facilities are declared and checked before extension code starts, including whether policy and the environment permit interaction. A missing required facility prevents launch. Optional interaction may be unavailable at the actual request, allowing explicit noninteractive behavior or a defined error. CI or lack of an approved terminal does not silently grant permission to open one, consume pipeline input, choose a default or answer yes. A confirmation expresses user intent; it does not replace the authorization of the resulting core operation.

Waiting for a turn, waiting for input and applying view updates are bounded by the request and invocation deadlines. The core bounds view size, updates and input queues, preserving capacity for cancellation and completion. It may combine replaceable visual updates, but answers, submitted values and consequential input actions cannot be silently dropped as logs. If their delivery cannot be maintained, the affected interaction closes with a defined error. Physical key capture, reliable event transfer and terminal restoration require executor-specific checks.

Cancellation, a completion report or invocation failure closes active interactions and stops new UI requests. The core releases focus, resolves outstanding requests, discards obsolete input and restores its terminal state within bounded finalization. Late input cannot reopen a view or change the committed outcome. A failure of mandatory UI cleanup is a finalization failure; ordinary diagnostic-log loss keeps its separate rules.

Secret input is masked or hidden by the core, excluded from its logs, traces, ordinary output and persisted view state, and delivered only to the authorized requesting invocation. It is not broadcast as general keyboard or value-change events. Validation errors must not echo it. The extension must also keep received secrets out of its own output; the process baseline does not provide isolation that can enforce that promise against arbitrary extension code.

The core must provide an embeddable library for these facilities. The CLI owner can add trusted components or operations and compile its own named CLI. If the standard facilities are insufficient, the owner implements the missing behavior behind this library boundary and ships a new core build. The new build advertises its versioned facilities; extensions declare which are required or optional.

This is a trusted integration supplied with the core build. Installing an ordinary extension never installs a privileged renderer, overrides input handling or expands core permissions. Missing support is an explicit compatibility failure, not a reason to grant direct terminal access. Core language, library API signatures and the widget toolkit remain separate decisions.

The core records one final outcome per invocation, using one of the stable codes below, an optional diagnostic code and the original executor status when available. Outcome codes are distinct from numeric process exit codes and operation-specific diagnostic codes. Human-readable error text is not a machine-readable discriminator.

TypeCodeMeaning
SuccesssucceededThe selected command or description request completed successfully and the executor completed the required finalization.
Command failurecommand_failedThe command or description handler reported an application failure, such as invalid project data; execution and reporting otherwise completed normally.
Pre-launch rejectionrejectedA static compatibility, input or access check prevented any extension code from starting.
Execution failureexecution_failedRuntime preparation, startup, protocol, executor or required cleanup failed; a reliable normal completion could not be established.
CancellationcancelledThe core accepted a cancellation before finalization committed. A stable reason distinguishes a user request, owner shutdown and an expired invocation deadline.

For a process-only command, normal exit zero means success and normal nonzero exit means command failure. Observed abnormal termination means execution failure; a numeric exit code alone cannot distinguish every crash from an intentional application failure.

A process using structured interaction must also provide one valid completion report: success requires normal exit zero, command failure requires normal nonzero exit, and any explicitly reported numeric code must match the actual exit. A missing or duplicate report, a contradiction or observed abnormal termination is an execution failure. Future non-process executors need their own equivalent completion evidence, not a mandatory OS exit code.

A valid completion report stops new core-service calls but does not finalize the invocation. The executor must finish execution, drain or account for output, settle outstanding service calls and release owned resources within a bounded finalization period. A child holding a stream open cannot keep the core waiting forever. Unconfirmed service outcomes or failed finalization prevent success and are recorded explicitly, including any incomplete cleanup. Ordinary diagnostic events have the separate bounded delivery rules below; an undelivered log record is not an unconfirmed service operation.

The core maps the outcome to the CLI exit status under ADR-0003 for both process mappings. Normal process command exit codes are preserved only after required finalization succeeds. Process-only commands use observed normal exit, without an invented completion report or handshake; managed commands require the additional report and channel evidence. A late error must never leave a failed invocation with a successful CLI exit status.

This state diagram explains the lifecycle, not a new set of public API states. Runtime preparation applies only to execute; describe checks an already ready environment. Pre-launch rejection only releases resources already acquired; it does not start extension code.

Completion report is not final completion
Completion report is not final completionChecks and runtime preparation lead to execution and bounded finalization. Rejection or preparation failure prevents launch. Cancellation accepted before the final outcome commits takes priority. Later cancellation does not change that outcome.Checks / runtime preparationStarting / runningBounded finalizationBounded cancellation shutdownOne immutable outcomeChecks and preparation pass,attempt launchRejection or preparation failureCompletion or failureCancellation acceptedCancellation acceptedCancellation before final commitFix outcome, cleanup errorsexclude successFix cancelled and shutdowndiagnosticsCompletion report is not final completionChecks and runtime preparation lead to execution and bounded finalization. Rejection or preparation failure prevents launch. Cancellation accepted before the final outcome commits takes priority. Later cancellation does not change that outcome.Checks / runtime preparationStarting / runningBounded finalizationBounded cancellation shutdownOne immutable outcomeChecks and preparation pass,attempt launchRejection or preparation failureCompletion or failureCancellation acceptedCancellation acceptedCancellation before final commitFix outcome, cleanup errorsexclude successFix cancelled and shutdowndiagnostics

Finalization closes new service calls and accounts for exit, output, outstanding calls and resource cleanup. Cancellation may still win during this phase. Once committed, the outcome cannot be replaced by late success or cancellation; incomplete cleanup and uncertain external effects remain visible.

The owner orders admission of a protected action against entering finalization. A service request accepted earlier but not yet admitted to its protected action cannot start that action after the boundary closes. Already started effects may finish or remain uncertain; waiting for them is bounded. Trusted cleanup and required audit obligations remain permitted under their established scope, without reopening extension service requests. An extension cannot create new work by labelling it cleanup.

The core orders cancellation, deadlines and finalization. If finalization has committed, a later cancellation changes nothing. Otherwise accepted cancellation stops new core-service requests, signals the executor and enters bounded shutdown. This includes cancellation after a completion report but before finalization commits. A later success report cannot replace the cancellation. Failures to stop or clean up remain explicit diagnostics; a cancellation request alone is not evidence that execution stopped.

Cancellation reasonAuthoritative trigger
user_requestAn explicit user or caller cancellation request, including core-owned Ctrl-C handling
owner_shutdownOrderly shutdown of the lifecycle owner or loss of the invocation’s execution scope while the owner can still finalize it
deadline_exceededThe core observes that the invocation deadline has expired before final commitment

The owner chooses the reason from its own events, not an extension field. The first accepted cancellation fixes that reason. Later cancellation sources do not replace it or restart shutdown; they may be recorded as diagnostics. Concurrent observations are serialized by the owner, and their acceptance order determines which cause wins. A dismissed dialog, exhausted stdin, an operation’s own timeout and a transport failure are not invocation cancellation by themselves.

The invocation uses a core-owned monotonic deadline, checked through final commitment, including during finalization. The owner checks expiry when admitting work and before committing an outcome, not only when a timer callback happens to run. Expiry during finalization accepts deadline_exceeded unless another cancellation was already accepted. It does not grant more cleanup time. A separate finalization deadline bounds stopping, draining, audit and cleanup; expiry of that budget is execution_failed unless accepted cancellation already governs the outcome. Already-authorized diagnostic logging cannot extend either deadline. Concrete limits and termination mechanisms follow ADR-0003 for the process mappings.

After code starts, a crash, cancellation or lost reply does not prove that external actions did not happen. The core reports an unknown external outcome when it cannot establish it. It does not automatically repeat a command or a core-service operation with possible side effects. Invocation and request IDs correlate activity; they do not provide exactly-once execution or rollback.

The CLI owner registers trusted service implementations before invocations begin. These may be built-in services or integrations supplied by the owner, such as CLI settings, limited credentials or a corporate API. Each published operation has a stable namespaced ID, version, input/result contract and an action/resource authorization rule. Namespace conflicts are configuration errors; extensions cannot register trusted handlers, replace a built-in operation or select an arbitrary internal core function. The selected operation contracts and implementations are fixed for the invocation.

The declaration distinguishes shared startup requirements from required and optional operations for each invocation kind. It requests access; it never grants it. The selected kind determines which requirements and restrictions apply; describe cannot inherit execute permissions. The extension uses the published contract rather than depending on the implementation of the service. Language-specific clients may make calls convenient, but the same semantics must remain implementable in any supported language or executor.

Requests and events are two distinct forms of interaction. They may share a control transport; this distinction does not require a separate connection per form. UI requests use the same versioned operation and invocation boundaries as other core services.

FormLogical contentsMeaning
Operation requestRequest ID, selected operation/version, inputs and resource referenceAsk the core to perform an authorized action within this invocation.
Operation responseMatching request ID and a value or a typed errorReport the operation’s result. A lost response leaves its effects uncertain; it is not permission to retry.
EventAgreed event type/version, source and its payloadReport information under that event’s admission and delivery rules. A log comes from the extension; managed-view input comes from the core. Delivery requirements belong to the event type, and an event does not authorize another operation.

The core binds all three forms to its own invocation context. Completion reports, cancellation and compatibility negotiation retain their existing control semantics; they are not droppable diagnostic events. This decision distinguishes extension log records from core-originated managed-view input. Other event types require their own agreed schemas and behavior before use. Progress presentation uses declared UI facilities rather than arbitrary terminal output or an undeclared event type.

This component view shows responsibilities and the owner’s integration point, not separate processes or a selected wire protocol.

Owner integrations behind one core boundary
Owner integrations behind one core boundaryThe owner registers trusted operation implementations. Extensions use checked operation requests or admitted log events. Neither path exposes arbitrary core functions.CLI ownerCore services and ownerintegrationsExtensionValidate request andauthorizeCheck event and invocationcontextCLI loggingRegisters trustedimplementationsOperation requestScoped callValue or errorLog eventAccepted recordOwner integrations behind one core boundaryThe owner registers trusted operation implementations. Extensions use checked operation requests or admitted log events. Neither path exposes arbitrary core functions.CLI ownerCore services and ownerintegrationsExtensionValidate request andauthorizeCheck event and invocationcontextCLI loggingRegisters trustedimplementationsOperation requestScoped callValue or errorLog eventAccepted record

The declaration distinguishes required operation support from access that must be established before any extension code runs. Both are checked before launch; an unresolved mandatory pre-launch permission prevents execution. Missing optional operations can be reported for reduced functionality. For a resource discovered during execution, operation support can still be mandatory, but access is decided at the actual call and is never presumed granted in advance.

Every service call identifies the operation, inputs and requested resource, with a correlation ID scoped to the active invocation. A duplicate call ID is rejected without repeating the operation. The operation’s trusted authorization rule derives the actual action and resource from validated inputs; an extension-supplied resource label cannot authorize a different target. The core derives subject, command and release from its own context, never from extension-supplied identity fields. Effective access is limited by owner policy, the subject’s permissions, the trusted extension’s scope, the selected command’s declaration and the invocation’s restrictions. A permission query is advisory, not a reusable grant.

The trusted implementation receives validated inputs, the authorized scope, the core-owned invocation context and a cancellation signal. Each request has a bounded deadline no later than the invocation deadline; the number of outstanding requests is bounded too. Its response carries the request ID and either a value or a typed error. An error has a stable code, a safe human-readable message and optional operation-defined details, without exposing internal credentials or stack traces by default. Operation errors remain distinct from the invocation’s final outcome. A timeout or cancellation does not prove that an external effect was prevented. These are semantic requirements, not a prescribed callback signature or message format.

Unknown operations, out-of-scope requests and requests after cancellation or completion are rejected. Unsupported operation, denied access, unavailable authorization and operation failure remain distinguishable outcomes; none permits bypassing a required check. A denial after launch is an operation error, not the invocation outcome rejected. The extension may recover where its command semantics permit; if a necessary operation cannot complete, it reports command failure. A configured broker failure does not enable local authorization. Local policy is an explicit mode; cache expiry, offline permissions and revocation require their own decision.

Extensions receive only the service handles or limited credentials needed for the operation, not general login or registry credentials. Authenticating the caller and binding it to the invocation is a requirement for each mapping. Existing external operations may still finish after cancellation; service-side enforcement is required, and this contract does not revoke previously issued external authority or constrain direct OS access.

The same call boundary applies to local policy, a broker integration and future executor mappings.

Authorization for each core-service call
Authorization for each core-service callThe core binds an operation to the invocation context and checks policy before calling the service. Denial or unavailable authorization prevents the operation.Core serviceLocal policy / brokerRukh coreExtensionCore serviceLocal policy / brokerRukh coreExtensionalt[Allowed and invocation still accepts calls][Denied, unavailable or invocation closing]alt[Invalid or inactive call][Eligible call]Operation, inputs, resource, request IDCheck call ID, declaration, support and invocation scopeOperation errorCore-owned identity + action + resourceAllow, deny or unavailableScoped operationValue or operation errorResponse for request IDOperation error, no policy fallbackAuthorization for each core-service callThe core binds an operation to the invocation context and checks policy before calling the service. Denial or unavailable authorization prevents the operation.Core serviceLocal policy / brokerRukh coreExtensionCore serviceLocal policy / brokerRukh coreExtensionalt[Allowed and invocation still accepts calls][Denied, unavailable or invocation closing]alt[Invalid or inactive call][Eligible call]Operation, inputs, resource, request IDCheck call ID, declaration, support and invocation scopeOperation errorCore-owned identity + action + resourceAllow, deny or unavailableScoped operationValue or operation errorResponse for request IDOperation error, no policy fallback

Structured logging is a negotiated feature. A command may declare its support required or optional, independently of operation permissions; required support does not promise persistent delivery. If optional support is absent, the extension can use ordinary stderr diagnostics. Logging policy is set by the CLI owner and checked within the invocation, without requiring a remote broker request per record. It permits diagnostic reporting only, not arbitrary destinations, actions or calls to another service.

Record partSource and rules
SeverityThe extension selects trace, debug, info, warn or error. The owner controls filtering. An error log does not set the command outcome.
Message and fieldsHuman-readable text and a bounded set of named scalar values. These are extension-supplied claims; they cannot overwrite core metadata.
Origin timeOptional extension timestamp. It does not establish trusted ordering or replace the core’s receipt time.
Core metadataThe core adds receipt time, invocation ID and kind, command identity and pinned extension release. Exported identity details are limited by owner policy.

The OpenTelemetry logs data model is a reference for separating severity, body, attributes and source/observation time. This does not select an SDK, exporter or wire format.

The owner chooses destinations and presentation: stderr, a local file or an external collection service. Terminal presentation always goes through the core’s renderer, coordinated with active interactions. The core applies field policy and redacts known secrets before handing records to destinations. Extensions must not log credentials or secrets; arbitrary sensitive text cannot be reliably detected by automatic redaction. Invocation arguments and environment values are not captured by default. Text is rendered as data, with control characters escaped where needed, so records cannot issue terminal commands or forge adjacent entries.

Every mapping must bound record size, field count, rate, memory use and shutdown draining. Valid records are filtered, admitted or dropped according to the declared logging policy. Invalid or disallowed log-event payloads are discarded and counted; broken framing or session integrity still follows the protocol-failure rules. This loss policy does not apply to UI answers or consequential input actions. A slow or unavailable destination may lose ordinary logs without changing the command outcome. Records may also be lost in a crash; acceptance into a local queue is not confirmation of delivery or durable storage. They are not automatically replayed into a later invocation.

Losses observed by the core are counted separately from the ordinary log queue and exposed in bounded invocation diagnostics, never in command stdout. This is not a guarantee to count records never received or lost with the core itself. Mappings reserve control-processing capacity so a log flood cannot indefinitely delay cancellation, responses or completion. Concrete limits and scheduling belong to the mapping, but unbounded queues or waits are not conforming implementations.

Once cancellation or a completion report closes operations, the core may still admit diagnostic records already permitted for that invocation until its fixed shutdown deadline or earlier context closure. This does not admit new event types or privileges. Remaining records are drained or counted as dropped within the same budget; logging cannot keep the invocation alive. After context closure, extension events are discarded without recreating the invocation. The core can still produce its own final diagnostics, but late extension records cannot change the committed outcome.

This flow describes ordinary diagnostic delivery. Mandatory audit records use the stronger rules below.

Bounded diagnostic logging
Bounded diagnostic loggingThe core checks and enriches extension records, then applies filtering and bounded delivery. Observed losses are accounted for outside the ordinary queue without changing a command's outcome.Extension log eventContext, policy and payloadvalid?Discard and account forobserved lossAdd core metadata andredact known secretsPasses level filter?Suppress by policyDelivery budget available?Deliver to owner-selecteddestinationsDiagnostic record deliveredNoYesNoYesNoYesDestination fails or deadlineexpiresDeliveredBounded diagnostic loggingThe core checks and enriches extension records, then applies filtering and bounded delivery. Observed losses are accounted for outside the ordinary queue without changing a command's outcome.Extension log eventContext, policy and payloadvalid?Discard and account forobserved lossAdd core metadata andredact known secretsPasses level filter?Suppress by policyDelivery budget available?Deliver to owner-selecteddestinationsDiagnostic record deliveredNoYesNoYesNoYesDestination fails or deadlineexpiresDelivered

Audit is owned by the core. Authorization decisions and observed operation outcomes are recorded from trusted context; an extension’s log claiming success is not audit evidence. If policy requires a confirmed record, the responsible service defines an acknowledged operation or an authorization gate with an explicit durability requirement. A mandatory pre-action record must be confirmed before the protected action begins; inability to confirm it prevents the action. Failure to confirm a record after an action leaves an explicit audit failure or uncertain outcome, prevents an ordinary success where audit is required, and does not roll back or automatically repeat the action. This ADR does not promise atomicity between an action and an external audit store. Storage, retention, access to records and exact audit guarantees require a separate decision.

An unmet mandatory post-action audit requirement is a required-finalization failure enforced by the core: the outcome is execution_failed even if the extension reports success and exits zero. If cancellation was already accepted before final commit, cancelled remains the outcome with explicit audit diagnostics. Handling an operation error in extension code cannot clear this obligation.

The interaction contract is versioned independently of extension releases, runtime versions, individual core services, event schemas and IPC bindings. For a stable contract, use major.minor.patch: incompatible meaning requires a new major version, compatible optional additions a minor version, and compatible corrections a patch. This follows Semantic Versioning; an initial stable version is not declared by this decision.

The descriptor states supported interaction versions and required features. The core selects an exact mutually supported version allowed by owner policy and verifies shared startup requirements and required features for the selected invocation kind before code starts. Contextual help is an optional feature, but its declared requirements are mandatory once describe is selected. Optional additions can be omitted. Unknown required fields, outcomes or authorization requirements are rejected. Unknown fields may be ignored only under extension rules already defined by the selected contract version, not a sender’s claim that a field is optional. Senders stay within that version and its extension rules. A version match never substitutes for feature or permission checks.

For an IPC mapping, the live session must confirm the selected version, invocation kind and required features before either execution or description dispatch. A contradiction terminates the session without dispatch, although process startup code may already have run. The confirmation cannot silently downgrade requirements or increase rights. A process-only adapter implements the applicable contract itself; an ordinary wrapped program need not speak IPC or report a Rukh version.

Static incompatibility prevents startup; a contradictory live session is an execution failure after startup. The diagram focuses on compatibility, alongside the input, access and lifecycle rules above.

Static compatibility and live confirmation
Static compatibility and live confirmationA shared version and required features are checked before launch. An IPC session confirms them before dispatch; disagreement stops the process and is not a pre-launch rejection.Verified declaration + ownerpolicyShared permitted version?rejected: no extension codestartsRequired features supported?Other pre-launch checks passStart selected executorUses IPC?Dispatch through selectedmappingSession confirms selection?Stop: execution_failed;startup code may have runNoYesNoYesNoYesYesNoStatic compatibility and live confirmationA shared version and required features are checked before launch. An IPC session confirms them before dispatch; disagreement stops the process and is not a pre-launch rejection.Verified declaration + ownerpolicyShared permitted version?rejected: no extension codestartsRequired features supported?Other pre-launch checks passStart selected executorUses IPC?Dispatch through selectedmappingSession confirms selection?Stop: execution_failed;startup code may have runNoYesNoYesNoYesYesNo
MappingApplication of this contract
Process onlyArgument passthrough, captured data streams and process exit; core-presented text or byte-preserving redirected output. Base help from metadata only. No direct interactive terminal, core-service calls, structured events or describe dispatch.
Process with core servicesDeclared execution inputs or a separately supported describe request, captured data streams, a separate control channel, scoped services, negotiated events and an explicit completion report alongside process supervision. UI operations are limited to execute.
Future WASM or another executorAn explicit mapping for supported invocation kinds, outcomes, scoped operations and managed interaction. The core retains terminal ownership. Missing required behavior prevents use; WASM compatibility and isolation are not implied.

ADR-0003 selects JSON-RPC for managed processes; WASI CLI is a reference for future CLI-oriented interfaces. The message format does not replace Rukh authorization, finalization or this semantic contract. ADR-0003 defines the process channel, ADR-0006 the declared value model, and ADR-0013 the machine result. The ABI and engine of an additional executor require their own compatible implementation and support decision.

  • Different languages and executors can share command, outcome and authorization semantics while retaining explicit platform limits.
  • Existing programs remain usable through the smaller process-only mapping, with executable-level access where their arguments are opaque.
  • Interactive programs that require their own terminal or raw screen output need adaptation. The core must supply a sufficient interaction library, including a trusted extension point for owner-built CLIs.
  • The core owns name resolution, parsing, compatibility and invocation state; adapters need conformance checks, not just successful message exchange. Aliases cannot customize behavior, and passthrough support is limited to target conventions the mapping can preserve.
  • Base help works without an installed runtime. Contextual help requires an authorized, already ready extension and a separate bounded invocation; its failure cannot silently become successful execution or alter the command schema.
  • Separate outcome and finalization handling adds work but prevents false success after a crash or incomplete shutdown.
  • Owners can supply services and logging destinations behind stable contracts. They must implement bounded delivery, trusted context and distinct diagnostic/audit guarantees.
  • This contract provides no sandbox, transaction rollback or exactly-once guarantee. Concrete IPC and authorization integrations remain unimplemented.

The 2026-09-24 refinement checks remain unrun. Verify the shared CLI/MCP path, unsupported result-feature rejection, current profile/task binding, bounded data, absence of replay and channel separation within this ADR’s responsibility. Historical results below do not cover these additions.

Completed on 2026-09-20: 137 checks passed, none failed or skipped. The reference suite in validation/adr-0002/ ran on Windows 11 x64 with Node.js 24.17.0 managed by mise. Run it from the repository root with mise run contract-check.

AreaEvidence
Inputs and preparationTyped parsing and aliases preserve one identity; ambiguous names and mode changes are rejected. A real child process receives empty arguments, Unicode and shell characters unchanged. A provider stub checks preparation order and the final compatibility/access checks.
Streams and completionReal processes preserve redirected bytes; the core escapes terminal control sequences. EOF, missing or contradictory completion, crashes and bounded shutdown have defined outcomes.
Cancellation and retriesRaces, timeouts, lost IPC and replies yield one final outcome. New service calls stop, and the fixture does not repeat the command or its recorded external effect.
PermissionsLocal policy and an HTTP broker stub pass through the same checks. Forged context, incorrect versions, missing requirements and calls outside the permitted scope are rejected; broker failure does not grant access.
Services, logs and auditSchemas, deadlines and duplicate request IDs are checked. Bounded logging preserves core metadata and stdout. Audit failure blocks the action or prevents success, including when a real child handles the error and exits with zero.
InteractionA headless terminal model checks dialog ownership, secrets, bounded input, stale events, cancellation, cleanup and custom components. A real child requests a secret dialog through the core; the secret is absent from captured output and presentation.
HelpFlag boundaries, separate permissions, ready-only environments and structured answers are checked. Errors can render base help without changing the failure outcome. A real describe process completes without dispatching execute.

The process fixture uses Node’s IPC channel separately from stdin/stdout/stderr. This is a test binding, not the chosen product transport. Terminal checks inspect core-owned presentation and injected events, not a physical terminal. Runtime providers, operation implementations and audit storage are controlled fixtures; their production integrations, durable storage, process-tree supervision, OS isolation and future executors are outside this evidence. A trusted synchronous callback cannot be forcibly interrupted by the model.

The checks support accepting the semantic contract and its refinements to ADR-0001. Each production executor and integration still needs conformance validation; neither the fixture nor its numerical limits are a stable API or product release.

The 2026-09-22 clarification additionally requires checking all three cancellation causes, their races through finalization, timer-callback delays, unchanged shutdown deadlines and protected actions queued before finalization. Existing fixtures retain historical user/deadline labels and do not establish these complete refined semantics; these additional checks have not been run.

  • ADR-0001: Extension execution model.
  • Architecture overview and decision register.
  • Follow-up decisions: concrete data schemas and KCL declarations, including structured help and language fallback; process IPC and session authentication; the core library and UI component API, terminal mappings and input-event delivery; authorization providers; service catalog and logging/audit storage guarantees; mappings and isolation for additional executors.

Diagram

Drag to move · + / − to zoom · 0 to fit · Esc to closeDrag to move and pinch to zoom

100%