Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions .github/workflows/desktop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -206,15 +206,28 @@ jobs:
- name: Build unsigned macOS app and DMG
run: |
mkdir -p .artifacts
set -o pipefail
pnpm tauri build --ci --verbose -- --locked 2>&1 | tee .artifacts/macos-build.log
set -euo pipefail
: > .artifacts/macos-build.log
for attempt in 1 2 3; do
attempt_log=".artifacts/macos-build-attempt-${attempt}.log"
if pnpm tauri build --ci --verbose -- --locked 2>&1 | tee "$attempt_log" | tee -a .artifacts/macos-build.log; then
break
else
build_status=$?
fi
if [ "$attempt" -eq 3 ] || ! grep -Fq 'hdiutil: create failed - Resource busy' "$attempt_log"; then
exit "$build_status"
fi
echo "::warning::DMG creation found a busy resource; retrying after $((attempt * 5)) seconds (attempt $attempt of 3)."
sleep "$((attempt * 5))"
done
- name: Upload failed macOS build diagnostics
if: failure()
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7
with:
name: desktop-macos-${{ matrix.runner }}-build-diagnostics
path: |
desktop/.artifacts/macos-build.log
desktop/.artifacts/macos-build*.log
desktop/src-tauri/target/release/bundle/dmg/bundle_dmg.sh
if-no-files-found: warn
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7
Expand Down
8 changes: 7 additions & 1 deletion desktop/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,13 @@ Connection checks call the public `get_server_info` operation through the existi

Qualification still requires an explicitly selected native record matching the bundled contract. A discovery 404 permits that record's independently qualified legacy operations; 401, 403, 503, malformed responses and incompatible products/required majors do not select a legacy fallback. Missing feature groups, unsupported feature versions and absent operation IDs restrict the affected operations. Exact Artifact reads require their own qualification; the `memory.explicit` group does not establish that support. Discovery metadata never grants authorization or attests a remote binary.

Connection checks and anonymous identity revalidation still perform readiness probes. The observed `server_id` is recorded with the check; deployment-aware context fencing and explicit readiness separation are not established by this discovery result.
Connection checks bind the observed `server_id` to the Principal and recheck the deployment after collecting connection facts. Scope listing/default lookup/selection and Memory search/exact reads revalidate deployment and Principal before dispatch and before returning private results. A changed identity, failed discovery, or disappearance of discovery clears the active context, invalidating Scope selections, cursors and pending reads. Reconnect explicitly to establish a new context. Package version changes alone do not change deployment identity.

Submitted writes retain their original endpoint, `serverId` (null for qualified legacy Servers), Principal, Scope and generation. If the post-write identity check fails, a successful HTTP reply is treated as an unknown outcome: private bodies/references are withheld and the write is never automatically replayed. The original deployment is shown in write recovery feedback. The submitted input survives that disconnection as a read-only recovery draft in the current window, with explicit copy/discard actions; it is never inserted into a new target's form or persisted. Closing the window loses it. Confirmed navigation discards it, and a late response cannot restore an explicitly discarded draft. A late write cannot invalidate a newly activated context. Qualified legacy sessions require discovery to remain 404; discovery appearing requires explicit rechecking rather than silently acquiring a deployment identity. A failed connection recheck removes the previous cached report instead of leaving old verification visible.

Desktop retains each submitted input in application memory from the start of the native call, independently of the form. An automatic disconnection during a concurrent read therefore leaves a read-only copy even while the write is pending. Recovery distinguishes pending, unknown, rejected and attempts without a receipt. A retry does not discard earlier uncertain input, including when that retry fails before its mutation reaches the Server. Each attempt has its own local identity: only a successful receipt clears its input, rejected saves keep their text for explicit recovery, and explicit discard removes only the selected copy (or all copies when confirming navigation). Late receipts cannot restore discarded inputs or clear another attempt. Inputs are limited to 8192 UTF-8 bytes per attempt; copies remain for pending, uncertain or rejected attempts until success, explicit discard or window close. They contain user input and observed original-target metadata, not returned private Server bodies, and are never replayed or persisted.

These checks observe identity around requests; the public mutation contract has no conditional Server-identity precondition. They cannot atomically prevent an endpoint from changing between identity verification and dispatch, or detect a replacement that changes back between observations. Legacy Servers have no durable deployment fence. Discovery does not attest binary identity. Connection checks and anonymous identity revalidation still perform readiness probes; explicit readiness separation remains pending.

Connections displays discovery separately from the compatibility verdict, with guidance for denied or temporarily unavailable discovery and the reason for an incompatible product or major version. A failed discovery check keeps business operations unavailable; it does not remove the selected qualification record. After correcting the connection or service, explicitly check the connection again to reassess operation availability. Only a discovery 404 permits the qualified legacy policy described above.

Expand Down
8 changes: 7 additions & 1 deletion desktop/README.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,13 @@ pnpm --dir desktop desktop:dev

操作资格仍要求明确选择与当前契约匹配的原生验证记录。仅发现接口的 404 允许沿用该记录已独立验证的旧版操作;401、403、503、无效响应、不兼容产品或必要 major 版本不会触发旧版回退。缺失功能组、不支持的功能版本和缺失 operation ID 分别限制受影响的操作。Artifact 精确读取需要独立资格,不能从 `memory.explicit` 推断。发现元数据不授予权限,也不证明远端二进制身份。

当前连接检查和匿名身份复核仍会调用 readiness。检查结果记录观察到的 `server_id`;这不代表已经实现部署身份隔离或显式 readiness 分离。
连接检查将观察到的 `server_id` 与 Principal 绑定,并在收集连接事实后再次核对部署。Scope 列举、默认范围查找、范围选择,以及记忆搜索和精确读取,均在发送请求前和返回私有结果前复核部署及 Principal。身份变化、发现失败或发现接口消失时,会清除活动上下文,使 Scope 选择、游标和进行中的读取失效;须明确重新连接以建立新上下文。仅软件包版本变化不视为部署变化。

已提交写入保留原始端点、`serverId`(已验证旧版 Server 为 null)、Principal、Scope 和代次。写入后的身份复核失败时,即使 HTTP 返回成功也按结果未知处理:不返回私有正文或引用,不自动重放。写入恢复反馈显示原部署;断连后原始输入在当前窗口内作为只读恢复草稿保留,可明确复制或丢弃,不会填入新目标的表单,也不持久化;关闭窗口会丢失草稿。确认离开页面会丢弃草稿,晚到响应不能恢复已明确丢弃的输入。旧写入晚到不会使新激活的上下文失效。已验证旧版会话要求发现接口持续返回 404;发现接口出现后须明确重新检查,不能静默获得部署身份。连接复查失败会移除旧缓存报告,避免继续显示过期的验证结果。

应用从调用原生提交开始,在内存中独立保留每次输入,不依赖表单是否仍存在。因此,并发读取触发自动断连时,即使写入还在等待回执,仍有只读副本可恢复。恢复界面区分进行中、结果未知、保存被拒绝和未取得回执的尝试。重试不会丢弃先前的未知输入,包括重试的写请求尚未发出就失败的情况。每次尝试有独立的本地标识:成功回执只清理该次输入,保存被拒绝时仍保留正文供明确恢复,明确丢弃只移除所选副本,确认离开页面则丢弃所有副本;晚到回执不能恢复已丢弃输入或清除另一尝试。每次输入最多 8192 UTF-8 字节;尚未成功保存的尝试会保留副本,直到成功、明确丢弃或关闭窗口。副本仅含用户输入及观察到的原目标元数据,不含 Server 返回的私有正文,不重放,也不持久化。

这些检查观察请求前后的身份;公开写入契约没有以 Server 身份为条件的前置约束,因此不能原子性阻止验证与发送之间的端点替换,也不能发现两次观察之间发生后又恢复的替换。旧版 Server 没有持久部署隔离保障;发现信息也不证明二进制身份。连接检查和匿名身份复核仍会调用 readiness,显式 readiness 分离尚未完成。

连接页分别展示发现状态和兼容性结论,说明访问被拒绝、暂时不可用时的恢复方法,以及产品或 major 版本不兼容的原因。发现检查失败时业务操作保持不可用,但不会删除已选择的资格记录。修正连接或服务后,须明确重新检查连接,重新判定操作可用性。仅发现接口的 404 适用上述已验证旧版策略。

Expand Down
4 changes: 3 additions & 1 deletion desktop/SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ Native code creates credential IDs and binds them to an exact endpoint/trust con

Native reqwest uses Windows system trust through native-tls/Schannel, plus an optional connection-local PEM CA (maximum 64 KiB). Hostname/certificate validation remains enabled. Automatic proxy inheritance and redirects are disabled. HTTP requires loopback. Userinfo, query, fragment, control characters, backslashes and dot segments are rejected before URL normalization. UTF-8 and percent-encoded base paths are accepted only after rejecting encoded separators, dot segments, control characters and ambiguous double encoding. Public operation paths come from the generated OpenAPI manifest and preserve the base prefix.

Typed adapters separate liveness, readiness, identity and capabilities. Native context generations cancel old reads on connection, identity and Scope changes; a separate query generation cancels superseded Scope searches. Business operations require explicit qualified compatibility and actual Server authorization. The real-Server fixture exercises native memory adapters; product Memory commands remain unavailable until immutable write context and unknown-result handling are implemented. No automatic write retry or persistent queue exists.
Typed adapters separate liveness, readiness, identity and capabilities. Native context generations cancel old reads on connection, identity and Scope changes; a separate query generation cancels superseded Scope searches. Business operations require explicit qualified compatibility and actual Server authorization. Scope and Memory reads validate both deployment and Principal before dispatch and after the response. Observed identity loss clears the active context. Dispatched writes keep immutable endpoint/deployment/Principal/Scope metadata; failed post-write verification makes successful HTTP outcomes unknown without returning private content or automatically replaying. Late writes cannot clear a newer context. Qualified legacy Servers retain a null deployment identity and require discovery to remain 404. Identity observations do not supply an atomic Server-side conditional write fence or binary attestation. No automatic write retry or persistent queue exists.

The renderer retains submitted input in window-local memory independently of form lifetime. Automatic disconnection does not discard it, and a retry cannot remove an earlier unresolved attempt. Recovery copies contain only user input and original-target metadata, never returned private Server bodies. Each copy is at most 8192 UTF-8 bytes plus metadata; retention grows with explicit user submissions awaiting success, recovery or discard. Successful receipts remove their own copy; rejected saves retain input for explicit recovery or discard; users can explicitly discard individual copies or all copies through navigation confirmation. Discarded identities cannot be revived by late receipts. Window close loses all copies. There is no disk persistence, background replay or export through session diagnostics.

## Budgets and error projection

Expand Down
67 changes: 67 additions & 0 deletions desktop/VALIDATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,73 @@ cargo test --locked --manifest-path desktop/src-tauri/Cargo.toml

[Desktop CI](../.github/workflows/desktop.yml) also builds a Server wheel, runs real Server and CLI tests, checks Windows credentials and process containment, builds the installer, and exercises installation, the installed UI and uninstallation. Consult the PR checks for the exact commit being reviewed. Reports, logs and screenshots are generated under ignored `.artifacts/` and uploaded as Actions artifacts; do not copy them into the repository.

## Deployment context validation

On 2026-10-28, Windows 10 x64 / Python 3.11.15 / Rust 1.95.0 ran the deployment-identity
working tree based on `aedf7beb1ecff2ea1fade6ccdd8e4bf6a298f799`.
All 70 native tests and 117 frontend tests passed, together with Clippy, Rust formatting,
IPC drift, TypeScript, frontend lint and the production build. Nine native HTTP regression tests
cover deployment replacement with an unchanged Principal, late Scope/Memory results, activation
races, Principal changes during reads, qualified legacy transitions and dispatched writes.
Replacement scenarios use synthetic TCP responses; they do not establish real deployment replacement
or installed-platform acceptance. The bilingual unknown-write regression checks the original Server ID,
preserved draft and explicit retry confirmation.

The real SQLite Server harness passed anonymous loopback, static Bearer, HTTPS with an explicit CA
and base path, and an enforced Provider. It exercised native save/search/exact reads, original write
deployment metadata, uncertain writes without replay, Principal changes/revocation and independent
Server survival. Exact inputs:

- Server sources: `aedf7beb1ecff2ea1fade6ccdd8e4bf6a298f799`.
- Wheel: `powercontext-1.4.2.dev36+gaedf7beb1.d20261028-py3-none-any.whl`;
SHA-256 `fde108a81a872a6228aad54832f2cd8f899a53bb4a7ff5c321871891fdbc5b68`.
- Normalized OpenAPI SHA-256: `c12901b88b8e958c9cd63469298b9a04b9d5e2b0b4564d83b50f7ae95542519a`.
- Debug `server_probe.exe` SHA-256: `a56d4db19650459c53094cd1a09fddfc40b6daa98f90dcf5f7d0c5514481390a`.

This run used the explicit disposable `desktop-ci-fixture` qualification, not a new production
catalog record. The report records a dirty Desktop working tree; historical qualification records
are unchanged. Native Manager p95 round trips on these small local fixtures were 24.86–53.66 ms for
save, 19.77–37.51 ms for search and 17.64–36.04 ms for exact reads, including identity revalidation.
These are observations, not approved budgets, UI measurements or remote-network guarantees.
Readiness separation, signed Windows 11/macOS packages and installed acceptance remain open.
Repository file hooks passed; default Windows `ty check` reports existing POSIX-only attributes in
unchanged Python files. `ty check --python-platform linux` passed.

## Submission recovery checks

On 2026-10-30, Windows 10 x64 / Rust 1.95.0 checks against baseline `90355b8e` cover
retry discovery failure with the original unknown receipt retained, and concurrent read disconnection
while a dispatched write is paused. Loopback TCP fixtures verify only the original mutation reaches
the Server, and that the pending receipt becomes unknown under its original operation identity.
All 89 native suite cases pass; proxy isolation additionally reruns its case in a spawned process.
All 146 frontend cases, TypeScript, lint, formatting, production build, Clippy and IPC drift checks pass.
Full App tests in English and Chinese verify retained original and edited retry inputs, recovery before
the write finishes, individual discard, late unknown receipts after discard, and conclusive late success
without restoring private results. TCP fixtures exercise the native Manager; jsdom tests mock IPC.
These checks do not establish installed WebView or live external Server acceptance of the new paths.

## Rejected submission recovery and Python CI checks

On 2026-10-31, Windows 10 x64 checks reproduce a paused save returning 403 after a concurrent
read disconnects the context. The native Manager retains the failed receipt and original target
without replay; the application keeps the rejected text available for explicit copy/discard.
English and Chinese App regressions verify copy, cancelled discard and explicit discard of rejected input.
All 90 native suite cases and 148 frontend tests pass, together with TypeScript, lint, production build,
Rust formatting, Clippy and IPC drift. Proxy isolation also reruns in a spawned process.
All 14 deployment-identity cases pass with real loopback sockets and synthetic HTTP responses;
App state-refresh tests use mocked IPC and do not qualify installed WebView behavior.

Python 3.11.15 checks pass 38 cases across the affected MySQL admission/runner, worker logging
and ZCode service-chain modules; 57 runner cases are skipped by their platform/backend guards.
The admission fixture accepts the maintenance connection's missing-database keyword while
retaining its read-only assertion. The real worker test waits for a completion observation
before consuming captured descriptors. ZCode checks use Node 24.22.1 with a real HTTP Server
and SQLite, including a response deliberately delayed past Stop's unchanged 800 ms request
budget: the result remains unknown, another Stop does not replay the mutation, and independent
reads verify committed memory, exact provenance and recall in a new session. A normal response
also exercised the cursor-reached path. These local checks do not establish Linux/Python matrix
success; consult the new CI run for those results.

## Native session observation checks

Windows x64 checks for the native session collector use Rust 1.95.0, Node 24.14.1 and pnpm 11.13.1.
Expand Down
12 changes: 8 additions & 4 deletions desktop/src-tauri/examples/server_probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,10 @@ async fn main() {
fixture.token.take(),
)
.unwrap();
assert!(matches!(
api.server_info().await.unwrap(),
DiscoveryDocument::Server(_)
));
let DiscoveryDocument::Server(server) = api.server_info().await.unwrap() else {
panic!("expected PowerContext discovery");
};
let server_id = server.server_id;
if args
.get(2)
.is_some_and(|argument| argument == "discovery-only")
Expand Down Expand Up @@ -175,6 +175,10 @@ async fn main() {
let expected = format!("{keyword} 中文 café\nSecond line.");
let saved = manager.remember(generation, &submitted).await.unwrap();
assert_eq!(saved.record.status, WriteStatus::Succeeded);
assert_eq!(
saved.record.context.server_id.as_deref(),
Some(server_id.as_str())
);
let result = saved.result.unwrap();
assert_eq!(
saved.record.references,
Expand Down
Loading
Loading