diff --git a/docs/api/simadmin-upstream-58e2204.md b/docs/api/simadmin-upstream-58e2204.md index 1c85a6e..e1eb8a8 100644 --- a/docs/api/simadmin-upstream-58e2204.md +++ b/docs/api/simadmin-upstream-58e2204.md @@ -9,7 +9,7 @@ - **Sensitive fields:** every item carries snapshot-rebuildable evidence (file/range/SHA-256/symbol/model field), with explicit container model chains for array paths and exact handler slices for dynamic keys. - **Boundary / semanticBindingReview:** structural checks prove source/model/field relationships, not semantic payload intent. Each registry operation is marked `semanticBindingReview: 'reviewed-phase-0.2'`; manual review fixes the method/path/handler/payload-symbol tuple, especially high-value response bindings. - **Policy:** every R2 requires at least explicit confirmation; every R3 requires strong confirmation. Auth/session-sensitive operations use dedicated flow/replay protections. -- **Phase 0.3 仅负责采集真实响应样本** (`responseFixtureStatus=pending-phase-0.3`); static evidence is not a runtime fixture. +- **Phase 0.3 真实响应样本 disposition:** the per-operation `responseFixtureStatus=pending-phase-0.3` value is retained as a frozen Phase 0.2 claim and is deprecated for current coverage decisions. The authoritative current value is `runtimeFixtureStatus` in `packages/operation-registry/src/acceptance-58e2204.ts`, derived from the collector selection and real fixture manifest. Static evidence is not a runtime fixture. ## Whole-file snapshot SHA-256 diff --git a/docs/product/current-system-audit.md b/docs/product/current-system-audit.md index 8354443..8dc1be7 100644 --- a/docs/product/current-system-audit.md +++ b/docs/product/current-system-audit.md @@ -4,7 +4,7 @@ > 产品对照基线:API-first Multi SimAdmin V1
> 上游证据基线:`3899/SimAdmin@58e220411d6599609f0eeda01eb7016e9212f970` -> Phase 0.1 文档导航:[返回项目章程](./project-charter.md)|**现状审计**(本文)|[角色与流程](./personas-and-workflows.md)|[信息架构](./information-architecture.md) +> Phase 0.1 文档导航:[返回项目章程](./project-charter.md)|**现状审计**(本文)|[角色与流程](./personas-and-workflows.md)|[信息架构](./information-architecture.md)|[117 项验收账本](./operation-acceptance-matrix.md) ## 1. 审计结论 diff --git a/docs/product/information-architecture.md b/docs/product/information-architecture.md index c313e00..9d41263 100644 --- a/docs/product/information-architecture.md +++ b/docs/product/information-architecture.md @@ -1,5 +1,7 @@ # API-first Multi SimAdmin 信息架构 +> Operation 的精确页面归属、surface、风险、确认、执行模式及 Fleet/资源批量语义见 [117 项验收账本](./operation-acceptance-matrix.md);Registry 是风险事实源。 + > 本文定义 V1 路由、导航、页面职责、上游模块映射、状态呈现与验收矩阵。路由与 OperationRegistry 是实现边界,不允许再建立“任意 endpoint 工作台”。 > Phase 0.1 文档导航:[返回项目章程](./project-charter.md)|[现状审计](./current-system-audit.md)|[角色与流程](./personas-and-workflows.md)|**信息架构**(本文) @@ -33,12 +35,12 @@ | `/instances/new` | 添加实例、连接/认证/capability 初探 | R0 health/auth-status;R1-S credential verify/login;保存 | 仅登记并校验 canonical origin;R1-S 限速、脱敏元数据审计、禁止重放;secret 不回显 | | `/instances/:id/overview` | 设备、SIM、网络、系统、关键异常摘要 | 刷新;服务/系统重启 | 查询 R0;重启 R3 Job | | `/instances/:id/cellular` | 蜂窝、信号、小区、运营商、射频、锁频/锁小区、数据/APN | register、lock、data 等 | R0/R1/R2;影响服务项 Job 化 | -| `/instances/:id/device-network` | 接口、地址、DDNS、WLAN | DDNS sync、WLAN connect/forget | R0/R1/R2,断连影响需预检 | +| `/instances/:id/device-network` | 接口、地址、DDNS、WLAN | DDNS sync、WLAN connect/forget | 查询 R0;WLAN connect R1,WLAN forget R2 Job;DDNS config R2 Job,断连影响需预检 | | `/instances/:id/messages` | 统计、列表、会话、发送、删除 | send、delete、clear | 查询 R0;发送按 registry;删除/清空 R3 | | `/instances/:id/calls` | 实时通话、拨号、设置、历史、IMS、voicemail | dial/answer/hangup/settings;删除通话记录 | 查询 R0;通话控制/设置按 R1/R2;通话记录删除 R3 Job | -| `/instances/:id/esim` | work mode、config、lpac、eUICC、profiles | download/enable/rename/delete | R1/R2;profile delete R3 | -| `/instances/:id/notifications` | config、channel test、logs、queue | test/retry/clear/delete | config R1;清理类 R3 | -| `/instances/:id/automation` | 配置、task test、logs | save/test/clear | config R1;测试按 operation;clear R3 | +| `/instances/:id/esim` | work mode、config、lpac、eUICC、profiles | download/enable/rename/delete | eSIM download/enable R1 direct;config/work mode R2 Job;profile delete R3 | +| `/instances/:id/notifications` | config、channel test、logs、queue | test/retry/clear/delete | notifications config R2 Job;清理类 R3 | +| `/instances/:id/automation` | 配置、task test、logs | save/test/clear | automation config R2 Job;task test R2 Job;clear R3 | | `/instances/:id/ota` | 当前/最新版本、上传、prepare、apply、cancel | upload/prepare/apply/cancel | prepare R2;apply R3 Job;专用流上传 | | `/jobs` | Job 表格与筛选 | 取消/重试入口 | cancel 默认 R1;retry 继承原 operation 风险且最低 R2,均由 registry 可提高 | | `/jobs/:jobId` | 阶段、items、attempt、事件、retry lineage 和关联 audit | cancel/retry items | cancel 默认 R1;retry 创建新 Job 且继承原风险(最低 R2);不复制/重放成功 item,非幂等先核实 | @@ -95,7 +97,7 @@ | SIM | SIM 信息、详情刷新、缓存更新 | `overview` | 与 cellular 交叉字段只引用同一 resource/query,不重复 endpoint 定义 | | 蜂窝网络 | network、cells、monitor、signal、location、operators、register | `cellular` | overview 仅摘要和深链 | | 射频与锁定 | radio mode、band lock、cell lock、unlock all | `cellular` | lock/unlock 预检并按 R2/R3 registry 执行 | -| 数据连接 | data、roaming、airplane、APN、baseband | `cellular` | R1 开关与 R2 restart 分开呈现 | +| 数据连接 | data、roaming、airplane、APN、baseband | `cellular` | R1 开关;baseband restart R3 Job,baseband restart status R0 read,必须分开呈现 | | 设备网络 | interfaces、addresses、DDNS、WLAN 全生命周期 | `device-network` | connect/disconnect 后需验证 connectivity | | 工作模式与 eSIM | work mode、config、lpac、eUICC、profiles | `esim` | profile delete R3;工作模式影响说明 | | 短信 | stats、list、conversation、send、delete、clear | `messages` | 删除/清空 R3;短信正文不进入 audit | @@ -188,12 +190,12 @@ R3 全部 Job 化。R2 默认 Job 化;仅单目标 operation 可在 OperationR | New instance | 实例与认证 | L,X,A,U/unknown capability | 配置专用保存/secret set | 连接与认证分阶段;失败可存 disabled 草稿;无 secret 回显 | | Overview | 设备系统、SIM、摘要 | L,E,S,X,U,A,P | restart service/system R3 | 摘要深链;重启进入 Job,不在断连时假报成功 | | Cellular | 蜂窝、射频、数据连接 | L,E,S,X,U,A,P | data/roaming R1;register/lock/baseband R2/3 | 当前值→目标值;断连影响;验证后状态 | -| Device Network | interfaces/DDNS/WLAN | L,E,S,X,U,A,P | config R1;connect/forget R2 | 扫描空与 unsupported 区分;变更后 connectivity 验证 | +| Device Network | interfaces/DDNS/WLAN | L,E,S,X,U,A,P | DDNS config R2 Job;WLAN connect R1;WLAN forget R2 Job | 扫描空与 unsupported 区分;变更后 connectivity 验证 | | Messages | SMS | L,E,S,X,U,A,P | send R2(默认 Job,`syncSafe` 例外);delete/clear R3 Job | 分页/会话;号码校验;删除数量强确认 | | Calls | Calls/IMS/voicemail | L,E,S,X,U,A,P | dial/answer/hangup/settings R1/2;history delete R3 Job | 实时与历史分离;拨号超时不重放;记录删除复述目标/数量并落 Job、刷新历史及脱敏 audit | -| eSIM | work mode/eSIM/lpac/eUICC/profile | L,E,S,X,U,A,P | config R1;download/enable R2;delete R3 | profile 身份可见;删除复述;完成后列表与连接核实 | -| Notifications | config/test/logs/queue | L,E,S,X,U,A,P | config R1;test/retry R2(默认 Job,`syncSafe` 例外);clear/delete R3 Job | channel 级反馈;队列逐项;清理确认数量 | -| Automation | config/test/logs | L,E,S,X,U,A,P | config R1;test R2(默认 Job,`syncSafe` 例外);clear R3 Job | schema 表单;测试与正式任务明确区分 | +| eSIM | work mode/eSIM/lpac/eUICC/profile | L,E,S,X,U,A,P | eSIM download/enable R1 direct;config/work mode R2 Job;delete R3 | profile 身份可见;删除复述;完成后列表与连接核实 | +| Notifications | config/test/logs/queue | L,E,S,X,U,A,P | notifications config R2 Job;test/retry R2 Job;clear/delete R3 Job | channel 级反馈;队列逐项;清理确认数量 | +| Automation | config/test/logs | L,E,S,X,U,A,P | automation config R2 Job;test R2 Job;clear R3 Job | schema 表单;测试与正式任务明确区分 | | OTA | status/release/upload/prepare/apply/cancel | L,E,S,X,U,A,P | prepare R2;apply R3 | 流式进度/校验;版本重现判完成;可取消性明确 | | Jobs | jobs/items/attempts | L,E,S,X,P | cancel 默认 R1(可提高);retry 继承原风险且最低 R2、创建新 Job | 重启恢复;终态不可变;失败项选择重试;新 jobId+attemptId 与 retry/root/source lineage 关联 | | Job Detail `/jobs/:jobId` | jobs/items/events/audit links | L,E,S,X,P | cancel 默认 R1;retry 最低 R2 | 阶段、逐项、attempt 与关联 audit;不可取消/重试原因可见 | @@ -225,11 +227,22 @@ R3 全部 Job 化。R2 默认 Job 化;仅单目标 operation 可在 OperationR ## 10. IA 阶段门禁 -进入 Web 实现前必须满足: +### Phase 0 → Phase 1 workspace/contract bootstrap gate + +进入 Phase 1 workspace/contract bootstrap 前只要求规格、结构化证据与复审门禁;不以尚未进入实现阶段的 Fleet 组件或 E2E 产物阻断 workspace bootstrap: + +- [x] 全部 117 个上游 operation 由显式 operation ID exact partition 映射到唯一主路由与可实施 section/surface;测试验证 exact cover、无重叠、无未知 ID,且 route owner 一致。 +- [x] loading/empty/stale/error/unsupported/auth/partial、policy-forbidden、unknown-result 与 owner-switch 由显式 operation ID scenario profile exact partition 决定,并保留逐 profile 不适用理由。 +- [x] policy 与 availability 均由显式 operation ID exact partition 决定;每个写动作具有 Registry 风险等级、确认形态、结构化政策和结果落点,缺失映射立即失败。 +- [x] 规格与测试不存在 iframe、任意 path、任意 JSON 或第二份 endpoint catalog 的产品入口。 +- [x] 1440–320 的导航与数据降级策略已形成可执行规格。 +- [x] 最终独立规格与质量/安全复审均已通过;防假绿 mutation、独立 Phase 0.2 Git-object 安全基线和 metadata-only fixture 扫描均获批准。 + +### Phase 5 implementation gate + +以下实现证据不能删除或伪造为已完成,但 deferred 到 Phase 5;它们不是 Phase 1 workspace/contract bootstrap blocker: -- [ ] 全部上游 operation 在 OperationRegistry 中有且仅有一个主页面 owner。 -- [ ] 目标路由均有 loading/empty/stale/error/unsupported/auth/partial 设计或明确不适用理由。 -- [ ] 每个写动作有风险等级、确认形态和结果落点。 - [ ] Fleet 默认列、排序、筛选和批量选择规则已形成组件/E2E 验收用例。 -- [ ] 不存在 iframe、任意 path、任意 JSON 或第二份 endpoint catalog 的产品入口。 -- [ ] 1440–320 的导航与数据降级策略可执行,而不是仅写“响应式”。 +- [ ] E2E 当前为 N/A;进入 Phase 5 Fleet 页面实现前,必须将上述规格落为可执行组件/E2E 验收并通过。 + +Phase 0 release gate 已通过;Phase 5 实现与 E2E 仍是各自阶段的独立门禁,不能由本次规格复审替代。 diff --git a/docs/product/operation-acceptance-matrix.md b/docs/product/operation-acceptance-matrix.md new file mode 100644 index 0000000..99bd5f5 --- /dev/null +++ b/docs/product/operation-acceptance-matrix.md @@ -0,0 +1,153 @@ +# Operation acceptance matrix — upstream 58e2204 + +> Generated from the structured ledger. Registry is the sole source for risk, confirmation, capability, and dedicated-flow facts; orchestration is bound by acceptance policy. `registeredOperation` is not a synonym for preparation Job. + +REAL_READ means captured evidence and is restricted to `captured-readonly`. REAL_READ_DEFERRED marks denied candidates without a real fixture. Fixture disposition is metadata-only: runtime status, count, safe observed status categories, and reason; it never renders fixture body/response, alias, identity, or URL. + +## Control-plane acceptance (independent flows) + +| flowId | route | risk / subtype | confirmation | preconditions | result | failure recovery | secret policy | evidence | +|---|---|---|---|---|---|---|---|---| +| `instance-create` | `/instances/new` | R1 / configuration | diff-confirm | canonical origin and unique stable ID | created instance revision and audit | retain draft on validation or reconcile failure | secret reference only; never return a value | WF-01 + product policy | +| `instance-update` | `/settings/instances/:id` | R1 / configuration | revision-diff | current revision | updated revision and audit | 409 refreshes diff without losing edits | preserve/set/clear explicit; never return a value | WF-01 + product policy | +| `instance-delete` | `/settings/instances/:id` | R3 / destructive | strong target restatement | fresh revision and no conflicting job | new jobId with two-phase deleting to tombstone result | before boundary restore; after boundary manual recovery | destroy reference without reading secret value | WF-13 | +| `secret-set` | `/settings/instances/:id` | R1 / secret-reference | explicit set | authorized SecretStore write | new reference metadata and audit | atomic rollback on reference write failure | never return or log secret value | WF-01 | +| `secret-preserve` | `/settings/instances/:id` | R1 / secret-reference | explicit preserve | existing reference metadata | unchanged reference metadata and audit | revision conflict refreshes metadata only | never read or return secret value | WF-01 | +| `secret-clear` | `/settings/instances/:id` | R1 / secret-reference | explicit clear | fresh revision | reference removed and audit | atomic rollback or explicit recovery state | never return destroyed secret value | WF-01 | +| `config-import-preview` | `/settings/instances` | R0 / preview | none | validated upload and canonical origins | redacted diff/conflict/count preview | fix file and regenerate preview | never include secret values in preview | IA settings import | +| `config-import-confirm` | `/settings/instances` | R2 / bulk-configuration | explicit preview token | fresh unexpired preview | new jobId with per-instance items | retry failed items in new lineage | references only; never import/export secret values | IA settings import | +| `credential-verify` | `/instances/new` | R1 / session-sensitive | actor initiated | rate limit and registered target | credential metadata-only result | no automatic replay; actor may retry after backoff | ephemeral input, never response/audit value | WF-01 | +| `saved-secret-login` | `/instances/new` | R1 / session-sensitive | actor initiated | saved reference and rate limit | session metadata-only result | no automatic replay; return to login flow | resolve server-side; never expose value | WF-12 | +| `temporary-secret-login` | `/instances/new` | R1 / session-sensitive | actor initiated | one-shot input and rate limit | session metadata-only result | no automatic replay; require new actor input | memory-only and never saved or returned | WF-12 | +| `logout` | `/settings/instances/:id` | R1 / session-sensitive | actor initiated | active session | session cleared and metadata audit | no replay; reconcile auth status | no credential value involved | WF-12 | +| `401-recovery` | `/instances/:id/overview` | R1 / session-recovery | actor chooses login | 401 evidence and retained stale data | return to owner; retry R0 only | never replay writes; preserve draft for new prepare | temporary/saved secret follows selected login policy | WF-12 | +| `auth-setup` | `/instances/new` | R3 / authentication-change | strong confirmation | fresh dedicated auth preflight | new jobId and metadata-only audit | new lineage after state verification | password never returned or audited | Registry postAuthSetup | +| `auth-password-change` | `/settings/instances/:id` | R3 / authentication-change | strong confirmation | fresh dedicated auth preflight | new jobId and metadata-only audit | new lineage after auth-status verification | old/new password never returned or audited | Registry postAuthPassword | +| `auth-settings-read` | `/settings/instances/:id` | R0 / authentication-read | none | registered instance and actor | structured auth settings | safe actor retry | no password/cookie/token fields | Registry getAuthSettings | +| `auth-settings-write` | `/settings/instances/:id` | R2 / authentication-change | explicit confirmation | fresh dedicated auth preflight | new jobId and metadata-only audit | new lineage; no replay | never return credential values | Registry postAuthSettings | +| `job-cancel` | `/jobs/:jobId` | R1 / job-control | explicit confirmation | cancelable current phase | immutable cancellation event | show non-cancelable boundary and reconcile | no secret data in event | IA jobs | +| `job-retry` | `/jobs/:jobId` | R2 / job-control | explicit failed-item selection | source terminal state and fresh preflight | new jobId with retryOf/root/source lineage | never mutate source or replay successful items | redacted parameter summary only | WF-10 | +| `audit-export` | `/audit` | R0 / data-export | explicit scope | authorized filters | redacted export artifact | regenerate from immutable audit | redaction applies; no sensitiveFields | WF-11 | +| `system-settings-update` | `/settings/system` | R1 / configuration | revision-diff | safe bounds and current revision | updated revision and audit | 409 refreshes diff; invalid bounds rejected | no secrets accepted | IA settings system | + +## Upstream operation acceptance (exact 117) + +| operationId | method/path | domain | route/surface | UI | availability/version evidence | scenario applicability | risk/confirmation/execution | policy IDs and resolved policies | real class | fixture disposition (metadata-only: runtime status / n / categories / reason) | evidence | +|---|---|---|---|---|---|---|---|---|---|---|---| +| `getDevice` | GET `/api/device` | device-system | `/instances/:id/overview`
`overview/device` | detail | planned: Frozen-scope read has two redacted captured responses; runtime support is still decided per registered instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Detail resource has no collection-empty rendering.)
partial:N (Atomic detail read has no meaningful partial rendering.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ | captured-readonly; n=2; categories=success; collector-selected fully-bound GET R0; two redacted real captures | `registry:getDevice`
`upstream:backend/src/main.rs:506-506`
`upstream:backend/src/handlers.rs:1076-1090`
`upstream:bruno-api/get_device_info.bru:8`
`fixture:captured-readonly` | +| `getSim` | GET `/api/sim` | sim | `/instances/:id/overview`
`overview/sim` | detail | planned: Frozen-scope read has two redacted captured responses; runtime support is still decided per registered instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Detail resource has no collection-empty rendering.)
partial:N (Atomic detail read has no meaningful partial rendering.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ | captured-readonly; n=2; categories=success; collector-selected fully-bound GET R0; two redacted real captures | `registry:getSim`
`upstream:backend/src/main.rs:508-508`
`upstream:backend/src/handlers.rs:1126-1145`
`upstream:bruno-api/get_sim_info.bru:8`
`fixture:captured-readonly` | +| `postSimDetailsRefresh` | POST `/api/sim/details/refresh` | sim | `/instances/:id/overview`
`overview/sim` | action | planned: Frozen-scope reversible command is contract-planned for a later controlled real-write acceptance phase.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R1; none
direct | command-current-state/write-revision/version-probe/verify-write/route-result
Refresh current state when available and bind the actor decision to its revision.
Refresh current state or require an explicit recorded revision-bound override.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Writes require outcome verification before actor-initiated retry.
Structured result returns to the owning route with a redacted audit link. | REAL_WRITE_LATER | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postSimDetailsRefresh`
`upstream:backend/src/main.rs:510-511`
`upstream:backend/src/handlers.rs:1148-1164`
`fixture:not-eligible-readonly-capture` | +| `postSimCache` | POST `/api/sim/cache` | sim | `/instances/:id/overview`
`overview/sim` | form | planned: Frozen-scope reversible command is contract-planned for a later controlled real-write acceptance phase.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R1; none
direct | command-current-state/write-revision/version-probe/verify-write/route-result
Refresh current state when available and bind the actor decision to its revision.
Refresh current state or require an explicit recorded revision-bound override.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Writes require outcome verification before actor-initiated retry.
Structured result returns to the owning route with a redacted audit link. | REAL_WRITE_LATER | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postSimCache`
`upstream:backend/src/main.rs:514-515`
`upstream:backend/src/handlers.rs:1167-1213`
`fixture:not-eligible-readonly-capture` | +| `getNetwork` | GET `/api/network` | cellular | `/instances/:id/cellular`
`cellular/serving-network` | detail | planned: Frozen-scope read has two redacted captured responses; runtime support is still decided per registered instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Detail resource has no collection-empty rendering.)
partial:N (Atomic detail read has no meaningful partial rendering.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ | captured-readonly; n=2; categories=success; collector-selected fully-bound GET R0; two redacted real captures | `registry:getNetwork`
`upstream:backend/src/main.rs:519-520`
`upstream:backend/src/handlers.rs:1218-1232`
`upstream:bruno-api/get_network_info.bru:8`
`fixture:captured-readonly` | +| `getCells` | GET `/api/cells` | cellular | `/instances/:id/cellular`
`cellular/cells` | table | planned: Frozen-scope read has two redacted captured responses; runtime support is still decided per registered instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:Y (Successful collection may contain no rows.)
partial:Y (Independent response sections may succeed or fail.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ | captured-readonly; n=2; categories=success; collector-selected fully-bound GET R0; two redacted real captures | `registry:getCells`
`upstream:backend/src/main.rs:522-522`
`upstream:backend/src/handlers.rs:1235-1249`
`upstream:bruno-api/get_cells_info.bru:8`
`fixture:captured-readonly` | +| `postCellMonitorStart` | POST `/api/cell-monitor/start` | cellular | `/instances/:id/cellular`
`cellular/cells` | action | planned: Frozen-scope reversible command is contract-planned for a later controlled real-write acceptance phase.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R1; none
direct | command-current-state/write-revision/version-probe/verify-write/route-result
Refresh current state when available and bind the actor decision to its revision.
Refresh current state or require an explicit recorded revision-bound override.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Writes require outcome verification before actor-initiated retry.
Structured result returns to the owning route with a redacted audit link. | REAL_WRITE_LATER | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postCellMonitorStart`
`upstream:backend/src/main.rs:524-525`
`upstream:backend/src/handlers.rs:1252-1282`
`fixture:not-eligible-readonly-capture` | +| `postCellMonitorStop` | POST `/api/cell-monitor/stop` | cellular | `/instances/:id/cellular`
`cellular/cells` | action | planned: Frozen-scope reversible command is contract-planned for a later controlled real-write acceptance phase.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R1; none
direct | command-current-state/write-revision/version-probe/verify-write/route-result
Refresh current state when available and bind the actor decision to its revision.
Refresh current state or require an explicit recorded revision-bound override.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Writes require outcome verification before actor-initiated retry.
Structured result returns to the owning route with a redacted audit link. | REAL_WRITE_LATER | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postCellMonitorStop`
`upstream:backend/src/main.rs:528-529`
`upstream:backend/src/handlers.rs:1285-1312`
`fixture:not-eligible-readonly-capture` | +| `getRadioMode` | GET `/api/radio-mode` | radio-lock | `/instances/:id/cellular`
`cellular/radio-locks` | read-panel | planned: Frozen-scope read has two redacted captured responses; runtime support is still decided per registered instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Detail resource has no collection-empty rendering.)
partial:N (Atomic detail read has no meaningful partial rendering.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ | captured-readonly; n=2; categories=success; collector-selected fully-bound GET R0; two redacted real captures | `registry:getRadioMode`
`upstream:backend/src/main.rs:532-533`
`upstream:backend/src/handlers.rs:1315-1329`
`upstream:bruno-api/get_radio_mode.bru:8`
`fixture:captured-readonly` | +| `postRadioMode` | POST `/api/radio-mode` | radio-lock | `/instances/:id/cellular`
`cellular/radio-locks` | form | planned: Frozen-scope reversible command is contract-planned for a later controlled real-write acceptance phase.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R1; none
direct | command-current-state/write-revision/version-probe/verify-write/route-result
Refresh current state when available and bind the actor decision to its revision.
Refresh current state or require an explicit recorded revision-bound override.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Writes require outcome verification before actor-initiated retry.
Structured result returns to the owning route with a redacted audit link. | REAL_WRITE_LATER | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postRadioMode`
`upstream:backend/src/main.rs:532-534`
`upstream:backend/src/handlers.rs:1332-1352`
`upstream:bruno-api/set_radio_mode_nr.bru:8`
`upstream:bruno-api/set_radio_mode_lte.bru:8`
`upstream:bruno-api/set_radio_mode_auto.bru:8`
`fixture:not-eligible-readonly-capture` | +| `getBandLock` | GET `/api/band-lock` | radio-lock | `/instances/:id/cellular`
`cellular/radio-locks` | read-panel | planned: Frozen-scope read has two redacted captured responses; runtime support is still decided per registered instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Detail resource has no collection-empty rendering.)
partial:N (Atomic detail read has no meaningful partial rendering.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ | captured-readonly; n=2; categories=success; collector-selected fully-bound GET R0; two redacted real captures | `registry:getBandLock`
`upstream:backend/src/main.rs:538-539`
`upstream:backend/src/handlers.rs:1355-1369`
`upstream:bruno-api/get_band_lock.bru:8`
`fixture:captured-readonly` | +| `postBandLock` | POST `/api/band-lock` | radio-lock | `/instances/:id/cellular`
`cellular/radio-locks` | job-action | planned: Frozen-scope high-risk operation is planned with simulation-only acceptance until controlled safety authorization exists.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R2; explicit
preparation-job | job-fresh-preflight/high-risk-block/version-probe/new-job-lineage/job-result
Required fresh preflight; inability to refresh blocks execution.
Fresh preflight is mandatory; stale, expired, or unknown state blocks execution.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Retry creates a new Job lineage (retryOfJobId/rootJobId); never replay successful items.
/jobs/:jobId contains per-item result, attempt lineage, and redacted audit. | SIMULATED_HIGH_RISK | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postBandLock`
`upstream:backend/src/main.rs:538-540`
`upstream:backend/src/handlers.rs:1372-1392`
`upstream:bruno-api/set_band_lock_nr_n78.bru:8`
`upstream:bruno-api/set_band_lock_lte_b1_b3.bru:8`
`upstream:bruno-api/unlock_all_bands.bru:8`
`upstream:bruno-api/set_band_lock_lte_nr_mix.bru:8`
`fixture:not-eligible-readonly-capture` | +| `getNetworkInterfaces` | GET `/api/network/interfaces` | device-network | `/instances/:id/device-network`
`device-network/interfaces` | table | planned: Frozen-scope read has two redacted captured responses; runtime support is still decided per registered instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:Y (Successful collection may contain no rows.)
partial:Y (Independent response sections may succeed or fail.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ | captured-readonly; n=2; categories=success; collector-selected fully-bound GET R0; two redacted real captures | `registry:getNetworkInterfaces`
`upstream:backend/src/main.rs:544-545`
`upstream:backend/src/handlers.rs:1619-1644`
`upstream:bruno-api/get_network_interfaces.bru:8`
`fixture:captured-readonly` | +| `getNetworkConnectionAddresses` | GET `/api/network/connection-addresses` | device-network | `/instances/:id/device-network`
`device-network/interfaces` | read-panel | planned: Frozen-scope read has two redacted captured responses; runtime support is still decided per registered instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Detail resource has no collection-empty rendering.)
partial:N (Atomic detail read has no meaningful partial rendering.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ | captured-readonly; n=2; categories=success; collector-selected fully-bound GET R0; two redacted real captures | `registry:getNetworkConnectionAddresses`
`upstream:backend/src/main.rs:548-549`
`upstream:backend/src/handlers.rs:1647-1666`
`fixture:captured-readonly` | +| `getDeviceNetworkDdnsConfig` | GET `/api/device-network/ddns/config` | device-network | `/instances/:id/device-network`
`device-network/ddns` | read-panel | planned: Frozen-scope read has two redacted captured responses; runtime support is still decided per registered instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Detail resource has no collection-empty rendering.)
partial:N (Atomic detail read has no meaningful partial rendering.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ | captured-readonly; n=2; categories=success; collector-selected fully-bound GET R0; two redacted real captures | `registry:getDeviceNetworkDdnsConfig`
`upstream:backend/src/main.rs:552-553`
`upstream:backend/src/handlers.rs:1669-1679`
`upstream:bruno-api/get_device_ddns_config.bru:8`
`fixture:captured-readonly` | +| `postDeviceNetworkDdnsConfig` | POST `/api/device-network/ddns/config` | device-network | `/instances/:id/device-network`
`device-network/ddns` | job-action | planned: Frozen-scope high-risk operation is planned with simulation-only acceptance until controlled safety authorization exists.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R2; explicit
preparation-job | job-fresh-preflight/high-risk-block/version-probe/new-job-lineage/job-result
Required fresh preflight; inability to refresh blocks execution.
Fresh preflight is mandatory; stale, expired, or unknown state blocks execution.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Retry creates a new Job lineage (retryOfJobId/rootJobId); never replay successful items.
/jobs/:jobId contains per-item result, attempt lineage, and redacted audit. | SIMULATED_HIGH_RISK | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postDeviceNetworkDdnsConfig`
`upstream:backend/src/main.rs:552-554`
`upstream:backend/src/handlers.rs:1682-1721`
`upstream:bruno-api/set_device_ddns_config.bru:8`
`fixture:not-eligible-readonly-capture` | +| `getDeviceNetworkDdnsStatus` | GET `/api/device-network/ddns/status` | device-network | `/instances/:id/device-network`
`device-network/ddns` | read-panel | planned: Frozen-scope read has two redacted captured responses; runtime support is still decided per registered instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Detail resource has no collection-empty rendering.)
partial:N (Atomic detail read has no meaningful partial rendering.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ | captured-readonly; n=2; categories=success; collector-selected fully-bound GET R0; two redacted real captures | `registry:getDeviceNetworkDdnsStatus`
`upstream:backend/src/main.rs:558-559`
`upstream:backend/src/handlers.rs:1750-1757`
`upstream:bruno-api/get_device_ddns_status.bru:8`
`fixture:captured-readonly` | +| `postDeviceNetworkDdnsSync` | POST `/api/device-network/ddns/sync` | device-network | `/instances/:id/device-network`
`device-network/ddns` | action | planned: Frozen-scope reversible command is contract-planned for a later controlled real-write acceptance phase.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R1; none
direct | command-current-state/write-revision/version-probe/verify-write/route-result
Refresh current state when available and bind the actor decision to its revision.
Refresh current state or require an explicit recorded revision-bound override.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Writes require outcome verification before actor-initiated retry.
Structured result returns to the owning route with a redacted audit link. | REAL_WRITE_LATER | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postDeviceNetworkDdnsSync`
`upstream:backend/src/main.rs:562-563`
`upstream:backend/src/handlers.rs:1760-1781`
`upstream:bruno-api/post_device_ddns_sync.bru:8`
`fixture:not-eligible-readonly-capture` | +| `getDeviceNetworkDdnsLogs` | GET `/api/device-network/ddns/logs` | device-network | `/instances/:id/device-network`
`device-network/ddns` | read-panel | planned: Frozen-scope read is modeled but real capture was explicitly denied; runtime support is probed per instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:Y (Successful collection may contain no rows.)
partial:Y (Independent response sections may succeed or fail.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ_DEFERRED | denied-readonly-candidate; n=0; categories=none; no safe limit contract | `registry:getDeviceNetworkDdnsLogs`
`upstream:backend/src/main.rs:566-567`
`upstream:backend/src/handlers.rs:1784-1790`
`upstream:bruno-api/get_device_ddns_logs.bru:8`
`fixture:denied-readonly-candidate` | +| `postDeviceNetworkDdnsLogsClear` | POST `/api/device-network/ddns/logs/clear` | device-network | `/instances/:id/device-network`
`device-network/ddns` | job-action | planned: Frozen-scope high-risk operation is planned with simulation-only acceptance until controlled safety authorization exists.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R2; explicit
preparation-job | job-fresh-preflight/high-risk-block/version-probe/new-job-lineage/job-result
Required fresh preflight; inability to refresh blocks execution.
Fresh preflight is mandatory; stale, expired, or unknown state blocks execution.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Retry creates a new Job lineage (retryOfJobId/rootJobId); never replay successful items.
/jobs/:jobId contains per-item result, attempt lineage, and redacted audit. | SIMULATED_HIGH_RISK | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postDeviceNetworkDdnsLogsClear`
`upstream:backend/src/main.rs:570-571`
`upstream:backend/src/handlers.rs:1793-1802`
`upstream:bruno-api/post_device_ddns_logs_clear.bru:8`
`fixture:not-eligible-readonly-capture` | +| `getDeviceNetworkWlanStatus` | GET `/api/device-network/wlan/status` | device-network | `/instances/:id/device-network`
`device-network/wlan` | read-panel | planned: Frozen-scope read has two redacted captured responses; runtime support is still decided per registered instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Detail resource has no collection-empty rendering.)
partial:N (Atomic detail read has no meaningful partial rendering.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ | captured-readonly; n=2; categories=success; collector-selected fully-bound GET R0; two redacted real captures | `registry:getDeviceNetworkWlanStatus`
`upstream:backend/src/main.rs:574-575`
`upstream:backend/src/handlers.rs:1805-1819`
`upstream:bruno-api/get_device_wlan_status.bru:8`
`fixture:captured-readonly` | +| `postDeviceNetworkWlanEnabled` | POST `/api/device-network/wlan/enabled` | device-network | `/instances/:id/device-network`
`device-network/wlan` | form | planned: Frozen-scope reversible command is contract-planned for a later controlled real-write acceptance phase.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R1; none
direct | command-current-state/write-revision/version-probe/verify-write/route-result
Refresh current state when available and bind the actor decision to its revision.
Refresh current state or require an explicit recorded revision-bound override.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Writes require outcome verification before actor-initiated retry.
Structured result returns to the owning route with a redacted audit link. | REAL_WRITE_LATER | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postDeviceNetworkWlanEnabled`
`upstream:backend/src/main.rs:578-579`
`upstream:backend/src/handlers.rs:1822-1841`
`upstream:bruno-api/set_device_wlan_enabled.bru:8`
`fixture:not-eligible-readonly-capture` | +| `postDeviceNetworkWlanScan` | POST `/api/device-network/wlan/scan` | device-network | `/instances/:id/device-network`
`device-network/wlan` | action | planned: Frozen-scope reversible command is contract-planned for a later controlled real-write acceptance phase.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R1; none
direct | command-current-state/write-revision/version-probe/verify-write/route-result
Refresh current state when available and bind the actor decision to its revision.
Refresh current state or require an explicit recorded revision-bound override.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Writes require outcome verification before actor-initiated retry.
Structured result returns to the owning route with a redacted audit link. | REAL_WRITE_LATER | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postDeviceNetworkWlanScan`
`upstream:backend/src/main.rs:582-583`
`upstream:backend/src/handlers.rs:1844-1858`
`upstream:bruno-api/post_device_wlan_scan.bru:8`
`fixture:not-eligible-readonly-capture` | +| `getDeviceNetworkWlanProfiles` | GET `/api/device-network/wlan/profiles` | device-network | `/instances/:id/device-network`
`device-network/wlan` | table | planned: Frozen-scope read has two redacted captured responses; runtime support is still decided per registered instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:Y (Successful collection may contain no rows.)
partial:Y (Independent response sections may succeed or fail.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ | captured-readonly; n=2; categories=success; collector-selected fully-bound GET R0; two redacted real captures | `registry:getDeviceNetworkWlanProfiles`
`upstream:backend/src/main.rs:586-587`
`upstream:backend/src/handlers.rs:1861-1875`
`upstream:bruno-api/get_device_wlan_profiles.bru:8`
`fixture:captured-readonly` | +| `postDeviceNetworkWlanForget` | POST `/api/device-network/wlan/forget` | device-network | `/instances/:id/device-network`
`device-network/wlan` | job-action | planned: Frozen-scope high-risk operation is planned with simulation-only acceptance until controlled safety authorization exists.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R2; explicit
preparation-job | job-fresh-preflight/high-risk-block/version-probe/new-job-lineage/job-result
Required fresh preflight; inability to refresh blocks execution.
Fresh preflight is mandatory; stale, expired, or unknown state blocks execution.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Retry creates a new Job lineage (retryOfJobId/rootJobId); never replay successful items.
/jobs/:jobId contains per-item result, attempt lineage, and redacted audit. | SIMULATED_HIGH_RISK | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postDeviceNetworkWlanForget`
`upstream:backend/src/main.rs:590-591`
`upstream:backend/src/handlers.rs:1878-1897`
`upstream:bruno-api/post_device_wlan_forget.bru:8`
`fixture:not-eligible-readonly-capture` | +| `postDeviceNetworkWlanConnect` | POST `/api/device-network/wlan/connect` | device-network | `/instances/:id/device-network`
`device-network/wlan` | form | planned: Frozen-scope reversible command is contract-planned for a later controlled real-write acceptance phase.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R1; none
direct | command-current-state/write-revision/version-probe/verify-write/route-result
Refresh current state when available and bind the actor decision to its revision.
Refresh current state or require an explicit recorded revision-bound override.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Writes require outcome verification before actor-initiated retry.
Structured result returns to the owning route with a redacted audit link. | REAL_WRITE_LATER | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postDeviceNetworkWlanConnect`
`upstream:backend/src/main.rs:594-595`
`upstream:backend/src/handlers.rs:1900-1955`
`upstream:bruno-api/post_device_wlan_connect.bru:8`
`fixture:not-eligible-readonly-capture` | +| `postDeviceNetworkWlanDisconnect` | POST `/api/device-network/wlan/disconnect` | device-network | `/instances/:id/device-network`
`device-network/wlan` | action | planned: Frozen-scope reversible command is contract-planned for a later controlled real-write acceptance phase.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R1; none
direct | command-current-state/write-revision/version-probe/verify-write/route-result
Refresh current state when available and bind the actor decision to its revision.
Refresh current state or require an explicit recorded revision-bound override.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Writes require outcome verification before actor-initiated retry.
Structured result returns to the owning route with a redacted audit link. | REAL_WRITE_LATER | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postDeviceNetworkWlanDisconnect`
`upstream:backend/src/main.rs:598-599`
`upstream:backend/src/handlers.rs:1958-1991`
`upstream:bruno-api/post_device_wlan_disconnect.bru:8`
`fixture:not-eligible-readonly-capture` | +| `postDeviceNetworkWlanProfile` | POST `/api/device-network/wlan/profile` | device-network | `/instances/:id/device-network`
`device-network/wlan` | form | planned: Frozen-scope reversible command is contract-planned for a later controlled real-write acceptance phase.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R1; none
direct | command-current-state/write-revision/version-probe/verify-write/route-result
Refresh current state when available and bind the actor decision to its revision.
Refresh current state or require an explicit recorded revision-bound override.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Writes require outcome verification before actor-initiated retry.
Structured result returns to the owning route with a redacted audit link. | REAL_WRITE_LATER | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postDeviceNetworkWlanProfile`
`upstream:backend/src/main.rs:602-603`
`upstream:backend/src/handlers.rs:1994-2013`
`upstream:bruno-api/post_device_wlan_profile.bru:8`
`fixture:not-eligible-readonly-capture` | +| `getNetworkSignalStrength` | GET `/api/network/signal-strength` | cellular | `/instances/:id/cellular`
`cellular/signal` | read-panel | planned: Frozen-scope read has two redacted captured responses; runtime support is still decided per registered instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Detail resource has no collection-empty rendering.)
partial:N (Atomic detail read has no meaningful partial rendering.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ | captured-readonly; n=2; categories=success; collector-selected fully-bound GET R0; two redacted real captures | `registry:getNetworkSignalStrength`
`upstream:backend/src/main.rs:606-607`
`upstream:backend/src/handlers.rs:2016-2030`
`upstream:bruno-api/get_signal_strength.bru:8`
`fixture:captured-readonly` | +| `getLocationCellInfo` | GET `/api/location/cell-info` | cellular | `/instances/:id/cellular`
`cellular/cells` | read-panel | planned: Frozen-scope read has two redacted captured responses; runtime support is still decided per registered instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Detail resource has no collection-empty rendering.)
partial:N (Atomic detail read has no meaningful partial rendering.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ | captured-readonly; n=2; categories=success; collector-selected fully-bound GET R0; two redacted real captures | `registry:getLocationCellInfo`
`upstream:backend/src/main.rs:610-611`
`upstream:backend/src/handlers.rs:1395-1409`
`upstream:bruno-api/get_cell_location_info.bru:8`
`fixture:captured-readonly` | +| `getNetworkOperators` | GET `/api/network/operators` | cellular | `/instances/:id/cellular`
`cellular/operator-registration` | table | planned: Frozen-scope read has two redacted captured responses; runtime support is still decided per registered instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:Y (Successful collection may contain no rows.)
partial:Y (Independent response sections may succeed or fail.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ | captured-readonly; n=2; categories=success; collector-selected fully-bound GET R0; two redacted real captures | `registry:getNetworkOperators`
`upstream:backend/src/main.rs:614-615`
`upstream:backend/src/handlers.rs:1412-1426`
`upstream:bruno-api/get_operators.bru:8`
`fixture:captured-readonly` | +| `getNetworkOperatorsScan` | GET `/api/network/operators/scan` | cellular | `/instances/:id/cellular`
`cellular/operator-registration` | read-panel | planned: Frozen-scope read is modeled but real capture was explicitly denied; runtime support is probed per instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:Y (A successful active operator scan may return no operators.)
partial:Y (The scan response may contain usable operators alongside per-result failures or incomplete entries.)
policy-forbidden:Y (Active scan may be blocked by capability, authorization, preflight, or active-scan policy.)
unknown-result:N (A timed-out scan result may be discarded; it is not an unknown persistent write outcome.)
owner-switch:Y (Owner switch discards scan responses with a mismatched instance key.) | R0; none
direct | command-current-state/write-revision/version-probe/verify-write/route-result
Refresh current state when available and bind the actor decision to its revision.
Refresh current state or require an explicit recorded revision-bound override.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Writes require outcome verification before actor-initiated retry.
Structured result returns to the owning route with a redacted audit link. | REAL_READ_DEFERRED | denied-readonly-candidate; n=0; categories=none; active radio/network scan | `registry:getNetworkOperatorsScan`
`upstream:backend/src/main.rs:618-619`
`upstream:backend/src/handlers.rs:1429-1443`
`upstream:bruno-api/scan_operators.bru:8`
`fixture:denied-readonly-candidate` | +| `postNetworkRegisterManual` | POST `/api/network/register-manual` | cellular | `/instances/:id/cellular`
`cellular/operator-registration` | job-action | planned: Frozen-scope high-risk operation is planned with simulation-only acceptance until controlled safety authorization exists.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R2; explicit
preparation-job | job-fresh-preflight/high-risk-block/version-probe/new-job-lineage/job-result
Required fresh preflight; inability to refresh blocks execution.
Fresh preflight is mandatory; stale, expired, or unknown state blocks execution.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Retry creates a new Job lineage (retryOfJobId/rootJobId); never replay successful items.
/jobs/:jobId contains per-item result, attempt lineage, and redacted audit. | SIMULATED_HIGH_RISK | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postNetworkRegisterManual`
`upstream:backend/src/main.rs:622-623`
`upstream:backend/src/handlers.rs:1446-1466`
`upstream:bruno-api/register_operator_manual.bru:8`
`fixture:not-eligible-readonly-capture` | +| `postNetworkRegisterAuto` | POST `/api/network/register-auto` | cellular | `/instances/:id/cellular`
`cellular/operator-registration` | job-action | planned: Frozen-scope high-risk operation is planned with simulation-only acceptance until controlled safety authorization exists.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R2; explicit
preparation-job | job-fresh-preflight/high-risk-block/version-probe/new-job-lineage/job-result
Required fresh preflight; inability to refresh blocks execution.
Fresh preflight is mandatory; stale, expired, or unknown state blocks execution.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Retry creates a new Job lineage (retryOfJobId/rootJobId); never replay successful items.
/jobs/:jobId contains per-item result, attempt lineage, and redacted audit. | SIMULATED_HIGH_RISK | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postNetworkRegisterAuto`
`upstream:backend/src/main.rs:626-627`
`upstream:backend/src/handlers.rs:1469-1486`
`upstream:bruno-api/register_operator_auto.bru:8`
`fixture:not-eligible-readonly-capture` | +| `getApn` | GET `/api/apn` | data-connection | `/instances/:id/cellular`
`cellular/data-apn` | read-panel | planned: Frozen-scope read has two redacted captured responses; runtime support is still decided per registered instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Detail resource has no collection-empty rendering.)
partial:N (Atomic detail read has no meaningful partial rendering.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ | captured-readonly; n=2; categories=success; collector-selected fully-bound GET R0; two redacted real captures | `registry:getApn`
`upstream:backend/src/main.rs:630-631`
`upstream:backend/src/handlers.rs:1489-1504`
`upstream:bruno-api/get_apn_list.bru:8`
`fixture:captured-readonly` | +| `postApn` | POST `/api/apn` | data-connection | `/instances/:id/cellular`
`cellular/data-apn` | form | planned: Frozen-scope reversible command is contract-planned for a later controlled real-write acceptance phase.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R1; none
direct | command-current-state/write-revision/version-probe/verify-write/route-result
Refresh current state when available and bind the actor decision to its revision.
Refresh current state or require an explicit recorded revision-bound override.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Writes require outcome verification before actor-initiated retry.
Structured result returns to the owning route with a redacted audit link. | REAL_WRITE_LATER | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postApn`
`upstream:backend/src/main.rs:630-632`
`upstream:backend/src/handlers.rs:1507-1571`
`upstream:bruno-api/set_apn.bru:8`
`fixture:not-eligible-readonly-capture` | +| `getCellLock` | GET `/api/cell-lock` | radio-lock | `/instances/:id/cellular`
`cellular/radio-locks` | read-panel | planned: Frozen-scope read has two redacted captured responses; runtime support is still decided per registered instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Detail resource has no collection-empty rendering.)
partial:N (Atomic detail read has no meaningful partial rendering.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ | captured-readonly; n=2; categories=success; collector-selected fully-bound GET R0; two redacted real captures | `registry:getCellLock`
`upstream:backend/src/main.rs:636-637`
`upstream:backend/src/handlers.rs:1574-1582`
`upstream:bruno-api/get_cell_lock.bru:8`
`fixture:captured-readonly` | +| `postCellLock` | POST `/api/cell-lock` | radio-lock | `/instances/:id/cellular`
`cellular/radio-locks` | job-action | planned: Frozen-scope high-risk operation is planned with simulation-only acceptance until controlled safety authorization exists.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R2; explicit
preparation-job | job-fresh-preflight/high-risk-block/version-probe/new-job-lineage/job-result
Required fresh preflight; inability to refresh blocks execution.
Fresh preflight is mandatory; stale, expired, or unknown state blocks execution.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Retry creates a new Job lineage (retryOfJobId/rootJobId); never replay successful items.
/jobs/:jobId contains per-item result, attempt lineage, and redacted audit. | SIMULATED_HIGH_RISK | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postCellLock`
`upstream:backend/src/main.rs:636-638`
`upstream:backend/src/handlers.rs:1585-1603`
`upstream:bruno-api/set_cell_lock_lte.bru:8`
`upstream:bruno-api/set_cell_lock_nr.bru:8`
`upstream:bruno-api/unlock_cell_nr.bru:8`
`fixture:not-eligible-readonly-capture` | +| `postCellLockUnlockAll` | POST `/api/cell-lock/unlock-all` | radio-lock | `/instances/:id/cellular`
`cellular/radio-locks` | job-action | planned: Frozen-scope high-risk operation is planned with simulation-only acceptance until controlled safety authorization exists.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R2; explicit
preparation-job | job-fresh-preflight/high-risk-block/version-probe/new-job-lineage/job-result
Required fresh preflight; inability to refresh blocks execution.
Fresh preflight is mandatory; stale, expired, or unknown state blocks execution.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Retry creates a new Job lineage (retryOfJobId/rootJobId); never replay successful items.
/jobs/:jobId contains per-item result, attempt lineage, and redacted audit. | SIMULATED_HIGH_RISK | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postCellLockUnlockAll`
`upstream:backend/src/main.rs:642-643`
`upstream:backend/src/handlers.rs:1606-1616`
`upstream:bruno-api/unlock_all_cells.bru:8`
`fixture:not-eligible-readonly-capture` | +| `getData` | GET `/api/data` | data-connection | `/instances/:id/cellular`
`cellular/data-apn` | read-panel | planned: Frozen-scope read has two redacted captured responses; runtime support is still decided per registered instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Detail resource has no collection-empty rendering.)
partial:N (Atomic detail read has no meaningful partial rendering.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ | captured-readonly; n=2; categories=success; collector-selected fully-bound GET R0; two redacted real captures | `registry:getData`
`upstream:backend/src/main.rs:647-648`
`upstream:backend/src/handlers.rs:2035-2062`
`upstream:bruno-api/get_data_status.bru:8`
`fixture:captured-readonly` | +| `postData` | POST `/api/data` | data-connection | `/instances/:id/cellular`
`cellular/data-apn` | form | planned: Frozen-scope reversible command is contract-planned for a later controlled real-write acceptance phase.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R1; none
direct | command-current-state/write-revision/version-probe/verify-write/route-result
Refresh current state when available and bind the actor decision to its revision.
Refresh current state or require an explicit recorded revision-bound override.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Writes require outcome verification before actor-initiated retry.
Structured result returns to the owning route with a redacted audit link. | REAL_WRITE_LATER | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postData`
`upstream:backend/src/main.rs:647-649`
`upstream:backend/src/handlers.rs:2065-2131`
`upstream:bruno-api/set_data_status_disable.bru:8`
`upstream:bruno-api/set_data_status_enable.bru:8`
`fixture:not-eligible-readonly-capture` | +| `getRoaming` | GET `/api/roaming` | data-connection | `/instances/:id/cellular`
`cellular/data-apn` | read-panel | planned: Frozen-scope read has two redacted captured responses; runtime support is still decided per registered instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Detail resource has no collection-empty rendering.)
partial:N (Atomic detail read has no meaningful partial rendering.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ | captured-readonly; n=2; categories=success; collector-selected fully-bound GET R0; two redacted real captures | `registry:getRoaming`
`upstream:backend/src/main.rs:653-654`
`upstream:backend/src/handlers.rs:2172-2193`
`upstream:bruno-api/get_roaming_status.bru:8`
`fixture:captured-readonly` | +| `postRoaming` | POST `/api/roaming` | data-connection | `/instances/:id/cellular`
`cellular/data-apn` | form | planned: Frozen-scope reversible command is contract-planned for a later controlled real-write acceptance phase.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R1; none
direct | command-current-state/write-revision/version-probe/verify-write/route-result
Refresh current state when available and bind the actor decision to its revision.
Refresh current state or require an explicit recorded revision-bound override.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Writes require outcome verification before actor-initiated retry.
Structured result returns to the owning route with a redacted audit link. | REAL_WRITE_LATER | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postRoaming`
`upstream:backend/src/main.rs:653-655`
`upstream:backend/src/handlers.rs:2196-2247`
`upstream:bruno-api/set_roaming_enable.bru:8`
`upstream:bruno-api/set_roaming_disable.bru:8`
`fixture:not-eligible-readonly-capture` | +| `getAirplaneMode` | GET `/api/airplane-mode` | data-connection | `/instances/:id/cellular`
`cellular/data-apn` | read-panel | planned: Frozen-scope read has two redacted captured responses; runtime support is still decided per registered instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Detail resource has no collection-empty rendering.)
partial:N (Atomic detail read has no meaningful partial rendering.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ | captured-readonly; n=2; categories=success; collector-selected fully-bound GET R0; two redacted real captures | `registry:getAirplaneMode`
`upstream:backend/src/main.rs:659-660`
`upstream:backend/src/handlers.rs:2315-2329`
`upstream:bruno-api/get_airplane_mode.bru:8`
`fixture:captured-readonly` | +| `postAirplaneMode` | POST `/api/airplane-mode` | data-connection | `/instances/:id/cellular`
`cellular/data-apn` | form | planned: Frozen-scope reversible command is contract-planned for a later controlled real-write acceptance phase.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R1; none
direct | command-current-state/write-revision/version-probe/verify-write/route-result
Refresh current state when available and bind the actor decision to its revision.
Refresh current state or require an explicit recorded revision-bound override.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Writes require outcome verification before actor-initiated retry.
Structured result returns to the owning route with a redacted audit link. | REAL_WRITE_LATER | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postAirplaneMode`
`upstream:backend/src/main.rs:659-661`
`upstream:backend/src/handlers.rs:2250-2312`
`upstream:bruno-api/set_airplane_mode_disable.bru:8`
`upstream:bruno-api/set_airplane_mode_enable.bru:8`
`fixture:not-eligible-readonly-capture` | +| `postBasebandRestart` | POST `/api/baseband/restart` | data-connection | `/instances/:id/cellular`
`cellular/data-apn` | job-action | planned: Frozen-scope high-risk operation is planned with simulation-only acceptance until controlled safety authorization exists.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R3; strong
preparation-job | job-fresh-preflight/high-risk-block/version-probe/new-job-lineage/job-result
Required fresh preflight; inability to refresh blocks execution.
Fresh preflight is mandatory; stale, expired, or unknown state blocks execution.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Retry creates a new Job lineage (retryOfJobId/rootJobId); never replay successful items.
/jobs/:jobId contains per-item result, attempt lineage, and redacted audit. | SIMULATED_HIGH_RISK | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postBasebandRestart`
`upstream:backend/src/main.rs:665-666`
`upstream:backend/src/handlers.rs:2133-2159`
`fixture:not-eligible-readonly-capture` | +| `getBasebandRestartStatus` | GET `/api/baseband/restart/status` | data-connection | `/instances/:id/cellular`
`cellular/data-apn` | read-panel | planned: Frozen-scope read has two redacted captured responses; runtime support is still decided per registered instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Detail resource has no collection-empty rendering.)
partial:N (Atomic detail read has no meaningful partial rendering.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ | captured-readonly; n=2; categories=success; collector-selected fully-bound GET R0; two redacted real captures | `registry:getBasebandRestartStatus`
`upstream:backend/src/main.rs:669-670`
`upstream:backend/src/handlers.rs:2161-2169`
`fixture:captured-readonly` | +| `getWorkMode` | GET `/api/work-mode` | workmode-esim | `/instances/:id/esim`
`esim/mode` | read-panel | planned: Frozen-scope read has two redacted captured responses; runtime support is still decided per registered instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Detail resource has no collection-empty rendering.)
partial:N (Atomic detail read has no meaningful partial rendering.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ | captured-readonly; n=2; categories=success; collector-selected fully-bound GET R0; two redacted real captures | `registry:getWorkMode`
`upstream:backend/src/main.rs:674-675`
`upstream:backend/src/handlers.rs:352-365`
`fixture:captured-readonly` | +| `postWorkMode` | POST `/api/work-mode` | workmode-esim | `/instances/:id/esim`
`esim/mode` | job-action | planned: Frozen-scope high-risk operation is planned with simulation-only acceptance until controlled safety authorization exists.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R2; explicit
preparation-job | job-fresh-preflight/high-risk-block/version-probe/new-job-lineage/job-result
Required fresh preflight; inability to refresh blocks execution.
Fresh preflight is mandatory; stale, expired, or unknown state blocks execution.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Retry creates a new Job lineage (retryOfJobId/rootJobId); never replay successful items.
/jobs/:jobId contains per-item result, attempt lineage, and redacted audit. | SIMULATED_HIGH_RISK | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postWorkMode`
`upstream:backend/src/main.rs:674-676`
`upstream:backend/src/handlers.rs:368-405`
`fixture:not-eligible-readonly-capture` | +| `getEsimConfig` | GET `/api/esim/config` | workmode-esim | `/instances/:id/esim`
`esim/config` | read-panel | planned: Frozen-scope read has two redacted captured responses; runtime support is still decided per registered instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Detail resource has no collection-empty rendering.)
partial:N (Atomic detail read has no meaningful partial rendering.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ | captured-readonly; n=2; categories=unsupported; collector-selected fully-bound GET R0; two redacted real captures | `registry:getEsimConfig`
`upstream:backend/src/main.rs:680-681`
`upstream:backend/src/handlers.rs:456-462`
`fixture:captured-readonly` | +| `postEsimConfig` | POST `/api/esim/config` | workmode-esim | `/instances/:id/esim`
`esim/config` | job-action | planned: Frozen-scope high-risk operation is planned with simulation-only acceptance until controlled safety authorization exists.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R2; explicit
preparation-job | job-fresh-preflight/high-risk-block/version-probe/new-job-lineage/job-result
Required fresh preflight; inability to refresh blocks execution.
Fresh preflight is mandatory; stale, expired, or unknown state blocks execution.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Retry creates a new Job lineage (retryOfJobId/rootJobId); never replay successful items.
/jobs/:jobId contains per-item result, attempt lineage, and redacted audit. | SIMULATED_HIGH_RISK | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postEsimConfig`
`upstream:backend/src/main.rs:680-682`
`upstream:backend/src/handlers.rs:465-482`
`fixture:not-eligible-readonly-capture` | +| `getEsimLpacStatus` | GET `/api/esim/lpac/status` | workmode-esim | `/instances/:id/esim`
`esim/lpac` | read-panel | planned: Frozen-scope read has two redacted captured responses; runtime support is still decided per registered instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Detail resource has no collection-empty rendering.)
partial:N (Atomic detail read has no meaningful partial rendering.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ | captured-readonly; n=2; categories=auth-required; collector-selected fully-bound GET R0; two redacted real captures | `registry:getEsimLpacStatus`
`upstream:backend/src/main.rs:686-687`
`upstream:backend/src/handlers.rs:408-416`
`fixture:captured-readonly` | +| `postEsimLpacRepair` | POST `/api/esim/lpac/repair` | workmode-esim | `/instances/:id/esim`
`esim/lpac` | job-action | planned: Frozen-scope high-risk operation is planned with simulation-only acceptance until controlled safety authorization exists.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R2; explicit
preparation-job | job-fresh-preflight/high-risk-block/version-probe/new-job-lineage/job-result
Required fresh preflight; inability to refresh blocks execution.
Fresh preflight is mandatory; stale, expired, or unknown state blocks execution.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Retry creates a new Job lineage (retryOfJobId/rootJobId); never replay successful items.
/jobs/:jobId contains per-item result, attempt lineage, and redacted audit. | SIMULATED_HIGH_RISK | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postEsimLpacRepair`
`upstream:backend/src/main.rs:690-691`
`upstream:backend/src/handlers.rs:419-453`
`fixture:not-eligible-readonly-capture` | +| `getEsimEuicc` | GET `/api/esim/euicc` | workmode-esim | `/instances/:id/esim`
`esim/euicc` | read-panel | planned: Frozen-scope read is modeled but real capture was explicitly denied; runtime support is probed per instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Detail resource has no collection-empty rendering.)
partial:N (Atomic detail read has no meaningful partial rendering.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ_DEFERRED | denied-readonly-candidate; n=0; categories=none; query-bearing endpoint omitted | `registry:getEsimEuicc`
`upstream:backend/src/main.rs:694-695`
`upstream:backend/src/handlers.rs:485-521`
`fixture:denied-readonly-candidate` | +| `getEsimProfiles` | GET `/api/esim/profiles` | workmode-esim | `/instances/:id/esim`
`esim/profiles` | table | planned: Frozen-scope read is modeled but real capture was explicitly denied; runtime support is probed per instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:Y (Successful collection may contain no rows.)
partial:Y (Independent response sections may succeed or fail.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ_DEFERRED | denied-readonly-candidate; n=0; categories=none; query-bearing endpoint omitted | `registry:getEsimProfiles`
`upstream:backend/src/main.rs:698-699`
`upstream:backend/src/handlers.rs:524-598`
`fixture:denied-readonly-candidate` | +| `postEsimProfiles` | POST `/api/esim/profiles` | workmode-esim | `/instances/:id/esim`
`esim/profiles` | form | planned: Frozen-scope reversible command is contract-planned for a later controlled real-write acceptance phase.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R1; none
direct | command-current-state/write-revision/version-probe/verify-write/route-result
Refresh current state when available and bind the actor decision to its revision.
Refresh current state or require an explicit recorded revision-bound override.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Writes require outcome verification before actor-initiated retry.
Structured result returns to the owning route with a redacted audit link. | REAL_WRITE_LATER | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postEsimProfiles`
`upstream:backend/src/main.rs:698-700`
`upstream:backend/src/handlers.rs:803-1071`
`fixture:not-eligible-readonly-capture` | +| `postEsimProfilesIccidEnable` | POST `/api/esim/profiles/{iccid}/enable` | workmode-esim | `/instances/:id/esim`
`esim/profiles` | action | planned: Frozen-scope reversible command is contract-planned for a later controlled real-write acceptance phase.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R1; none
direct | command-current-state/write-revision/version-probe/verify-write/route-result
Refresh current state when available and bind the actor decision to its revision.
Refresh current state or require an explicit recorded revision-bound override.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Writes require outcome verification before actor-initiated retry.
Structured result returns to the owning route with a redacted audit link. | REAL_WRITE_LATER | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postEsimProfilesIccidEnable`
`upstream:backend/src/main.rs:704-705`
`upstream:backend/src/handlers.rs:601-726`
`fixture:not-eligible-readonly-capture` | +| `postEsimProfilesIccidRename` | POST `/api/esim/profiles/{iccid}/rename` | workmode-esim | `/instances/:id/esim`
`esim/profiles` | form | planned: Frozen-scope reversible command is contract-planned for a later controlled real-write acceptance phase.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R1; none
direct | command-current-state/write-revision/version-probe/verify-write/route-result
Refresh current state when available and bind the actor decision to its revision.
Refresh current state or require an explicit recorded revision-bound override.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Writes require outcome verification before actor-initiated retry.
Structured result returns to the owning route with a redacted audit link. | REAL_WRITE_LATER | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postEsimProfilesIccidRename`
`upstream:backend/src/main.rs:708-709`
`upstream:backend/src/handlers.rs:729-750`
`fixture:not-eligible-readonly-capture` | +| `deleteEsimProfilesIccid` | DELETE `/api/esim/profiles/{iccid}` | workmode-esim | `/instances/:id/esim`
`esim/profiles` | job-action | planned: Frozen-scope high-risk operation is planned with simulation-only acceptance until controlled safety authorization exists.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R3; strong
preparation-job | job-fresh-preflight/high-risk-block/version-probe/new-job-lineage/job-result
Required fresh preflight; inability to refresh blocks execution.
Fresh preflight is mandatory; stale, expired, or unknown state blocks execution.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Retry creates a new Job lineage (retryOfJobId/rootJobId); never replay successful items.
/jobs/:jobId contains per-item result, attempt lineage, and redacted audit. | SIMULATED_HIGH_RISK | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:deleteEsimProfilesIccid`
`upstream:backend/src/main.rs:712-713`
`upstream:backend/src/handlers.rs:753-780`
`fixture:not-eligible-readonly-capture` | +| `getCalls` | GET `/api/calls` | calls | `/instances/:id/calls`
`calls/live` | table | planned: Frozen-scope read has two redacted captured responses; runtime support is still decided per registered instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:Y (Successful collection may contain no rows.)
partial:Y (Independent response sections may succeed or fail.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ | captured-readonly; n=2; categories=success; collector-selected fully-bound GET R0; two redacted real captures | `registry:getCalls`
`upstream:backend/src/main.rs:717-718`
`upstream:backend/src/handlers.rs:2634-2655`
`upstream:bruno-api/get_calls.bru:8`
`fixture:captured-readonly` | +| `postCallDial` | POST `/api/call/dial` | calls | `/instances/:id/calls`
`calls/live` | form | planned: Frozen-scope reversible command is contract-planned for a later controlled real-write acceptance phase.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R1; none
direct | command-current-state/write-revision/version-probe/verify-write/route-result
Refresh current state when available and bind the actor decision to its revision.
Refresh current state or require an explicit recorded revision-bound override.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Writes require outcome verification before actor-initiated retry.
Structured result returns to the owning route with a redacted audit link. | REAL_WRITE_LATER | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postCallDial`
`upstream:backend/src/main.rs:721-722`
`upstream:backend/src/handlers.rs:2657-2689`
`upstream:bruno-api/post_call_dial.bru:8`
`fixture:not-eligible-readonly-capture` | +| `postCallHangup` | POST `/api/call/hangup` | calls | `/instances/:id/calls`
`calls/live` | form | planned: Frozen-scope reversible command is contract-planned for a later controlled real-write acceptance phase.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R1; none
direct | command-current-state/write-revision/version-probe/verify-write/route-result
Refresh current state when available and bind the actor decision to its revision.
Refresh current state or require an explicit recorded revision-bound override.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Writes require outcome verification before actor-initiated retry.
Structured result returns to the owning route with a redacted audit link. | REAL_WRITE_LATER | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postCallHangup`
`upstream:backend/src/main.rs:725-726`
`upstream:backend/src/handlers.rs:2691-2725`
`upstream:bruno-api/post_call_hangup.bru:8`
`fixture:not-eligible-readonly-capture` | +| `postCallHangupAll` | POST `/api/call/hangup-all` | calls | `/instances/:id/calls`
`calls/live` | action | planned: Frozen-scope reversible command is contract-planned for a later controlled real-write acceptance phase.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R1; none
direct | command-current-state/write-revision/version-probe/verify-write/route-result
Refresh current state when available and bind the actor decision to its revision.
Refresh current state or require an explicit recorded revision-bound override.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Writes require outcome verification before actor-initiated retry.
Structured result returns to the owning route with a redacted audit link. | REAL_WRITE_LATER | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postCallHangupAll`
`upstream:backend/src/main.rs:729-730`
`upstream:backend/src/handlers.rs:2727-2760`
`upstream:bruno-api/post_call_hangup_all.bru:8`
`fixture:not-eligible-readonly-capture` | +| `postCallAnswer` | POST `/api/call/answer` | calls | `/instances/:id/calls`
`calls/live` | form | planned: Frozen-scope reversible command is contract-planned for a later controlled real-write acceptance phase.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R1; none
direct | command-current-state/write-revision/version-probe/verify-write/route-result
Refresh current state when available and bind the actor decision to its revision.
Refresh current state or require an explicit recorded revision-bound override.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Writes require outcome verification before actor-initiated retry.
Structured result returns to the owning route with a redacted audit link. | REAL_WRITE_LATER | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postCallAnswer`
`upstream:backend/src/main.rs:733-734`
`upstream:backend/src/handlers.rs:2762-2789`
`upstream:bruno-api/post_call_answer.bru:8`
`fixture:not-eligible-readonly-capture` | +| `getCallVolume` | GET `/api/call/volume` | calls | `/instances/:id/calls`
`calls/volume` | read-panel | planned: Frozen-scope read has two redacted captured responses; runtime support is still decided per registered instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Detail resource has no collection-empty rendering.)
partial:N (Atomic detail read has no meaningful partial rendering.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ | captured-readonly; n=2; categories=success; collector-selected fully-bound GET R0; two redacted real captures | `registry:getCallVolume`
`upstream:backend/src/main.rs:737-738`
`upstream:backend/src/handlers.rs:2899-2906`
`upstream:bruno-api/get_call_volume.bru:8`
`fixture:captured-readonly` | +| `postCallVolume` | POST `/api/call/volume` | calls | `/instances/:id/calls`
`calls/volume` | form | planned: Frozen-scope reversible command is contract-planned for a later controlled real-write acceptance phase.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R1; none
direct | command-current-state/write-revision/version-probe/verify-write/route-result
Refresh current state when available and bind the actor decision to its revision.
Refresh current state or require an explicit recorded revision-bound override.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Writes require outcome verification before actor-initiated retry.
Structured result returns to the owning route with a redacted audit link. | REAL_WRITE_LATER | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postCallVolume`
`upstream:backend/src/main.rs:737-739`
`upstream:backend/src/handlers.rs:2908-2922`
`upstream:bruno-api/set_call_volume.bru:8`
`fixture:not-eligible-readonly-capture` | +| `getCallForwarding` | GET `/api/call/forwarding` | calls | `/instances/:id/calls`
`calls/forwarding` | read-panel | planned: Frozen-scope read has two redacted captured responses; runtime support is still decided per registered instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Detail resource has no collection-empty rendering.)
partial:N (Atomic detail read has no meaningful partial rendering.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ | captured-readonly; n=2; categories=success; collector-selected fully-bound GET R0; two redacted real captures | `registry:getCallForwarding`
`upstream:backend/src/main.rs:743-744`
`upstream:backend/src/handlers.rs:2924-2931`
`upstream:bruno-api/get_call_forwarding.bru:8`
`fixture:captured-readonly` | +| `postCallForwarding` | POST `/api/call/forwarding` | calls | `/instances/:id/calls`
`calls/forwarding` | form | planned: Frozen-scope reversible command is contract-planned for a later controlled real-write acceptance phase.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R1; none
direct | command-current-state/write-revision/version-probe/verify-write/route-result
Refresh current state when available and bind the actor decision to its revision.
Refresh current state or require an explicit recorded revision-bound override.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Writes require outcome verification before actor-initiated retry.
Structured result returns to the owning route with a redacted audit link. | REAL_WRITE_LATER | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postCallForwarding`
`upstream:backend/src/main.rs:743-745`
`upstream:backend/src/handlers.rs:2933-2943`
`upstream:bruno-api/set_call_forwarding.bru:8`
`fixture:not-eligible-readonly-capture` | +| `getCallSettings` | GET `/api/call/settings` | calls | `/instances/:id/calls`
`calls/settings` | read-panel | planned: Frozen-scope read has two redacted captured responses; runtime support is still decided per registered instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Detail resource has no collection-empty rendering.)
partial:N (Atomic detail read has no meaningful partial rendering.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ | captured-readonly; n=2; categories=success; collector-selected fully-bound GET R0; two redacted real captures | `registry:getCallSettings`
`upstream:backend/src/main.rs:749-750`
`upstream:backend/src/handlers.rs:2852-2866`
`upstream:bruno-api/get_call_settings.bru:8`
`fixture:captured-readonly` | +| `postCallSettings` | POST `/api/call/settings` | calls | `/instances/:id/calls`
`calls/settings` | job-action | planned: Frozen-scope high-risk operation is planned with simulation-only acceptance until controlled safety authorization exists.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R2; explicit
preparation-job | job-fresh-preflight/high-risk-block/version-probe/new-job-lineage/job-result
Required fresh preflight; inability to refresh blocks execution.
Fresh preflight is mandatory; stale, expired, or unknown state blocks execution.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Retry creates a new Job lineage (retryOfJobId/rootJobId); never replay successful items.
/jobs/:jobId contains per-item result, attempt lineage, and redacted audit. | SIMULATED_HIGH_RISK | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postCallSettings`
`upstream:backend/src/main.rs:749-751`
`upstream:backend/src/handlers.rs:2868-2897`
`upstream:bruno-api/set_call_settings.bru:8`
`fixture:not-eligible-readonly-capture` | +| `getCallHistory` | GET `/api/call/history` | calls | `/instances/:id/calls`
`calls/history` | table | planned: Frozen-scope read is modeled but real capture was explicitly denied; runtime support is probed per instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:Y (Successful collection may contain no rows.)
partial:Y (Independent response sections may succeed or fail.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ_DEFERRED | denied-readonly-candidate; n=0; categories=none; query limit is not encoded in Registry path contract | `registry:getCallHistory`
`upstream:backend/src/main.rs:755-756`
`upstream:backend/src/handlers.rs:2791-2813`
`fixture:denied-readonly-candidate` | +| `deleteCallHistoryId` | DELETE `/api/call/history/{id}` | calls | `/instances/:id/calls`
`calls/history` | job-action | planned: Frozen-scope high-risk operation is planned with simulation-only acceptance until controlled safety authorization exists.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R3; strong
preparation-job | job-fresh-preflight/high-risk-block/version-probe/new-job-lineage/job-result
Required fresh preflight; inability to refresh blocks execution.
Fresh preflight is mandatory; stale, expired, or unknown state blocks execution.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Retry creates a new Job lineage (retryOfJobId/rootJobId); never replay successful items.
/jobs/:jobId contains per-item result, attempt lineage, and redacted audit. | SIMULATED_HIGH_RISK | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:deleteCallHistoryId`
`upstream:backend/src/main.rs:759-760`
`upstream:backend/src/handlers.rs:2815-2832`
`upstream:bruno-api/delete_call_history.bru:8`
`fixture:not-eligible-readonly-capture` | +| `postCallHistoryClear` | POST `/api/call/history/clear` | calls | `/instances/:id/calls`
`calls/history` | job-action | planned: Frozen-scope high-risk operation is planned with simulation-only acceptance until controlled safety authorization exists.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R3; strong
preparation-job | job-fresh-preflight/high-risk-block/version-probe/new-job-lineage/job-result
Required fresh preflight; inability to refresh blocks execution.
Fresh preflight is mandatory; stale, expired, or unknown state blocks execution.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Retry creates a new Job lineage (retryOfJobId/rootJobId); never replay successful items.
/jobs/:jobId contains per-item result, attempt lineage, and redacted audit. | SIMULATED_HIGH_RISK | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postCallHistoryClear`
`upstream:backend/src/main.rs:763-764`
`upstream:backend/src/handlers.rs:2834-2850`
`upstream:bruno-api/clear_call_history.bru:8`
`fixture:not-eligible-readonly-capture` | +| `getImsStatus` | GET `/api/ims/status` | calls | `/instances/:id/calls`
`calls/ims-voicemail` | read-panel | planned: Frozen-scope read has two redacted captured responses; runtime support is still decided per registered instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Detail resource has no collection-empty rendering.)
partial:N (Atomic detail read has no meaningful partial rendering.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ | captured-readonly; n=2; categories=success; collector-selected fully-bound GET R0; two redacted real captures | `registry:getImsStatus`
`upstream:backend/src/main.rs:767-768`
`upstream:backend/src/handlers.rs:2945-2952`
`upstream:bruno-api/get_ims_status.bru:8`
`fixture:captured-readonly` | +| `getVoicemailStatus` | GET `/api/voicemail/status` | calls | `/instances/:id/calls`
`calls/ims-voicemail` | read-panel | planned: Frozen-scope read has two redacted captured responses; runtime support is still decided per registered instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Detail resource has no collection-empty rendering.)
partial:N (Atomic detail read has no meaningful partial rendering.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ | captured-readonly; n=2; categories=success; collector-selected fully-bound GET R0; two redacted real captures | `registry:getVoicemailStatus`
`upstream:backend/src/main.rs:771-772`
`upstream:backend/src/handlers.rs:2954-2961`
`upstream:bruno-api/get_voicemail_status.bru:8`
`fixture:captured-readonly` | +| `postSmsSend` | POST `/api/sms/send` | messages | `/instances/:id/messages`
`messages/compose` | job-action | planned: Frozen-scope high-risk operation is planned with simulation-only acceptance until controlled safety authorization exists.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R2; explicit
preparation-job | job-fresh-preflight/high-risk-block/version-probe/new-job-lineage/job-result
Required fresh preflight; inability to refresh blocks execution.
Fresh preflight is mandatory; stale, expired, or unknown state blocks execution.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Retry creates a new Job lineage (retryOfJobId/rootJobId); never replay successful items.
/jobs/:jobId contains per-item result, attempt lineage, and redacted audit. | SIMULATED_HIGH_RISK | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postSmsSend`
`upstream:backend/src/main.rs:776-777`
`upstream:backend/src/handlers.rs:2373-2404`
`upstream:bruno-api/post_sms_send.bru:8`
`fixture:not-eligible-readonly-capture` | +| `getSmsList` | GET `/api/sms/list` | messages | `/instances/:id/messages`
`messages/list` | table | planned: Frozen-scope read is modeled but real capture was explicitly denied; runtime support is probed per instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:Y (Successful collection may contain no rows.)
partial:Y (Independent response sections may succeed or fail.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ_DEFERRED | denied-readonly-candidate; n=0; categories=none; message list can expose body; intentionally not collected | `registry:getSmsList`
`upstream:backend/src/main.rs:780-781`
`upstream:backend/src/handlers.rs:2407-2434`
`fixture:denied-readonly-candidate` | +| `getSmsConversation` | GET `/api/sms/conversation` | messages | `/instances/:id/messages`
`messages/conversation` | table | planned: Frozen-scope read is modeled but real capture was explicitly denied; runtime support is probed per instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:Y (Successful collection may contain no rows.)
partial:Y (Independent response sections may succeed or fail.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ_DEFERRED | denied-readonly-candidate; n=0; categories=none; requires correspondent query and exposes message bodies | `registry:getSmsConversation`
`upstream:backend/src/main.rs:784-785`
`upstream:backend/src/handlers.rs:2437-2458`
`fixture:denied-readonly-candidate` | +| `getSmsStats` | GET `/api/sms/stats` | messages | `/instances/:id/messages`
`messages/stats` | read-panel | planned: Frozen-scope read has two redacted captured responses; runtime support is still decided per registered instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Detail resource has no collection-empty rendering.)
partial:N (Atomic detail read has no meaningful partial rendering.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ | captured-readonly; n=2; categories=success; collector-selected fully-bound GET R0; two redacted real captures | `registry:getSmsStats`
`upstream:backend/src/main.rs:788-789`
`upstream:backend/src/handlers.rs:2461-2477`
`upstream:bruno-api/get_sms_stats.bru:8`
`fixture:captured-readonly` | +| `postSmsBatchDelete` | POST `/api/sms/batch-delete` | messages | `/instances/:id/messages`
`messages/destructive` | job-action | planned: Frozen-scope high-risk operation is planned with simulation-only acceptance until controlled safety authorization exists.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:Y (Job items may independently succeed, fail, or be skipped.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R3; strong
preparation-job | job-fresh-preflight/high-risk-block/version-probe/new-job-lineage/job-result
Required fresh preflight; inability to refresh blocks execution.
Fresh preflight is mandatory; stale, expired, or unknown state blocks execution.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Retry creates a new Job lineage (retryOfJobId/rootJobId); never replay successful items.
/jobs/:jobId contains per-item result, attempt lineage, and redacted audit. | SIMULATED_HIGH_RISK | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postSmsBatchDelete`
`upstream:backend/src/main.rs:792-793`
`upstream:backend/src/handlers.rs:2551-2578`
`fixture:not-eligible-readonly-capture` | +| `deleteSmsConversationPhoneNumber` | DELETE `/api/sms/conversation/{phone_number}` | messages | `/instances/:id/messages`
`messages/destructive` | job-action | planned: Frozen-scope high-risk operation is planned with simulation-only acceptance until controlled safety authorization exists.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R3; strong
preparation-job | job-fresh-preflight/high-risk-block/version-probe/new-job-lineage/job-result
Required fresh preflight; inability to refresh blocks execution.
Fresh preflight is mandatory; stale, expired, or unknown state blocks execution.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Retry creates a new Job lineage (retryOfJobId/rootJobId); never replay successful items.
/jobs/:jobId contains per-item result, attempt lineage, and redacted audit. | SIMULATED_HIGH_RISK | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:deleteSmsConversationPhoneNumber`
`upstream:backend/src/main.rs:796-797`
`upstream:backend/src/handlers.rs:2528-2548`
`fixture:not-eligible-readonly-capture` | +| `deleteSmsMessageId` | DELETE `/api/sms/message/{id}` | messages | `/instances/:id/messages`
`messages/destructive` | job-action | planned: Frozen-scope high-risk operation is planned with simulation-only acceptance until controlled safety authorization exists.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R3; strong
preparation-job | job-fresh-preflight/high-risk-block/version-probe/new-job-lineage/job-result
Required fresh preflight; inability to refresh blocks execution.
Fresh preflight is mandatory; stale, expired, or unknown state blocks execution.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Retry creates a new Job lineage (retryOfJobId/rootJobId); never replay successful items.
/jobs/:jobId contains per-item result, attempt lineage, and redacted audit. | SIMULATED_HIGH_RISK | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:deleteSmsMessageId`
`upstream:backend/src/main.rs:800-801`
`upstream:backend/src/handlers.rs:2508-2525`
`fixture:not-eligible-readonly-capture` | +| `postSmsClear` | POST `/api/sms/clear` | messages | `/instances/:id/messages`
`messages/destructive` | job-action | planned: Frozen-scope high-risk operation is planned with simulation-only acceptance until controlled safety authorization exists.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R3; strong
preparation-job | job-fresh-preflight/high-risk-block/version-probe/new-job-lineage/job-result
Required fresh preflight; inability to refresh blocks execution.
Fresh preflight is mandatory; stale, expired, or unknown state blocks execution.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Retry creates a new Job lineage (retryOfJobId/rootJobId); never replay successful items.
/jobs/:jobId contains per-item result, attempt lineage, and redacted audit. | SIMULATED_HIGH_RISK | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postSmsClear`
`upstream:backend/src/main.rs:804-805`
`upstream:backend/src/handlers.rs:2480-2505`
`upstream:bruno-api/post_sms_clear.bru:8`
`fixture:not-eligible-readonly-capture` | +| `getStats` | GET `/api/stats` | device-system | `/instances/:id/overview`
`overview/stats` | read-panel | planned: Frozen-scope read has two redacted captured responses; runtime support is still decided per registered instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Detail resource has no collection-empty rendering.)
partial:Y (Independent response sections may succeed or fail.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | command-current-state/read-retain/version-probe/safe-read/route-result
Refresh current state when available and bind the actor decision to its revision.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ | captured-readonly; n=2; categories=success; collector-selected fully-bound GET R0; two redacted real captures | `registry:getStats`
`upstream:backend/src/main.rs:808-808`
`upstream:backend/src/handlers.rs:3272-3288`
`upstream:bruno-api/get_stats.bru:8`
`fixture:captured-readonly` | +| `getStatsCpu` | GET `/api/stats/cpu` | device-system | `/instances/:id/overview`
`overview/stats` | read-panel | planned: Frozen-scope read has two redacted captured responses; runtime support is still decided per registered instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Detail resource has no collection-empty rendering.)
partial:Y (Independent response sections may succeed or fail.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | command-current-state/read-retain/version-probe/safe-read/route-result
Refresh current state when available and bind the actor decision to its revision.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ | captured-readonly; n=2; categories=success; collector-selected fully-bound GET R0; two redacted real captures | `registry:getStatsCpu`
`upstream:backend/src/main.rs:809-809`
`upstream:backend/src/handlers.rs:3291-3302`
`upstream:bruno-api/get_cpu_info.bru:8`
`fixture:captured-readonly` | +| `getConnectivity` | GET `/api/connectivity` | device-system | `/instances/:id/overview`
`overview/auth-health` | read-panel | planned: Frozen-scope read is modeled but real capture was explicitly denied; runtime support is probed per instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Detail resource has no collection-empty rendering.)
partial:Y (Independent response sections may succeed or fail.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | command-current-state/read-retain/version-probe/safe-read/route-result
Refresh current state when available and bind the actor decision to its revision.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ_DEFERRED | denied-readonly-candidate; n=0; categories=none; handler performs active connectivity ping | `registry:getConnectivity`
`upstream:backend/src/main.rs:811-812`
`upstream:backend/src/handlers.rs:3305-3322`
`fixture:denied-readonly-candidate` | +| `postSystemReboot` | POST `/api/system/reboot` | device-system | `/instances/:id/overview`
`overview/system-actions` | job-action | planned: Frozen-scope high-risk operation is planned with simulation-only acceptance until controlled safety authorization exists.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R3; strong
preparation-job | job-fresh-preflight/high-risk-block/version-probe/new-job-lineage/job-result
Required fresh preflight; inability to refresh blocks execution.
Fresh preflight is mandatory; stale, expired, or unknown state blocks execution.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Retry creates a new Job lineage (retryOfJobId/rootJobId); never replay successful items.
/jobs/:jobId contains per-item result, attempt lineage, and redacted audit. | SIMULATED_HIGH_RISK | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postSystemReboot`
`upstream:backend/src/main.rs:815-816`
`upstream:backend/src/handlers.rs:3381-3406`
`upstream:bruno-api/post_system_reboot.bru:8`
`fixture:not-eligible-readonly-capture` | +| `postServiceRestart` | POST `/api/service/restart` | device-system | `/instances/:id/overview`
`overview/system-actions` | job-action | planned: Frozen-scope high-risk operation is planned with simulation-only acceptance until controlled safety authorization exists.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R3; strong
preparation-job | job-fresh-preflight/high-risk-block/version-probe/new-job-lineage/job-result
Required fresh preflight; inability to refresh blocks execution.
Fresh preflight is mandatory; stale, expired, or unknown state blocks execution.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Retry creates a new Job lineage (retryOfJobId/rootJobId); never replay successful items.
/jobs/:jobId contains per-item result, attempt lineage, and redacted audit. | SIMULATED_HIGH_RISK | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postServiceRestart`
`upstream:backend/src/main.rs:819-820`
`upstream:backend/src/handlers.rs:3578-3601`
`fixture:not-eligible-readonly-capture` | +| `getNotificationsConfig` | GET `/api/notifications/config` | notifications | `/instances/:id/notifications`
`notifications/config` | read-panel | planned: Frozen-scope read has two redacted captured responses; runtime support is still decided per registered instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Detail resource has no collection-empty rendering.)
partial:N (Atomic detail read has no meaningful partial rendering.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ | captured-readonly; n=2; categories=success; collector-selected fully-bound GET R0; two redacted real captures | `registry:getNotificationsConfig`
`upstream:backend/src/main.rs:824-825`
`upstream:backend/src/handlers.rs:3641-3652`
`upstream:bruno-api/get_notification_config.bru:8`
`fixture:captured-readonly` | +| `postNotificationsConfig` | POST `/api/notifications/config` | notifications | `/instances/:id/notifications`
`notifications/config` | job-action | planned: Frozen-scope high-risk operation is planned with simulation-only acceptance until controlled safety authorization exists.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R2; explicit
preparation-job | job-fresh-preflight/high-risk-block/version-probe/new-job-lineage/job-result
Required fresh preflight; inability to refresh blocks execution.
Fresh preflight is mandatory; stale, expired, or unknown state blocks execution.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Retry creates a new Job lineage (retryOfJobId/rootJobId); never replay successful items.
/jobs/:jobId contains per-item result, attempt lineage, and redacted audit. | SIMULATED_HIGH_RISK | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postNotificationsConfig`
`upstream:backend/src/main.rs:824-826`
`upstream:backend/src/handlers.rs:3655-3672`
`upstream:bruno-api/set_notification_config.bru:8`
`fixture:not-eligible-readonly-capture` | +| `postNotificationsTestChannel` | POST `/api/notifications/test/{channel}` | notifications | `/instances/:id/notifications`
`notifications/config` | job-action | planned: Frozen-scope high-risk operation is planned with simulation-only acceptance until controlled safety authorization exists.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R2; explicit
preparation-job | job-fresh-preflight/high-risk-block/version-probe/new-job-lineage/job-result
Required fresh preflight; inability to refresh blocks execution.
Fresh preflight is mandatory; stale, expired, or unknown state blocks execution.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Retry creates a new Job lineage (retryOfJobId/rootJobId); never replay successful items.
/jobs/:jobId contains per-item result, attempt lineage, and redacted audit. | SIMULATED_HIGH_RISK | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postNotificationsTestChannel`
`upstream:backend/src/main.rs:830-831`
`upstream:backend/src/handlers.rs:3675-3704`
`fixture:not-eligible-readonly-capture` | +| `getNotificationsLogs` | GET `/api/notifications/logs` | notifications | `/instances/:id/notifications`
`notifications/logs` | table | planned: Frozen-scope read is modeled but real capture was explicitly denied; runtime support is probed per instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:Y (Successful collection may contain no rows.)
partial:Y (Independent response sections may succeed or fail.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ_DEFERRED | denied-readonly-candidate; n=0; categories=none; query limit is not encoded in Registry path contract | `registry:getNotificationsLogs`
`upstream:backend/src/main.rs:835-836`
`upstream:backend/src/handlers.rs:3709-3734`
`fixture:denied-readonly-candidate` | +| `postNotificationsLogsClear` | POST `/api/notifications/logs/clear` | notifications | `/instances/:id/notifications`
`notifications/logs` | job-action | planned: Frozen-scope high-risk operation is planned with simulation-only acceptance until controlled safety authorization exists.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R3; strong
preparation-job | job-fresh-preflight/high-risk-block/version-probe/new-job-lineage/job-result
Required fresh preflight; inability to refresh blocks execution.
Fresh preflight is mandatory; stale, expired, or unknown state blocks execution.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Retry creates a new Job lineage (retryOfJobId/rootJobId); never replay successful items.
/jobs/:jobId contains per-item result, attempt lineage, and redacted audit. | SIMULATED_HIGH_RISK | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postNotificationsLogsClear`
`upstream:backend/src/main.rs:839-840`
`upstream:backend/src/handlers.rs:3737-3760`
`fixture:not-eligible-readonly-capture` | +| `getNotificationsQueue` | GET `/api/notifications/queue` | notifications | `/instances/:id/notifications`
`notifications/queue` | table | planned: Frozen-scope read is modeled but real capture was explicitly denied; runtime support is probed per instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:Y (Successful collection may contain no rows.)
partial:Y (Independent response sections may succeed or fail.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ_DEFERRED | denied-readonly-candidate; n=0; categories=none; query limit is not encoded in Registry path contract | `registry:getNotificationsQueue`
`upstream:backend/src/main.rs:843-844`
`upstream:backend/src/notification_queue.rs:24-41`
`fixture:denied-readonly-candidate` | +| `postNotificationsQueueRetryAll` | POST `/api/notifications/queue/retry-all` | notifications | `/instances/:id/notifications`
`notifications/queue` | job-action | planned: Frozen-scope high-risk operation is planned with simulation-only acceptance until controlled safety authorization exists.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:Y (Job items may independently succeed, fail, or be skipped.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R2; explicit
preparation-job | job-fresh-preflight/high-risk-block/version-probe/new-job-lineage/job-result
Required fresh preflight; inability to refresh blocks execution.
Fresh preflight is mandatory; stale, expired, or unknown state blocks execution.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Retry creates a new Job lineage (retryOfJobId/rootJobId); never replay successful items.
/jobs/:jobId contains per-item result, attempt lineage, and redacted audit. | SIMULATED_HIGH_RISK | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postNotificationsQueueRetryAll`
`upstream:backend/src/main.rs:847-848`
`upstream:backend/src/notification_queue.rs:84-100`
`fixture:not-eligible-readonly-capture` | +| `postNotificationsQueueClear` | POST `/api/notifications/queue/clear` | notifications | `/instances/:id/notifications`
`notifications/queue` | job-action | planned: Frozen-scope high-risk operation is planned with simulation-only acceptance until controlled safety authorization exists.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:Y (Job items may independently succeed, fail, or be skipped.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R3; strong
preparation-job | job-fresh-preflight/high-risk-block/version-probe/new-job-lineage/job-result
Required fresh preflight; inability to refresh blocks execution.
Fresh preflight is mandatory; stale, expired, or unknown state blocks execution.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Retry creates a new Job lineage (retryOfJobId/rootJobId); never replay successful items.
/jobs/:jobId contains per-item result, attempt lineage, and redacted audit. | SIMULATED_HIGH_RISK | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postNotificationsQueueClear`
`upstream:backend/src/main.rs:851-852`
`upstream:backend/src/notification_queue.rs:103-119`
`fixture:not-eligible-readonly-capture` | +| `deleteNotificationsQueueId` | DELETE `/api/notifications/queue/{id}` | notifications | `/instances/:id/notifications`
`notifications/queue` | job-action | planned: Frozen-scope high-risk operation is planned with simulation-only acceptance until controlled safety authorization exists.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R3; strong
preparation-job | job-fresh-preflight/high-risk-block/version-probe/new-job-lineage/job-result
Required fresh preflight; inability to refresh blocks execution.
Fresh preflight is mandatory; stale, expired, or unknown state blocks execution.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Retry creates a new Job lineage (retryOfJobId/rootJobId); never replay successful items.
/jobs/:jobId contains per-item result, attempt lineage, and redacted audit. | SIMULATED_HIGH_RISK | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:deleteNotificationsQueueId`
`upstream:backend/src/main.rs:855-856`
`upstream:backend/src/notification_queue.rs:64-81`
`fixture:not-eligible-readonly-capture` | +| `postNotificationsQueueIdRetry` | POST `/api/notifications/queue/{id}/retry` | notifications | `/instances/:id/notifications`
`notifications/queue` | job-action | planned: Frozen-scope high-risk operation is planned with simulation-only acceptance until controlled safety authorization exists.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R2; explicit
preparation-job | job-fresh-preflight/high-risk-block/version-probe/new-job-lineage/job-result
Required fresh preflight; inability to refresh blocks execution.
Fresh preflight is mandatory; stale, expired, or unknown state blocks execution.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Retry creates a new Job lineage (retryOfJobId/rootJobId); never replay successful items.
/jobs/:jobId contains per-item result, attempt lineage, and redacted audit. | SIMULATED_HIGH_RISK | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postNotificationsQueueIdRetry`
`upstream:backend/src/main.rs:859-860`
`upstream:backend/src/notification_queue.rs:44-61`
`fixture:not-eligible-readonly-capture` | +| `getAutomationConfig` | GET `/api/automation/config` | automation | `/instances/:id/automation`
`automation/config` | read-panel | planned: Frozen-scope read has two redacted captured responses; runtime support is still decided per registered instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Detail resource has no collection-empty rendering.)
partial:N (Atomic detail read has no meaningful partial rendering.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ | captured-readonly; n=2; categories=unsupported; collector-selected fully-bound GET R0; two redacted real captures | `registry:getAutomationConfig`
`upstream:backend/src/main.rs:864-865`
`upstream:backend/src/handlers.rs:3968-3979`
`fixture:captured-readonly` | +| `postAutomationConfig` | POST `/api/automation/config` | automation | `/instances/:id/automation`
`automation/config` | job-action | planned: Frozen-scope high-risk operation is planned with simulation-only acceptance until controlled safety authorization exists.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R2; explicit
preparation-job | job-fresh-preflight/high-risk-block/version-probe/new-job-lineage/job-result
Required fresh preflight; inability to refresh blocks execution.
Fresh preflight is mandatory; stale, expired, or unknown state blocks execution.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Retry creates a new Job lineage (retryOfJobId/rootJobId); never replay successful items.
/jobs/:jobId contains per-item result, attempt lineage, and redacted audit. | SIMULATED_HIGH_RISK | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postAutomationConfig`
`upstream:backend/src/main.rs:864-866`
`upstream:backend/src/handlers.rs:3982-3999`
`fixture:not-eligible-readonly-capture` | +| `getAutomationLogs` | GET `/api/automation/logs` | automation | `/instances/:id/automation`
`automation/logs` | table | planned: Frozen-scope read is modeled but real capture was explicitly denied; runtime support is probed per instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:Y (Successful collection may contain no rows.)
partial:Y (Independent response sections may succeed or fail.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ_DEFERRED | denied-readonly-candidate; n=0; categories=none; query limit is not encoded in Registry path contract | `registry:getAutomationLogs`
`upstream:backend/src/main.rs:870-871`
`upstream:backend/src/handlers.rs:4002-4027`
`fixture:denied-readonly-candidate` | +| `postAutomationLogsClear` | POST `/api/automation/logs/clear` | automation | `/instances/:id/automation`
`automation/logs` | job-action | planned: Frozen-scope high-risk operation is planned with simulation-only acceptance until controlled safety authorization exists.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R3; strong
preparation-job | job-fresh-preflight/high-risk-block/version-probe/new-job-lineage/job-result
Required fresh preflight; inability to refresh blocks execution.
Fresh preflight is mandatory; stale, expired, or unknown state blocks execution.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Retry creates a new Job lineage (retryOfJobId/rootJobId); never replay successful items.
/jobs/:jobId contains per-item result, attempt lineage, and redacted audit. | SIMULATED_HIGH_RISK | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postAutomationLogsClear`
`upstream:backend/src/main.rs:874-875`
`upstream:backend/src/handlers.rs:4030-4053`
`fixture:not-eligible-readonly-capture` | +| `postAutomationTestTaskId` | POST `/api/automation/test/{task_id}` | automation | `/instances/:id/automation`
`automation/task-test` | job-action | planned: Frozen-scope high-risk operation is planned with simulation-only acceptance until controlled safety authorization exists.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R2; explicit
preparation-job | job-fresh-preflight/high-risk-block/version-probe/new-job-lineage/job-result
Required fresh preflight; inability to refresh blocks execution.
Fresh preflight is mandatory; stale, expired, or unknown state blocks execution.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Retry creates a new Job lineage (retryOfJobId/rootJobId); never replay successful items.
/jobs/:jobId contains per-item result, attempt lineage, and redacted audit. | SIMULATED_HIGH_RISK | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postAutomationTestTaskId`
`upstream:backend/src/main.rs:878-879`
`upstream:backend/src/handlers.rs:4056-4146`
`fixture:not-eligible-readonly-capture` | +| `getOtaStatus` | GET `/api/ota/status` | ota | `/instances/:id/ota`
`ota/status` | read-panel | planned: Frozen-scope read has two redacted captured responses; runtime support is still decided per registered instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Detail resource has no collection-empty rendering.)
partial:N (Atomic detail read has no meaningful partial rendering.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/read-retain/version-probe/safe-read/route-result
Registered instance, authorized actor, and runtime capability decision are required.
Retain last success as stale and permit an explicit safe refresh.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Safe reads may retry within deadline and retain stale success.
Structured result returns to the owning route with a redacted audit link. | REAL_READ | captured-readonly; n=2; categories=success; collector-selected fully-bound GET R0; two redacted real captures | `registry:getOtaStatus`
`upstream:backend/src/main.rs:882-883`
`upstream:backend/src/handlers.rs:3763-3769`
`upstream:bruno-api/get_ota_status.bru:8`
`fixture:captured-readonly` | +| `postOtaUpload` | POST `/api/ota/upload` | ota | `/instances/:id/ota`
`ota/upload` | dedicated-upload-flow | planned: Frozen-scope high-risk operation is planned with simulation-only acceptance until controlled safety authorization exists.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R3; strong
preparation-job | job-fresh-preflight/high-risk-block/ota-version-probe/new-job-lineage/job-result
Required fresh preflight; inability to refresh blocks execution.
Fresh preflight is mandatory; stale, expired, or unknown state blocks execution.
OTA 404/405/501 is frozen-version evidence; preserve release/status probe context and never infer global support.
Retry creates a new Job lineage (retryOfJobId/rootJobId); never replay successful items.
/jobs/:jobId contains per-item result, attempt lineage, and redacted audit. | SIMULATED_HIGH_RISK | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postOtaUpload`
`upstream:backend/src/main.rs:886-887`
`upstream:backend/src/handlers.rs:3772-3792`
`fixture:not-eligible-readonly-capture` | +| `postOtaLatestRelease` | POST `/api/ota/latest-release` | ota | `/instances/:id/ota`
`ota/release` | form | planned: Frozen-scope reversible command is contract-planned for a later controlled real-write acceptance phase.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R1; none
direct | command-current-state/write-revision/version-probe/verify-write/route-result
Refresh current state when available and bind the actor decision to its revision.
Refresh current state or require an explicit recorded revision-bound override.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Writes require outcome verification before actor-initiated retry.
Structured result returns to the owning route with a redacted audit link. | REAL_WRITE_LATER | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postOtaLatestRelease`
`upstream:backend/src/main.rs:892-893`
`upstream:backend/src/handlers.rs:3795-3825`
`fixture:not-eligible-readonly-capture` | +| `postOtaOnlinePrepare` | POST `/api/ota/online-prepare` | ota | `/instances/:id/ota`
`ota/release` | job-action | planned: Frozen-scope high-risk operation is planned with simulation-only acceptance until controlled safety authorization exists.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R2; explicit
preparation-job | job-fresh-preflight/high-risk-block/ota-version-probe/new-job-lineage/job-result
Required fresh preflight; inability to refresh blocks execution.
Fresh preflight is mandatory; stale, expired, or unknown state blocks execution.
OTA 404/405/501 is frozen-version evidence; preserve release/status probe context and never infer global support.
Retry creates a new Job lineage (retryOfJobId/rootJobId); never replay successful items.
/jobs/:jobId contains per-item result, attempt lineage, and redacted audit. | SIMULATED_HIGH_RISK | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postOtaOnlinePrepare`
`upstream:backend/src/main.rs:896-897`
`upstream:backend/src/handlers.rs:3828-3889`
`fixture:not-eligible-readonly-capture` | +| `postOtaApply` | POST `/api/ota/apply` | ota | `/instances/:id/ota`
`ota/apply` | job-action | planned: Frozen-scope high-risk operation is planned with simulation-only acceptance until controlled safety authorization exists.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R3; strong
preparation-job | job-fresh-preflight/high-risk-block/ota-version-probe/new-job-lineage/job-result
Required fresh preflight; inability to refresh blocks execution.
Fresh preflight is mandatory; stale, expired, or unknown state blocks execution.
OTA 404/405/501 is frozen-version evidence; preserve release/status probe context and never infer global support.
Retry creates a new Job lineage (retryOfJobId/rootJobId); never replay successful items.
/jobs/:jobId contains per-item result, attempt lineage, and redacted audit. | SIMULATED_HIGH_RISK | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postOtaApply`
`upstream:backend/src/main.rs:900-901`
`upstream:backend/src/handlers.rs:3892-3911`
`upstream:bruno-api/post_ota_apply_restart.bru:8`
`upstream:bruno-api/post_ota_apply.bru:8`
`fixture:not-eligible-readonly-capture` | +| `postOtaCancel` | POST `/api/ota/cancel` | ota | `/instances/:id/ota`
`ota/status` | action | planned: Frozen-scope reversible command is contract-planned for a later controlled real-write acceptance phase.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R1; none
direct | command-current-state/write-revision/version-probe/verify-write/route-result
Refresh current state when available and bind the actor decision to its revision.
Refresh current state or require an explicit recorded revision-bound override.
404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.
Writes require outcome verification before actor-initiated retry.
Structured result returns to the owning route with a redacted audit link. | REAL_WRITE_LATER | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postOtaCancel`
`upstream:backend/src/main.rs:904-905`
`upstream:backend/src/handlers.rs:3914-3931`
`upstream:bruno-api/post_ota_cancel.bru:8`
`fixture:not-eligible-readonly-capture` | +| `postAuthPassword` | POST `/api/auth/password` | instances-auth | `/settings/instances/:id`
`settings/auth-settings` | dedicated-auth-flow | planned: Frozen-scope high-risk operation is planned with simulation-only acceptance until controlled safety authorization exists.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R3; strong
dedicated-flow | auth-dedicated/auth-no-replay/auth-version-probe/new-job-lineage/auth-result
Dedicated auth endpoint, actor/session/instance rate limits, and fresh preflight of authentication state are required.
Credential state is never inferred from stale data; require actor-initiated auth flow and no replay.
Auth 404/405/501 is version evidence; do not downgrade to a generic proxy or expose credentials.
Retry creates a new Job lineage (retryOfJobId/rootJobId); never replay successful items.
Dedicated auth jobs return through /jobs/:jobId; session flows return a credential metadata-only audit result and link. | SIMULATED_HIGH_RISK | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postAuthPassword`
`upstream:backend/src/main.rs:908-909`
`upstream:backend/src/auth.rs:491-553`
`upstream:backend/src/auth.rs:43-43`
`fixture:not-eligible-readonly-capture` | +| `getAuthSettings` | GET `/api/auth/settings` | instances-auth | `/settings/instances/:id`
`settings/auth-settings` | detail | planned: Frozen-scope read has two redacted captured responses; runtime support is still decided per registered instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Detail resource has no collection-empty rendering.)
partial:N (Atomic detail read has no meaningful partial rendering.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/auth-no-replay/auth-version-probe/auth-actor-only/auth-result
Registered instance, authorized actor, and runtime capability decision are required.
Credential state is never inferred from stale data; require actor-initiated auth flow and no replay.
Auth 404/405/501 is version evidence; do not downgrade to a generic proxy or expose credentials.
No automatic replay; require an actor-initiated dedicated flow.
Dedicated auth jobs return through /jobs/:jobId; session flows return a credential metadata-only audit result and link. | REAL_READ | captured-readonly; n=2; categories=success,unsupported; collector-selected fully-bound GET R0; two redacted real captures | `registry:getAuthSettings`
`upstream:backend/src/main.rs:912-913`
`upstream:backend/src/auth.rs:555-570`
`upstream:backend/src/auth.rs:43-43`
`fixture:captured-readonly` | +| `postAuthSettings` | POST `/api/auth/settings` | instances-auth | `/settings/instances/:id`
`settings/auth-settings` | dedicated-auth-flow | planned: Frozen-scope high-risk operation is planned with simulation-only acceptance until controlled safety authorization exists.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R2; explicit
dedicated-flow | auth-dedicated/auth-no-replay/auth-version-probe/new-job-lineage/auth-result
Dedicated auth endpoint, actor/session/instance rate limits, and fresh preflight of authentication state are required.
Credential state is never inferred from stale data; require actor-initiated auth flow and no replay.
Auth 404/405/501 is version evidence; do not downgrade to a generic proxy or expose credentials.
Retry creates a new Job lineage (retryOfJobId/rootJobId); never replay successful items.
Dedicated auth jobs return through /jobs/:jobId; session flows return a credential metadata-only audit result and link. | SIMULATED_HIGH_RISK | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postAuthSettings`
`upstream:backend/src/main.rs:912-914`
`upstream:backend/src/auth.rs:572-640`
`upstream:backend/src/auth.rs:43-43`
`fixture:not-eligible-readonly-capture` | +| `getHealth` | GET `/api/health` | instances-auth | `/instances/new`
`settings/auth-entry` | detail | planned: Frozen-scope read has two redacted captured responses; runtime support is still decided per registered instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Detail resource has no collection-empty rendering.)
partial:N (Atomic detail read has no meaningful partial rendering.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/auth-no-replay/auth-version-probe/auth-actor-only/auth-result
Registered instance, authorized actor, and runtime capability decision are required.
Credential state is never inferred from stale data; require actor-initiated auth flow and no replay.
Auth 404/405/501 is version evidence; do not downgrade to a generic proxy or expose credentials.
No automatic replay; require an actor-initiated dedicated flow.
Dedicated auth jobs return through /jobs/:jobId; session flows return a credential metadata-only audit result and link. | REAL_READ | captured-readonly; n=2; categories=success; collector-selected fully-bound GET R0; two redacted real captures | `registry:getHealth`
`upstream:backend/src/main.rs:923-923`
`upstream:backend/src/handlers.rs:65-75`
`upstream:bruno-api/get_health.bru:8`
`upstream:backend/src/auth.rs:43-43`
`fixture:captured-readonly` | +| `getAuthStatus` | GET `/api/auth/status` | instances-auth | `/instances/new`
`settings/auth-entry` | detail | planned: Frozen-scope read has two redacted captured responses; runtime support is still decided per registered instance.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Detail resource has no collection-empty rendering.)
partial:N (Atomic detail read has no meaningful partial rendering.)
policy-forbidden:N (Safe read has no write-confirmation policy; support remains orthogonal.)
unknown-result:N (Read failure is an error/freshness state, not unknown write outcome.)
owner-switch:Y (Owner switch discards responses with a mismatched instance key.) | R0; none
direct | query-capability/auth-no-replay/auth-version-probe/auth-actor-only/auth-result
Registered instance, authorized actor, and runtime capability decision are required.
Credential state is never inferred from stale data; require actor-initiated auth flow and no replay.
Auth 404/405/501 is version evidence; do not downgrade to a generic proxy or expose credentials.
No automatic replay; require an actor-initiated dedicated flow.
Dedicated auth jobs return through /jobs/:jobId; session flows return a credential metadata-only audit result and link. | REAL_READ | captured-readonly; n=2; categories=success,unsupported; collector-selected fully-bound GET R0; two redacted real captures | `registry:getAuthStatus`
`upstream:backend/src/main.rs:925-926`
`upstream:backend/src/auth.rs:345-364`
`upstream:backend/src/auth.rs:43-43`
`fixture:captured-readonly` | +| `postAuthSetup` | POST `/api/auth/setup` | instances-auth | `/instances/new`
`settings/auth-entry` | dedicated-auth-flow | planned: Frozen-scope high-risk operation is planned with simulation-only acceptance until controlled safety authorization exists.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R3; strong
dedicated-flow | auth-dedicated/auth-no-replay/auth-version-probe/new-job-lineage/auth-result
Dedicated auth endpoint, actor/session/instance rate limits, and fresh preflight of authentication state are required.
Credential state is never inferred from stale data; require actor-initiated auth flow and no replay.
Auth 404/405/501 is version evidence; do not downgrade to a generic proxy or expose credentials.
Retry creates a new Job lineage (retryOfJobId/rootJobId); never replay successful items.
Dedicated auth jobs return through /jobs/:jobId; session flows return a credential metadata-only audit result and link. | SIMULATED_HIGH_RISK | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postAuthSetup`
`upstream:backend/src/main.rs:929-930`
`upstream:backend/src/auth.rs:366-427`
`upstream:backend/src/auth.rs:43-43`
`fixture:not-eligible-readonly-capture` | +| `postAuthLogin` | POST `/api/auth/login` | instances-auth | `/instances/new`
`settings/auth-entry` | dedicated-auth-flow | planned: Frozen-scope reversible command is contract-planned for a later controlled real-write acceptance phase.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R1; none
dedicated-flow | auth-dedicated/auth-no-replay/auth-version-probe/auth-actor-only/auth-result
Dedicated auth endpoint, actor/session/instance rate limits, and fresh preflight of authentication state are required.
Credential state is never inferred from stale data; require actor-initiated auth flow and no replay.
Auth 404/405/501 is version evidence; do not downgrade to a generic proxy or expose credentials.
No automatic replay; require an actor-initiated dedicated flow.
Dedicated auth jobs return through /jobs/:jobId; session flows return a credential metadata-only audit result and link. | REAL_WRITE_LATER | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postAuthLogin`
`upstream:backend/src/main.rs:933-934`
`upstream:backend/src/auth.rs:429-489`
`upstream:backend/src/auth.rs:43-43`
`fixture:not-eligible-readonly-capture` | +| `postAuthLogout` | POST `/api/auth/logout` | instances-auth | `/settings/instances/:id`
`settings/auth-settings` | dedicated-auth-flow | planned: Frozen-scope reversible command is contract-planned for a later controlled real-write acceptance phase.
Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict. | empty:N (Command result is not a collection-empty state.)
partial:N (Single-target command has no meaningful partial result.)
policy-forbidden:Y (Write can be blocked by capability, authorization, risk, or preflight policy.)
unknown-result:Y (Disconnect or timeout requires outcome verification before retry.)
owner-switch:Y (Owner switch preserves execution lineage but discards mismatched route responses.) | R1; none
dedicated-flow | auth-dedicated/auth-no-replay/auth-version-probe/auth-actor-only/auth-result
Dedicated auth endpoint, actor/session/instance rate limits, and fresh preflight of authentication state are required.
Credential state is never inferred from stale data; require actor-initiated auth flow and no replay.
Auth 404/405/501 is version evidence; do not downgrade to a generic proxy or expose credentials.
No automatic replay; require an actor-initiated dedicated flow.
Dedicated auth jobs return through /jobs/:jobId; session flows return a credential metadata-only audit result and link. | REAL_WRITE_LATER | not-eligible-readonly-capture; n=0; categories=none; not a collector-eligible fully-bound GET R0 operation | `registry:postAuthLogout`
`upstream:backend/src/main.rs:937-938`
`upstream:backend/src/auth.rs:642-655`
`upstream:backend/src/auth.rs:43-43`
`fixture:not-eligible-readonly-capture` | diff --git a/docs/product/personas-and-workflows.md b/docs/product/personas-and-workflows.md index 7e52a0d..f38721c 100644 --- a/docs/product/personas-and-workflows.md +++ b/docs/product/personas-and-workflows.md @@ -128,7 +128,7 @@ R2/R3 还必须显示预检结果和失败阻断项;确认 token 的完整绑 ### WF-04 执行 R1 可逆设置 -**例:** 开关 data、roaming、WLAN 或修改通知配置。
+**例:** 开关 data、roaming、WLAN 等可逆设置。具体 operation 风险、确认和执行方式以 [117 项验收账本](./operation-acceptance-matrix.md) 引用的 OperationRegistry 为唯一事实源;例如 WLAN connect 为 R1,而 notifications config 为 R2 Job,不因出现在同一产品模块而套用统一风险。
**角色:** P1/P2 1. 在业务页读取当前值和 `fetchedAt`。 @@ -161,7 +161,7 @@ R2/R3 还必须显示预检结果和失败阻断项;确认 token 的完整绑 ### WF-06 批量操作 **角色:** P1/P3
-**入口:** `/fleet` 多选;只显示 registry 标记 `batchable` 的动作。 +**入口:** `/fleet` 多选;只显示验收账本明确标记 `fleetBatchable=true` 的动作。上游 Registry 旧字段 `batchable` 仅表示单实例内的资源批量,不表示跨实例 Fleet 批量。 1. 用户多选实例并选择业务动作。 2. 系统逐实例预检 capability、认证、当前值和冲突 Job。 @@ -190,8 +190,8 @@ R2/R3 还必须显示预检结果和失败阻断项;确认 token 的完整绑 **页面:** `/instances/:id/esim` 1. 查看 work mode、eSIM config、lpac、eUICC 和 profiles capability。 -2. 下载 profile 时校验激活信息并进入 R2 Job。 -3. enable/rename 使用专用表单;切换工作模式说明连接影响。 +2. 下载 profile 时校验激活信息;其风险与执行方式按 Registry 当前值 R1/direct 验收,不在产品文档中另行升级。 +3. enable/rename 使用专用表单;enable 的 Registry 当前值为 R1/direct;切换工作模式按其 Registry R2/Job 政策说明连接影响。 4. delete 为 R3:展示 ICCID/名称/状态,要求复述 profile,确认不可恢复。 5. 完成后重新获取 profile 和 connectivity;失败保留上次 profile 数据并标 stale。 diff --git a/docs/product/project-charter.md b/docs/product/project-charter.md index f74eeb3..306876f 100644 --- a/docs/product/project-charter.md +++ b/docs/product/project-charter.md @@ -5,7 +5,7 @@ > 上游证据基线:[`3899/SimAdmin@58e220411d6599609f0eeda01eb7016e9212f970`](https://github.com/3899/SimAdmin/commit/58e220411d6599609f0eeda01eb7016e9212f970)
> 适用范围:Multi SimAdmin V1 的产品、API/BFF、Web 控制台、测试与迁移决策 -> Phase 0.1 文档导航:**项目章程**(本文)|[现状审计](./current-system-audit.md)|[角色与流程](./personas-and-workflows.md)|[信息架构](./information-architecture.md) +> Phase 0.1 文档导航:**项目章程**(本文)|[现状审计](./current-system-audit.md)|[角色与流程](./personas-and-workflows.md)|[信息架构](./information-architecture.md)|[117 项验收账本](./operation-acceptance-matrix.md) ## 1. 立项结论 @@ -43,7 +43,7 @@ - **能力驱动:** 不支持的能力不显示为可点击动作;必要时保留解释性占位,不用 `-` 假装正常。 - **目标明确:** 每个写操作都显示实例、当前值、目标值、字段 diff、影响和风险。 - **默认拒绝:** 未注册 operation、未知方法/路径、认证端点通用代理均拒绝。 -- **部分结果真实:** 批量动作逐实例给出成功、失败、跳过及原因,不制造“全部成功”。 +- **部分结果真实:** Fleet 批量与单实例资源批量分开建模;Fleet 逐实例给出成功、失败、跳过及原因,不制造“全部成功”。具体 `resourceBulk`、`fleetBatchable` 与逐项聚合以验收账本为准。 - **凭据服务端化:** 密码、Cookie、token 不进入浏览器响应、日志、审计或 fixture。 - **高密度但可访问:** 主要工作流兼容键盘和窄屏;视觉不能牺牲错误、焦点和操作可达性。 diff --git a/docs/release/phase-0-gate.md b/docs/release/phase-0-gate.md new file mode 100644 index 0000000..9ada500 --- /dev/null +++ b/docs/release/phase-0-gate.md @@ -0,0 +1,40 @@ +# Phase 0 release gate + +Scope: Phase 0.4 evidence only. No commit, no production server/public/root-package modification, no network refetch, and no local instance URL/name/secret is recorded here. + +## Gate result + +| Check | Status | Evidence / rationale | +|---|---|---| +| RED first | RECORDED | Process evidence: expanded acceptance tests first failed because structured exports were absent; this historical step is not machine-recomputable from the final tree. | +| Repository `npm test` | PASS | Final recomputation: 84 tests passed, 0 failed. | +| Phase 0 acceptance tests | PASS | `packages/operation-registry/test/acceptance-gate.test.ts`: 12/12 pass, including exact partitions, fixture-bound real classes and metadata-only categories, product prose, policies, and control-plane rows. | +| Independent safety baseline | PASS | Phase 0.2 commit `9dffadbec271227da7ffb192f31dc78c6ee3c2be` independently freezes exact 117 unique `operationId` + risk + confirmation + capability + execution-policy tuples. Tests read that committed Git object with `git show`, never current docs/current Registry for expected values; a missing/wrong object fails, and synchronized working-tree matrix/fixture edits cannot bypass it. Risk, confirmation, capability, and execution-policy mutation tests all fail closed. Baseline changes require explicit safety review. | +| Registry parity | PASS | exact 117 operations, 100 paths, GET 50 / POST 62 / DELETE 5; operation IDs unique and frozen route parity tests pass. | +| Acceptance ledger | PASS | exact 117 operation-to-section mapping plus independent exact policy/scenario/availability operation-ID partitions; each partition rejects overlap, omission, and unknown IDs; route ownership, Registry risk/confirmation, scenario applicability, and high-risk/auth/write policies are enforced without method/risk fallback. | +| Registry risk distribution | PASS | R0 50, R1 28, R2 21, R3 18. Product prose now defers to the Registry-backed ledger; eSIM download/enable and notifications config conflicts are corrected. | +| Fixture disposition | PASS | authoritative map exact 117: 39 `captured-readonly`, 11 `denied-readonly-candidate`, 67 `not-eligible-readonly-capture`; selection and deny reasons are recomputed from the collector. No operation remains currently `pending`. | +| Real fixture manifest | PASS | 78 real fixtures, 0 synthetic, exactly two aliases per 39 eligible operation; manifest SHA-256 `4f2475bdd6ee2c4ca6057d951161eb97d2e842cdf9d378e44195d7d6c87af459`. Every matrix row renders metadata-only `categories=` sorted from fixture disposition (`success`, `unsupported`, `auth-required` only), or `none` when uncaptured; no fixture body/response, alias, identity, or URL is rendered. | +| Explicit readonly denials | PASS | exact 11 and equal to collector `DENY_REASONS`; active connectivity/operator scan and privacy-sensitive/query endpoints are not misrepresented as captures. | +| Batch semantics | PASS | Registry `batchable` is treated as resource bulk, never Fleet authorization. `postSmsBatchDelete`: resource bulk true / Fleet false. `getHealth`: Fleet true read refresh / per-item aggregation. All writes Fleet false. | +| Dedicated auth | PASS | setup/password/settings/login/logout use dedicated auth strategy/flow; login/logout session-sensitive, no automatic replay, credential metadata-only audit. Control-plane acceptance is listed in the matrix. | +| R2/R3 and Job lineage | PASS | fresh preflight blocks when unavailable; one-use Registry confirmation; R3 Registry capability is Job; retry creates new Job lineage and never mutates/replays successful source items. | +| Capability/404, stale, orthogonal state, partial policies | PASS | machine ledger plus generated matrix bind these policies per operation; a fixture 404 is evidence, not a permanent unsupported declaration. | +| Real vs simulated acceptance | PASS | exact fixture-bound classification: 39 captured reads are `REAL_READ`; 11 denied candidates are `REAL_READ_DEFERRED`; no uncaptured GET claims real evidence; remaining writes/noneligible operations use deferred, simulated, or contract-only classes. | +| Timeout / non-JSON real coverage | DEFERRED | neither category occurred in the 78 real captures. Unit categorization tests pass, but real timeout/non-JSON acceptance is deferred to controlled fault injection; no coverage is claimed. | +| Listener introduced by Phase 0 commands | RECORDED | Process evidence: validation used Node test/inject and did not intentionally start a listener; this is not labeled machine-recomputable. | +| Old production tree unchanged | PASS | `git diff -- server public package.json` is empty. Root `package.json` was not changed. | +| Frozen upstream provenance | PASS with accepted gap | Upstream URL: `https://github.com/3899/SimAdmin`; full commit `58e220411d6599609f0eeda01eb7016e9212f970`; snapshot paths: `packages/operation-registry/test/fixtures/upstream-58e2204/`; mechanism: snapshot `manifest.json` pins SHA-256 per whole file and tests re-hash exact files/handler slices. Fetch/capture time is not recorded and is an accepted provenance gap; no value was invented and no network was used. | +| Frozen whitespace exception | N/A | Frozen upstream files were not edited. Existing snapshot bytes and hashes are authoritative, including upstream whitespace; no formatter is run over the snapshot. | +| Secret scan scope | PASS | Existing fixture test recursively scans configured credential strings and every fixture string leaf (including tags/capabilities/auth/password) without printing values; all 78 real fixture envelopes, hashes, response-shape baseline, and redaction contracts pass. This is fixture-scope evidence, not a claim of a future workspace-wide scanner. | +| `git diff --check` | PASS | No whitespace errors. | +| pnpm workspace | N/A | Phase 1 workspace does not exist; not run and not marked PASS. | +| lint | N/A | No Phase 1 workspace lint command exists; not run and not marked PASS. | +| strict typecheck | N/A | No Phase 1 workspace typecheck command exists; not run and not marked PASS. | +| E2E | N/A | Phase 1+ application/E2E harness does not exist; not run and not marked PASS. | +| Independent Phase 0.4 specification/security review | PASS | Final specification review passed after exact-partition, scenario, availability and gate corrections. Independent quality/security reviews also approved the committed-Git-object safety baseline and metadata-only fixture matrix; the original synchronized-risk mutation was reproduced in `/tmp` and now fails closed. | +| full product/security audit | DEFERRED | The independent Phase 8/9 audit is not performed or claimed here. | + +## Release decision + +**PASS.** Phase 0 is complete and may enter the Phase 1 workspace/contract bootstrap. Deferred timeout/non-JSON real coverage, Phase 1 lint/typecheck, and Phase 5 E2E remain outside Phase 0 and must not be relabeled as completed evidence. diff --git a/packages/operation-registry/src/acceptance-58e2204.ts b/packages/operation-registry/src/acceptance-58e2204.ts new file mode 100644 index 0000000..f90aa0a --- /dev/null +++ b/packages/operation-registry/src/acceptance-58e2204.ts @@ -0,0 +1,116 @@ +import { readFileSync } from 'node:fs'; +import { fileURLToPath } from 'node:url'; +import { upstream58e2204Operations } from './upstream-58e2204.ts'; +import { DENY_REASONS, selectReadonlyOperations } from '../../test-fixtures/scripts/collector.ts'; + +type Risk='R0'|'R1'|'R2'|'R3'; +export type RuntimeFixtureStatus='captured-readonly'|'denied-readonly-candidate'|'not-eligible-readonly-capture'|'not-eligible-readonly-capture-parameterized-r0'; +const routeByOwner:Record={fleet:'/fleet','instances-new':'/instances/new','settings-instance':'/settings/instances/:id',overview:'/instances/:id/overview',cellular:'/instances/:id/cellular','device-network':'/instances/:id/device-network',messages:'/instances/:id/messages',calls:'/instances/:id/calls',esim:'/instances/:id/esim',notifications:'/instances/:id/notifications',automation:'/instances/:id/automation',ota:'/instances/:id/ota'}; +const dedicatedAuth=new Set(['postAuthSetup','postAuthPassword','postAuthSettings','postAuthLogin','postAuthLogout']); +const resourceBulk=new Set(['postSmsBatchDelete','postNotificationsQueueRetryAll','postNotificationsQueueClear']); + +const fixtureManifest=JSON.parse(readFileSync(fileURLToPath(new URL('../../test-fixtures/src/manifest.json',import.meta.url)),'utf8')); +const fixtureRoot=fileURLToPath(new URL('../../test-fixtures/',import.meta.url)); +const selected=selectReadonlyOperations(upstream58e2204Operations as any[]); +const selectedIds=new Set(selected.selected.map((x:any)=>x.operationId)); +const deniedById=new Map(selected.denied.map((x:any)=>[x.operationId,x.denyReason])); +const observations=new Map,categories:Set,count:number}>(); +for(const file of fixtureManifest.files){const envelope=JSON.parse(readFileSync(`${fixtureRoot}${file.path}`,'utf8'));const current=observations.get(envelope.operationId)??{aliases:new Set(),categories:new Set(),count:0};current.aliases.add(envelope.sourceInstanceAlias);current.categories.add(envelope.statusCategory);current.count++;observations.set(envelope.operationId,current);} +export const fixtureDisposition58e2204=upstream58e2204Operations.map((op:any)=>{const observed=observations.get(op.operationId);if(selectedIds.has(op.operationId))return {operationId:op.operationId,runtimeFixtureStatus:'captured-readonly' as RuntimeFixtureStatus,fixtureCount:observed?.count??0,aliases:[...(observed?.aliases??[])].sort(),observedCategories:[...(observed?.categories??[])].sort(),reason:'collector-selected fully-bound GET R0; two redacted real captures'};const deny=deniedById.get(op.operationId);if(deny)return {operationId:op.operationId,runtimeFixtureStatus:'denied-readonly-candidate' as RuntimeFixtureStatus,fixtureCount:0,aliases:[],observedCategories:[],reason:deny};const parameterized=op.method==='GET'&&op.riskLevel==='R0'&&/[{}]/.test(op.pathTemplate);return {operationId:op.operationId,runtimeFixtureStatus:(parameterized?'not-eligible-readonly-capture-parameterized-r0':'not-eligible-readonly-capture') as RuntimeFixtureStatus,fixtureCount:0,aliases:[],observedCategories:[],reason:parameterized?'R0 read requires bound path parameters; collector accepts only fully-bound Registry paths':'not a collector-eligible fully-bound GET R0 operation'};}); +const fixtureById=new Map(fixtureDisposition58e2204.map(x=>[x.operationId,x])); + +export const acceptancePolicyCatalog={ + preconditionPolicyId:{'query-capability':'Registered instance, authorized actor, and runtime capability decision are required.','command-current-state':'Refresh current state when available and bind the actor decision to its revision.','job-fresh-preflight':'Required fresh preflight; inability to refresh blocks execution.','auth-dedicated':'Dedicated auth endpoint, actor/session/instance rate limits, and fresh preflight of authentication state are required.'}, + stalePolicyId:{'read-retain':'Retain last success as stale and permit an explicit safe refresh.','write-revision':'Refresh current state or require an explicit recorded revision-bound override.','high-risk-block':'Fresh preflight is mandatory; stale, expired, or unknown state blocks execution.','auth-no-replay':'Credential state is never inferred from stale data; require actor-initiated auth flow and no replay.'}, + unsupportedPolicyId:{'version-probe':'404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.','auth-version-probe':'Auth 404/405/501 is version evidence; do not downgrade to a generic proxy or expose credentials.','ota-version-probe':'OTA 404/405/501 is frozen-version evidence; preserve release/status probe context and never infer global support.'}, + retryPolicyId:{'safe-read':'Safe reads may retry within deadline and retain stale success.','verify-write':'Writes require outcome verification before actor-initiated retry.','new-job-lineage':'Retry creates a new Job lineage (retryOfJobId/rootJobId); never replay successful items.','auth-actor-only':'No automatic replay; require an actor-initiated dedicated flow.'}, + resultPolicyId:{'route-result':'Structured result returns to the owning route with a redacted audit link.','job-result':'/jobs/:jobId contains per-item result, attempt lineage, and redacted audit.','auth-result':'Dedicated auth jobs return through /jobs/:jobId; session flows return a credential metadata-only audit result and link.'} +} as const; + +/* Exact declarative UI partition. Every operation ID occurs in exactly one group; construction below throws on overlap/omission. */ +const uiGroups:Record= { + detail:['getDevice','getSim','getNetwork','getHealth','getAuthStatus','getAuthSettings'], + table:['getCells','getNetworkInterfaces','getNetworkOperators','getDeviceNetworkWlanProfiles','getEsimProfiles','getCalls','getCallHistory','getSmsList','getSmsConversation','getNotificationsLogs','getNotificationsQueue','getAutomationLogs'], + 'dedicated-auth-flow':['postAuthSetup','postAuthPassword','postAuthSettings','postAuthLogin','postAuthLogout'], + 'dedicated-upload-flow':['postOtaUpload'], + 'job-action':['postBandLock','postDeviceNetworkDdnsConfig','postDeviceNetworkDdnsLogsClear','postDeviceNetworkWlanForget','postNetworkRegisterManual','postNetworkRegisterAuto','postCellLock','postCellLockUnlockAll','postBasebandRestart','postWorkMode','postEsimConfig','postEsimLpacRepair','deleteEsimProfilesIccid','postCallSettings','deleteCallHistoryId','postCallHistoryClear','postSmsSend','postSmsBatchDelete','deleteSmsConversationPhoneNumber','deleteSmsMessageId','postSmsClear','postSystemReboot','postServiceRestart','postNotificationsConfig','postNotificationsTestChannel','postNotificationsLogsClear','postNotificationsQueueRetryAll','postNotificationsQueueClear','deleteNotificationsQueueId','postNotificationsQueueIdRetry','postAutomationConfig','postAutomationLogsClear','postAutomationTestTaskId','postOtaApply','postOtaOnlinePrepare'], + form:['postSimCache','postRadioMode','postDeviceNetworkWlanEnabled','postDeviceNetworkWlanConnect','postDeviceNetworkWlanProfile','postApn','postData','postRoaming','postAirplaneMode','postEsimProfiles','postEsimProfilesIccidRename','postCallDial','postCallHangup','postCallAnswer','postCallVolume','postCallForwarding','postOtaLatestRelease'], + action:['postSimDetailsRefresh','postCellMonitorStart','postCellMonitorStop','postDeviceNetworkDdnsSync','postDeviceNetworkWlanScan','postDeviceNetworkWlanDisconnect','postEsimProfilesIccidEnable','postCallHangupAll','postOtaCancel'], + 'read-panel':['getRadioMode','getBandLock','getNetworkConnectionAddresses','getDeviceNetworkDdnsConfig','getDeviceNetworkDdnsStatus','getDeviceNetworkDdnsLogs','getDeviceNetworkWlanStatus','getNetworkSignalStrength','getLocationCellInfo','getNetworkOperatorsScan','getApn','getCellLock','getData','getRoaming','getAirplaneMode','getBasebandRestartStatus','getWorkMode','getEsimConfig','getEsimLpacStatus','getEsimEuicc','getCallVolume','getCallForwarding','getCallSettings','getImsStatus','getVoicemailStatus','getSmsStats','getStats','getStatsCpu','getConnectivity','getNotificationsConfig','getAutomationConfig','getOtaStatus'] +}; +const strategyById=new Map();for(const [strategy,ids] of Object.entries(uiGroups))for(const id of ids){if(strategyById.has(id))throw new Error(`duplicate acceptance override ${id}`);strategyById.set(id,strategy);} +const operationIds=new Set(upstream58e2204Operations.map((x:any)=>x.operationId));if(strategyById.size!==operationIds.size||[...operationIds].some(id=>!strategyById.has(id)))throw new Error('acceptance override partition must exactly cover frozen 117 operations'); +const route=(owner:string)=>routeByOwner[owner]; +export const surfaceGroups58e2204=[ + {surfaceId:'overview/device',primaryRoute:route('overview'),ids:['getDevice']},{surfaceId:'overview/sim',primaryRoute:route('overview'),ids:['getSim','postSimDetailsRefresh','postSimCache']},{surfaceId:'overview/stats',primaryRoute:route('overview'),ids:['getStats','getStatsCpu']},{surfaceId:'overview/auth-health',primaryRoute:route('overview'),ids:['getConnectivity']},{surfaceId:'overview/system-actions',primaryRoute:route('overview'),ids:['postSystemReboot','postServiceRestart']}, + {surfaceId:'cellular/serving-network',primaryRoute:route('cellular'),ids:['getNetwork']},{surfaceId:'cellular/cells',primaryRoute:route('cellular'),ids:['getCells','postCellMonitorStart','postCellMonitorStop','getLocationCellInfo']},{surfaceId:'cellular/signal',primaryRoute:route('cellular'),ids:['getNetworkSignalStrength']},{surfaceId:'cellular/operator-registration',primaryRoute:route('cellular'),ids:['getNetworkOperators','getNetworkOperatorsScan','postNetworkRegisterManual','postNetworkRegisterAuto']},{surfaceId:'cellular/radio-locks',primaryRoute:route('cellular'),ids:['getRadioMode','postRadioMode','getBandLock','postBandLock','getCellLock','postCellLock','postCellLockUnlockAll']},{surfaceId:'cellular/data-apn',primaryRoute:route('cellular'),ids:['getApn','postApn','getData','postData','getRoaming','postRoaming','getAirplaneMode','postAirplaneMode','postBasebandRestart','getBasebandRestartStatus']}, + {surfaceId:'device-network/interfaces',primaryRoute:route('device-network'),ids:['getNetworkInterfaces','getNetworkConnectionAddresses']},{surfaceId:'device-network/ddns',primaryRoute:route('device-network'),ids:['getDeviceNetworkDdnsConfig','postDeviceNetworkDdnsConfig','getDeviceNetworkDdnsStatus','postDeviceNetworkDdnsSync','getDeviceNetworkDdnsLogs','postDeviceNetworkDdnsLogsClear']},{surfaceId:'device-network/wlan',primaryRoute:route('device-network'),ids:['getDeviceNetworkWlanStatus','postDeviceNetworkWlanEnabled','postDeviceNetworkWlanScan','getDeviceNetworkWlanProfiles','postDeviceNetworkWlanForget','postDeviceNetworkWlanConnect','postDeviceNetworkWlanDisconnect','postDeviceNetworkWlanProfile']}, + {surfaceId:'calls/live',primaryRoute:route('calls'),ids:['getCalls','postCallDial','postCallHangup','postCallHangupAll','postCallAnswer']},{surfaceId:'calls/volume',primaryRoute:route('calls'),ids:['getCallVolume','postCallVolume']},{surfaceId:'calls/forwarding',primaryRoute:route('calls'),ids:['getCallForwarding','postCallForwarding']},{surfaceId:'calls/settings',primaryRoute:route('calls'),ids:['getCallSettings','postCallSettings']},{surfaceId:'calls/history',primaryRoute:route('calls'),ids:['getCallHistory','deleteCallHistoryId','postCallHistoryClear']},{surfaceId:'calls/ims-voicemail',primaryRoute:route('calls'),ids:['getImsStatus','getVoicemailStatus']}, + {surfaceId:'messages/stats',primaryRoute:route('messages'),ids:['getSmsStats']},{surfaceId:'messages/list',primaryRoute:route('messages'),ids:['getSmsList']},{surfaceId:'messages/conversation',primaryRoute:route('messages'),ids:['getSmsConversation']},{surfaceId:'messages/compose',primaryRoute:route('messages'),ids:['postSmsSend']},{surfaceId:'messages/destructive',primaryRoute:route('messages'),ids:['postSmsBatchDelete','deleteSmsConversationPhoneNumber','deleteSmsMessageId','postSmsClear']}, + {surfaceId:'esim/mode',primaryRoute:route('esim'),ids:['getWorkMode','postWorkMode']},{surfaceId:'esim/config',primaryRoute:route('esim'),ids:['getEsimConfig','postEsimConfig']},{surfaceId:'esim/lpac',primaryRoute:route('esim'),ids:['getEsimLpacStatus','postEsimLpacRepair']},{surfaceId:'esim/euicc',primaryRoute:route('esim'),ids:['getEsimEuicc']},{surfaceId:'esim/profiles',primaryRoute:route('esim'),ids:['getEsimProfiles','postEsimProfiles','postEsimProfilesIccidEnable','postEsimProfilesIccidRename','deleteEsimProfilesIccid']}, + {surfaceId:'notifications/config',primaryRoute:route('notifications'),ids:['getNotificationsConfig','postNotificationsConfig','postNotificationsTestChannel']},{surfaceId:'notifications/logs',primaryRoute:route('notifications'),ids:['getNotificationsLogs','postNotificationsLogsClear']},{surfaceId:'notifications/queue',primaryRoute:route('notifications'),ids:['getNotificationsQueue','postNotificationsQueueRetryAll','postNotificationsQueueClear','deleteNotificationsQueueId','postNotificationsQueueIdRetry']}, + {surfaceId:'automation/config',primaryRoute:route('automation'),ids:['getAutomationConfig','postAutomationConfig']},{surfaceId:'automation/logs',primaryRoute:route('automation'),ids:['getAutomationLogs','postAutomationLogsClear']},{surfaceId:'automation/task-test',primaryRoute:route('automation'),ids:['postAutomationTestTaskId']}, + {surfaceId:'ota/status',primaryRoute:route('ota'),ids:['getOtaStatus','postOtaCancel']},{surfaceId:'ota/release',primaryRoute:route('ota'),ids:['postOtaLatestRelease','postOtaOnlinePrepare']},{surfaceId:'ota/upload',primaryRoute:route('ota'),ids:['postOtaUpload']},{surfaceId:'ota/apply',primaryRoute:route('ota'),ids:['postOtaApply']}, + {surfaceId:'settings/auth-settings',primaryRoute:route('settings-instance'),ids:['postAuthPassword','getAuthSettings','postAuthSettings','postAuthLogout']},{surfaceId:'settings/auth-entry',primaryRoute:route('instances-new'),ids:['getHealth','getAuthStatus','postAuthSetup','postAuthLogin']} +] as const; + +const policies=(preconditionPolicyId:string,stalePolicyId:string,retryPolicyId:string,resultPolicyId:string,unsupportedPolicyId='version-probe')=>({preconditionPolicyId,stalePolicyId,unsupportedPolicyId,retryPolicyId,resultPolicyId}); +export const policyGroups58e2204=[ + {groupId:'query-list',ids:['getCells','getNetworkInterfaces','getDeviceNetworkDdnsLogs','getDeviceNetworkWlanProfiles','getNetworkOperators','getEsimProfiles','getCalls','getCallHistory','getSmsList','getSmsConversation','getNotificationsLogs','getNotificationsQueue','getAutomationLogs'],policies:policies('query-capability','read-retain','safe-read','route-result')}, + {groupId:'query-detail',ids:['getDevice','getSim','getNetwork','getRadioMode','getBandLock','getNetworkConnectionAddresses','getDeviceNetworkDdnsConfig','getDeviceNetworkDdnsStatus','getDeviceNetworkWlanStatus','getNetworkSignalStrength','getLocationCellInfo','getApn','getCellLock','getData','getRoaming','getAirplaneMode','getBasebandRestartStatus','getWorkMode','getEsimConfig','getEsimLpacStatus','getEsimEuicc','getCallVolume','getCallForwarding','getCallSettings','getImsStatus','getVoicemailStatus','getSmsStats','getNotificationsConfig','getAutomationConfig','getOtaStatus'],policies:policies('query-capability','read-retain','safe-read','route-result')}, + {groupId:'query-composite',ids:['getStats','getStatsCpu','getConnectivity'],policies:policies('command-current-state','read-retain','safe-read','route-result')}, + {groupId:'query-active-scan',ids:['getNetworkOperatorsScan'],policies:policies('command-current-state','write-revision','verify-write','route-result')}, + {groupId:'query-auth-detail',ids:['getAuthSettings','getHealth','getAuthStatus'],policies:policies('query-capability','auth-no-replay','auth-actor-only','auth-result','auth-version-probe')}, + {groupId:'direct-reversible-setting',ids:['postSimDetailsRefresh','postSimCache','postCellMonitorStart','postCellMonitorStop','postRadioMode','postDeviceNetworkDdnsSync','postDeviceNetworkWlanEnabled','postDeviceNetworkWlanScan','postDeviceNetworkWlanConnect','postDeviceNetworkWlanDisconnect','postDeviceNetworkWlanProfile','postApn','postData','postRoaming','postAirplaneMode','postEsimProfiles','postEsimProfilesIccidEnable','postEsimProfilesIccidRename','postCallDial','postCallHangup','postCallHangupAll','postCallAnswer','postCallVolume','postCallForwarding'],policies:policies('command-current-state','write-revision','verify-write','route-result')}, + {groupId:'session-auth-flow',ids:['postAuthLogin','postAuthLogout'],policies:policies('auth-dedicated','auth-no-replay','auth-actor-only','auth-result','auth-version-probe')}, + {groupId:'high-risk-network-job',ids:['postBandLock','postDeviceNetworkDdnsConfig','postDeviceNetworkDdnsLogsClear','postDeviceNetworkWlanForget','postNetworkRegisterManual','postNetworkRegisterAuto','postCellLock','postCellLockUnlockAll','postBasebandRestart','postWorkMode','postEsimConfig','postEsimLpacRepair','postCallSettings','postSmsSend','postNotificationsConfig','postNotificationsTestChannel','postNotificationsQueueRetryAll','postNotificationsQueueIdRetry','postAutomationConfig','postAutomationTestTaskId'],policies:policies('job-fresh-preflight','high-risk-block','new-job-lineage','job-result')}, + {groupId:'destructive-cleanup-job',ids:['deleteEsimProfilesIccid','deleteCallHistoryId','postCallHistoryClear','postSmsBatchDelete','deleteSmsConversationPhoneNumber','deleteSmsMessageId','postSmsClear','postSystemReboot','postServiceRestart','postNotificationsLogsClear','postNotificationsQueueClear','deleteNotificationsQueueId','postAutomationLogsClear'],policies:policies('job-fresh-preflight','high-risk-block','new-job-lineage','job-result')}, + {groupId:'ota-direct-control',ids:['postOtaLatestRelease','postOtaCancel'],policies:policies('command-current-state','write-revision','verify-write','route-result')}, + {groupId:'ota-job',ids:['postOtaUpload','postOtaOnlinePrepare','postOtaApply'],policies:policies('job-fresh-preflight','high-risk-block','new-job-lineage','job-result','ota-version-probe')}, + {groupId:'high-risk-auth-flow',ids:['postAuthPassword','postAuthSettings','postAuthSetup'],policies:policies('auth-dedicated','auth-no-replay','new-job-lineage','auth-result','auth-version-probe')} +] as const; + +const readScenario=(empty:boolean,partial:boolean)=>({empty:{applicable:empty,rationale:empty?'Successful collection may contain no rows.':'Detail resource has no collection-empty rendering.'},partial:{applicable:partial,rationale:partial?'Independent response sections may succeed or fail.':'Atomic detail read has no meaningful partial rendering.'},'policy-forbidden':{applicable:false,rationale:'Safe read has no write-confirmation policy; support remains orthogonal.'},'unknown-result':{applicable:false,rationale:'Read failure is an error/freshness state, not unknown write outcome.'},'owner-switch':{applicable:true,rationale:'Owner switch discards responses with a mismatched instance key.'}}); +const writeScenario=(partial:boolean)=>({empty:{applicable:false,rationale:'Command result is not a collection-empty state.'},partial:{applicable:partial,rationale:partial?'Job items may independently succeed, fail, or be skipped.':'Single-target command has no meaningful partial result.'},'policy-forbidden':{applicable:true,rationale:'Write can be blocked by capability, authorization, risk, or preflight policy.'},'unknown-result':{applicable:true,rationale:'Disconnect or timeout requires outcome verification before retry.'},'owner-switch':{applicable:true,rationale:'Owner switch preserves execution lineage but discards mismatched route responses.'}}); +const activeScanScenario={empty:{applicable:true,rationale:'A successful active operator scan may return no operators.'},partial:{applicable:true,rationale:'The scan response may contain usable operators alongside per-result failures or incomplete entries.'},'policy-forbidden':{applicable:true,rationale:'Active scan may be blocked by capability, authorization, preflight, or active-scan policy.'},'unknown-result':{applicable:false,rationale:'A timed-out scan result may be discarded; it is not an unknown persistent write outcome.'},'owner-switch':{applicable:true,rationale:'Owner switch discards scan responses with a mismatched instance key.'}}; +export const scenarioGroups58e2204=[ + {profileId:'list-read',ids:['getCells','getNetworkInterfaces','getDeviceNetworkDdnsLogs','getDeviceNetworkWlanProfiles','getNetworkOperators','getEsimProfiles','getCalls','getCallHistory','getSmsList','getSmsConversation','getNotificationsLogs','getNotificationsQueue','getAutomationLogs'],scenarios:readScenario(true,true)}, + {profileId:'detail-read',ids:['getDevice','getSim','getNetwork','getRadioMode','getBandLock','getNetworkConnectionAddresses','getDeviceNetworkDdnsConfig','getDeviceNetworkDdnsStatus','getDeviceNetworkWlanStatus','getNetworkSignalStrength','getLocationCellInfo','getApn','getCellLock','getData','getRoaming','getAirplaneMode','getBasebandRestartStatus','getWorkMode','getEsimConfig','getEsimLpacStatus','getEsimEuicc','getCallVolume','getCallForwarding','getCallSettings','getImsStatus','getVoicemailStatus','getSmsStats','getNotificationsConfig','getAutomationConfig','getOtaStatus','getAuthSettings','getHealth','getAuthStatus'],scenarios:readScenario(false,false)}, + {profileId:'composite-read',ids:['getStats','getStatsCpu','getConnectivity'],scenarios:readScenario(false,true)}, + {profileId:'active-scan',ids:['getNetworkOperatorsScan'],scenarios:activeScanScenario}, + {profileId:'direct-write',ids:['postSimDetailsRefresh','postSimCache','postCellMonitorStart','postCellMonitorStop','postRadioMode','postDeviceNetworkDdnsSync','postDeviceNetworkWlanEnabled','postDeviceNetworkWlanScan','postDeviceNetworkWlanConnect','postDeviceNetworkWlanDisconnect','postDeviceNetworkWlanProfile','postApn','postData','postRoaming','postAirplaneMode','postEsimProfiles','postEsimProfilesIccidEnable','postEsimProfilesIccidRename','postCallDial','postCallHangup','postCallHangupAll','postCallAnswer','postCallVolume','postCallForwarding'],scenarios:writeScenario(false)}, + {profileId:'job-single',ids:['postBandLock','postDeviceNetworkDdnsConfig','postDeviceNetworkDdnsLogsClear','postDeviceNetworkWlanForget','postNetworkRegisterManual','postNetworkRegisterAuto','postCellLock','postCellLockUnlockAll','postWorkMode','postEsimConfig','postEsimLpacRepair','postCallSettings','postSmsSend','postNotificationsConfig','postNotificationsTestChannel','postNotificationsQueueIdRetry','postAutomationConfig','postAutomationTestTaskId','deleteEsimProfilesIccid','deleteCallHistoryId','postCallHistoryClear','deleteSmsConversationPhoneNumber','deleteSmsMessageId','postSmsClear','postBasebandRestart','postSystemReboot','postServiceRestart','postNotificationsLogsClear','deleteNotificationsQueueId','postAutomationLogsClear'],scenarios:writeScenario(false)}, + {profileId:'job-resource-bulk',ids:['postSmsBatchDelete','postNotificationsQueueRetryAll','postNotificationsQueueClear'],scenarios:writeScenario(true)}, + {profileId:'ota-direct',ids:['postOtaLatestRelease','postOtaCancel'],scenarios:writeScenario(false)}, + {profileId:'ota-job-single',ids:['postOtaUpload','postOtaOnlinePrepare','postOtaApply'],scenarios:writeScenario(false)}, + {profileId:'auth-flow',ids:['postAuthPassword','postAuthSettings','postAuthSetup','postAuthLogin','postAuthLogout'],scenarios:writeScenario(false)} +] as const; + +const versionEvidencePolicy='Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict.'; +export const availabilityGroups58e2204=[ + {groupId:'captured-real-read',realAcceptance:'REAL_READ',availability:'planned',reason:'Frozen-scope read has two redacted captured responses; runtime support is still decided per registered instance.',versionEvidencePolicy,ids:['getDevice','getSim','getNetwork','getCells','getRadioMode','getBandLock','getNetworkInterfaces','getNetworkConnectionAddresses','getDeviceNetworkDdnsConfig','getDeviceNetworkDdnsStatus','getDeviceNetworkWlanStatus','getDeviceNetworkWlanProfiles','getNetworkSignalStrength','getLocationCellInfo','getNetworkOperators','getApn','getCellLock','getData','getRoaming','getAirplaneMode','getBasebandRestartStatus','getWorkMode','getEsimConfig','getEsimLpacStatus','getCalls','getCallVolume','getCallForwarding','getCallSettings','getImsStatus','getVoicemailStatus','getSmsStats','getStats','getStatsCpu','getNotificationsConfig','getAutomationConfig','getOtaStatus','getAuthSettings','getHealth','getAuthStatus']}, + {groupId:'denied-read-candidate',realAcceptance:'REAL_READ_DEFERRED',availability:'planned',reason:'Frozen-scope read is modeled but real capture was explicitly denied; runtime support is probed per instance.',versionEvidencePolicy,ids:['getDeviceNetworkDdnsLogs','getNetworkOperatorsScan','getEsimEuicc','getEsimProfiles','getCallHistory','getSmsList','getSmsConversation','getConnectivity','getNotificationsLogs','getNotificationsQueue','getAutomationLogs']}, + {groupId:'write-later',realAcceptance:'REAL_WRITE_LATER',availability:'planned',reason:'Frozen-scope reversible command is contract-planned for a later controlled real-write acceptance phase.',versionEvidencePolicy,ids:['postSimDetailsRefresh','postSimCache','postCellMonitorStart','postCellMonitorStop','postRadioMode','postDeviceNetworkDdnsSync','postDeviceNetworkWlanEnabled','postDeviceNetworkWlanScan','postDeviceNetworkWlanConnect','postDeviceNetworkWlanDisconnect','postDeviceNetworkWlanProfile','postApn','postData','postRoaming','postAirplaneMode','postEsimProfiles','postEsimProfilesIccidEnable','postEsimProfilesIccidRename','postCallDial','postCallHangup','postCallHangupAll','postCallAnswer','postCallVolume','postCallForwarding','postOtaLatestRelease','postOtaCancel','postAuthLogin','postAuthLogout']}, + {groupId:'high-risk-simulated',realAcceptance:'SIMULATED_HIGH_RISK',availability:'planned',reason:'Frozen-scope high-risk operation is planned with simulation-only acceptance until controlled safety authorization exists.',versionEvidencePolicy,ids:['postBandLock','postDeviceNetworkDdnsConfig','postDeviceNetworkDdnsLogsClear','postDeviceNetworkWlanForget','postNetworkRegisterManual','postNetworkRegisterAuto','postCellLock','postCellLockUnlockAll','postBasebandRestart','postWorkMode','postEsimConfig','postEsimLpacRepair','deleteEsimProfilesIccid','postCallSettings','deleteCallHistoryId','postCallHistoryClear','postSmsSend','postSmsBatchDelete','deleteSmsConversationPhoneNumber','deleteSmsMessageId','postSmsClear','postSystemReboot','postServiceRestart','postNotificationsConfig','postNotificationsTestChannel','postNotificationsLogsClear','postNotificationsQueueRetryAll','postNotificationsQueueClear','deleteNotificationsQueueId','postNotificationsQueueIdRetry','postAutomationConfig','postAutomationLogsClear','postAutomationTestTaskId','postOtaUpload','postOtaOnlinePrepare','postOtaApply','postAuthPassword','postAuthSettings','postAuthSetup']} +] as const; + +function exactLookup(groups:readonly any[],label:string){const map=new Map();for(const group of groups)for(const id of group.ids){if(map.has(id))throw new Error(`${label} overlap: ${id}`);map.set(id,group);}for(const id of map.keys())if(!operationIds.has(id))throw new Error(`${label} unknown operation: ${id}`);if(map.size!==operationIds.size||[...operationIds].some(id=>!map.has(id)))throw new Error(`${label} must exactly cover frozen 117 operations`);return map;} +const surfaceById=exactLookup(surfaceGroups58e2204,'surface');const policyById=exactLookup(policyGroups58e2204,'policy');const scenarioById=exactLookup(scenarioGroups58e2204,'scenario');const availabilityById=exactLookup(availabilityGroups58e2204,'availability'); +export const operationAcceptanceOverrides58e2204=Object.fromEntries(upstream58e2204Operations.map((op:any)=>{const surface=surfaceById.get(op.operationId);const policy=policyById.get(op.operationId);const scenario=scenarioById.get(op.operationId);const availability=availabilityById.get(op.operationId);if(!surface||!policy||!scenario||!availability)throw new Error(`missing exact acceptance decision: ${op.operationId}`);return [op.operationId,{surfaceId:surface.surfaceId,primaryRoute:surface.primaryRoute,uiStrategy:strategyById.get(op.operationId),...policy.policies,scenarioAcceptance:scenario.scenarios,availability:availability.availability,availabilityReason:availability.reason,versionEvidencePolicy:availability.versionEvidencePolicy,realAcceptance:availability.realAcceptance}];})); +export const operationAcceptance58e2204=upstream58e2204Operations.map((op:any)=>{const override:any=operationAcceptanceOverrides58e2204[op.operationId];const policies:any=acceptancePolicyCatalog;const high=['R2','R3'].includes(op.riskLevel);const auth=dedicatedAuth.has(op.operationId);const scenarios=override.scenarioAcceptance;return {operationId:op.operationId,method:op.method,pathTemplate:op.pathTemplate,upstreamDomain:op.upstreamDomain,primaryRoute:override.primaryRoute,surfaceId:override.surfaceId,uiStrategy:override.uiStrategy,availability:override.availability,availabilityReason:override.availabilityReason,versionEvidencePolicy:override.versionEvidencePolicy,requiredStates:{request:['idle','loading','success','error'],freshness:['fresh','stale','expired','unknown'],support:['supported','unsupported','auth-required','degraded','unknown'],scenarios:Object.entries(scenarios).filter(([,v]:any)=>v.applicable).map(([k])=>k),naRationale:'Non-applicable scenarios retain an explicit rationale in scenarioAcceptance.'},scenarioAcceptance:scenarios,riskLevel:op.riskLevel as Risk,confirmationUX:op.confirmationPolicy,executionMode:auth?'dedicated-flow':high?'preparation-job':'direct',executionDerivation:auth?'Registry executionPolicy=dedicatedFlow binds the dedicated authentication orchestration.':high?'Registry capability=job plus product prepare-confirm-execute policy derives preparation-job orchestration.':'Registry capability plus acceptance policy derives direct structured execution.',executionPolicySource:'Registry supplies risk, confirmation, capability and dedicatedFlow; acceptance policy supplies orchestration.',...Object.fromEntries(['preconditionPolicyId','stalePolicyId','unsupportedPolicyId','retryPolicyId','resultPolicyId'].map(k=>[k,override[k]])),preconditions:policies.preconditionPolicyId[override.preconditionPolicyId],stalePolicy:policies.stalePolicyId[override.stalePolicyId],unsupportedPolicy:policies.unsupportedPolicyId[override.unsupportedPolicyId],retryRecovery:policies.retryPolicyId[override.retryPolicyId],resultDestination:policies.resultPolicyId[override.resultPolicyId],resourceBulk:resourceBulk.has(op.operationId),fleetBatchable:op.operationId==='getHealth',partialAggregationPolicy:op.operationId==='getHealth'?'per-item':resourceBulk.has(op.operationId)?'resource-item':'not-applicable',realAcceptance:override.realAcceptance,evidenceIds:[`registry:${op.operationId}`,...op.sourceEvidence.map((x:string)=>`upstream:${x}`),`fixture:${fixtureById.get(op.operationId)?.runtimeFixtureStatus}`],...(op.sessionSensitive?{sessionSubtype:'session-sensitive'}:{})};}); + +const flow=(flowId:string,route:string,risk:string,riskSubtype:string,confirmation:string,preconditions:string,result:string,failureRecovery:string,secretPolicy:string,evidence:string)=>({flowId,route,risk,riskSubtype,confirmation,preconditions,result,failureRecovery,secretPolicy,evidence}); +export const controlPlaneAcceptance=[ + flow('instance-create','/instances/new','R1','configuration','diff-confirm','canonical origin and unique stable ID','created instance revision and audit','retain draft on validation or reconcile failure','secret reference only; never return a value','WF-01 + product policy'), + flow('instance-update','/settings/instances/:id','R1','configuration','revision-diff','current revision','updated revision and audit','409 refreshes diff without losing edits','preserve/set/clear explicit; never return a value','WF-01 + product policy'), + flow('instance-delete','/settings/instances/:id','R3','destructive','strong target restatement','fresh revision and no conflicting job','new jobId with two-phase deleting to tombstone result','before boundary restore; after boundary manual recovery','destroy reference without reading secret value','WF-13'), + flow('secret-set','/settings/instances/:id','R1','secret-reference','explicit set','authorized SecretStore write','new reference metadata and audit','atomic rollback on reference write failure','never return or log secret value','WF-01'),flow('secret-preserve','/settings/instances/:id','R1','secret-reference','explicit preserve','existing reference metadata','unchanged reference metadata and audit','revision conflict refreshes metadata only','never read or return secret value','WF-01'),flow('secret-clear','/settings/instances/:id','R1','secret-reference','explicit clear','fresh revision','reference removed and audit','atomic rollback or explicit recovery state','never return destroyed secret value','WF-01'), + flow('config-import-preview','/settings/instances','R0','preview','none','validated upload and canonical origins','redacted diff/conflict/count preview','fix file and regenerate preview','never include secret values in preview','IA settings import'),flow('config-import-confirm','/settings/instances','R2','bulk-configuration','explicit preview token','fresh unexpired preview','new jobId with per-instance items','retry failed items in new lineage','references only; never import/export secret values','IA settings import'), + flow('credential-verify','/instances/new','R1','session-sensitive','actor initiated','rate limit and registered target','credential metadata-only result','no automatic replay; actor may retry after backoff','ephemeral input, never response/audit value','WF-01'),flow('saved-secret-login','/instances/new','R1','session-sensitive','actor initiated','saved reference and rate limit','session metadata-only result','no automatic replay; return to login flow','resolve server-side; never expose value','WF-12'),flow('temporary-secret-login','/instances/new','R1','session-sensitive','actor initiated','one-shot input and rate limit','session metadata-only result','no automatic replay; require new actor input','memory-only and never saved or returned','WF-12'),flow('logout','/settings/instances/:id','R1','session-sensitive','actor initiated','active session','session cleared and metadata audit','no replay; reconcile auth status','no credential value involved','WF-12'),flow('401-recovery','/instances/:id/overview','R1','session-recovery','actor chooses login','401 evidence and retained stale data','return to owner; retry R0 only','never replay writes; preserve draft for new prepare','temporary/saved secret follows selected login policy','WF-12'), + flow('auth-setup','/instances/new','R3','authentication-change','strong confirmation','fresh dedicated auth preflight','new jobId and metadata-only audit','new lineage after state verification','password never returned or audited','Registry postAuthSetup'),flow('auth-password-change','/settings/instances/:id','R3','authentication-change','strong confirmation','fresh dedicated auth preflight','new jobId and metadata-only audit','new lineage after auth-status verification','old/new password never returned or audited','Registry postAuthPassword'),flow('auth-settings-read','/settings/instances/:id','R0','authentication-read','none','registered instance and actor','structured auth settings','safe actor retry','no password/cookie/token fields','Registry getAuthSettings'),flow('auth-settings-write','/settings/instances/:id','R2','authentication-change','explicit confirmation','fresh dedicated auth preflight','new jobId and metadata-only audit','new lineage; no replay','never return credential values','Registry postAuthSettings'), + flow('job-cancel','/jobs/:jobId','R1','job-control','explicit confirmation','cancelable current phase','immutable cancellation event','show non-cancelable boundary and reconcile','no secret data in event','IA jobs'),flow('job-retry','/jobs/:jobId','R2','job-control','explicit failed-item selection','source terminal state and fresh preflight','new jobId with retryOf/root/source lineage','never mutate source or replay successful items','redacted parameter summary only','WF-10'),flow('audit-export','/audit','R0','data-export','explicit scope','authorized filters','redacted export artifact','regenerate from immutable audit','redaction applies; no sensitiveFields','WF-11'),flow('system-settings-update','/settings/system','R1','configuration','revision-diff','safe bounds and current revision','updated revision and audit','409 refreshes diff; invalid bounds rejected','no secrets accepted','IA settings system') +]; + +const esc=(v:any)=>String(v).replace(/\|/g,'\\|').replace(/\n/g,' '); +export function renderOperationAcceptanceMatrix(){const controlRows=controlPlaneAcceptance.map(x=>`| \`${x.flowId}\` | \`${x.route}\` | ${x.risk} / ${x.riskSubtype} | ${x.confirmation} | ${esc(x.preconditions)} | ${esc(x.result)} | ${esc(x.failureRecovery)} | ${esc(x.secretPolicy)} | ${esc(x.evidence)} |`).join('\n');const rows=operationAcceptance58e2204.map((x:any)=>{const f:any=fixtureById.get(x.operationId);const scenarios=Object.entries(x.scenarioAcceptance).map(([k,v]:any)=>`${k}:${v.applicable?'Y':'N'} (${v.rationale})`).join('
');const policies=`${x.preconditionPolicyId}/${x.stalePolicyId}/${x.unsupportedPolicyId}/${x.retryPolicyId}/${x.resultPolicyId}`;const categories=f.observedCategories.length?[...f.observedCategories].sort().join(','):'none';return `| \`${x.operationId}\` | ${x.method} \`${x.pathTemplate}\` | ${x.upstreamDomain} | \`${x.primaryRoute}\`
\`${x.surfaceId}\` | ${x.uiStrategy} | ${x.availability}: ${esc(x.availabilityReason)}
${esc(x.versionEvidencePolicy)} | ${scenarios} | ${x.riskLevel}; ${x.confirmationUX}
${x.executionMode} | ${policies}
${esc(x.preconditions)}
${esc(x.stalePolicy)}
${esc(x.unsupportedPolicy)}
${esc(x.retryRecovery)}
${esc(x.resultDestination)} | ${x.realAcceptance} | ${f.runtimeFixtureStatus}; n=${f.fixtureCount}; categories=${categories}; ${esc(f.reason)} | ${x.evidenceIds.map((e:string)=>`\`${e}\``).join('
')} |`;}).join('\n');return `# Operation acceptance matrix — upstream 58e2204\n\n> Generated from the structured ledger. Registry is the sole source for risk, confirmation, capability, and dedicated-flow facts; orchestration is bound by acceptance policy. \`registeredOperation\` is not a synonym for preparation Job.\n\nREAL_READ means captured evidence and is restricted to \`captured-readonly\`. REAL_READ_DEFERRED marks denied candidates without a real fixture. Fixture disposition is metadata-only: runtime status, count, safe observed status categories, and reason; it never renders fixture body/response, alias, identity, or URL.\n\n## Control-plane acceptance (independent flows)\n\n| flowId | route | risk / subtype | confirmation | preconditions | result | failure recovery | secret policy | evidence |\n|---|---|---|---|---|---|---|---|---|\n${controlRows}\n\n## Upstream operation acceptance (exact 117)\n\n| operationId | method/path | domain | route/surface | UI | availability/version evidence | scenario applicability | risk/confirmation/execution | policy IDs and resolved policies | real class | fixture disposition (metadata-only: runtime status / n / categories / reason) | evidence |\n|---|---|---|---|---|---|---|---|---|---|---|---|\n${rows}\n`;} +export const fixtureDenyReasons58e2204=DENY_REASONS; diff --git a/packages/operation-registry/test/acceptance-gate.test.ts b/packages/operation-registry/test/acceptance-gate.test.ts new file mode 100644 index 0000000..be7d6da --- /dev/null +++ b/packages/operation-registry/test/acceptance-gate.test.ts @@ -0,0 +1,69 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { readFile } from 'node:fs/promises'; +import { fileURLToPath } from 'node:url'; +import { upstream58e2204Operations } from '../src/upstream-58e2204.ts'; +import { operationAcceptance58e2204, operationAcceptanceOverrides58e2204, acceptancePolicyCatalog, controlPlaneAcceptance, fixtureDisposition58e2204, renderOperationAcceptanceMatrix, surfaceGroups58e2204, policyGroups58e2204, scenarioGroups58e2204, availabilityGroups58e2204 } from '../src/acceptance-58e2204.ts'; +import { DENY_REASONS, selectReadonlyOperations } from '../../test-fixtures/scripts/collector.ts'; + +const fixtureManifestPath=fileURLToPath(new URL('../../test-fixtures/src/manifest.json',import.meta.url)); +const matrixPath=fileURLToPath(new URL('../../../docs/product/operation-acceptance-matrix.md',import.meta.url)); +const acceptanceSourcePath=fileURLToPath(new URL('../src/acceptance-58e2204.ts',import.meta.url)); +const iaPath=fileURLToPath(new URL('../../../docs/product/information-architecture.md',import.meta.url)); +const manifest=JSON.parse(await readFile(fixtureManifestPath,'utf8')); +const byId=new Map(upstream58e2204Operations.map((o:any)=>[o.operationId,o])); +const canonicalRoutes=new Set(['/fleet','/instances/new','/instances/:id/overview','/instances/:id/cellular','/instances/:id/device-network','/instances/:id/messages','/instances/:id/calls','/instances/:id/esim','/instances/:id/notifications','/instances/:id/automation','/instances/:id/ota','/settings/instances/:id']); +const assertExactPartition=(groups:readonly {ids:readonly string[]}[])=>{const ids=groups.flatMap(g=>[...g.ids]);assert.equal(ids.length,117);assert.equal(new Set(ids).size,117);assert.deepEqual([...ids].sort(),[...byId.keys()].sort());}; + +test('RED→GREEN: surface/policy/scenario/availability are literal exact operation partitions',()=>{ + for(const groups of [surfaceGroups58e2204,policyGroups58e2204,scenarioGroups58e2204,availabilityGroups58e2204])assertExactPartition(groups); + assert.ok(surfaceGroups58e2204.filter(g=>g.surfaceId.startsWith('calls/')).length>=5); + assert.ok(surfaceGroups58e2204.filter(g=>g.surfaceId.startsWith('cellular/')).length>=5); + for(const group of surfaceGroups58e2204)for(const id of group.ids){const row=operationAcceptance58e2204.find(x=>x.operationId===id)!;assert.equal(row.surfaceId,group.surfaceId);assert.equal(row.primaryRoute,group.primaryRoute);} + const tuples=new Set(policyGroups58e2204.map(g=>Object.values(g.policies).join('/')));assert.ok(policyGroups58e2204.length>=8);assert.ok(tuples.size>=8); + const policy=(id:string)=>policyGroups58e2204.find(g=>g.ids.includes(id))!;assert.match(policy('getSmsList').groupId,/list/);assert.match(policy('getHealth').groupId,/detail/);assert.match(policy('getNetworkOperatorsScan').groupId,/scan/);assert.match(policy('postData').groupId,/direct/);assert.match(policy('postAuthLogin').groupId,/auth/);assert.match(policy('postSmsBatchDelete').groupId,/destructive/);assert.match(policy('postOtaApply').groupId,/ota/); + assert.ok(scenarioGroups58e2204.length>=8);const scenario=(id:string)=>scenarioGroups58e2204.find(g=>g.ids.includes(id))!.scenarios;assert.equal(scenario('getHealth').empty.applicable,false);assert.equal(scenario('getSmsList').empty.applicable,true);assert.equal(scenario('getStats').partial.applicable,true);assert.equal(scenario('getAuthStatus').partial.applicable,false);assert.equal(scenario('getNetworkOperatorsScan').empty.applicable,true);assert.equal(scenario('getNetworkOperatorsScan').partial.applicable,true);assert.equal(scenario('getNetworkOperatorsScan')['policy-forbidden'].applicable,true);assert.equal(scenario('getNetworkOperatorsScan')['unknown-result'].applicable,false);for(const id of ['postSmsBatchDelete','postNotificationsQueueRetryAll','postNotificationsQueueClear'])assert.equal(scenario(id).partial.applicable,true,id);for(const id of ['postSystemReboot','postServiceRestart','deleteCallHistoryId','postBandLock','postOtaApply','postOtaUpload','postOtaOnlinePrepare','postOtaLatestRelease','postOtaCancel','postData'])assert.equal(scenario(id).partial.applicable,false,id);for(const id of ['postData','postSmsBatchDelete']){assert.equal(scenario(id)['policy-forbidden'].applicable,true);assert.equal(scenario(id)['unknown-result'].applicable,true);}for(const id of ['getHealth','getSmsList']){assert.equal(scenario(id)['policy-forbidden'].applicable,false);assert.equal(scenario(id)['unknown-result'].applicable,false);}for(const group of scenarioGroups58e2204)assert.equal(group.scenarios['owner-switch'].applicable,true); + assert.ok(availabilityGroups58e2204.length>=4);for(const group of availabilityGroups58e2204)assert.ok(group.reason.length>20); +}); + +test('RED→GREEN: acceptance ledger is exact, Registry-bound and IA-owned',()=>{ + assert.equal(operationAcceptance58e2204.length,117); + assert.equal(new Set(operationAcceptance58e2204.map(x=>x.operationId)).size,117); + assert.deepEqual([...operationAcceptance58e2204.map(x=>x.operationId)].sort(),[...byId.keys()].sort()); + for(const row of operationAcceptance58e2204){const op:any=byId.get(row.operationId);assert.ok(op);assert.equal(row.method,op.method);assert.equal(row.pathTemplate,op.pathTemplate);assert.equal(row.upstreamDomain,op.upstreamDomain);assert.equal(row.riskLevel,op.riskLevel);assert.equal(row.confirmationUX,op.confirmationPolicy);assert.ok(canonicalRoutes.has(row.primaryRoute),row.operationId);assert.ok(row.surfaceId);assert.ok(row.uiStrategy);assert.match(row.availability,/^(planned|unsupported-version|deferred-with-reason)$/);if(row.availability==='deferred-with-reason')assert.ok(row.availabilityReason);assert.ok(row.requiredStates.request.length&&row.requiredStates.freshness.length&&row.requiredStates.support.length);assert.ok(row.requiredStates.scenarios.length||row.requiredStates.naRationale);assert.ok(row.evidenceIds.length);} +}); + +test('RED→GREEN: exact overrides and policy-bound acceptance are auditable',()=>{ + assert.deepEqual(Object.keys(operationAcceptanceOverrides58e2204).sort(),[...byId.keys()].sort()); + const policyFields=['preconditionPolicyId','stalePolicyId','unsupportedPolicyId','retryPolicyId','resultPolicyId']; + for(const row of operationAcceptance58e2204){const override:any=(operationAcceptanceOverrides58e2204 as any)[row.operationId];assert.ok(override);assert.ok(row.availabilityReason?.length>20,row.operationId);assert.ok(row.versionEvidencePolicy?.length>20,row.operationId);assert.equal(row.surfaceId,override.surfaceId);assert.equal(row.uiStrategy,override.uiStrategy);for(const scenario of ['empty','partial','policy-forbidden','unknown-result','owner-switch']){assert.equal(typeof row.scenarioAcceptance[scenario].applicable,'boolean');assert.ok(row.scenarioAcceptance[scenario].rationale.length>8);}for(const field of policyFields){assert.equal((row as any)[field],override[field]);assert.ok((acceptancePolicyCatalog as any)[field][(row as any)[field]],`${row.operationId}:${field}`);}} +}); + +test('real acceptance is an explicit availability decision coherent with fixture disposition',async()=>{const fixture=new Map(fixtureDisposition58e2204.map(x=>[x.operationId,x]));const valid=new Set(['REAL_READ','REAL_READ_DEFERRED','REAL_WRITE_LATER','SIMULATED_HIGH_RISK','CONTRACT_ONLY']);for(const group of availabilityGroups58e2204){assert.ok(valid.has(group.realAcceptance),group.groupId);for(const id of group.ids){const row=operationAcceptance58e2204.find(x=>x.operationId===id)!;assert.equal(row.realAcceptance,group.realAcceptance,id);const status=fixture.get(id)!.runtimeFixtureStatus;if(status==='captured-readonly')assert.equal(group.realAcceptance,'REAL_READ',id);if(status==='denied-readonly-candidate')assert.equal(group.realAcceptance,'REAL_READ_DEFERRED',id);if(group.realAcceptance==='REAL_READ')assert.equal(status,'captured-readonly',id);if(group.realAcceptance==='REAL_READ_DEFERRED')assert.equal(status,'denied-readonly-candidate',id);}}const source=await readFile(acceptanceSourcePath,'utf8');assert.doesNotMatch(source,/function\s+realAcceptance\s*\(/);assert.doesNotMatch(source,/realAcceptanceIntent/);assert.equal(operationAcceptance58e2204.filter(x=>x.realAcceptance==='REAL_READ').length,39);assert.equal(operationAcceptance58e2204.filter(x=>x.realAcceptance==='REAL_READ_DEFERRED').length,11);}); + +test('Phase 1 bootstrap gate defers Fleet component/E2E evidence to the Phase 5 implementation gate',async()=>{const ia=await readFile(iaPath,'utf8');const bootstrap=ia.match(/### Phase 0 → Phase 1 workspace\/contract bootstrap gate([\s\S]*?)(?=### Phase 5 implementation gate)/)?.[1];const phase5=ia.match(/### Phase 5 implementation gate([\s\S]*)/)?.[1];assert.ok(bootstrap);assert.ok(phase5);assert.doesNotMatch(bootstrap!,/Fleet[^\n]*组件\/E2E/);assert.match(phase5!,/Fleet[^\n]*组件\/E2E/);assert.match(phase5!,/\[ \][^\n]*Fleet/);assert.match(bootstrap!,/\[x\][^\n]*最终独立规格与质量\/安全复审均已通过/);assert.doesNotMatch(bootstrap!,/PENDING final independent review/);assert.match(ia,/E2E[^\n]*N\/A[^\n]*Phase 5/);}); + +test('control-plane flows are independent, structured, and preserve safety invariants',()=>{const ids=['instance-create','instance-update','instance-delete','secret-set','secret-preserve','secret-clear','config-import-preview','config-import-confirm','credential-verify','saved-secret-login','temporary-secret-login','logout','401-recovery','auth-setup','auth-password-change','auth-settings-read','auth-settings-write','job-cancel','job-retry','audit-export','system-settings-update'];assert.deepEqual(controlPlaneAcceptance.map(x=>x.flowId).sort(),ids.sort());for(const flow of controlPlaneAcceptance)for(const field of ['route','risk','riskSubtype','confirmation','preconditions','result','failureRecovery','secretPolicy','evidence'])assert.ok(String((flow as any)[field]).length>1,`${flow.flowId}:${field}`);assert.match(controlPlaneAcceptance.find(x=>x.flowId==='instance-delete')!.result,/new jobId.*two-phase/i);for(const id of ['secret-set','secret-preserve','secret-clear'])assert.match(controlPlaneAcceptance.find(x=>x.flowId===id)!.secretPolicy,/never.*value/i);for(const id of ['saved-secret-login','temporary-secret-login'])assert.match(controlPlaneAcceptance.find(x=>x.flowId===id)!.failureRecovery,/no automatic replay/i);assert.match(controlPlaneAcceptance.find(x=>x.flowId==='job-retry')!.result,/new jobId.*lineage/i);}); + +test('product risk prose has no conflicting action summaries',async()=>{const root=fileURLToPath(new URL('../../../docs/product/',import.meta.url));const names=['project-charter.md','personas-and-workflows.md','information-architecture.md','current-system-audit.md'];const docs=(await Promise.all(names.map(n=>readFile(`${root}${n}`,'utf8')))).join('\n');for(const expected of [/notifications config[^\n]*R2[^\n]*Job/i,/automation config[^\n]*R2[^\n]*Job/i,/eSIM[^\n]*download[^\n]*R1[^\n]*direct/i,/WLAN connect[^\n]*R1[^\n]*forget[^\n]*R2/i,/DDNS config[^\n]*R2[^\n]*Job/i,/baseband restart[^\n]*R3[^\n]*status[^\n]*R0/i])assert.match(docs,expected);assert.doesNotMatch(docs,/Notifications[^\n]*config R1/i);assert.doesNotMatch(docs,/Automation[^\n]*config R1/i);}); +test('R2/R3, dedicated auth, write strategy, and split bulk/fleet policy are gated',()=>{ + for(const row of operationAcceptance58e2204){const op:any=byId.get(row.operationId);if(['R2','R3'].includes(row.riskLevel)){assert.equal(row.executionMode,op.executionPolicy==='dedicatedFlow'?'dedicated-flow':'preparation-job');assert.match(row.preconditions,/fresh preflight/i);assert.match(row.confirmationUX,/explicit|strong/);assert.match(row.resultDestination,/jobs\/:jobId/);assert.match(row.retryRecovery,/new Job lineage/i);}if(row.riskLevel==='R3')assert.equal(op.capability,'job');if(row.method!=='GET'){assert.notEqual(row.uiStrategy,'read-panel');assert.notEqual(row.realAcceptance,'REAL_READ');assert.equal(row.fleetBatchable,false);}} + const auth=operationAcceptance58e2204.filter(x=>['postAuthSetup','postAuthPassword','postAuthSettings','postAuthLogin','postAuthLogout'].includes(x.operationId));assert.equal(auth.length,5);assert.ok(auth.every(x=>x.uiStrategy==='dedicated-auth-flow'&&x.executionMode==='dedicated-flow'));for(const id of ['postAuthLogin','postAuthLogout']){const row=auth.find(x=>x.operationId===id)!;assert.equal(row.sessionSubtype,'session-sensitive');assert.match(row.retryRecovery,/no automatic replay/i);assert.match(row.resultDestination,/metadata-only audit/i);} + const sms=operationAcceptance58e2204.find(x=>x.operationId==='postSmsBatchDelete')!;assert.equal(sms.resourceBulk,true);assert.equal(sms.fleetBatchable,false);for(const id of ['postNotificationsQueueRetryAll','postNotificationsQueueClear'])assert.equal(operationAcceptance58e2204.find(x=>x.operationId===id)!.resourceBulk,true,id);for(const id of ['postSmsClear','postCallHistoryClear','postNotificationsLogsClear','postAutomationLogsClear'])assert.equal(operationAcceptance58e2204.find(x=>x.operationId===id)!.resourceBulk,false,id); + const health=operationAcceptance58e2204.find(x=>x.operationId==='getHealth')!;assert.equal(health.fleetBatchable,true);assert.equal(health.partialAggregationPolicy,'per-item'); +}); + +test('fixture disposition is authoritative and exactly follows collector plus 78-file manifest',()=>{ + assert.equal(fixtureDisposition58e2204.length,117);assert.equal(new Set(fixtureDisposition58e2204.map(x=>x.operationId)).size,117); + const selected=selectReadonlyOperations(upstream58e2204Operations as any[]);assert.equal(selected.selected.length,39);assert.equal(selected.denied.length,11);assert.equal(Object.keys(DENY_REASONS).length,11); + const selectedIds=new Set(selected.selected.map((x:any)=>x.operationId));const denied=new Map(selected.denied.map((x:any)=>[x.operationId,x.denyReason])); + const filesById=new Map();for(const file of manifest.files){const match=/instance-[12]--([^.]+)\.json$/.exec(file.path);assert.ok(match);const list=filesById.get(match[1])??[];list.push(file);filesById.set(match[1],list);} + for(const d of fixtureDisposition58e2204){if(selectedIds.has(d.operationId)){assert.equal(d.runtimeFixtureStatus,'captured-readonly');assert.equal(d.fixtureCount,2);assert.equal(filesById.get(d.operationId)?.length,2);assert.deepEqual(d.aliases,['instance-1','instance-2']);assert.ok(d.observedCategories.length);}else if(denied.has(d.operationId)){assert.equal(d.runtimeFixtureStatus,'denied-readonly-candidate');assert.equal(d.reason,denied.get(d.operationId));assert.equal(d.fixtureCount,0);}else {assert.match(d.runtimeFixtureStatus,/^not-eligible-readonly-capture/);assert.equal(d.fixtureCount,0);}} + assert.equal(manifest.realFixtureCount,78); +}); + +test('product risk prose defers to Registry and known conflicts stay corrected',async()=>{const productRoot=fileURLToPath(new URL('../../../docs/product/',import.meta.url));const workflows=await readFile(`${productRoot}personas-and-workflows.md`,'utf8');const charter=await readFile(`${productRoot}project-charter.md`,'utf8');assert.doesNotMatch(workflows,/下载 profile[^\n]*R2 Job/);assert.match(workflows,/WLAN connect 为 R1/);assert.match(workflows,/notifications config 为 R2 Job/);assert.match(workflows,/fleetBatchable=true/);assert.match(charter,/resourceBulk.*fleetBatchable/);for(const id of ['postEsimProfiles','postEsimProfilesIccidEnable','postDeviceNetworkWlanConnect','postNotificationsConfig','postAutomationConfig','postDeviceNetworkDdnsConfig','postBasebandRestart']){assert.ok(operationAcceptance58e2204.find(x=>x.operationId===id),id);}}); + +test('generated matrix is synchronized and contains each operation exactly once',async()=>{const doc=await readFile(matrixPath,'utf8');assert.equal(doc,renderOperationAcceptanceMatrix());const ids=[...doc.matchAll(/^\| `([^`]+)` \|/gm)].map(x=>x[1]).filter(id=>byId.has(id));assert.equal(ids.length,117);assert.deepEqual(ids.sort(),[...byId.keys()].sort());}); + +test('matrix renders metadata-only safe fixture categories for every operation',()=>{const doc=renderOperationAcceptanceMatrix();const safeCategories=new Set(['success','unsupported','auth-required']);const fixture=new Map(fixtureDisposition58e2204.map(x=>[x.operationId,x]));const rows=[...doc.matchAll(/^\| `([^`]+)` \|.*?\| ([^|]*categories=([^;|]+);[^|]*) \|/gm)].filter(match=>byId.has(match[1]));assert.equal(rows.length,117);for(const [,id,metadata,rendered] of rows){const disposition=fixture.get(id)!;const expected=disposition.observedCategories.length?[...disposition.observedCategories].sort().join(','):'none';assert.equal(rendered.trim(),expected,id);for(const category of disposition.observedCategories)assert.ok(safeCategories.has(category),`${id}:${category}`);assert.doesNotMatch(metadata,/"(?:response|body|sourceInstanceAlias)"\s*:|https?:\/\//i,id);}assert.match(doc,/metadata-only/i);}); diff --git a/packages/operation-registry/test/independent-safety-baseline.test.ts b/packages/operation-registry/test/independent-safety-baseline.test.ts new file mode 100644 index 0000000..a8eae59 --- /dev/null +++ b/packages/operation-registry/test/independent-safety-baseline.test.ts @@ -0,0 +1,59 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { execFileSync } from 'node:child_process'; +import { fileURLToPath } from 'node:url'; +import { upstream58e2204Operations } from '../src/upstream-58e2204.ts'; + +const PHASE_0_2_COMMIT='9dffadbec271227da7ffb192f31dc78c6ee3c2be'; +const BASELINE_PATH='packages/operation-registry/src/upstream-58e2204.ts'; +const repoRoot=fileURLToPath(new URL('../../../',import.meta.url)); +const safetyFields=['operationId','riskLevel','confirmationPolicy','capability','executionPolicy'] as const; +type SafetyRow=Record<(typeof safetyFields)[number],string>; + +function git(...args:string[]){return execFileSync('git',args,{cwd:repoRoot,encoding:'utf8',stdio:['ignore','pipe','pipe']}).trim();} + +/** Parse only the JSON array literal assigned to the frozen raw operations constant; never evaluate TypeScript. */ +export function parseFrozenSafetyBaseline(source:string):SafetyRow[]{ + const marker='const rawUpstream58e2204Operations = '; + const start=source.indexOf('[',source.indexOf(marker)+marker.length); + assert.ok(source.includes(marker)&&start>=0,'frozen operation array marker missing'); + let quoted=false,escaped=false,depth=0,end=-1; + for(let i=start;istart,'unterminated frozen operation array'); + const parsed=JSON.parse(source.slice(start,end)); + assert.ok(Array.isArray(parsed),'frozen operation baseline is not an array'); + return parsed.map((row:any,index:number)=>Object.fromEntries(safetyFields.map(field=>{assert.equal(typeof row?.[field],'string',`baseline row ${index} missing ${field}`);return [field,row[field]];})) as SafetyRow); +} + +export function assertExactSafetyBaseline(actual:readonly SafetyRow[],expected:readonly SafetyRow[]){ + assert.equal(expected.length,117,'independent baseline must contain exactly 117 operations'); + assert.equal(new Set(expected.map(x=>x.operationId)).size,117,'independent baseline operationIds must be unique'); + assert.equal(actual.length,117,'current Registry must contain exactly 117 operations'); + assert.equal(new Set(actual.map(x=>x.operationId)).size,117,'current Registry operationIds must be unique'); + const sort=(rows:readonly SafetyRow[])=>[...rows].sort((a,b)=>a.operationId.localeCompare(b.operationId)); + assert.deepEqual(sort(actual),sort(expected),'current Registry safety fields differ from frozen Phase 0.2 Git object; update requires explicit safety review'); +} + +const project=(rows:readonly any[]):SafetyRow[]=>rows.map(row=>Object.fromEntries(safetyFields.map(field=>[field,row[field]])) as SafetyRow); + +test('independent Phase 0.2 Git object freezes all 117 Registry safety decisions',()=>{ + assert.equal(git('rev-parse','9dffadb'),PHASE_0_2_COMMIT); + assert.equal(git('cat-file','-t',PHASE_0_2_COMMIT),'commit'); + const baseline=parseFrozenSafetyBaseline(git('show',`${PHASE_0_2_COMMIT}:${BASELINE_PATH}`)); + assertExactSafetyBaseline(project(upstream58e2204Operations),baseline); +}); + +test('independent safety comparison rejects risk, confirmation, capability and execution-policy mutations',()=>{ + const baseline=parseFrozenSafetyBaseline(git('show',`${PHASE_0_2_COMMIT}:${BASELINE_PATH}`)); + const mutations:[string,string,string][]=[ + ['postData','riskLevel','R0'], + ['postSmsSend','confirmationPolicy','none'], + ['postSmsSend','capability','query'], + ['postSmsSend','executionPolicy','dedicatedFlow'] + ]; + for(const [operationId,field,value] of mutations){const current=structuredClone(project(upstream58e2204Operations));(current.find(x=>x.operationId===operationId)! as any)[field]=value;assert.throws(()=>assertExactSafetyBaseline(current,baseline),/safety fields differ/,`${operationId}:${field}`);} +});