diff --git a/.project-docs/00-brief/success-criteria.md b/.project-docs/00-brief/success-criteria.md index acbb95f..4d5f22c 100644 --- a/.project-docs/00-brief/success-criteria.md +++ b/.project-docs/00-brief/success-criteria.md @@ -10,14 +10,14 @@ - Electron 双视口预检必须检查与最终 `built_archive` 相同的内存文件字节;预检失败不得上传,预检成功不得被表述为可信审核凭据。 - 上传协议必须同时携带源码归档、构建归档和严格版本化 artifact contract;服务端独立重算摘要、校验合同并固化不可变 Release。 - Renderer 不得获得发布凭据、归档、临时目录、构建 origin 或任意本地路径;旧客户端和旧 sandbox/browser 任务必须提示升级后重新构建提交。 -- Robot Guided Hotspot Binding 实现必须保持固件零改动、Main-owned default-off capability、固定系统浏览器 portal 和现有六位 Binding facade;Makelore 不得接触 Wi-Fi 凭据,也不得把 Binding 成功等同于在线。 +- Robot Guided Hotspot Binding 必须保持固件零改动、Main-owned default-on capability、精确环境值 `0` 回滚、固定系统浏览器 portal 和现有六位 Binding facade;Makelore 不得接触 Wi-Fi 凭据,也不得把 Binding 成功等同于在线。 ## Quality Checks - 发布安装包前运行 `pnpm verify:publish-runtime`,并对最终 Windows 产物运行 `pnpm verify:artifact:win`;固定 npm 闭包缺失或版本不符时 fail closed。 - 至少覆盖 release builder/静态产物服务/发布路由/Renderer 状态的聚焦测试、typecheck、scoped lint、Vite 构建和真实 Electron 双视口 production-seam E2E。 - 真实生产发布仍需成组验证服务端合同、不可变 Release、运营审核、CDN/App 播放;客户端本地验证不能替代该整链验收。 -- Robot 试点开启前必须通过聚焦 Renderer/Main 测试、真实 Host API Electron E2E、指定固件镜像核对、六位码发行契约核对与真机 smoke;公共/default 配置保持关闭。 +- Robot 默认引导路径必须通过聚焦 Renderer/Main 测试并保留 `NIANCODE_AI_HARDWARE_GUIDED_HOTSPOT_BINDING=0` 回滚。真实 Host API Electron E2E、指定固件镜像、六位码发行契约与真机 smoke 未完成前,不得宣称完整硬件兼容或端到端配网已经验收。 ## Last Reviewed diff --git a/.project-docs/10-decisions/adr-002-robot-guided-hotspot-binding-v1.md b/.project-docs/10-decisions/adr-002-robot-guided-hotspot-binding-v1.md index 74c1f92..17f9688 100644 --- a/.project-docs/10-decisions/adr-002-robot-guided-hotspot-binding-v1.md +++ b/.project-docs/10-decisions/adr-002-robot-guided-hotspot-binding-v1.md @@ -4,7 +4,7 @@ Accepted -Implementation status: implemented; the capability remains disabled by default until all pilot release gates pass. +Implementation status: implemented and enabled by default. Exact environment value `NIANCODE_AI_HARDWARE_GUIDED_HOTSPOT_BINDING=0` disables the guided path as an operational rollback. ## Date @@ -39,9 +39,9 @@ V1 is a Renderer-guided, Main-gated workflow: - Binding continues to use the existing `bindAiHardwareDevice(activationCode, agentId, { operationId })` cloud contract. - `bound` means account Binding succeeded. It does not prove the Robot is currently online or protocol-ready. -The guided path is controlled by a Main-owned `guidedHotspotBinding` capability. It is `false` by default. Public builds with the capability disabled retain the existing direct six-digit Binding flow. +The guided path is controlled by a Main-owned `guidedHotspotBinding` capability and is `true` by default. Electron Main derives the packaged default from `NIANCODE_AI_HARDWARE_GUIDED_HOTSPOT_BINDING !== '0'`; an exact `0` disables the path without changing the binary. Disabled builds and Renderer capability-read failures retain the existing direct six-digit Binding flow. -The planned Host API surface is deliberately small: +The implemented Host API surface is deliberately small: - `GET /api/works/ai-hardware/provisioning-capabilities` has no body/query and succeeds with the standard Host envelope `{ success: true, data: { guided_hotspot_binding: boolean } }`. - `POST /api/works/ai-hardware/provisioning-portal/open` accepts the exact body `{}`, has no query, and succeeds with `{ success: true, data: { opened: true } }`. @@ -50,21 +50,21 @@ The planned Host API surface is deliberately small: ## Security And Recovery Rules -- The current firmware hotspot and portal are open/plain HTTP. V1 is an internal pilot only; product copy must warn the user not to perform the flow in an untrusted public environment. +- The current firmware hotspot and portal are open/plain HTTP. Default-on accepts that compatibility risk but does not make the channel authenticated; product copy must warn the user not to perform the flow in an untrusted public environment. - Wi-Fi SSID/password entry stays in the firmware portal. Makelore must not collect, log, persist, or proxy Wi-Fi credentials. - Cancel, back, and application restart never imply that Wi-Fi changes on the Robot were reverted. The user is guided to reconnect and restart provisioning if needed. - A same-process ambiguous Binding retry reuses the same operation ID. An invalid/expired/consumed activation code clears both code and retained operation ID; the next freshly issued code gets a new operation ID. - Main must project `ai_hardware_activation_code_invalid` as non-retryable even if an upstream response incorrectly marks it retryable. - After application restart, Makelore cannot correlate an earlier code or Binding outcome from the current overview DTO. It must not replay the old code or operation ID; the user obtains a fresh code or stops. -## Release Gates +## Default-On Acceptance And Remaining Verification -The capability may be enabled only after all of the following are evidenced: +The user explicitly chose default-on on 2026-08-16 while retaining the current firmware behavior. The open SoftAP/plain-HTTP channel remains a known residual risk; choosing the product default is not evidence that the physical flow or its security has passed. Before claiming complete hardware compatibility or end-to-end provisioning acceptance, all of the following still require evidence: -1. The exact shipped Robot component/image is confirmed to use the audited Hotspot portal flow and fixed portal address. +1. The exact shipped Robot component/image uses the audited Hotspot portal flow and fixed portal address. 2. The deployed activation issuer emits exactly six ASCII digits accepted by the existing Works Binding validator, with documented freshness and consumption behavior. -3. Focused Renderer/Main route tests, Electron E2E through the real Host API seam, and a physical-device smoke all pass. -4. The public/default configuration remains disabled until the open SoftAP/plain-HTTP risk is explicitly accepted for the intended pilot population. +3. Electron E2E through the real Host API/native-opener seam and a physical-device smoke pass; focused Renderer/Main route tests already cover default-on, exact `0` rollback, fixed portal ownership, local-before-cloud behavior, and error redaction. +4. Release/support instructions retain the exact `=0` rollback and do not describe V1 as authenticated discovery, automatic Wi-Fi delivery, automatic claim, or proof of online readiness. ## Consequences diff --git a/.project-docs/10-decisions/decision-index.md b/.project-docs/10-decisions/decision-index.md index c73799b..845b8c7 100644 --- a/.project-docs/10-decisions/decision-index.md +++ b/.project-docs/10-decisions/decision-index.md @@ -5,7 +5,7 @@ | ID | Decision | Status | Date | Applies To | Detail | |---|---|---|---|---|---| | ADR-001 | AI 绘画采用 Workspace / Conversation / Task 分层状态与服务端持久 Conversation Session | Accepted | 2026-08-11 | AI 绘画客户端、Main 适配器、Works Square API | `adr-001-ai-design-conversation-ownership.md` | -| ADR-002 | Robot V1 采用 Main 门控的引导式热点配网并衔接现有六位 Binding | Accepted / implemented, default off | 2026-08-16 | Robot Renderer、Host API、Electron Main、现有固件热点入口 | `adr-002-robot-guided-hotspot-binding-v1.md` | +| ADR-002 | Robot V1 采用 Main 门控的引导式热点配网并衔接现有六位 Binding | Accepted / implemented, default on | 2026-08-16 | Robot Renderer、Host API、Electron Main、现有固件热点入口 | `adr-002-robot-guided-hotspot-binding-v1.md` | ## Superseded Decisions diff --git a/.project-docs/20-architecture/data-flow.md b/.project-docs/20-architecture/data-flow.md index bf0a5a9..7d9e999 100644 --- a/.project-docs/20-architecture/data-flow.md +++ b/.project-docs/20-architecture/data-flow.md @@ -19,7 +19,7 @@ | 设计消息与确认 | 当前 Conversation | Main ↔ 持久 Agent Gateway Session WebSocket → Conversation 快照 | `command.submit`、Run 与设计事件共用连接;传输失败才以同一幂等 ID 回退 REST;结构化业务错误不重试且未知文本由 Main 脱敏;请求和流式结果同时绑定 Workspace + Conversation | | 设计单图来源选择 | 当前 Workspace 已完成图片 / 本地图片 | 现有 Asset 上传或选择 → `attachmentAssetIds` → 当前 Conversation Turn | 图片 Brief 用作图生图参考图;视频 Brief 用作首帧;只提交一个真实 Workspace Asset ID | | 设计任务同步 | 任一 Conversation 的事件流 / Quote REST 对账 | Renderer Workspace 任务列表 | Task 和 Asset 按 Workspace 归并;任务已落库但 Run 失败时恢复可见性,内部对账失败不覆盖新会话错误,切换 Conversation 后仍同步任务但不回写旧会话 | -| Robot 引导式热点配网 V1(计划、默认关闭) | Robot Binding 页面 | 用户进入固件配网模式 → 操作系统连接 Robot 热点 → Main 在系统浏览器打开固定 portal → 用户在 portal 配置 Wi-Fi → 电脑恢复互联网 → 现有六位 Binding | Makelore 不扫描附近设备、不收集 Wi-Fi 密码、不修改固件;`bound` 不等于 online/ready | +| Robot 引导式热点配网 V1(已实现、默认开启) | Robot Binding 页面 | 用户选择引导配网 → 进入固件配网模式 → 操作系统连接 Robot 热点 → Main 在系统浏览器打开固定 portal → 用户在 portal 配置 Wi-Fi → 电脑恢复互联网 → 现有六位 Binding | 精确环境值 `0` 或 capability 读取失败回退直接六位码;Makelore 不扫描设备、不收集 Wi-Fi 密码、不修改固件,`bound` 不等于 online/ready | ## State Ownership @@ -46,7 +46,7 @@ - Main-owned electron-updater IPC 与 Works Square 平台/架构稳定 feed;正式安装产物发布不由 Renderer 控制。 - 服务端安全投影后的公共 `play_url`;只接受同源 HTTPS、精确 App 路径和可信版本状态。 - Works Square Workspace/Conversation API、每个 Conversation 的持久 Agent Gateway Session、单次 WebSocket ticket、双向命令/事件帧与幂等 REST 传输回退。 -- 计划中的本机 Robot provisioning capability 与固定 portal-open Host API。它们是本地 Main 操作,不读取 Works access token、不调用上游,也不接受任意 URL。 +- 已实现的本机 Robot provisioning capability 与固定 portal-open Host API。它们是本地 Main 操作,不读取 Works access token、不调用上游,也不接受任意 URL。 ## Last Updated diff --git a/.project-docs/20-architecture/module-map.md b/.project-docs/20-architecture/module-map.md index 9f838b0..ee89630 100644 --- a/.project-docs/20-architecture/module-map.md +++ b/.project-docs/20-architecture/module-map.md @@ -29,9 +29,9 @@ | `src/stores/image-workspace.ts` | 当前 Workspace/Conversation、项目任务及流式更新状态 | Quote/task 按 Workspace 无 UI 错误副作用地对账;Conversation 写入按 Workspace-load + Conversation-selection generation/revision 防护 | | `src/pages/ImageCanvas/index.tsx` | Conversation 对话、Quote 确认、统一任务列表、新会话入口与单图来源选择器 | 图片 Brief 选择/上传图生图参考图;视频 Brief 绑定首帧;均提交一个 Workspace Asset ID | | `src/components/layout/ImageWorkspaceSidebar.tsx` | Workspace 与近期 Conversation 切换/创建 | 切换会话不清空项目级任务 | -| `src/pages/AiHardware/index.tsx` | Robot 管理、现有六位 Binding,以及计划中的 default-off 引导式热点配网状态机 | 只编排非敏感步骤;不接收 Wi-Fi 密码,不把 `bound` 展示为在线证明 | -| `src/lib/ai-hardware.ts` | Renderer 侧 Robot Host API 类型、安全错误映射和稳定 Binding facade | 计划新增只读 capability 与无参数固定 portal-open 操作;不添加任意 URL 或 Renderer IPC | -| `electron/api/routes/ai-hardware.ts` | Main-owned Robot 云端代理,以及计划中的本地配网 capability/portal action | 本地操作必须在认证/上游访问前返回;系统浏览器只能打开固定 `http://192.168.4.1/` | +| `src/pages/AiHardware/index.tsx` | Robot 管理、现有六位 Binding,以及已实现的 default-on 引导式热点配网状态机 | 只编排非敏感步骤;不接收 Wi-Fi 密码,不把 `bound` 展示为在线证明 | +| `src/lib/ai-hardware.ts` | Renderer 侧 Robot Host API 类型、安全错误映射和稳定 Binding facade | 读取 Main-owned capability 并调用无参数固定 portal-open;不添加任意 URL 或 Renderer IPC | +| `electron/api/routes/ai-hardware.ts` | Main-owned Robot 云端代理,以及已实现的本地配网 capability/portal action | 默认开启、精确环境值 `0` 回滚;本地操作必须在 Works token/上游访问前返回,系统浏览器只能打开固定 `http://192.168.4.1/` | ## Dependency Direction @@ -55,7 +55,7 @@ - Gateway 命令的 REST fallback 只处理 WebSocket 发送、断连和 ACK 超时,必须复用 `client_command_id`;业务错误回退会造成重复提交。Quote 任务恢复只更新 Workspace 所有的任务,不能覆盖当前 Conversation。 - `closeEventSessions` 只负责本地流和缓存生命周期;远端 Conversation Session 是服务端持久资源。 - 单图来源选择器当前仍由精确中文 quick reply 触发,并以 Brief medium 判断图生图或视频首帧用途;扩展更多输入用途前应先把消息协议升级为结构化 action/purpose,避免展示文案与行为继续耦合。 -- Guided Hotspot Binding 依赖当前固件的开放热点和明文 HTTP portal,只能作为 default-off 内部试点;未完成指定固件镜像核对、六位码发行契约和真机 smoke 前不得开启。 +- Guided Hotspot Binding 已由产品决策默认开启,但仍依赖当前固件的开放热点和明文 HTTP portal。未完成指定固件镜像核对、六位码发行契约、原生 opener E2E 和真机 smoke 前不得宣称完整兼容;现场异常使用精确环境值 `0` 回滚。 ## Last Updated diff --git a/.project-docs/20-architecture/system-overview.md b/.project-docs/20-architecture/system-overview.md index 0186da6..73aad82 100644 --- a/.project-docs/20-architecture/system-overview.md +++ b/.project-docs/20-architecture/system-overview.md @@ -24,7 +24,7 @@ Makelore 是 Electron 桌面客户端。Renderer 负责项目操作与状态展 | Robot Workspace | Account-scoped agent configuration, device activation/binding, assignment, and credential-recovery UI | Renderer receives only safe Works Square projections. Configuration choices come from the USER-scoped safe catalog; unavailable current values remain editable without exposing provider credentials or configuration internals. | | AI Hardware Main Route | Fixed `/api/works/ai-hardware` Host API to Works Square proxy | Main owns Bearer auth, stable operation IDs, bounded retry, ETag/If-Match, request/response limits, error redaction, and the fixed no-store configuration-catalog proxy. Versioned responses accept only canonical strong or weak numeric ETags that equal the DTO revision; mutations always emit strong `If-Match`. It never forwards Renderer authorization headers. | -| Guided Hotspot Binding V1 | Implemented, default-off Renderer journey over the current firmware Hotspot portal and six-digit Binding | System Wi-Fi selection and portal credential entry remain user/firmware-owned. Main exposes only a boolean capability and a fixed system-browser open action; no BLE, automatic claim, or firmware change is part of V1. | +| Guided Hotspot Binding V1 | Implemented, default-on Renderer journey over the current firmware Hotspot portal and six-digit Binding | Exact environment value `0` disables the journey. System Wi-Fi selection and portal credential entry remain user/firmware-owned; no BLE, automatic claim, or firmware change is part of V1. | ## Important Boundaries @@ -33,7 +33,7 @@ Makelore 是 Electron 桌面客户端。Renderer 负责项目操作与状态展 - Robot model, language, and voice choices are dynamically projected from the Xiaozhi USER catalog through Works Square and Electron Main; the catalog is bounded, account-scoped, and `private, no-store` at each public hop. - One local account maps to one server-side Xiaozhi account binding. Agents and devices are resources beneath that account binding, not separate Xiaozhi users. - Robot/Canvas/module-selection routes must not initialize AI Programming projects or providers. -- Guided Hotspot Binding is implemented behind a Main-owned capability that remains false by default; disabled/public builds keep the current direct six-digit Binding UI. The guided state is process-local, opener failures expose only the same fixed address for manual copy, and Binding conflicts refresh the safe account overview. +- Guided Hotspot Binding is implemented behind a Main-owned capability that is true by default; exact `NIANCODE_AI_HARDWARE_GUIDED_HOTSPOT_BINDING=0` disables it, while capability-read failure falls back to direct six-digit Binding. The guided state is process-local, opener failures expose only the same fixed address for manual copy, and Binding conflicts refresh the safe account overview. - The fixed portal action may open only `http://192.168.4.1/` in the system browser and must not acquire cloud credentials or call Works Square. Renderer never supplies a portal URL and never handles Wi-Fi credentials. - A successful Binding means account ownership was established; it is not evidence that the Robot is currently online or protocol-ready. diff --git a/.project-docs/30-worklog/current-state.md b/.project-docs/30-worklog/current-state.md index 00c283d..c4ca966 100644 --- a/.project-docs/30-worklog/current-state.md +++ b/.project-docs/30-worklog/current-state.md @@ -4,7 +4,8 @@ This file is the integrated default-branch snapshot. Feature tasks record progre ## Integrated Through -- `b7a1590` / `14afe4a`: implemented and accepted firmware-zero-change Guided Hotspot Binding V1 with a Main-owned default-off capability, fixed portal action, in-memory Renderer journey, and existing six-digit Binding contract. +- `b78fc07`: Guided Hotspot Binding is enabled by default in Electron Main, with exact environment value `0` as rollback and direct six-digit fallback on capability-read failure; firmware and Host/cloud contracts are unchanged. +- `b7a1590` / `14afe4a`: initial firmware-zero-change Guided Hotspot Binding V1 implementation and decision used a Main-owned default-off capability, fixed portal action, in-memory Renderer journey, and existing six-digit Binding contract; `b78fc07` above supersedes only that default. - `ea75b06`: Robot configuration reads accept canonical weak numeric response ETags introduced by public response compression only when the numeric revision exactly matches the strictly projected DTO; configuration and assignment writes continue to emit strong `If-Match`. - `fe55dee`: Robot configuration editing uses the safe Xiaozhi/Works catalog for model, language, and voice selections, with bounded sliders for TTS numeric controls and no-store catalog responses. - `fd9b5b46a913c515e94e4e26f185d43866c2581f` / `7a811590c4943b7b1b7ea5f3b4d3ce3ce05622a5`: Codex-style persistent AI Programming context-compaction timeline, run-lifecycle separation, polling-idle completion, and cold-hydration hardening. @@ -32,9 +33,12 @@ AI 编程首次发送在新建 OpenCode session 已知为空时不再等待冗 Updater 仍由 Electron Main 选择目标 feed、记录原始诊断并保持失败语义。正式稳定源缺少对应平台 manifest 时,设置页只显示一条简洁中文提示并允许重试,不把缺包误报为已是最新版,也不向普通界面暴露堆栈、URL、路径或错误码;签名产物发布和真实升级安装仍属于外部 Release Gate。 +Robot 绑定设备默认先显示“引导配网 / 已有激活码”路径选择。引导路径只编排现有固件 Hotspot、固定 `http://192.168.4.1/` 系统浏览器页面、恢复互联网和现有六位 Binding;精确环境值 `0` 可回滚为直接六位码。Makelore 不扫描附近设备、不接收 Wi-Fi 密码、不修改固件,也不把 Binding 成功等同于在线。指定固件/发行契约、原生 opener 和真机整链仍未完成验证。 + ## Recently Completed -- 2026-08-16: Implemented ADR-002's Robot onboarding V1 in Makelore without changing firmware. Main owns a default-off capability and the fixed portal opener; Renderer guides manual hotspot/Wi-Fi handoff and reuses six-digit Binding, clears secrets/retry identity safely, and does not equate Binding with online readiness. Pilot enablement still requires the documented firmware, issuer/validator, gate-on Electron, and physical-device evidence. +- 2026-08-16: Enabled the existing Guided Hotspot Binding journey by default after explicit product confirmation. Exact `NIANCODE_AI_HARDWARE_GUIDED_HOTSPOT_BINDING=0` remains the operational rollback; fixed portal ownership, direct-code fallback, security warnings, firmware-zero-change, and Binding-without-online semantics are preserved. +- 2026-08-16: Initially implemented ADR-002's Robot onboarding V1 without changing firmware, behind a default-off Main capability and fixed portal opener. The later `b78fc07` decision above changes only the default; the same firmware/issuer/native-opener/physical evidence remains outstanding. - 2026-08-15: Corrected the deployed Robot configuration-read contract after the compressed public Works response was observed with `ETag: W/\"0\"` and matching numeric `config_revision: 0`. Electron Main now accepts only canonical strong or weak numeric response tags, still requires exact DTO revision equality, and always sends strong `If-Match` for mutations. No production client rollout is claimed. - 2026-08-16: Integrated selection-oriented Robot configuration editing. Enabled model and caller-safe voice metadata now flows from Xiaozhi through Works Square and Electron Main without exposing provider secrets; unavailable current values and `clear_fields` semantics remain intact. Production deployment of the matching service endpoints is still required. - 2026-08-15:AI 编程上下文压缩改为 Codex 风格的会话内时间线事件;手动与自动压缩原位展示并持久保留,历史回放去重且状态只允许从 running 单调进入 completed,压缩完成不再冒充整个 run idle。 @@ -57,7 +61,7 @@ Updater 仍由 Electron Main 选择目标 feed、记录原始诊断并保持失 ## Next Recommended Steps -1. 核对指定固件镜像与六位码发行/消费契约,补齐 capability-on 原生 opener Electron E2E 和真机 smoke 后,再决定是否只为内部试点开启 Guided Hotspot Binding。 +1. 对 default-on Guided Hotspot Binding 核对指定固件镜像与六位码发行/消费契约,补齐原生 opener Electron E2E 和真机 smoke;发布支持保留精确 `=0` 回滚,不把缺失证据表述为已验收。 2. 成组核对客户端 source+built+contract 上传 → 服务端逐字节校验 → OSS immutable Release → CDN/Edge 的发布契约与客户端 `play_url` 消费契约。 3. 配置真实生产环境,分别执行“小游戏/小程序创建 → 客户端本地构建与同字节预检 → 提交 → 服务端合同/摘要校验与不可变 Release 固化 → 运营批准 → App 播放”。 @@ -67,6 +71,7 @@ Updater 仍由 Electron Main 选择目标 feed、记录原始诊断并保持失 ## Risky Areas +- Guided Hotspot Binding 默认开启,但当前 Hotspot/portal 仍是开放 SoftAP + 明文 HTTP,且精确出货镜像、激活码发行契约、原生 opener 与真机整链尚未验证。界面必须保留环境警告,异常发布可用精确环境值 `0` 回滚;不得宣称自动发现、自动认领或在线证明。 - 一键提交已成功但本机 submission binding 落盘失败时必须保持提交成功、显示固定 `binding_warning` 并继续轮询,避免用户误判上传失败。 - 公共 `play_url` 必须满足 Works Square 同源 HTTPS、无 userinfo/loopback、精确 `/apps/{app_id}/` 路径、无 query/fragment、版本非空且上游标记可播放。 - `/api/works/projects/publish-source` 必须在读取凭据和项目文件前校验 Renderer capability;Host token/base 不能替代该 UI 边界。 diff --git a/.project-docs/30-worklog/tasks/20260813-sync-push-main-9c2f71.md b/.project-docs/30-worklog/tasks/20260813-sync-push-main-9c2f71.md index 9ed6616..372a338 100644 --- a/.project-docs/30-worklog/tasks/20260813-sync-push-main-9c2f71.md +++ b/.project-docs/30-worklog/tasks/20260813-sync-push-main-9c2f71.md @@ -8,10 +8,11 @@ - Worktree: D:\Datas\OthersProjects\makelore - Base commit: 4fbd1d3b24493532ce479729da2c5e5e1709c8a9 - Owner: codex -- Status: Blocked +- Status: In Review ## Scope +- On 2026-08-16, resume the existing Integration owner to merge reviewed default-on Guided Hotspot Binding source `b78fc07` into local `main`, accept the user's explicit reversal of the prior default-off policy, preserve exact environment value `0` as rollback, and keep firmware edits and remote push outside this resumption. - On 2026-08-16, resume the existing Integration owner to merge reviewed Robot Guided Hotspot Binding implementation commit `b7a1590` into local `main`, reconcile canonical memory from planned to implemented/default-off, and keep firmware edits, capability enablement, and remote push outside this resumption. - On 2026-08-16, resume the existing Integration owner to accept reviewed Robot Guided Hotspot Binding V1 design commit `14afe4a`, promote only its confirmed minimal-firmware decision into canonical memory, and keep product implementation, firmware changes, and remote push outside this integration step. - On 2026-08-16, resume the existing Integration owner to merge reviewed Robot configuration-catalog/editor source `fe55dee` into local `main`, promote the accepted dynamic catalog boundary, and keep remote push outside this resumption. @@ -46,6 +47,7 @@ - The 2026-08-15 context-compaction request likewise authorizes only a local `main` merge. It does not authorize a remote push or changing the existing OpenCode/model compaction threshold. - The 2026-08-16 Robot onboarding confirmation accepts the current-firmware Hotspot + six-digit Binding V1. It does not authorize firmware edits, claim automatic nearby discovery, enable the pilot capability by default, or revive the unready Security 2/automatic-claim proposal as a V1 contract. - The reviewed implementation may move canonical truth from planned to present, but release guidance must retain the exact shipped-firmware, issuer/validator, gate-on Electron, and physical-device smoke prerequisites. The source task record remains read-only and must stay on its feature history. +- The user's latest instruction explicitly authorizes default-on and supersedes only the earlier default-off/capability-not-enabled constraint. It does not authorize firmware changes, automatic discovery/claim claims, arbitrary portal URLs, Wi-Fi credential handling, or remote push. Missing installed-Electron/physical-device evidence remains an explicit residual release risk, not completed evidence. ## Project Context Loaded @@ -93,6 +95,14 @@ Relevant understanding: Gate result: - Passed. +### 2026-08-16 Guided Hotspot Default-On Integration Resume + +- Reused the existing Integration owner because it still exclusively owns clean local `main` at `971865c256c2ddfe1f8ab4f8f4731a1f608599c0`; `task_context.py touch` refreshed the reservation and the registry reports this exact `main` worktree/branch owner. +- Verified feature task `20260816-enable-guided-hotspot-4e91c2` is `ready_for_integration`, reviewed source commit `b78fc07` is based exactly on current `main`, and independent final Sol review returned PASS with no P0-P3 findings. +- Read the source outcome, verification, follow-ups, and promotion candidate against ADR-002 and current canonical Robot memory. The source changes only the Main default and focused route tests; firmware, Renderer, Host wire shape, fixed portal, cloud Binding, and credential boundaries remain unchanged. +- The current user decision resolves the sole semantic conflict: Guided Hotspot Binding is now default-on, with exact environment value `0` as rollback. The exact shipped firmware/issuer and real native-opener/physical-device evidence remain unverified and must not be claimed as passed. +- Gate result: Passed for local merge and canonical policy reconciliation. Remote push remains outside this resumption. + ### 2026-08-14 AI Canvas Integration Resume - Reused the same Integration owner because it still exclusively owns `main` and the repository integration lock; `task_context.py touch` refreshed the existing reservation. @@ -137,6 +147,13 @@ Gate result: ## Plan +### 2026-08-16 Guided Hotspot Default-On Integration Plan + +1. Merge reviewed source `b78fc07` into local `main` with a normal no-ff merge, preserve feature history, and exclude the source-owned task record from the integrated tree. +2. Reconcile ADR-002, decision index, success criteria, Robot architecture/domain/current-state memory, glossary, and support wording from default-off/planned enablement to default-on with exact `=0` rollback; preserve all non-default security and evidence caveats. +3. Run Robot focused tests, typecheck, scoped lint, production build, bounded-concurrency full tests, task-aware document drift, whitespace/topology checks, and an independent final Sol integration review. +4. Commit the verified local merge. Do not modify firmware or push remotely. + ### 2026-08-16 Robot Guided Hotspot Binding Implementation Integration Plan 1. Merge reviewed source commit `b7a1590` into local `main` with a normal no-ff merge, preserve the source parent, and exclude the source-owned task record from the final main tree. @@ -188,6 +205,10 @@ Gate result: ## Outcome +- On 2026-08-16, started a normal `--no-ff --no-commit` merge of reviewed default-on source `b78fc07`; Git reported no textual conflicts. The feature task record remains reachable on the source commit/branch and is excluded from the integrated `main` tree. +- Integrated only the Main default change and focused route regressions: unset environment reports guided capability true; exact `NIANCODE_AI_HARDWARE_GUIDED_HOTSPOT_BINDING=0` and dependency-injected false report disabled. Renderer, Host wire shape, fixed portal, cloud Binding contract, credentials, and firmware are unchanged. +- Reconciled ADR-002, decision index, success criteria, current state, Robot architecture/domain/glossary, README, and commitments to default-on with exact `=0` rollback. The documents explicitly preserve unverified shipped-firmware, activation-issuer, native-opener, and physical-device evidence instead of claiming those checks passed. +- Prepared merge topology uses first parent `971865c256c2ddfe1f8ab4f8f4731a1f608599c0` and reviewed source second parent `b78fc07dba0d720e88c55667f8a36ebc50134f87`. Final independent integration re-review returned PASS; the merge is ready to commit. - On 2026-08-16, started a normal `--no-ff --no-commit` merge of reviewed Guided Hotspot Binding source `b7a1590`; Git reported no textual conflicts. The source task record remains reachable on the source commit/feature branch and is excluded from the local `main` result. - Integrated the default-off Main capability and strict local Host actions, fixed system-browser portal ownership, in-memory Renderer guided/direct Binding journey, safe conflict/retry/secret cleanup, and Bound-without-online semantics. No firmware file, BLE/Wi-Fi discovery, cloud claim route, or capability enablement was added. - Reconciled canonical current state, system overview, and decision index from planned/not implemented to implemented/default off. Release gates remain exact firmware and issuer/validator verification, gate-on native-opener Electron coverage, and physical-device smoke. @@ -283,6 +304,15 @@ Gate result: ## Verification +- 2026-08-16 default-on feature final Sol review — `PASS`, no P0-P3 findings; independently confirmed default-on, exact environment opt-out, dependency-injection opt-out, fixed portal ownership, local-before-Works-token behavior, error redaction, unchanged Binding contract, and zero firmware changes. +- Merged-main Robot selection — 3 files / 94 tests passed, including fresh-module exact `=0` rollback coverage. +- Merged-main `pnpm run typecheck` and scoped ESLint on the two source files — passed. +- Merged-main `pnpm run build:vite` — Renderer, Electron Main, and Preload passed; only existing mixed-import/chunk-size warnings remain. +- Merged-main bounded full suite `pnpm vitest run --maxWorkers=4` — 156 files / 1758 tests passed. The source task separately records why bounded concurrency was used after the unrelated OpenCode port-release test hit its 10-second limit under default all-suite parallelism but passed 40/40 in isolation. +- Merged-main Electron module-navigation smoke — 1/1 passed through the existing packaged Main/Renderer fixture and entered `/ai-hardware`. The fixture still cannot authenticate a Robot overview or observe the native external opener, so that deeper E2E remains a tracked release-validation gap. +- `check_project_docs.py` — passed; all required planning/canonical files exist. +- Integration task-aware document drift, task registry doctor, staged/unstaged whitespace checks, unmerged-entry check, and exact merge-base/parent checks — passed. +- Independent final default-on integration review initially returned `FAIL`: default-on was incorrectly expanded into explicit SoftAP/plain-HTTP risk acceptance, one obsolete follow-up still said “before pilot enablement,” and the default-on test depended on ambient environment state. Canonical wording now records only the explicit default-on/current-firmware decision while retaining the security risk; the follow-up targets the next default-on release/compatibility claim; the test deletes the environment variable and reloads the route module before asserting true. Final re-review returned `PASS` with no P0-P3 findings and independently passed route tests both normally and with ambient `NIANCODE_AI_HARDWARE_GUIDED_HOTSPOT_BINDING=0`. - 2026-08-16 Robot device-selection merged-tree regression selection — 3 files / 92 tests passed. - Robot device-selection merged-tree `pnpm run typecheck` and scoped ESLint — passed. - Staged/unstaged whitespace checks and unmerged-entry checks — passed before independent review. @@ -365,8 +395,9 @@ Gate result: ## Follow-ups -- Keep `NIANCODE_AI_HARDWARE_GUIDED_HOTSPOT_BINDING` unset/default false in production; do not touch `D:\Datas\HardwareProjects\xiaozhi-esp32-firmware` for this V1. -- Before any pilot enablement, identify the exact shipped Robot component/firmware image, verify that the deployed issuer produces six ASCII digits with compatible freshness/consumption semantics, and pass a real device smoke through the Host API/Electron flow. +- Before claiming complete compatibility for the default-on Robot journey, verify the exact shipped firmware/fixed portal, six-digit issuer/validator freshness and consumption semantics, real Host API/native opener behavior, and a physical-device provisioning + Binding smoke. Keep exact `NIANCODE_AI_HARDWARE_GUIDED_HOTSPOT_BINDING=0` in support rollback instructions. +- The earlier default-false production instruction is superseded by the user's explicit default-on decision. Keep exact value `0` only as rollback, and do not touch `D:\Datas\HardwareProjects\xiaozhi-esp32-firmware` for this V1. +- Before the next default-on release or any complete-compatibility claim, identify the exact shipped Robot component/firmware image, verify that the deployed issuer produces six ASCII digits with compatible freshness/consumption semantics, and pass a real device smoke through the Host API/Electron flow. - Deploy matching Xiaozhi and Works Square catalog endpoints before releasing this client; otherwise the editor preserves current values but cannot populate dynamic choices. - The Robot integration is complete on local `main`. Production still requires matching Works Square/Xiaozhi deployment, feature configuration, credentials, and a real one-time activation-code smoke. - Stable mutation operation IDs are retained across ambiguous retries in the running app but are not persisted across an application restart. @@ -384,6 +415,7 @@ Gate result: ## Promotion Candidates -- The implementation truth from `b7a1590` was promoted from planned to implemented/default-off in canonical current state, system overview, and ADR index. Its release-enablement procedure/support matrix candidate remains deferred because the mandatory gate-on Electron and physical/firmware evidence does not yet exist. +- The default-on candidate from `b78fc07` was promoted into ADR-002, current state, decision/success criteria, Robot architecture/domain/glossary, README, and a concrete release-validation commitment. No unresolved canonical candidate remains; the missing native/physical evidence is tracked as a pending commitment rather than overclaimed. +- The implementation truth from `b7a1590` was originally promoted as implemented/default-off. `b78fc07` now supersedes only that default; its former enablement evidence requirements remain tracked as default-on release validation and rollback commitments. - The Guided Hotspot Binding V1 candidate from `14afe4a` was promoted into ADR-002, success criteria, system/module/data-flow architecture, business rules, glossary, and current state. No unresolved candidate remains for this design acceptance. - The context-compaction source candidate was promoted into current state, module map, data flow, evidence, and upgrade commitments. No unresolved candidate remains for this local merge. diff --git a/.project-docs/40-domain/business-rules.md b/.project-docs/40-domain/business-rules.md index 739f87e..6d49e0e 100644 --- a/.project-docs/40-domain/business-rules.md +++ b/.project-docs/40-domain/business-rules.md @@ -30,10 +30,10 @@ - 视频首帧与图生图参考图复用单图选择器,但用途由当前 Brief medium 决定:只有明确 `image` 使用图生图语义,`video`、null 或缺失值保持视频首帧兼容语义。选择作品或上传成功后应关闭弹窗。 - 当前 quick reply 文案匹配只是兼容契约;新增更多素材输入用途前,应改为结构化 action/purpose,不得继续依赖本地化展示字符串推断行为。 - Robot V1 把现有固件 Hotspot 配网指导放进现有 Binding 体验,但不宣称自动发现附近设备、自动下发 Wi-Fi、自动认领设备或自动确认在线;这些能力需要另行接受的协议与 ADR。 -- Guided Hotspot Binding capability 由 Electron Main 持有且默认关闭。关闭时保留现有六位码 Binding;Renderer 可以读取 capability,但不能覆盖它。 +- Guided Hotspot Binding capability 由 Electron Main 持有且默认开启。精确 `NIANCODE_AI_HARDWARE_GUIDED_HOTSPOT_BINDING=0` 关闭引导;关闭或 capability 读取失败时保留现有六位码 Binding,Renderer 可以读取但不能覆盖它。 - Robot portal 必须由 Main 以系统浏览器打开固定 `http://192.168.4.1/`。Renderer 不得提交任意 URL;该本地动作不得读取 Works 凭据或访问云端。 - Wi-Fi SSID/密码只在现有固件 portal 内输入。Makelore 不收集、不代理、不日志记录、不持久化 Wi-Fi 凭据。 -- 当前开放 SoftAP 与明文 HTTP portal 只允许 default-off 内部试点。未核对精确固件镜像、六位码发行/消费契约并完成真机 smoke 前,不得面向公共用户开启。 +- 产品已明确选择在保留当前固件行为时 default-on;开放 SoftAP 与明文 HTTP portal 仍是已知残余风险,不能据此推断安全风险已被单独验收。界面必须保留不在不可信公共环境操作的警告。未核对精确固件镜像、六位码发行/消费契约、原生 opener E2E 与真机 smoke 前,不得宣称完整兼容或端到端验收;现场异常必须可用精确环境值 `0` 回滚。 - Binding 成功仅表示设备与账号/Agent 的云端关系建立,不表示设备在线或业务协议 ready。 - 同一进程内无法确认结果的 Binding 重试必须复用原 operation ID。无效、过期或已消费 activation code 必须清除 code 与 operation ID;下一个新码使用新 operation ID。Main 必须把 `ai_hardware_activation_code_invalid` 投影为 non-retryable,不信任上游相反标记。 - 应用重启后,当前 overview DTO 不能证明旧码对应的 Binding 结果;客户端不得重放旧码或旧 operation ID,必须要求新码,无法取得时停止流程。 @@ -47,7 +47,7 @@ - 图生图随客户端发布前,需确认相匹配的服务端 `image_to_image` Brief/Quote/Task 冻结、私有源图复核与 Bailian edit 执行链已部署,并使用真实 Workspace Asset 完成生产 smoke。 - Updater 生产恢复仍需对齐权威版本、发布正式签名/公证的平台产物,并从旧安装版本执行发现、下载、重启和安装 smoke;源码提示修复本身不构成发布链恢复。 - AI Canvas 双向 Gateway 与 Quote 任务恢复仍需真实账号执行一次生产确认 smoke,核对 WebSocket 不产生 `/runs/{run_id}` 轮询、任务按 Quote 出现在 Workspace 列表,并区分真正的 `agent_runtime_unavailable` worker 故障。 -- Robot Guided Hotspot Binding 试点需确认指定硬件/固件确实提供被审计的 Hotspot portal、部署端签发严格六位 ASCII 数字码且与 Works validator 的时效/消费语义一致,并完成真实设备端到端 smoke。 +- Robot Guided Hotspot Binding default-on 发布仍需确认指定硬件/固件确实提供被审计的 Hotspot portal、部署端签发严格六位 ASCII 数字码且与 Works validator 的时效/消费语义一致,并完成真实设备端到端 smoke。 ## Last Reviewed diff --git a/.project-docs/40-domain/glossary.md b/.project-docs/40-domain/glossary.md index 23fefdc..c4c3b7e 100644 --- a/.project-docs/40-domain/glossary.md +++ b/.project-docs/40-domain/glossary.md @@ -19,5 +19,5 @@ | Robot Provisioning | 让 Robot 获得目标 Wi-Fi 凭据并尝试联网的阶段 | 与云端 Activation、账号 Binding、协议在线是不同阶段;V1 由现有固件 Hotspot portal 完成 | | Robot Activation | Robot 联网后向既有服务获取六位激活码的阶段 | 激活码由设备展示/播报给用户;Makelore 不生成该码 | | Robot Binding | 用户把六位激活码和 Agent 提交到 Works Square,建立账号侧设备关系 | `bound` 不等于设备当前 online 或 protocol-ready | -| Guided Hotspot Binding | Makelore 内计划提供的 default-off 引导流程 | 串联系统热点连接、固定 portal、恢复互联网和现有 Binding;不包含自动发现、Wi-Fi 代填或固件修改 | -| `guidedHotspotBinding` | Electron Main 所有的本地 capability | Renderer 只读;公共默认 false,试点满足 release gates 后才可开启 | +| Guided Hotspot Binding | Makelore 已实现的 default-on 引导流程 | 串联系统热点连接、固定 portal、恢复互联网和现有 Binding;不包含自动发现、Wi-Fi 代填或固件修改 | +| `guidedHotspotBinding` | Electron Main 所有的本地 capability | Renderer 只读;默认 true,精确环境值 `0` 回滚为 false,读取失败时 Renderer 回退直接六位码 | diff --git a/.project-docs/80-commitments/commitments.md b/.project-docs/80-commitments/commitments.md index a318214..20178bc 100644 --- a/.project-docs/80-commitments/commitments.md +++ b/.project-docs/80-commitments/commitments.md @@ -4,6 +4,7 @@ Track future-facing memory: promised follow-ups, unfinished loops, timed checks, | Date | Commitment | Trigger / Due | Owner | Status | Next Action | |---|---|---|---|---|---| +| 2026-08-16 | 验收 default-on Robot Guided Hotspot Binding 的真实设备链路 | 下一份包含 default-on 行为的安装包发布前 | 客户端/硬件/服务端集成 | Pending | 核对精确出货固件与固定 portal、六位码发行/消费语义,执行真实 Host API/native opener Electron smoke 和真机端到端配网+Binding;保留 `NIANCODE_AI_HARDWARE_GUIDED_HOTSPOT_BINDING=0` 回滚,并记录支持矩阵 | | 2026-08-10 | 完成客户端提交到 App `play_url` 播放的真实生产整链验收 | source+built+contract 服务端协议、OSS immutable Release、CDN/Edge 与 App 消费链成组集成后 | 客户端/服务端集成 | Pending | 使用真实账号执行小游戏和小程序创建、客户端本地构建与同字节预检、双归档提交、服务端逐字节校验/不可变 Release 固化、运营批准、CDN 发布、App 播放与监控核对;如需不可绕过 runtime gate,另行设计可信 verifier | | 2026-08-12 | Windows 发布流水线保留固定 npm 运行时产物门禁 | 每次生成正式 Windows 安装器时 | 客户端发布 | Pending | 运行 `pnpm verify:publish-runtime` 和 `pnpm verify:artifact:win`;当前 223,547,912-byte / SHA-256 `08A0BB7BC66EE4AD8B120E8955B149951CD86AF53CC966C7973AD4D77A5815C1` 安装器仅为本地证据,尚未发布 | | 2026-08-10 | 删除客户端 `runtime_url` 兼容回退 | 一个客户端兼容版本结束,且服务端与存量数据稳定提供 `play_url` | 客户端 | Pending | 删除类型字段、读取分支和对应回归测试 | diff --git a/README.md b/README.md index 1e98569..cd24cd4 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Makelore 是一个面向软件、视觉创作与智能机器人的 AI 桌面工 - `Makelore Code|AI 编程`:管理本地项目、项目 Agent、会话、文件上下文、代码变更和运行时。 - `Makelore Canvas|AI 绘画`:以设计项目(Workspace)组织 Agent 对话、方向确认、文生图、单参考图生图、视频生成任务和私有结果;参考图可从当前项目作品选择或从本地上传。 -- `Makelore Robot|AI 机器`:管理机器人智能体、设备激活绑定、智能体配置与设备分配,让 AI 能力走进真实世界。 +- `Makelore Robot|AI 机器`:管理机器人智能体、设备激活绑定、智能体配置与设备分配;绑定设备时默认先选择“引导配网”或“已有激活码”,引导路径复用机器人现有热点配网页面,不修改固件,也不由 Makelore 接收 Wi-Fi 密码。 - `Makelore Learning|AI 学习`:数学与知识宇宙主题的学习入口,当前暂未接入;入口保持可见,点击暂不进入工作区。 应用启动默认进入 AI 模块入口选择页。入口页可在未登录状态浏览;未登录用户点击已开通模块时进入浏览器授权,授权成功后回到入口选择页,已登录用户可直接进入对应工作区。 diff --git a/electron/api/routes/ai-hardware.ts b/electron/api/routes/ai-hardware.ts index c08f94c..ae86963 100644 --- a/electron/api/routes/ai-hardware.ts +++ b/electron/api/routes/ai-hardware.ts @@ -18,7 +18,7 @@ const CATALOG_ID = /^[A-Za-z0-9][A-Za-z0-9_.:-]{0,254}$/; const OPERATION_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; const MAX_RETRY_AFTER_SECONDS = 2; const PROVISIONING_PORTAL_URL = 'http://192.168.4.1/'; -const GUIDED_HOTSPOT_BINDING_ENABLED = process.env.NIANCODE_AI_HARDWARE_GUIDED_HOTSPOT_BINDING === '1'; +const GUIDED_HOTSPOT_BINDING_ENABLED = process.env.NIANCODE_AI_HARDWARE_GUIDED_HOTSPOT_BINDING !== '0'; type TokenGetter = typeof getValidWorksSquareAccessToken; diff --git a/tests/unit/ai-hardware-routes.test.ts b/tests/unit/ai-hardware-routes.test.ts index 33d0973..34b2941 100644 --- a/tests/unit/ai-hardware-routes.test.ts +++ b/tests/unit/ai-hardware-routes.test.ts @@ -65,8 +65,41 @@ async function invoke(handler: ReturnType, } describe('AI hardware Host API route', () => { - it('returns the default-off provisioning capability before credentials or upstream access', async () => { - const { handler, fetchImpl, getAccessToken } = setup(); + it('returns the default-on provisioning capability before credentials or upstream access', async () => { + const environmentName = 'NIANCODE_AI_HARDWARE_GUIDED_HOTSPOT_BINDING'; + const previousValue = process.env[environmentName]; + delete process.env[environmentName]; + vi.resetModules(); + try { + const { createAiHardwareRouteHandler: createHandlerWithoutEnv } = await import('@electron/api/routes/ai-hardware'); + const fetchImpl = vi.fn().mockResolvedValue(jsonResponse(overview)); + const getAccessToken = vi.fn().mockResolvedValue('secret-token'); + const handler = createHandlerWithoutEnv({ + fetchImpl, + getAccessToken, + apiBaseUrl: 'https://square.example', + }); + const result = await invoke( + handler, + 'GET', + '/api/works/ai-hardware/provisioning-capabilities', + ); + + expect(result.payload).toEqual({ + success: true, + data: { guided_hotspot_binding: true }, + }); + expect(getAccessToken).not.toHaveBeenCalled(); + expect(fetchImpl).not.toHaveBeenCalled(); + } finally { + if (previousValue === undefined) delete process.env[environmentName]; + else process.env[environmentName] = previousValue; + vi.resetModules(); + } + }); + + it('honors an explicitly injected disabled provisioning capability', async () => { + const { handler, fetchImpl, getAccessToken } = setup(undefined, { guidedHotspotBinding: false }); const result = await invoke( handler, 'GET', @@ -81,6 +114,36 @@ describe('AI hardware Host API route', () => { expect(fetchImpl).not.toHaveBeenCalled(); }); + it('honors the exact environment opt-out when Main loads the route', async () => { + vi.stubEnv('NIANCODE_AI_HARDWARE_GUIDED_HOTSPOT_BINDING', '0'); + vi.resetModules(); + try { + const { createAiHardwareRouteHandler: createHandlerWithEnv } = await import('@electron/api/routes/ai-hardware'); + const fetchImpl = vi.fn().mockResolvedValue(jsonResponse(overview)); + const getAccessToken = vi.fn().mockResolvedValue('secret-token'); + const handler = createHandlerWithEnv({ + fetchImpl, + getAccessToken, + apiBaseUrl: 'https://square.example', + }); + const result = await invoke( + handler, + 'GET', + '/api/works/ai-hardware/provisioning-capabilities', + ); + + expect(result.payload).toEqual({ + success: true, + data: { guided_hotspot_binding: false }, + }); + expect(getAccessToken).not.toHaveBeenCalled(); + expect(fetchImpl).not.toHaveBeenCalled(); + } finally { + vi.unstubAllEnvs(); + vi.resetModules(); + } + }); + it('rejects queried, body-bearing, and wrong-method capability requests locally', async () => { const { handler, fetchImpl, getAccessToken } = setup(); const queried = await invoke( @@ -129,7 +192,7 @@ describe('AI hardware Host API route', () => { it('rejects disabled, expanded, or queried portal opens before invoking native or cloud dependencies', async () => { const openExternal = vi.fn().mockResolvedValue(undefined); - const disabled = setup(undefined, { openExternal }); + const disabled = setup(undefined, { guidedHotspotBinding: false, openExternal }); const disabledResult = await invoke( disabled.handler, 'POST',