Generative UI’s production boundary is intent
A model returns a button with onclick=”shutdownInstances([‘i-123’, ‘i-456’])”. If a live application injects that generated HTML and executes the handler, the model can influence application behavior. It is doing more than choosing how to present a task.
A safer design gives the model a narrower role. The model describes interface intent. The application decides how that intent becomes UI and behavior. This distinction matters for AI-driven products that dynamically assemble charts, tables, forms, confirmations, or workflow steps.
This is a run-time architecture problem. ChatGPT, GitHub Copilot, Cursor, or another AI coding assistant can generate components, templates, stylesheets, tests, and features during development. Developers can review, edit, test, commit, and maintain that output through the normal software lifecycle. It then becomes part of the application codebase.
A live model response arrives as external data during a user session and can change with each request. The production architecture needs an explicit rule for how much authority that response receives.
Run-time code generation hands the model application authority
Consider a cloud-cost investigation. A user asks why spending increased and what can be done about it. A useful response may include a cost comparison, a service breakdown, anomalies, affected resources, and remediation controls. Arbitrary HTML and JavaScript can express all of these elements. They can also let the model make decisions that normally belong to several application layers.
Generated markup determines structure. Generated styles can define presentation outside the product’s established components. Generated handlers define behavior and can make an operation appear available. Generated state can shape what the interface tells the user.
A component system can encode accessibility behavior, interaction rules, loading and error states, analytics conventions, performance decisions, and testing practices. UI produced outside that system does not automatically inherit those rules.
Generated JavaScript carries greater authority because the application can execute it. A model-generated handler can call an internal function, pass unexpected parameters, or expose an operation that the application normally places behind an approval workflow.
Authorization shows why interface generation and operational authority require separate decisions. The presence of a “Shut down” button tells the user that an operation appears available. Permission to shut down a resource depends on current identity, resource scope, business rules, and server-side authorization. Producing the control does not establish any of those conditions.
State creates a related problem. The interface may reflect conversational context while the application has newer data. A model might describe a resource as unused, and that resource can later change. The application needs current state before it presents or executes an operation based on that description.
Components, actions, state, approvals, and authorization all carry forms of application authority. A live model therefore needs a contract that lets it influence composition while the application retains control of those capabilities.
A project in mind?
Schedule a 30-minute meeting with us.
Senior experts helping you move faster across product, engineering, cloud & AI.
Generate constrained generative UI intent instead
That contract can take the form of structured UI intent: run-time data that identifies predefined interface blocks and supplies parameters from an allowed vocabulary.
For the cloud-cost request, the response could ask for cost-summary, service-cost-breakdown, anomaly-list, and remediation-options blocks. cost-summary might accept current-week or current-month as its period and an allowed previous period for comparison. anomaly-list might accept medium or high severity. remediation-options might be limited to compute, storage, or network.
The model still exercises judgment. It decides which supported blocks fit the request and chooses their supported parameters. The application defines what each block means, how it looks, where its data comes from, how errors work, and which interactions it exposes.
A TypeScript discriminated union is one way to express this contract in code. A discriminated union is a type whose members carry an identifying field so code can distinguish the allowed cases. A UIBlock type can enumerate cost-summary, service-cost-breakdown, anomaly-list, remediation-options, and confirmation, together with the props each accepts. The compiler can then check how application code handles those declared cases.
Run-time validation is still required because model output arrives as external data. TypeScript’s compile-time checks do not validate an incoming response. The application can use JSON Schema, Zod, Valibot, or another validation mechanism to treat incoming output as unknown data and check it before rendering.
Validation can reject unknown block types, malformed props, unsupported action references, and structures outside the declared contract. A production schema can also constrain layout rules, component limits, allowed nesting, action references, and contract versions. The model may propose many descriptions. The application admits only those that satisfy its contract.
Failure needs a defined path. If validation fails, the application can show a safe fallback, ask the user to rephrase, or return a conventional text response. Malformed output should remain outside the rendering and execution paths.
Validation establishes the shape of the request. The next design question is what that validated request is allowed to render and do.
Rendering and execution need separate control planes
A component registry can control rendering. A component registry is an application-owned mapping between allowed identifiers and trusted component implementations. An action registry can separately control which application-owned operations an interface may request.
For example, the component registry can map cost-summary to the application’s CostSummary implementation and anomaly-list to AnomalyList. When validated intent requests one of those identifiers, ordinary application code instantiates the corresponding component with validated props.
The engineering rules remain inside the component. CostSummary can obtain billing data through established APIs, use the product’s loading and error patterns, implement accessibility requirements, and record analytics through established instrumentation. Model output chooses the registered component. It does not redefine that component for each request.
Execution needs a separate mechanism because dynamic interfaces can contain controls with real effects, such as shutting down an instance, resizing a database, opening a support ticket, approving a deployment, updating a policy, or changing account settings.
Suppose the model proposes a confirmation asking whether to open a remediation task for unused compute instances. Its structured output can reference actionId: “create-remediation-task”. That identifier requests a registered operation. The application can define a UIAction contract containing operations such as create-remediation-task and open-support-ticket. An application-owned actionRegistry can map those identifiers to createRemediationTask and openSupportTicket.
This is how the opening shutdownInstances case changes under the constrained design. The model does not supply an onclick handler that calls shutdownInstances directly. It can request an allowed action identifier, while application code controls the implementation behind it.
Several checks belong between the request and execution. The operation must be registered, the current user must have permission to request it, and referenced resources must fall within that user’s scope. The operation must also remain valid against current application state.
Higher-risk operations can require confirmation, auditing, approval routing, or an additional server-side permission check. These are application rules. The model can select a path through the interface without defining them.
Schema validation and authorization answer different questions. A generated object can satisfy a confirmation schema while referring to an action the current user cannot perform. Validation establishes that data has an accepted form. Authorization establishes whether a specific actor may produce a specific effect under current conditions.
The same distinction applies to parameters. A schema may establish that resourceIds is an array of strings. Authorization and application logic must determine whether those resources fall within the user’s permitted scope and whether the requested operation is valid for their current state.
For operations that change protected resources, the server should enforce the final authorization decision. The front end can use permission information to decide which actions to present. The server applies the business rules before the protected operation takes effect.
Stable component implementations and named action entry points also create clear units for testing. Teams can test component behavior, permission and state checks, and action implementations as application code even while model-selected compositions vary between requests.
The model still has a useful role. It can decide that a user needs a comparison first, an anomaly list next, and then a remediation flow. It can select registered capabilities that fit an open-ended request. The application controls what those selections can cause.
The application must also remain the authority on state
Component and action controls still depend on trustworthy state. An AI-driven session can contain summaries, inferences, earlier tool results, and prior user statements. Those inputs can help the model decide what to display, but they can become stale.
A compute instance described as unused may later change status. Billing data behind a remediation suggestion may be updated. A pending workflow may complete or fail after it was discussed.
Dynamically selected components should obtain verified state through ordinary application paths. CostSummary should receive or fetch billing information through the same trusted application mechanisms used elsewhere. A remediation action should update state through the normal workflow. A confirmation component should obtain the current facts it needs to decide what it can present.
Under this design, generated UI becomes a projection of application state. The model chooses a useful view. Application systems retain the operational record.
This distinction matters more as agentic applications evolve across multiple turns. A user can investigate a result, request an operation, change their mind, encounter an approval requirement, leave, and return later. The product needs a durable account of what completed, what failed, what remains pending, and what still requires human approval.
Workflow state belongs in systems designed to persist, update, invalidate, and authorize it. Model context can inform interface composition without becoming the system that records whether an operation actually occurred.
Constraint preserves the reason to use a model
A natural design question follows: if rendering, execution, authorization, and state remain under application control, what role remains for the model?
The cloud-cost scenario gives the answer. Different requests can require different compositions from the same trusted capabilities. One investigation may call for a summary and comparison. Another may call for anomalies and resource links. A third may call for remediation choices, confirmations, and several workflow steps.
A conventional application can encode anticipated paths. A model adds a way to interpret an open-ended request and select a composition from the components and actions the product has chosen to expose.
The useful variability lies in selection and composition. Component implementation, action execution, authorization, and durable state remain application responsibilities.
Key takeaways for leaders
- Keep executable code out of live model output: Treat run-time model responses as external data. Models can shape interface intent, while reviewed application code retains authority over behavior.
- Generate structured UI intent instead: Define a constrained, validated contract for the components and parameters a model can request. Reject malformed or unsupported output before it reaches rendering or execution paths.
- Separate rendering from execution: Use application-owned component and action registries to control what generated interfaces can display and request. Keep authorization, permission checks, confirmations, and protected operations outside model control.
- Keep application state authoritative: Use trusted application systems for current and durable state rather than relying on conversational context. Components and actions should verify current conditions before presenting or executing operations.
- Preserve flexibility through controlled composition: Use models to interpret open-ended requests and compose trusted capabilities dynamically. This preserves the value of generative UI without giving models control over implementation, authorization, execution, or state.
A project in mind?
Schedule a 30-minute meeting with us.
Senior experts helping you move faster across product, engineering, cloud & AI.


