From 8a55e8d56e324f0d1c914987ff3e0fa568534c08 Mon Sep 17 00:00:00 2001 From: Nahiyan Khan Date: Wed, 10 Jun 2026 23:09:27 -0400 Subject: [PATCH] add controlled state lifecycles --- docs/adoption/integration.md | 10 + docs/adoption/security.md | 3 + examples/surface-gallery/src/capabilities.ts | 10 +- .../tests/gallery-smoke.spec.ts | 105 ++++++++- packages/engine/src/contracts.ts | 6 + packages/engine/src/index.ts | 2 + .../src/runtime-validator/binding-rules.ts | 89 ++++++++ .../src/runtime-validator/capabilities.ts | 1 + .../engine/test/capability-contract.test.ts | 42 +++- .../test/runtime-validator-bindings.test.ts | 100 ++++++++ packages/host/src/capability-registry.ts | 86 ++++++- packages/host/src/index.ts | 2 + packages/host/src/policy.ts | 2 + .../host/test/capability-registry.test.ts | 215 ++++++++++++++++++ packages/sandbox-runtime/src/bootstrap.js | 14 +- packages/summon/src/index.ts | 2 + 16 files changed, 675 insertions(+), 14 deletions(-) diff --git a/docs/adoption/integration.md b/docs/adoption/integration.md index c04602f..29e8832 100644 --- a/docs/adoption/integration.md +++ b/docs/adoption/integration.md @@ -70,6 +70,16 @@ const capabilityContract = registry.toContract(); `capabilityContract.pack` is model-facing. `capabilityContract.validationCapabilities` and `capabilityContract.initialState` are runtime-facing. +Data resources can expose a host-owned empty state when "no results" is a +merchant-facing condition. Add `stateKeys.empty` and, when array length is not +the right definition, `isEmpty(data)`. The generated surface should render +`$alias.empty`; it should not infer "no results" from missing pre-load data. + +Actions can opt into a tiny lifecycle with `controlled: true`. Summon then +pushes pending, done, and error state around the host handler so generated UI can +disable the trigger, show host errors, and render success only after the host +actually finishes. Existing actions remain uncontrolled unless they opt in. + Approval actions are still host tools. The difference is that the host can prepare the exact operation before asking for a decision. The generated surface gets only small status state such as pending, approved, denied, failed, and a diff --git a/docs/adoption/security.md b/docs/adoption/security.md index 79eb37b..6944e53 100644 --- a/docs/adoption/security.md +++ b/docs/adoption/security.md @@ -96,6 +96,9 @@ requires a compatible host registry for the same reason. - Use `defineWorkerAction` / `defineWorkerResource` for host-owned background work and `defineApprovalAction` for operations that require a host approval adapter before the handler runs. +- Use controlled action state for merchant-facing pending, success, and error + UI. Generated surfaces should render those keys; they should not invent local + completion or failure state for host actions. - Treat approval as a workflow owned by the host, not a generated modal. For approval actions, the host may `prepare` the exact operation into an `ApprovalRequest`; the user approves or denies that request in host UI; the diff --git a/examples/surface-gallery/src/capabilities.ts b/examples/surface-gallery/src/capabilities.ts index 61e59d0..e977576 100644 --- a/examples/surface-gallery/src/capabilities.ts +++ b/examples/surface-gallery/src/capabilities.ts @@ -76,10 +76,10 @@ function galleryCapabilityDefinitions(opts: GalleryCapabilityOptions): Capabilit argsSchema: searchArgsSchema, resultSchema: searchResultSchema, defaultData: [], - stateKeys: { loading: 'searching', data: 'results', error: 'searchError' }, + stateKeys: { loading: 'searching', data: 'results', error: 'searchError', empty: 'noResults' }, triggers: ['submit', 'mount'], stateShape: - '{searching: boolean, query: string, results: Array<{title: string, snippet: string, source: string}> | null, searchError: string | null}', + '{searching: boolean, query: string, results: Array<{title: string, snippet: string, source: string}> | null, searchError: string | null, noResults: boolean}', patterns: [ { name: 'Search resource', @@ -90,6 +90,7 @@ function galleryCapabilityDefinitions(opts: GalleryCapabilityOptions): Capabilit

Searching...

+

No matching results.