@mhome/app-facade-protocol 1.119.1 → 1.126.0
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.
- package/README.md +19 -0
- package/contract/client-space-focus-v1.md +1 -1
- package/contract/entity-v1.md +53 -0
- package/contract/pod-commissioning-v1.md +12 -12
- package/contract/reference-presentation-v1.md +1 -1
- package/contract/resource-read-v1.md +7 -2
- package/contract/space-presentation-v1.md +13 -3
- package/contract/trace-v1.md +17 -0
- package/contract/user-navigation-v1.md +47 -0
- package/fixtures/authorization/decisions.json +114 -114
- package/fixtures/billing.conformance.json +49 -2
- package/fixtures/card-skins/space.json +1 -1
- package/fixtures/entity-presentation.conformance.json +2 -2
- package/fixtures/integration-presentation.conformance.json +1 -1
- package/fixtures/local-hub-state.configured.json +1 -1
- package/fixtures/pod-commission.awaiting-authorization.json +3 -3
- package/fixtures/presentation/cases.json +666 -35
- package/fixtures/recording.conformance.json +15 -8
- package/fixtures/routing/cases.json +48 -48
- package/fixtures/trace/linear.json +6 -0
- package/fixtures/widget-presentation.v1.json +3 -3
- package/manifest/app-facade.v1.json +47 -3
- package/manifest/authorization.v1.json +15 -6
- package/manifest/host-provision-targets.v1.json +1 -1
- package/manifest/hub-targets.v1.json +1 -1
- package/manifest/navigation-cards.v1.json +29 -0
- package/manifest/pod-targets.v1.json +2 -2
- package/manifest/presentation-times.v1.json +377 -29
- package/manifest/recording.v1.json +18 -15
- package/manifest/routing.v1.json +5 -4
- package/manifest/targets.v1.json +1 -1
- package/package.json +1 -1
- package/protocol.json +2 -1
- package/schema/delegated-call.v1.schema.json +2 -2
- package/schema/local-hub-state.v1.schema.json +2 -2
- package/schema/pod-commission.v1.schema.json +6 -6
- package/schema/routing.v1.schema.json +3 -3
- package/schema/system-hosts.v1.schema.json +2 -2
- package/schema/trace.v1.schema.json +278 -0
- package/schema/user-navigation.v1.schema.json +133 -0
package/README.md
CHANGED
|
@@ -49,6 +49,25 @@ results and compose real Facade calls; they are not alternative DTOs for a singl
|
|
|
49
49
|
Facade operation. A UI view model may derive display state after the SDK boundary.
|
|
50
50
|
Composition does not imply a distributed transaction or automatic rollback.
|
|
51
51
|
|
|
52
|
+
## Full values and Essential presentation
|
|
53
|
+
|
|
54
|
+
Full preserves canonical typed values, numeric instants and source units. Business execution,
|
|
55
|
+
caching, aggregation, media validation and UI consumers use Full. Only Essential replaces
|
|
56
|
+
explicitly tagged runtime primitives with display text. Numeric timestamp fields use the
|
|
57
|
+
shared `manifest/presentation-times.v1.json` paths and declared units; arbitrary numbers,
|
|
58
|
+
field names and provider payloads are never guessed. Declared decimal-string Storage times
|
|
59
|
+
are rendered at their explicit paths; Full keeps the Storage wire types.
|
|
60
|
+
|
|
61
|
+
Timestamp text includes Home's IANA timezone, offset and millisecond precision when present.
|
|
62
|
+
Missing or invalid Home timezone uses UTC. User/connection operations have no Space presentation context and use UTC/source units; an incidental Space ID never grants Home/preference reads. Scope temperature/length/weight preferences apply
|
|
63
|
+
only to Essential unit display. Native Agent workflows extract typed facts and media from Full
|
|
64
|
+
before rendering final model history with the same rules, using structured context facts tied
|
|
65
|
+
to the user-scope generation. Prepared control objects are not business display data.
|
|
66
|
+
|
|
67
|
+
Core and cloud direct calls resolve presentation context before executing an Essential write.
|
|
68
|
+
MCP workflow projection preserves already settled outcomes if its optional context lookup fails:
|
|
69
|
+
it adds a presentation warning, uses UTC and retains source units, without replaying the operation.
|
|
70
|
+
|
|
52
71
|
## App result contract
|
|
53
72
|
|
|
54
73
|
`control.responseDetail` selects information depth: `full` (default) returns
|
|
@@ -15,7 +15,7 @@ Both request-response (`arq`) and one-way state synchronization (`ad`) use this
|
|
|
15
15
|
same envelope. Native switch orchestration retains its existing ACK and session
|
|
16
16
|
fences. Background synchronization remains per physical connection and is not
|
|
17
17
|
an additional user switch. It never replays an obsolete clear after reconnect.
|
|
18
|
-
For Cloud clear, the existing envelope `
|
|
18
|
+
For Cloud clear, the existing envelope `spaceId`, when non-empty, is a condition:
|
|
19
19
|
a delayed clear for A cannot erase a newer focus on B. Omitted/blank scope clears
|
|
20
20
|
unconditionally. Local connections can only focus/clear their bound Space.
|
|
21
21
|
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# General Entity reads
|
|
2
|
+
|
|
3
|
+
All reads use authenticated tenant and Space. Device, Automation and Widget types
|
|
4
|
+
belong to their dedicated domains. Location and concrete AppClient endpoints are
|
|
5
|
+
built-in Entity types; other types are Integration-defined.
|
|
6
|
+
|
|
7
|
+
- `/app/entity/list`: `{entityType, includeGroups?, integrationId?, connectId?}` returns `{entities: [EntityMetadata]}` for the
|
|
8
|
+
exact type, including same-type Groups by default. `includeGroups:false` reads only
|
|
9
|
+
ordinary entities. A Provider filter selects ordinary source resources only;
|
|
10
|
+
`connectId` requires `integrationId`. There is one list, with no mode or refresh.
|
|
11
|
+
- `/app/entity/detail/get`: `{entityType, ids}` returns ordered deduplicated
|
|
12
|
+
`{items: [{requestedId, found, reason?, entity?}]}`. `entity` is exactly the same
|
|
13
|
+
metadata model as a list row; callers with known IDs need not list first.
|
|
14
|
+
- `/app/entity/state/get`: `{entityType, ids}` (1–100 nonblank IDs before
|
|
15
|
+
deduplication) returns ordered `{items: [{requestedId, found, reason?, resource?}]}`.
|
|
16
|
+
`resource` is `{entityId, latest: [{capabilityId, state?, event?, log?}]}`.
|
|
17
|
+
These are latest persisted observations, without refreshing a Provider or deriving
|
|
18
|
+
online status. Groups read their own observations without expanding members.
|
|
19
|
+
Existing Location/AppClient endpoints return `latest: []`; missing resources use
|
|
20
|
+
`found: false, reason: "not_found"` and omit resource. No observations is not missing.
|
|
21
|
+
- `/app/entity/history/list` remains independent and returns at most 100 records.
|
|
22
|
+
|
|
23
|
+
Metadata contains id, name, entityType, integration?, connectId?, active, capabilities, data,
|
|
24
|
+
tags (Blueprint IDs), availabilityStatus?, issue, group and entityIds (Group members).
|
|
25
|
+
It contains no current capability values. Full retains the complete metadata.
|
|
26
|
+
Essential omits Blueprint tags, ordinary entityIds, absent optional values and
|
|
27
|
+
redundant built-in data identity fields. It preserves arbitrary Integration data,
|
|
28
|
+
including Provider-local data.id, falsy values and Group entityIds. State payloads
|
|
29
|
+
are not trimmed; only declared observation timestamps are localized by the shared
|
|
30
|
+
presentation-time rules. No arbitrary business-data timestamp inference is used.
|
|
31
|
+
|
|
32
|
+
List, detail and state respect installed Provider and persisted source visibility.
|
|
33
|
+
Ordinary reads accept Provider-owned Twins only. Groups are read from independent
|
|
34
|
+
definitions; their derived Twins cannot masquerade as ordinary entities.
|
|
35
|
+
Provider-defined types resolve installation through their parent Integration.
|
|
36
|
+
Built-ins are scoped to the current Space and reject Provider filters.
|
|
37
|
+
|
|
38
|
+
## Device sources
|
|
39
|
+
|
|
40
|
+
`/app/device/list` accepts `{includeGroups?, integrationId?, connectId?}` with the
|
|
41
|
+
same source filter semantics. Its rows contain standard Device metadata, never
|
|
42
|
+
card relationships or current observations. UI Resources and House compose domain
|
|
43
|
+
reads; they do not require a multi-type backend catalog.
|
|
44
|
+
|
|
45
|
+
`/app/device/source/resolve` is a read-only onboarding helper. Input is
|
|
46
|
+
`{integrationId, connectId?, sourceIds}` (1–100 nonblank opaque IDs before
|
|
47
|
+
ordered deduplication). A connected Provider requires one exact Connection; a
|
|
48
|
+
direct Provider omits it. Each result is `{requestedSourceId, found, entityId?,
|
|
49
|
+
reason?}`. Missing IDs return `not_found`; multiple canonical IDs for one source
|
|
50
|
+
identity fail the request. Only installed, visible Provider-owned Devices match.
|
|
51
|
+
The operation never synchronizes sources or returns metadata, state or cards.
|
|
52
|
+
Source synchronization remains `/app/integration/entity/refresh`; onboarding reads
|
|
53
|
+
standard Device metadata only after an explicitly successful synchronization.
|
|
@@ -135,7 +135,7 @@ the response also carries `"mode":"reprovision"` and `wifiConfigured` is false
|
|
|
135
135
|
|
|
136
136
|
`pod-credential` requests:
|
|
137
137
|
|
|
138
|
-
- `{"op":"deliver","clientMode":"user","podId","loginGrant","
|
|
138
|
+
- `{"op":"deliver","clientMode":"user","podId","loginGrant","spaceId","cloudApi","cloudWs"}` →
|
|
139
139
|
`{"ok":true,"state":"activating"}`. Accepted only in `wifi_connected` outside a
|
|
140
140
|
Wi-Fi change window; otherwise `{"ok":false,"state":<current pod state>,"reason"}`
|
|
141
141
|
with `reason` `invalid_payload` (a missing or malformed field, including a
|
|
@@ -241,7 +241,7 @@ Lion returns `{podId, loginGrant, activateBefore}`. The random approval expires
|
|
|
241
241
|
in ten minutes and is bound to this User and the Pod identity key. It is not a
|
|
242
242
|
login refresh token. The helper must never deliver its own login credentials.
|
|
243
243
|
|
|
244
|
-
The App delivers the approval through the paired LAN channel. `
|
|
244
|
+
The App delivers the approval through the paired LAN channel. `spaceId` in the
|
|
245
245
|
device delivery selects the initial UI Space; it does not constrain the User
|
|
246
246
|
identity or grant Space authority. The device independently redeems the approval
|
|
247
247
|
at `POST /api/v1/pod/login/redeem` with `{podId, loginGrant, proof}`. The proof is
|
|
@@ -304,7 +304,7 @@ never show these values or `message` verbatim.
|
|
|
304
304
|
| `invalid_wifi` | SSID not 1–32 bytes, or password not empty or 8–63 characters (64 hex digits) |
|
|
305
305
|
| `bluetooth_unavailable` | no usable Bluetooth adapter |
|
|
306
306
|
| `not_signed_in` | no signed-in MeowLink account |
|
|
307
|
-
| `
|
|
307
|
+
| `space_not_offered` | the Space is not among those offered, or the account has no Space |
|
|
308
308
|
| `cloud_unreachable_for_device` | the cloud address is loopback |
|
|
309
309
|
| `busy` | the native Client cannot take the request now; retry |
|
|
310
310
|
|
|
@@ -318,7 +318,7 @@ never show these values or `message` verbatim.
|
|
|
318
318
|
| `/local/pod/commission/code` | `{"sessionId","code"}` | session snapshot |
|
|
319
319
|
| `/local/pod/commission/wifi` | `{"sessionId","ssid","password"?}` | session snapshot |
|
|
320
320
|
| `/local/pod/commission/wifi/scan` | `{"sessionId"}` | session snapshot |
|
|
321
|
-
| `/local/pod/commission/authorize` | `{"sessionId","
|
|
321
|
+
| `/local/pod/commission/authorize` | `{"sessionId","spaceId"}` | session snapshot |
|
|
322
322
|
| `/local/pod/commission/status` | `{}` | `{"session": snapshot or null}` |
|
|
323
323
|
| `/local/pod/bluetooth/settings` | `{}` | `{"opened"}` |
|
|
324
324
|
|
|
@@ -346,7 +346,7 @@ page does not end setup; returning to it reads `status`. Waiting for user input
|
|
|
346
346
|
also counts toward the deadline. Discovery leases still control scanning.
|
|
347
347
|
|
|
348
348
|
Sign-in and Spaces are checked at `start` (`not_signed_in`,
|
|
349
|
-
`
|
|
349
|
+
`space_not_offered`) and again when the authorization prompt is built.
|
|
350
350
|
|
|
351
351
|
A pod inside a Wi-Fi change window (`pod-info` `"mode":"reprovision"`) gets a
|
|
352
352
|
Wi-Fi-only session. From `reading_info` on, its snapshots carry
|
|
@@ -409,7 +409,7 @@ The schemas are `schema/pod-discovery.v1.schema.json` and
|
|
|
409
409
|
## 8. Space Client local credential
|
|
410
410
|
|
|
411
411
|
`POST /api/v1/pod/space/credential/issue` uses the assisting App's user
|
|
412
|
-
`Authorization` and `
|
|
412
|
+
`Authorization` and `SpaceId`, with the same device identity request as personal
|
|
413
413
|
issuance. Only the current Space owner may assist. The Space must be local and have one
|
|
414
414
|
published Hub with business and Host TLS identities. Cloud Spaces must complete
|
|
415
415
|
Hub setup first. The issuer derives `appClientId` from tenant, Space and the validated
|
|
@@ -417,12 +417,12 @@ persistent device-key fingerprint. The same device in the same Space retains thi
|
|
|
417
417
|
ID across retries; a factory reset rotates its key. Candidate local JWTs use the
|
|
418
418
|
existing Space signing key and local-client audience.
|
|
419
419
|
|
|
420
|
-
Claims are `token_type: local_space_client_access`, `sub: <
|
|
421
|
-
`tenant_id`, `
|
|
420
|
+
Claims are `token_type: local_space_client_access`, `sub: <spaceId>`,
|
|
421
|
+
`tenant_id`, `space_id`, `hub_id`, `app_client_id`, `jti`, `iat`, `exp`, `is_test`.
|
|
422
422
|
The expiry follows existing long-lived User local credentials. No `userId`,
|
|
423
|
-
initializer identity, `podId` or `
|
|
423
|
+
initializer identity, `podId` or `issuedSpaceId` grants authority. User local
|
|
424
424
|
credentials retain `local_client_access` and must not contain `app_client_id`.
|
|
425
|
-
Personal Pod issuance no longer stores `
|
|
425
|
+
Personal Pod issuance no longer stores `issuedSpaceId`.
|
|
426
426
|
|
|
427
427
|
Lion sends `{jwtToken, clientSource:"pod"}` to the authenticated Hub Bridge
|
|
428
428
|
`/hub/client/space/register`. Core verifies signature, audience, issuer, current
|
|
@@ -437,12 +437,12 @@ The Bridge returns `{credentialId, appClientId, jwtToken, status, activateBefore
|
|
|
437
437
|
Lion returns `{credentialId, appClientId, jwtToken, credentialState, activateBefore,
|
|
438
438
|
hubIdentity}`. State is `pending` with its epoch-millisecond first-use deadline, or
|
|
439
439
|
`active` with `activateBefore:null`. Issuance alone does not publish a device-list entry. The Hub identity includes its business P-256 key and
|
|
440
|
-
Host TLS public key, bound to `tenantId`, `
|
|
440
|
+
Host TLS public key, bound to `tenantId`, `spaceId`, `hubId` and `hostId`.
|
|
441
441
|
|
|
442
442
|
The secured LAN `pod-credential` delivery is:
|
|
443
443
|
|
|
444
444
|
```json
|
|
445
|
-
{"op":"deliver","clientMode":"space","credentialId":"…","appClientId":"a:…","jwtToken":"…","tenantId":"…","
|
|
445
|
+
{"op":"deliver","clientMode":"space","credentialId":"…","appClientId":"a:…","jwtToken":"…","tenantId":"…","spaceId":"…","hubIdentity":{}}
|
|
446
446
|
```
|
|
447
447
|
|
|
448
448
|
The actual `hubIdentity` is the complete issuer response. User credential fields
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Reference picker presentation
|
|
2
2
|
|
|
3
3
|
UI-only Space reads use the standard FacadeCall and success/data/error envelope.
|
|
4
|
-
Both targets require spaceRead and follow
|
|
4
|
+
Both targets require spaceRead and follow spaceMode routing. They are not native
|
|
5
5
|
Agent, CLI or MCP tools. Full and Essential preserve the same business fields;
|
|
6
6
|
Essential represents them as YAML.
|
|
7
7
|
|
|
@@ -11,6 +11,9 @@ follow the Space execution authority on both local Core and cloud.
|
|
|
11
11
|
| --- | --- | --- |
|
|
12
12
|
| `/app/device/detail/get` | `{ids}` | Existing ordered `items` with `device` definitions, capabilities, group membership, physical Twin enablement or Group application state, source metadata; no online or state fields |
|
|
13
13
|
| `/app/device/state/get` | `{ids}` | Ordered `items` with `resource: {entityId, online?, onlineUpdatedAtSec?, latest}` |
|
|
14
|
+
| `/app/entity/list` | `{entityType}` | Complete static directory metadata of the exact general Entity type; same-type Groups included; no list modes |
|
|
15
|
+
| `/app/entity/detail/get` | `{entityType, ids}` | Selected metadata identical to list rows, under ordered `items[].entity` |
|
|
16
|
+
| `/app/entity/state/get` | `{entityType, ids}` | Ordered `items` with `resource: {entityId, latest}`; no source refresh or derived online; see [Entity contract](entity-v1.md) |
|
|
14
17
|
| `/app/widget/list` | `{}` | Summaries: id, displayName, description, type, tag; Data adds dataType/repeated. No list mode or full-definition catalog |
|
|
15
18
|
| `/app/widget/detail/get` | `{ids}` | Existing ordered `items` with `widget` configuration; no value, next or nextTimestamp |
|
|
16
19
|
| `/app/widget/value/get` | `{ids}` | Ordered `items` with `resource: {widgetId, revision, value, next?, nextTimestamp?}` |
|
|
@@ -30,7 +33,9 @@ Observation batches accept 1–100 IDs, reject blanks, trim and deduplicate pres
|
|
|
30
33
|
request order. Every normalized ID gets `{requestedId, found, resource?}`; missing
|
|
31
34
|
resources have `found: false, reason: "not_found"`. A known Device with no observations
|
|
32
35
|
has `found: true` and `latest: []`. Unknown reachability omits `online`; it is not false.
|
|
33
|
-
Device state supports visible physical devices and Device groups.
|
|
36
|
+
Device state supports visible physical devices and Device groups. General Entity state
|
|
37
|
+
also requires entityType, supports same-type Groups and respects source visibility;
|
|
38
|
+
known Location/AppClient endpoints return empty latest without querying Twin. Automation status
|
|
34
39
|
accepts user program IDs, including retained one-time programs, not groups or internal
|
|
35
40
|
Twin programs. Widget value supports Data, Button and Circadian resources. JSON null
|
|
36
41
|
is a value, not a missing resource. Button values may be null.
|
|
@@ -83,7 +88,7 @@ identity/connection/capabilities. Schema publication does not expire them. Expli
|
|
|
83
88
|
device refresh rereads attributes and reconciles related Groups. These are write-side
|
|
84
89
|
operations; definition reads remain cache-only.
|
|
85
90
|
|
|
86
|
-
`/twin/entityUpdate` pushes `{
|
|
91
|
+
`/twin/entityUpdate` pushes `{spaceId, entityIds}` after cached metadata changes.
|
|
87
92
|
Open views refresh their definition batches; other Spaces and unrelated entities
|
|
88
93
|
are ignored. A new definition owns its capability membership: old observations
|
|
89
94
|
for removed capabilities must not be merged back into the current view. History
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# Space presentation and focused business readers
|
|
2
2
|
|
|
3
3
|
All calls use the standard App Facade envelope. These presentation targets are
|
|
4
|
-
UI reads, not Agent tools.
|
|
4
|
+
UI reads, not Agent tools. List/get execute in the cloud and require user identity;
|
|
5
|
+
the Location reader follows Space mode.
|
|
5
6
|
|
|
6
7
|
- `/app/presentation/space/list` accepts `{}` and returns `{spaces}`. Each item
|
|
7
8
|
has `spaceId`, `name`, `mode`, `llmMode`, `betaMode`, `defaultActive`,
|
|
@@ -13,11 +14,14 @@ UI reads, not Agent tools. They execute in the cloud and require user identity.
|
|
|
13
14
|
Only `includeMembers:true` reads the Space membership list and member profiles.
|
|
14
15
|
Each member has `userId`, `email`, `name`, `pending` and `role`. Active members
|
|
15
16
|
may read this presentation; management mutations still require their own grants.
|
|
16
|
-
- `/app/space/location/list` accepts `{}` and returns `{locations}`. Each Location
|
|
17
|
+
- `/app/presentation/space/location/list` accepts `{}` and returns `{locations}`. Each Location
|
|
17
18
|
has `spaceId`, `locationId`, `name`, `address`, `timezone`, `countryCode`,
|
|
18
19
|
`latitude`, `longitude`, and `altitude`. It executes according to Space mode,
|
|
19
20
|
reading only the authoritative backend's Locations; it does not read Space
|
|
20
21
|
details, members or preferences and has no cloud fallback in local mode.
|
|
22
|
+
|
|
23
|
+
## Shared Space management
|
|
24
|
+
|
|
21
25
|
- `/app/space/delete/status` accepts `{operationId}` and returns `accepted`,
|
|
22
26
|
`operationId`, `spaceId`, `state`, `message`, and `deleted`. It queries the
|
|
23
27
|
existing durable operation and never starts or restarts deletion. Authorization
|
|
@@ -25,13 +29,19 @@ UI reads, not Agent tools. They execute in the cloud and require user identity.
|
|
|
25
29
|
of membership and Space data. The request retains the operation's Space ID.
|
|
26
30
|
- `/app/space/beta/update` accepts `{betaMode: boolean}` and returns
|
|
27
31
|
`{betaMode, poll}`. It saves through the existing Space Beta service. A true
|
|
28
|
-
`poll` means the
|
|
32
|
+
`poll` means the Hub has not yet been verified to apply the selected mode.
|
|
33
|
+
This owner-only business mutation is shared by UI, native Agent/Skill, MCP
|
|
34
|
+
and CLI. A local Space may restart and interrupt running operations. The
|
|
35
|
+
receipt confirms the saved selection; it never proves Hub restart completion.
|
|
29
36
|
|
|
30
37
|
Invitation accept/reject remain membership operations and never select a Client's
|
|
31
38
|
Space. The UI explicitly uses the connection focus protocol after joining when
|
|
32
39
|
its flow intends to enter the joined Space. Current-Space switches must not be
|
|
33
40
|
discarded by the previous Space's request guard.
|
|
34
41
|
|
|
42
|
+
The retired `/app/space/location/list` has no compatibility alias. The UI uses
|
|
43
|
+
`/app/presentation/space/location/list`; no independent Agent/MCP/CLI reader is exposed.
|
|
44
|
+
|
|
35
45
|
Client handling of presentation replies unwraps successful Full Facade responses
|
|
36
46
|
before updating cloud-authoritative membership/connection caches. Presentation
|
|
37
47
|
wire DTOs (`spaceId`, `llmMode`) are explicitly projected into native state;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Linear execution attribution
|
|
2
|
+
|
|
3
|
+
Trace is an ordered array of flat items, each with an uppercase type and Unix-millisecond timestamp. Source kinds reuse Facade source kinds. The array records user-visible attribution boundaries; Automation instructions, conditions, branches and payloads are not trace items.
|
|
4
|
+
|
|
5
|
+
Ignite explicitly starts an Automation, trigger starts an ordinary run, and resume continues an existing run. Resume preserves the saved run chain. Internal asynchronous instruction completions do not append resume items. Trace uses run-level attribution, without per-branch causal graphs.
|
|
6
|
+
|
|
7
|
+
Command history stores an optional `r` reference. Snapshots are immutable and scoped by tenant and Space; IDs are positive signed 64-bit integers, represented as decimal strings in JSON. The trace and referencing command use the existing command storage owner. Recording is best effort: missing attribution cannot fail an operation or suppress its command history.
|
|
8
|
+
|
|
9
|
+
Each cross-runtime invocation keeps its own frozen attribution prefix in the caller's in-memory context. The provider returns only the local suffix for each command it recorded; the caller composes each suffix with that invocation's prefix independently. Responses may complete in any order. Different calls do not share a mutable trace, and no shared placeholder list, durable pending record or cross-runtime command ID is required. Closing a request discards later recording work under the existing best-effort bounds.
|
|
10
|
+
|
|
11
|
+
Trace collection does not change Twin observation semantics. Reports and observed state values update Twin when received; unchanged state retains its transition time and existing deduplication, while events represent separate occurrences. Received command records update the latest command log and append history under the existing rules. A late response is not rejected merely because another command started later. A command log is distinct from reported device state and does not establish that a physical state change occurred.
|
|
12
|
+
|
|
13
|
+
`/app/trace/get` accepts `input.traceIds`, at most 100 decimal IDs in the current Space. The result contains `traces` (ID to array) and `missingTraceIds`. Normal history reads do not implicitly resolve references. Entity and device history timelines return the command’s optional `r` unchanged as a decimal string; state and event entries omit it. Their `traceSteps` contains only legacy stored string sources, never a downgraded projection of a structured snapshot. Callers that need attribution batch the selected references through `/app/trace/get`; missing or expired snapshots leave the command visible. Full returns the original structured model with Unix-millisecond timestamps; Essential preserves its attribution fields and renders item times in the Space Home timezone. Authorization is Space read and routing follows Space mode. Legacy stored string sources remain readable without inventing per-item times.
|
|
14
|
+
|
|
15
|
+
Snapshots have a seven-day best-effort retention window. Local cleanup is opportunistic on writes; cloud providers use native TTL. Reuse is a small in-memory optimization, without a global deduplication guarantee.
|
|
16
|
+
|
|
17
|
+
Attribution is bounded to 256 items and 32 KiB encoded JSON. Oversized attribution is omitted. Consecutive resumes of the same Automation keep the latest resume timestamp in the new snapshot, preserving the original chain and previously persisted snapshots.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Personal home navigation
|
|
2
|
+
|
|
3
|
+
The signed-in User owns two independent profiles, `compact` and `wide`, shared
|
|
4
|
+
across Web, desktop and phone Clients and independent of the focused Space.
|
|
5
|
+
`/app/user/navigation/catalog` takes an empty object and returns the canonical
|
|
6
|
+
`manifest/navigation-cards.v1.json` catalog. `get` takes `{layout}`. `update`
|
|
7
|
+
takes `{layout, cardIds, menuCardIds?}`. Both return
|
|
8
|
+
`{layout, cardIds, menuCardIds, revision}`. The authenticated caller selects
|
|
9
|
+
the owner; requests cannot select a User, Client, Space, URL or arbitrary page.
|
|
10
|
+
|
|
11
|
+
`cardIds` is the ordered Nav list. Dashboard, Now and Menu each occur exactly
|
|
12
|
+
once; Menu is always projected and stored last. Requests and stored profiles
|
|
13
|
+
from older Clients with Menu elsewhere are normalized, preserving all other
|
|
14
|
+
relative order. Additional entries are Menu card IDs from the catalog.
|
|
15
|
+
Membership makes the card's root route a home page; child editors and object
|
|
16
|
+
details retain their ordinary navigation lifecycle.
|
|
17
|
+
|
|
18
|
+
`menuCardIds` is the ordered list of remaining Menu cards. A complete edit supplies
|
|
19
|
+
both lists and replaces them in one document write. They must be disjoint and
|
|
20
|
+
together contain every catalog ID exactly once. Required Nav entries cannot move
|
|
21
|
+
to Menu. Unavailable cards remain in their saved lists/order when a Client filters
|
|
22
|
+
its UI, so a different Space/platform does not delete personal preferences.
|
|
23
|
+
New catalog cards absent from an older request or stored profile are appended in
|
|
24
|
+
default order. Unknown IDs, duplicates and overlap are rejected.
|
|
25
|
+
|
|
26
|
+
For backward compatibility, omitting `menuCardIds` changes only Nav membership
|
|
27
|
+
and order: retained Menu entries keep their saved relative order, newly unpinned
|
|
28
|
+
entries are appended, and newly pinned entries leave Menu. Older readers may
|
|
29
|
+
ignore the additive response field. The default Nav list is
|
|
30
|
+
`[dashboard, now, menu]`; the default Menu order is Devices, Automations, Widgets,
|
|
31
|
+
Integrations, Messaging Apps, NFC, Matter, Camera, Floor Plan, People, Storage,
|
|
32
|
+
Phone Setting. No startup data rewrite or separate Menu document is needed.
|
|
33
|
+
|
|
34
|
+
UI edits are drafts until the user clicks Done. Normal browsing does not reorder
|
|
35
|
+
or show per-card editing menus. Editing blocks page navigation, supports transfers
|
|
36
|
+
between Menu and Nav and sorting within both, and leaves Menu fixed last with a
|
|
37
|
+
thin separator. Dropping outside both lists changes nothing; touch scrolling
|
|
38
|
+
stays native until a held drag activates. Cancel discards the draft; a failed
|
|
39
|
+
save retains it for retry. Screen profiles remain independent.
|
|
40
|
+
|
|
41
|
+
Writes are last-write-wins per profile, without a caller revision precondition.
|
|
42
|
+
An opaque revision identifies a saved snapshot, not a Client clock. Each write
|
|
43
|
+
replaces one profile only. `/user/navigation/changed` invalidates that authenticated
|
|
44
|
+
user's profile across their connected Clients. Consumers re-read authoritative
|
|
45
|
+
state and also resync on reconnection/focus; notifications are not a second durable
|
|
46
|
+
store. Saving a profile does not require an App Client online and never performs
|
|
47
|
+
a page's business action.
|