@mhome/app-facade-protocol 1.93.0 → 1.109.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +13 -1
  2. package/contract/automation-presentation-v1.md +76 -0
  3. package/contract/automation-v1.md +28 -0
  4. package/contract/client-space-focus-v1.md +26 -0
  5. package/contract/entity-presentation-v1.md +24 -1
  6. package/contract/integration-presentation-v1.md +1 -1
  7. package/contract/plugin-preview-v1.md +16 -0
  8. package/contract/reference-presentation-v1.md +35 -0
  9. package/contract/resource-read-v1.md +2 -2
  10. package/contract/space-presentation-v1.md +45 -0
  11. package/contract/storage-v1.md +3 -1
  12. package/contract/widget-presentation-v1.md +36 -0
  13. package/contract/widget-writes-v1.md +44 -0
  14. package/fixtures/authorization/decisions.json +96 -0
  15. package/fixtures/entity-presentation.conformance.json +216 -0
  16. package/fixtures/nfc.conformance.json +442 -0
  17. package/fixtures/presentation/cases.json +48 -50
  18. package/fixtures/reference-presentation.v1.json +5 -0
  19. package/fixtures/routing/cases.json +192 -0
  20. package/fixtures/widget-presentation.v1.json +96 -0
  21. package/manifest/app-facade.v1.json +87 -6
  22. package/manifest/authorization.v1.json +75 -99
  23. package/manifest/host-provision-targets.v1.json +1 -1
  24. package/manifest/hub-targets.v1.json +1 -1
  25. package/manifest/pod-targets.v1.json +1 -1
  26. package/manifest/presentation-times.v1.json +50 -1
  27. package/manifest/routing.v1.json +14 -4
  28. package/manifest/targets.v1.json +1 -1
  29. package/package.json +1 -1
  30. package/protocol.json +1 -1
  31. package/schema/automation-presentation.v1.schema.json +602 -0
  32. package/schema/automation.v1.schema.json +142 -0
  33. package/schema/entity-presentation.v1.schema.json +111 -0
  34. package/schema/routing.v1.schema.json +5 -1
package/README.md CHANGED
@@ -121,6 +121,16 @@ Room uses one `/app/room/list` read. Every Room has `roomId`, `roomName`,
121
121
  `index`, `deviceCount`, and `deviceIds`; full and essential keep all five fields.
122
122
  Membership IDs include Device groups without expanding their members or state.
123
123
 
124
+ Room writes use `/app/room/create`, `/rename`, `/delete`, `/reorder`,
125
+ `/devices/move`, and `/devices/remove` under the same `/app/room` prefix.
126
+ Reorder accepts every current Room ID exactly once in the desired order.
127
+ Moves require a destination for each unique member ID; removal explicitly unassigns
128
+ members. Repeating a move or removal is a successful no-op. Deleting a Room
129
+ unassigns its members without deleting Devices or Device groups. Only deletion
130
+ supports the prepared confirmation flow; other writes execute directly.
131
+ Room write inputs use their declared fields and JSON string IDs/names; legacy fields
132
+ and numeric or boolean IDs/names are rejected rather than silently converted.
133
+
124
134
  Targets without a dedicated projection retain their business fields in YAML;
125
135
  essential does not promise a smaller field set for every domain. Targets own that selection.
126
136
 
@@ -249,7 +259,7 @@ Space-bound `nodeId`, `version`, and Plugin-owned `payload`. The former
249
259
  contains only the generic transport contract. Binding/offline observations come from
250
260
  `/app/plugin/enabled/list`, independently of the runtime's status.
251
261
 
252
- UI Integration metadata uses `/app/integration/presentation/get`; see
262
+ UI Integration metadata uses `/app/presentation/integration/get`; see
253
263
  [the presentation contract](contract/integration-presentation-v1.md).
254
264
 
255
265
  ## Artifact delivery
@@ -268,3 +278,5 @@ still applies. Local file download remains a client workflow over ordinary resol
268
278
 
269
279
  Agent Profile Markdown image ownership and canonical read-back are specified in
270
280
  [Agent Profile media](contract/agent-profile-media-v1.md).
281
+
282
+ NFC management: [contract](contract/nfc-v1.md), [schema](schema/nfc.v1.schema.json).
@@ -0,0 +1,76 @@
1
+ # Automation UI presentation
2
+
3
+ All targets use the normal App Facade envelope. Reads require Space read authority; editor and mutation targets require Space write authority.
4
+ They are UI entrypoints, not Agent/CLI/MCP tools. Inputs reject unknown fields.
5
+ Full and Essential use the same business read scope; UI requests Full.
6
+
7
+ | Target | Input | Output and read boundary |
8
+ | --- | --- | --- |
9
+ | `/app/presentation/automation/names/get` | `{ids}`: 1–100 selected, trimmed/deduplicated IDs | `{automations: {id: label}}`. Missing IDs omitted. Selected definition metadata only; no source downloads, parsing, state, history or graph reads. |
10
+ | `/app/presentation/automation/list` | `{type: "RECURRING" \| "TASK_FLOW"}` | `{automations}`: current-type regular user programs, with labels, runnable/health/expiry metadata, enabled/running/blocked, last-run `timestamp`, status timestamps and tags. No temporary-program, Group, Widget, snapshot, graph or authored-source expansion. |
11
+ | `/app/presentation/automation/overview/get` | `{automationId}`: nonblank, trimmed | `{id,name,type,enabled,runnable,corrupted,blocked,widgets}`. Selected metadata, status, cloud quota display state and owned Widget configuration/current values. No source, graph traversal, running history or snapshots. Missing definition is not-found. |
12
+ | `/app/presentation/automation/placements/get` | `{automationId}`: nonblank, trimmed | `{canvases:[{id,name,containsAutomation}]}`. Reads owned Widgets, Widget-to-Card relations and canvas metadata/card IDs; never loads Card bodies. Retains existing default-canvas initialization when the Space has none. |
13
+
14
+ Labels use saved preferredName, then the script-derived saved name, then the
15
+ existing unknown-label fallback. Metadata reads never require source storage to
16
+ be available. Names and overview permit generated-program inspection within the
17
+ authorized Space; this does not widen user-program mutation Facades. Group
18
+ management remains in the Group domain.
19
+
20
+ Core filters the regular definition table by type in SQL and uses selected state
21
+ reads. Cloud definition storage is partitioned by Space, so list reads that
22
+ partition's stored metadata and filters by type before loading state. It does not
23
+ hydrate source bodies or read the temporary/Group stores. This is not a promise
24
+ of a type-specific physical index. Billing display and lifecycle state retain
25
+ their existing authorities; enabled, runnable and running are independent.
26
+
27
+ List Full timestamps are milliseconds. Essential renders explicit timestamp
28
+ fields in the Space's Home timezone. Widget values retain their existing Widget
29
+ DTO semantics; their numbers are not interpreted as timestamps.
30
+
31
+ The frontend loads only the active list type, caches visited types within the
32
+ active Space, applies realtime events to loaded rows, and invalidates inactive
33
+ caches on incomplete updates or reconnect. Unloaded tab counts are unknown,
34
+ not zero. Overview and placements are separate on-demand reads. Snapshot panels
35
+ continue using `/app/automation/snapshot/list` only when opened.
36
+
37
+ Automation detail includes `runnable` from its already-loaded definition; Group
38
+ entries omit it. Automation status includes `corrupted` from the existing status
39
+ and temporary `expireAt` from creation/last-run timestamps. Ordinary configuration
40
+ editor initialization supplies the metadata it already loaded, so subsequent
41
+ runtime hydration only needs status.
42
+
43
+ ## Selected summaries and configuration editing
44
+
45
+ `/app/presentation/automation/summaries/get` accepts the same bounded selected
46
+ `{ids}` as names. It returns `{items:[{requestedId,found,resource}]}` in normalized input
47
+ order. A found resource contains ListItem fields plus `automationId`, `temp`,
48
+ nullable `lastRunAt` and `expireAt`. Missing items omit resource. It supports
49
+ stored Standard, Temporary and generated programs; it does not load script
50
+ bodies, parse source, expand Widgets, snapshots or graphs. Public program status
51
+ also uses metadata rather than downloading authored source.
52
+
53
+ | Target | Input | Result / retained behavior |
54
+ | --- | --- | --- |
55
+ | `/app/presentation/automation/overview/init` | `{automationId?,inspireId?,inspireVersion?,snapshotId?,args?}` | Existing editor session, required installs, AutomationOverview and already-loaded metadata. Snapshot takes precedence over existing program, then Inspire. Uses the authenticated Client and existing staged-Inspire developer session. |
56
+ | `/app/presentation/automation/overview/config/provide` | `{sessionId,var,input}` | Existing option query and editor revision. |
57
+ | `/app/presentation/automation/overview/config/update` | `{sessionId,var,inputs?,widgetize?}` | Existing config evaluation, diagnostics and revision. |
58
+ | `/app/presentation/automation/overview/save` | `{sessionId,automationId?,inspireId?,inspireVersion?,snapshotId?,preferredName?,debug?,addCard?,canvasIds?}` | `{automationId,runnable,enabled}`. Existing single-save guard, Client ownership, prepare/save/complete revision checks and internal normalized-source persistence. Legacy snapshotId/addCard/canvasIds are accepted but do not perform placement; frontend creates placements separately. |
59
+ | `/app/presentation/automation/overview/cancel` | `{sessionId}` | Canonical acknowledgement without data. Existing Client ownership check and session cleanup. |
60
+ | `/app/presentation/automation/copy` | `{automationId,name?}` | `{automationId,runnable,enabled}`. Copies the existing script/template metadata using the internal save operation. |
61
+ | `/app/presentation/automation/enroll` | `{automationId,name?}` | `{automationId,runnable,enabled}`. Existing enrollment behavior. |
62
+ | `/app/presentation/automation/share` | `{automationId}` | Existing `{sharedId}` from the share service, using trusted caller identity. |
63
+ | `/app/presentation/automation/generated/enable` | `{automationId}` | Existing AutomationStatus for the selected generated program. Enables listening; does not force execution. |
64
+ | `/app/presentation/automation/generated/disable` | `{automationId}` | Existing AutomationStatus after disabling. |
65
+ | `/app/presentation/automation/generated/run` | `{automationId,timezone?,locationSymbol?}` | Canonical acknowledgement without data. Forces the existing stored generated program's threads, including Recurring programs. Runtime execution remains asynchronous. |
66
+
67
+ Generated control accepts only stored `P-`, `S-`, `G-` program IDs. It does not
68
+ rename generated programs, control Group entities, or widen public user-program
69
+ start/stop. Actor and Client come from trusted ingress, never arbitrary sideData.
70
+ Activity logging is best effort and cannot turn a successful control into a
71
+ failure. These UI targets are not automatically exposed to Agent/CLI/MCP.
72
+
73
+ The script-source editor and all legacy `/automation/*` Controller routes are
74
+ retired. Configuration editing retains the existing internal save behavior;
75
+ public `/app/automation/save` retains its independent validated-source contract
76
+ for Agent/CLI/MCP. Internal compiler/format capabilities remain available.
@@ -0,0 +1,28 @@
1
+ # Automation controls and display names
2
+
3
+ These Space-scoped public operations accept user programs, including temporary Task Flows.
4
+ Generated Twin and Group programs remain internal resources.
5
+
6
+ - `/app/automation/start` accepts `automationId` and optional `timezone` and
7
+ `locationSymbol` for execution text. Authenticated actor information is server-owned.
8
+ Recurring programs are enabled; Task Flows are activated and triggered.
9
+ - `/app/automation/stop` accepts `automationId`, disables the program and cancels
10
+ all its instances, including durable waits. Completed actions are not undone.
11
+ - Both return Full `data: {automationId, enabled, running}` with observed state.
12
+ A short Task Flow may already be idle when start returns. Essential renders the
13
+ same fields as YAML. Failures use the canonical error envelope, never `success:false`
14
+ inside successful business data. State pushes may supersede a request result.
15
+ - `/app/automation/rename` accepts only Standard Automations (`AutomationUtil.is_std`),
16
+ excluding Group entity IDs. Twin, Group, Temporary and other prefixed internal
17
+ programs cannot be renamed here. Rename a Group through its Group operation.
18
+ It accepts `automationId` and a nonblank `name`, and returns
19
+ `{automationId, name}`. It changes display metadata without parsing, compiling,
20
+ replacing script source, creating a snapshot or restarting a generation. Public
21
+ list/detail and UI labels use this display name. Ordinary source edits preserve it;
22
+ snapshot rollback restores the snapshot's configuration.
23
+ - `/app/automation/description/update` accepts `automationId`, `expectedScriptHash`
24
+ and at least one of `description` or nonempty `varDescription`. It validates the
25
+ exact source hash and parses the rewritten description. `name` is rejected; use
26
+ rename. The script's authored root name remains available in the script itself.
27
+
28
+ Inputs are closed objects. The schema is `schema/automation.v1.schema.json`.
@@ -0,0 +1,26 @@
1
+ # Client Space focus
2
+
3
+ `/app/client/space/focus` is a Client-only, connection-owned App Facade. It replaces
4
+ `/focus`; no legacy alias exists. It is not a user preference or an Agent/MCP tool.
5
+ The receiving transport must prove the live authenticated Client connection and
6
+ must never forward this call to a Space owner or accept a client ID in input.
7
+
8
+ Request: `{"control":{"mode":"direct"},"input":{"targetSpaceId":"space-b"}}`.
9
+ `targetSpaceId` is required: non-empty unpadded text selects a Space, explicit
10
+ `null` clears focus. User membership is checked authoritatively by Cloud; a local
11
+ Host accepts only its authenticated session's Space (or clear). A bound Space
12
+ Client can only affect its enrolled Space. No read of the old Space is required.
13
+
14
+ Both request-response (`arq`) and one-way state synchronization (`ad`) use this
15
+ same envelope. Native switch orchestration retains its existing ACK and session
16
+ fences. Background synchronization remains per physical connection and is not
17
+ an additional user switch. It never replays an obsolete clear after reconnect.
18
+ For Cloud clear, the existing envelope `scopeId`, when non-empty, is a condition:
19
+ a delayed clear for A cannot erase a newer focus on B. Omitted/blank scope clears
20
+ unconditionally. Local connections can only focus/clear their bound Space.
21
+
22
+ Full success: `{"status":"success","data":{"focusedSpaceId":"space-b"}}`;
23
+ clear returns `focusedSpaceId:null`. `essential` uses normal Facade projection.
24
+ One-way synchronization emits no response. Persistence failure retires the
25
+ connection; success never means that every other connection has synchronized.
26
+ Internal Host/Core presence events and `/bridge/app/focus` retain their own roles.
@@ -1,6 +1,29 @@
1
1
  # Entity UI presentation
2
2
 
3
- `/app/entity/presentation/get` is a read-only App Facade for the UI. Its direct
3
+ ## Selected reference metadata
4
+
5
+ `/app/presentation/entity/meta/get` accepts `{entityType, ids}` under the normal
6
+ Facade envelope. It is a Space-read UI operation, routed by Space mode, not an
7
+ Agent/MCP catalog entry. IDs are nonblank, trimmed, deduplicated in request order
8
+ and bounded to 100 input entries. Empty input and unknown fields are rejected.
9
+ The response is `{items: [{requestedId, found, resource? , reason?}]}`. A found
10
+ resource contains only `id`, `entityType`, `displayName`, and `group`. A missing,
11
+ deleted or wrong-type reference returns `found: false, reason: "not_found"`.
12
+
13
+ Automation uses definition metadata; Widget uses its own definition; other
14
+ entities use their stored Twin identity. `g:` IDs read the Group definition and
15
+ check its member type (an empty legacy member type accepts the requested type).
16
+ No Group members are resolved. All reads use authenticated tenant and Space.
17
+ No source refresh, room lookup, runtime status, history, card/relationship read or
18
+ script expansion is performed. Missing display names remain empty; the UI owns
19
+ its fallback label. Both response detail modes preserve these business fields.
20
+
21
+ This is a selected-reference resolver, not a replacement for the domain catalogs
22
+ of special entities such as AppClient or Location. It does not enumerate them.
23
+
24
+ ## Entity renderer aggregate
25
+
26
+ `/app/presentation/entity/get` is a read-only App Facade for the UI. Its direct
4
27
  input is `{entityType, entityId}`; tenant, Space and caller come from authenticated
5
28
  context. It requires Space read access. It is not an Agent or MCP catalog entry.
6
29
 
@@ -1,6 +1,6 @@
1
1
  # Integration UI presentation
2
2
 
3
- `/app/integration/presentation/get` is a read-only App Facade for UI consumers.
3
+ `/app/presentation/integration/get` is a read-only App Facade for UI consumers.
4
4
  Input is `{integrationId}` for one root Integration. Tenant, Space and caller
5
5
  come from authenticated context; Space read membership is required. Empty IDs,
6
6
  unknown fields and Provider IDs are rejected; missing Integrations are not found.
@@ -0,0 +1,16 @@
1
+ # Native Plugin Preview preflight
2
+
3
+ `/app/plugin/preview/check` is a read-only App Facade on the selected Local
4
+ Space's Hub. It accepts the empty business input `{}` and requires Space Manage
5
+ access. Its business result is `{ "spaceId": "<selected-space>" }`, inside the
6
+ standard Facade response envelope. Unknown input fields are rejected.
7
+
8
+ The desktop CLI must complete this check before asking its local Host to install
9
+ or restart a native Preview. Cloud Spaces cannot route this Hub-only operation.
10
+ The check creates no session, token, reservation or persistent data. It does not
11
+ grant Host installation permission: the Host's existing local authorization still
12
+ applies. Binding rechecks Space authority after installation; this preflight does
13
+ not guarantee that permissions cannot change during the operation.
14
+
15
+ Integration Preview status remains a Space Read operation. Native Preview must
16
+ not use that status read as evidence of management permission.
@@ -0,0 +1,35 @@
1
+ # Reference picker presentation
2
+
3
+ UI-only Space reads use the standard FacadeCall and success/data/error envelope.
4
+ Both targets require spaceRead and follow scopeMode routing. They are not native
5
+ Agent, CLI or MCP tools. Full and Essential preserve the same business fields;
6
+ Essential represents them as YAML.
7
+
8
+ | Target | Input | Business output |
9
+ | --- | --- | --- |
10
+ | `/app/presentation/reference/type/list` | `{}` | `{types:[{entityType,name,category}]}` |
11
+ | `/app/presentation/reference/item/list` | `{entityType}` | `{items:[{id,name,entityType,category,capabilities,group}]}` |
12
+
13
+ Inputs reject unknown fields. entityType is a nonblank string, trimmed at the
14
+ boundary. category is device, widget, automation or entity, derived by the server.
15
+ Candidate names do not replace IDs. Empty capabilities and false group values are
16
+ preserved. No business input selects another Space.
17
+
18
+ Types come from installed and auto-installed Integration standalone declarations,
19
+ deduplicated and sorted by entityType. Empty types are not hidden by counting their
20
+ instances. Item reads select one type only. Widget reads preserve the shared global
21
+ catalog (including global Automation-owned Widgets), not the narrower Widget-page
22
+ visibility rules. Device and Entity groups retain their existing capability
23
+ resolution. Automation references include regular and temporary programs and
24
+ Automation groups, with the existing internal-object exclusions and display names.
25
+ Location and AppClient retain their dedicated discovery rules.
26
+
27
+ Reference reads omit room/enablement enrichment, group runtime state, Automation
28
+ status and script hydration. They do not load Widget values, histories, owner
29
+ names or full configurations. Metadata reads may still require underlying stored
30
+ rows and group capability resolution; this contract does not promise one DB query.
31
+
32
+ The UI lazily caches types and items by Space/type and merges in-flight reads.
33
+ Search remains client-side. Failed requests are retryable and are not successful
34
+ empty catalogs. Space changes discard late results. Message reference identity and
35
+ serialization remain unchanged. Legacy `/reference/*` request routes are retired.
@@ -14,7 +14,7 @@ follow the Space execution authority on both local Core and cloud.
14
14
  | `/app/widget/list` | `{}` | Summaries: id, displayName, description, type, tag; Data adds dataType/repeated. No list mode or full-definition catalog |
15
15
  | `/app/widget/detail/get` | `{ids}` | Existing ordered `items` with `widget` configuration; no value, next or nextTimestamp |
16
16
  | `/app/widget/value/get` | `{ids}` | Ordered `items` with `resource: {widgetId, revision, value, next?, nextTimestamp?}` |
17
- | `/app/automation/status` | `{ids}` | Ordered `items` with `resource: {automationId, enabled, runnable, running, blocked, lastRunAt}` |
17
+ | `/app/automation/status` | `{ids}` | Ordered `items` with `resource: {automationId, enabled, runnable, corrupted, running, blocked, lastRunAt}`; temporary programs also include `expireAt` |
18
18
  | `/app/graph/relations/get` | `{resource: {kind, id}}` | `{resource, relations: [{from, relation, to, capabilityIds?}]}` |
19
19
 
20
20
  Device list and detail discriminate status using `group`: physical Devices (`false`)
@@ -101,7 +101,7 @@ open detail renderer; relationships by the view that displays them. Pushes recei
101
101
  during a snapshot request are applied after it lands. Switching scopes or disposing
102
102
  controllers prevents late responses from landing in another Space.
103
103
 
104
- House continues to use `/app/entity/presentation/get` and legacy Twin reads as its
104
+ House continues to use `/app/presentation/entity/get` and legacy Twin reads as its
105
105
  separate development boundary. Pod Deck retains its existing complete Card content
106
106
  reader. These consumers are not silently migrated to static Canvas responses.
107
107
 
@@ -0,0 +1,45 @@
1
+ # Space presentation and focused business readers
2
+
3
+ All calls use the standard App Facade envelope. These presentation targets are
4
+ UI reads, not Agent tools. They execute in the cloud and require user identity.
5
+
6
+ - `/app/presentation/space/list` accepts `{}` and returns `{spaces}`. Each item
7
+ has `spaceId`, `name`, `mode`, `llmMode`, `betaMode`, `defaultActive`,
8
+ `lifecycleState`, `connectionMode`, and `currentUserRole`. Nullable metadata
9
+ remains nullable. It reads enrolled memberships and Space metadata, then live
10
+ connection state; it never expands members, Locations, preferences or profiles.
11
+ - `/app/presentation/space/get` accepts `{includeMembers?: boolean}` (default
12
+ false) and returns `{space, members?}`. `space` has the same summary shape.
13
+ Only `includeMembers:true` reads the Space membership list and member profiles.
14
+ Each member has `userId`, `email`, `name`, `pending` and `role`. Active members
15
+ may read this presentation; management mutations still require their own grants.
16
+ - `/app/space/location/list` accepts `{}` and returns `{locations}`. Each Location
17
+ has `spaceId`, `locationId`, `name`, `address`, `timezone`, `countryCode`,
18
+ `latitude`, `longitude`, and `altitude`. It executes according to Space mode,
19
+ reading only the authoritative backend's Locations; it does not read Space
20
+ details, members or preferences and has no cloud fallback in local mode.
21
+ - `/app/space/delete/status` accepts `{operationId}` and returns `accepted`,
22
+ `operationId`, `spaceId`, `state`, `message`, and `deleted`. It queries the
23
+ existing durable operation and never starts or restarts deletion. Authorization
24
+ uses the operation initiator and authenticated user, so polling survives removal
25
+ of membership and Space data. The request retains the operation's Space ID.
26
+ - `/app/space/beta/update` accepts `{betaMode: boolean}` and returns
27
+ `{betaMode, poll}`. It saves through the existing Space Beta service. A true
28
+ `poll` means the Client must wait for the Hub to apply the selected mode.
29
+
30
+ Invitation accept/reject remain membership operations and never select a Client's
31
+ Space. The UI explicitly uses the connection focus protocol after joining when
32
+ its flow intends to enter the joined Space. Current-Space switches must not be
33
+ discarded by the previous Space's request guard.
34
+
35
+ Client handling of presentation replies unwraps successful Full Facade responses
36
+ before updating cloud-authoritative membership/connection caches. Presentation
37
+ wire DTOs (`spaceId`, `llmMode`) are explicitly projected into native state;
38
+ login and Hub authorization retain their independently owned wire DTOs.
39
+
40
+ Legacy `/scope/list`, `/scope/get`, `/scope/preference/get`, `/scope/agent/get`,
41
+ and `/scope/location/list` readers are retired, without fallback aliases. Native
42
+ UI membership refresh uses `/app/presentation/space/list`; the CLI uses
43
+ `/app/space/list` for identity-only listing and selection checks, and presentation
44
+ listing for its full management view. Neither lightweight flow expands per-Space
45
+ context. `/focus` remains connection control, not a business Facade.
@@ -180,7 +180,9 @@ and removal-only cleanup authority.
180
180
 
181
181
  ## Physical backend and reads
182
182
 
183
- `storage.backend.v2` remains separate from the public `storage.v1` capability.
183
+ `storage.backend.v1` remains separate from the public `storage.v1` capability.
184
+ Storage is in development: both protocols evolve in place as v1, with no legacy
185
+ readers, version negotiation or compatibility branches.
184
186
  Describe reports capacity; grant authorizes stat/read/delete; prepare-write reserves
185
187
  space for an immutable Blob before issuing a write grant. A lost reply can have
186
188
  reserved bytes. Receipts are resolved before changing repositories. Issued grants survive temporary Core control disconnection until normal expiry;
@@ -0,0 +1,36 @@
1
+ # Widget UI presentation reads
2
+
3
+ These are UI App Facades with the standard Full/Essential response and Space read
4
+ policy. They are not Agent/CLI/MCP tools. Inputs reject unknown fields.
5
+
6
+ | Target | Input | Business output and read boundary |
7
+ | --- | --- | --- |
8
+ | `/app/presentation/widget/list` | Required `{type: "data" \| "button" \| "circadian"}` | `{items, automationNames}`. Reads only that type's Widget table. Global standalone Widgets, defaults and Automation-owned parameters are included; card interiors, Twin and weather internals are excluded. Owner labels use one selected metadata batch, only for returned Automation owners. No script, relation, history or all-type reads. |
9
+ | `/app/presentation/widget/names/get` | `{ids}`: 1–100 selected, trimmed, deduplicated IDs | `{names: {id: displayName}}`. Missing/blank names omitted. Only selected Widget rows; no legacy `name` fallback. |
10
+ | `/app/presentation/widget/defaults/get` | `{}` | `{items, automationNames:{}}`. Preserves the existing default Widget lookup and lazy initialization, then reads the selected Data Widgets. |
11
+
12
+ Rows carry ID, revision, displayName, description, source/baseId, global, tag,
13
+ current value, and type-specific control configuration. Circadian rows contain
14
+ only `curve`, `curvePreview` (active curve's UI points), current/next values and
15
+ `nextTimestamp`; they omit complete schedules, location and internal state.
16
+ Complete configuration uses `/app/widget/detail/get`; runtime uses
17
+ `/app/widget/value/get`, both selected-ID reads. Full timestamps are milliseconds;
18
+ Essential renders explicit timestamps and typed Timestamp values in Home timezone.
19
+
20
+ `/app/widget/list` remains the shared summary catalog. Optional `type` limits the
21
+ physical Widget-table read; optional `excludeSources` filters known sources
22
+ (`widget`, `automation`, `special`, `twin`, `weather`). Omitted filters preserve
23
+ the existing global supported-type catalog. It never returns control values or
24
+ full definitions. Dashboard selection excludes Automation-owned parameters and
25
+ adds selected IDs directly without definition/value reads.
26
+
27
+ The UI loads only the active type, caches visited tabs within the Space and never
28
+ fetches inactive counts. Automation parameters are grouped by owning Automation;
29
+ users can operate them and open the owner but cannot edit/delete their definition.
30
+ Realtime changes update loaded rows, deletes remove them, and reconnect or
31
+ incomplete presentation changes refresh only the relevant active tab. Space
32
+ switches clear caches and reject late responses. All public Widget request
33
+ Controllers, including reads and writes, are removed. Writes use the shared
34
+ `/app/widget/*` Facades described in `widget-writes-v1.md`.
35
+ `/widget/reportUpdate` remains a push protocol; internal Automation dispatch
36
+ remains separate from public request routing.
@@ -0,0 +1,44 @@
1
+ # Widget writes
2
+
3
+ Widget create and configuration Facades are shared by UI, Agent, CLI and MCP.
4
+ Creation requires displayName; description may be omitted. A supplied description
5
+ must remain nonblank. Existing text value inputs remain supported.
6
+
7
+ ## Data runtime values
8
+
9
+ `/app/widget/data/update` accepts widgetId and exactly one non-null input:
10
+
11
+ - value: a text-encoded scalar, for example "false" or "25C".
12
+ - values: an array of text-encoded values, including an empty array.
13
+ - typedValue: typed JSON, for example false, 0, [], or
14
+ {"_type":"Temperature","value":25,"unit":"C"}.
15
+
16
+ Explicit typedValue:null is rejected; clearing is a separate Automation capability.
17
+ The stored definition determines type, scalar/array shape, options and bounds.
18
+ Object selections are restricted to stored Automation candidates. Only their type
19
+ and UID identify a candidate; stored business fields replace caller-supplied fields.
20
+ Both text and typed inputs use the same manager, write constraints, history and events.
21
+ The request never changes configuration, visibility or ownership.
22
+
23
+ ## Circadian runtime controls
24
+
25
+ `/app/widget/circadian/runtime/update` accepts widgetId and at least one of curve,
26
+ offset, multiplier. Unknown fields and explicit null controls are rejected.
27
+ Curve must identify an existing schedule; offset must match the stored data type;
28
+ multiplier must be finite. Updates retain existing manual adjustment timing and
29
+ recompute the current and next values. Automation-owned parameters are operable.
30
+ Visibility, owner, name, location and schedule definitions remain unchanged.
31
+ This is a direct operation, independent of prepared definition edits.
32
+
33
+ Data value updates, Button triggers, Circadian runtime controls and delete return
34
+ an acknowledgement: {"status":"success"}, without business data.
35
+ Create/configuration responses retain action, widgetId, type and displayName.
36
+
37
+ ## Deletion
38
+
39
+ `/app/widget/delete` retains the required explicit safe flag. The UI passes false
40
+ to preserve its previous deletion behavior. This migration does not change the
41
+ shared safe=true protection for Widgets referenced by Automations.
42
+
43
+ All legacy public /widget/* request Controllers are retired. /widget/reportUpdate
44
+ is a push event and internal /dispatch/widget/* operations retain their own roles.
@@ -826,5 +826,101 @@
826
826
  "scopeId": "space",
827
827
  "target": "/app/object-storage/folders/set-limit",
828
828
  "allowed": false
829
+ },
830
+ {
831
+ "name": "space-ui:unscoped:/app/presentation/space/list",
832
+ "principal": {
833
+ "kind": "user",
834
+ "userId": "user"
835
+ },
836
+ "memberRole": null,
837
+ "tenantId": "tenant",
838
+ "scopeId": "space",
839
+ "target": "/app/presentation/space/list",
840
+ "allowed": true
841
+ },
842
+ {
843
+ "name": "space-ui:MEMBER:/app/presentation/space/get",
844
+ "principal": {
845
+ "kind": "user",
846
+ "userId": "user"
847
+ },
848
+ "memberRole": "MEMBER",
849
+ "tenantId": "tenant",
850
+ "scopeId": "space",
851
+ "target": "/app/presentation/space/get",
852
+ "allowed": true
853
+ },
854
+ {
855
+ "name": "space-ui:unscoped:/app/presentation/space/get",
856
+ "principal": {
857
+ "kind": "user",
858
+ "userId": "user"
859
+ },
860
+ "memberRole": null,
861
+ "tenantId": "tenant",
862
+ "scopeId": "space",
863
+ "target": "/app/presentation/space/get",
864
+ "allowed": false
865
+ },
866
+ {
867
+ "name": "space-ui:MEMBER:/app/space/location/list",
868
+ "principal": {
869
+ "kind": "user",
870
+ "userId": "user"
871
+ },
872
+ "memberRole": "MEMBER",
873
+ "tenantId": "tenant",
874
+ "scopeId": "space",
875
+ "target": "/app/space/location/list",
876
+ "allowed": true
877
+ },
878
+ {
879
+ "name": "space-ui:unscoped:/app/space/location/list",
880
+ "principal": {
881
+ "kind": "user",
882
+ "userId": "user"
883
+ },
884
+ "memberRole": null,
885
+ "tenantId": "tenant",
886
+ "scopeId": "space",
887
+ "target": "/app/space/location/list",
888
+ "allowed": false
889
+ },
890
+ {
891
+ "name": "space-ui:unscoped:/app/space/delete/status",
892
+ "principal": {
893
+ "kind": "user",
894
+ "userId": "user"
895
+ },
896
+ "memberRole": null,
897
+ "tenantId": "tenant",
898
+ "scopeId": "space",
899
+ "target": "/app/space/delete/status",
900
+ "allowed": true
901
+ },
902
+ {
903
+ "name": "space-ui:MEMBER:/app/space/beta/update",
904
+ "principal": {
905
+ "kind": "user",
906
+ "userId": "user"
907
+ },
908
+ "memberRole": "MEMBER",
909
+ "tenantId": "tenant",
910
+ "scopeId": "space",
911
+ "target": "/app/space/beta/update",
912
+ "allowed": false
913
+ },
914
+ {
915
+ "name": "space-ui:OWNER:/app/space/beta/update",
916
+ "principal": {
917
+ "kind": "user",
918
+ "userId": "user"
919
+ },
920
+ "memberRole": "OWNER",
921
+ "tenantId": "tenant",
922
+ "scopeId": "space",
923
+ "target": "/app/space/beta/update",
924
+ "allowed": true
829
925
  }
830
926
  ]