Problem
The React view layer has a headless control surface (com.top_logic.layout.react.scripting), built under #29108. It serves three consumers off one mechanism: the script recorder, deterministic replay, and an agent driving a session on a user's behalf.
The substrate works and is verified live against the demo:
- observe / resolve / act on ScriptingSession, dispatching through the very same ReactControl.executeCommand the browser uses.
- Stable semantic addressing (ScriptingTreeProjector): role[name] paths plus navigation slots, with business-key (ModelName) identity for data nodes; address drift hard-fails instead of silently substituting.
- Recorder: browser commands captured as typed ReactCommand configuration items, replay-stable arguments (selectByKey), assertion steps, a recorder side window with a step debugger.
- Affordance-first observe?mode=actions projection and JSON argument schemas derived from the command's ConfigurationItem descriptor.
What was missing is everything that turns a working prototype into a feature: the authorization and session model for non-browser callers, the read-concurrency model, and productionization.
Done
Command handlers refuse what the user interface does not offer
Acting goes through the same dispatch as the browser, so a command whose executability is re-evaluated at execution time was already safe — but not every path was. A scan of all 84 @ReactCommandHandler methods found and closed four classes of gap: field writes that ignored editability, form lifecycle commands that bypassed their own command models, dispatcher commands that invoked an action without re-checking it, and a button whose CommandModel granted execution unconditionally. The field check is structural: one final handler funnels every client value through a single guarded entry, and subclasses override the apply/commit hooks below it.
A form can now be configured with the permission to edit its object at all (<edit-executability> taking the usual ViewExecutabilityRule`s, e.g. a `SecurityRule); previously there was no configuration point for the permission that opens write access.
An account invites an agent into its session
An account issues an invitation — the application's publicly reachable address together with a token, in one line, so an agent is invited with a single copy. The token is bound to the session it was issued from and dies with it; only its hash is stored and the plain value is readable exactly once. Scope is observe or observe and act; an observing token is refused on every writing request. Owners withdraw their own tokens, administrators everyone's, withdrawing deletes them, and a daily task deletes the expired ones.
A module of its own
The agent-specific part left the core React module: com.top_logic.layout.view.agent (tl-layout-view-agent) holds the AgentServlet with its /agent-api/* mapping, the admission (AgentAuthenticator), and the access tokens with their model, views, cleanup task and admin section. The interface itself stays in the core module, where the script recorder consumes it, and is named after the legacy application scripting. An application that does not want agent access does not deploy the module — which also settles the per-environment enablement question.
Remaining work
The remaining work is filed separately: #30048 (agent access: a session of its own, who may issue tokens, tokens of ended sessions, audit, MCP wrapper, integration test), #30049 (observation without blocking the session, quiescence, executable actions only, bounded observation, raw vs. curated projection) and #30050 (script recorder: persisted scripts, replay-stable trees and multi-selection, session-independent assertions, variables, cross-session test).
Reference
Developer articles view-layer/headless-interface (tl-layout-react) and view-layer/agent-access (tl-layout-view-agent).