better-dsh 0.2.2-a → 0.2.2-c
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/50_test-reports/2026-09-06-write/345/267/245/345/205/267sandbox/345/215/207/347/272/247/351/200/217/344/274/240bug/345/244/215/345/217/221/345/217/212/346/214/202/350/265/267-/344/272/213/344/273/266/346/212/245/345/221/212.md +43 -0
- package/docs/50_test-reports/v0.2.4-ios-focus-zoom-suppression/345/256/236/346/265/213/346/212/245/345/221/212.md +158 -0
- package/docs/60_exploration-and-research/01-cordis-runtime/bun-compile-cordis-runtime-bootstrap-research.md +536 -0
- package/docs/60_exploration-and-research/01-cordis-runtime/cordis-customization-and-override-mechanics.md +418 -0
- package/docs/60_exploration-and-research/01-cordis-runtime/cordis-research.md +350 -0
- package/docs/60_exploration-and-research/01-cordis-runtime/dsh-cordis-hotplug-mcp-patch-research.md +265 -0
- package/docs/60_exploration-and-research/02-dsh-webui/dsh-web-profile-package-map.md +186 -0
- package/docs/60_exploration-and-research/02-dsh-webui/dsh-web-ui-slot-system-research.md +310 -0
- package/docs/60_exploration-and-research/02-dsh-webui/dsh-webui-backend-data-inventory.md +633 -0
- package/docs/60_exploration-and-research/02-dsh-webui/dsh-webui-strip-boundary-research.md +300 -0
- package/docs/60_exploration-and-research/02-dsh-webui/dsh-webui-wire-appendix.md +3729 -0
- package/docs/60_exploration-and-research/02-dsh-webui/web-frontend-composability-research.md +191 -0
- package/docs/60_exploration-and-research/02-dsh-webui/webui-wire-data/capture-live-turn.json +1 -0
- package/docs/60_exploration-and-research/02-dsh-webui/webui-wire-data/observed-endpoints.json +224 -0
- package/docs/60_exploration-and-research/02-dsh-webui/webui-wire-data/remote-inventory.json +110954 -0
- package/docs/60_exploration-and-research/02-dsh-webui/webui-wire-data/served-index-sample.html +47 -0
- package/docs/60_exploration-and-research/02-dsh-webui/webui-wire-data/session-events.json +9411 -0
- package/docs/60_exploration-and-research/02-dsh-webui/webui-wire-data/ws-frame-examples.json +20 -0
- package/docs/60_exploration-and-research/03-mobile-ios/dsh-mobile-spa-ios-input-experience-research.md +160 -0
- package/docs/60_exploration-and-research/03-mobile-ios/ios-chat-app-bridge-research.md +324 -0
- package/docs/60_exploration-and-research/04-session-storage/alpha5-compaction-jsonl-mapping.md +1532 -0
- package/docs/60_exploration-and-research/04-session-storage/alpha5-compaction-sample/episode-failed.json +46 -0
- package/docs/60_exploration-and-research/04-session-storage/alpha5-compaction-sample/episode1.json +1124 -0
- package/docs/60_exploration-and-research/04-session-storage/alpha5-compaction-sample/episode2.json +1240 -0
- package/docs/60_exploration-and-research/05-dashr-dev/plugin-development.md +148 -0
- package/docs/60_exploration-and-research/05-dashr-dev/upstream-alignment.md +102 -0
- package/docs/60_exploration-and-research/README.md +87 -0
- package/docs/60_exploration-and-research/bun-compile-cordis-runtime-bootstrap-research.md +348 -0
- package/docs/60_exploration-and-research/dsh-mobile-spa-ios-input-experience-research.md +160 -0
- package/lib/index.d.ts +9 -1
- package/lib/index.js +258 -6
- package/package.json +1 -1
- package/docs/REPL-/345/267/245/345/205/267/350/260/203/347/224/250-/346/210/252/346/226/255/350/257/212/346/226/255.md +0 -110
- package/docs/adr/0001-bridge-tool-layer-not-service-layer.md +0 -14
- package/docs/adr/0002-masking-is-presentation-only.md +0 -15
- package/docs/plans/A2A-messaging-channel-test-archive.md +0 -256
- package/docs/plans/code-mode-vs-rlm-ipython-comparison.md +0 -137
- package/docs/plans/dashr-blueprint-review.md +0 -201
- package/docs/plans/dashr-blueprint.md +0 -561
- package/docs/plans/dashr-compaction-window-and-archive.md +0 -307
- package/docs/plans/dashr-profile-layer-feasibility.md +0 -367
- package/docs/plans/dashr-sandbox-escalation-semantics-gap.md +0 -171
- package/docs/plans/dashr-security-sandbox-analysis.md +0 -187
- package/docs/plans/dashr-surface-invariant-and-omp-imports.md +0 -97
- package/docs/plans/ipython-kernel-interactive-interface-test-report.md +0 -152
- package/docs/plans/kernel-refactoring/Dash-IPython-Control-Prompt-draft.md +0 -146
- package/docs/plans/kernel-refactoring/Dash-IPython-Control-Prompt-draft_v3.md +0 -50
- package/docs/plans/kernel-refactoring/Dash-IPython-Control-Prompt-draft_v4.md +0 -79
- package/docs/plans/kernel-refactoring/Dash-vs-PrimeAgent-systemprompt-toolcatalog-comparison.md +0 -138
- package/docs/plans/kernel-refactoring/RLM-system-prompt-injection-gap-report.md +0 -161
- package/docs/plans/kernel-refactoring/V0.1.5-development-plan.md +0 -109
- package/docs/plans/kernel-refactoring/actinoable-surface-to-llm-in-agent-runtime_dsh.md +0 -50
- package/docs/plans/kernel-refactoring/actinoable-surface-to-llm-in-agent-runtime_prime.md +0 -113
- package/docs/plans/recallable-compaction.md +0 -147
- package/docs/plans/spike-tag-repro.mjs +0 -102
- package/docs/plans/upstream-analysis.md +0 -128
- package/docs/v0.1.8-improved-/345/256/236/346/265/213/346/212/245/345/221/212.md +0 -142
- package/docs/v0.1.8-/345/256/236/346/265/213/346/212/245/345/221/212.md +0 -193
- package/docs/v0.1.8b-/345/256/236/346/265/213/346/212/245/345/221/212.md +0 -96
- package/docs/v0.1.8c-/345/256/236/346/265/213/346/212/245/345/221/212.md +0 -127
- package/docs/v0.1.8d-/345/256/236/346/265/213/346/212/245/345/221/212.md +0 -150
- package/docs/v0.1.8d_artifacts/README.md +0 -138
- package/docs/v0.1.8d_artifacts/code-mode-repl-only.observation.md +0 -74
- package/docs/v0.1.8d_artifacts/dsh-session-session-4a293388-9ae1-474b-87a0-9e17bb556d94.jsonl +0 -3890
- package/docs/v0.1.8d_artifacts/dsh-session-session-4a293388-9ae1-474b-87a0-9e17bb556d94.w-sample-0435.jsonl +0 -544
- package/docs/v0.1.8d_artifacts/functions.json +0 -592
- package/docs/v0.1.8d_artifacts/skills-catalog.snapshot.md +0 -30
- package/docs/v0.1.8d_artifacts/tools-sdk.output-schemas.json +0 -1236
- package/docs/v0.1.8d_artifacts/tools-sdk.python.txt +0 -592
- package/docs/v0.1.8d_artifacts/tools-sdk.typescript.txt +0 -516
- package/docs/v0.1.8d_artifacts/wire-vs-transcription.diff.md +0 -54
- package/docs/v0.1.8e-/345/256/236/346/265/213/346/212/245/345/221/212.md +0 -224
- package/docs/v0.1.9a-/345/256/236/346/265/213/346/212/245/345/221/212.md +0 -168
- package/docs/v0.2.0b-/345/256/236/346/265/213/346/212/245/345/221/212.md +0 -123
- package/docs/v0.2.0b_artifacts/f2probe/Cargo.lock +0 -7
- package/docs/v0.2.0b_artifacts/f2probe/Cargo.toml +0 -6
- package/docs/v0.2.0b_artifacts/f2probe/src/bin/messy.rs +0 -8
- package/docs/v0.2.0b_artifacts/f2probe/src/main.rs +0 -4
- package/docs/v0.2.0b_artifacts/hashline-probe.md +0 -5
- package/docs/v0.2.0b_artifacts/slowprobe/Cargo.lock +0 -7
- package/docs/v0.2.0b_artifacts/slowprobe/Cargo.toml +0 -7
- package/docs/v0.2.0b_artifacts/slowprobe/build.rs +0 -4
- package/docs/v0.2.0b_artifacts/slowprobe/src/main.rs +0 -13
- package/docs/v0.2.1-/345/256/236/346/265/213/346/212/245/345/221/212.md +0 -110
- package/docs/v0.2.1b-/345/256/236/346/265/213/346/212/245/345/221/212.md +0 -86
- package/docs/v0.2.1c-/345/256/236/346/265/213/346/212/245/345/221/212.md +0 -66
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# 事件报告:write 工具 sandbox 升级透传 bug(复发 + 新失败模式:调用挂起)
|
|
2
|
+
|
|
3
|
+
> 日期:2026-09-06 · 环境:DSH v0.1.2-alpha 线(prod 3080 / Web GUI),session 模型 GLM,
|
|
4
|
+
> file policy = workspace-write(workspace:`/home/u1/workspaces/dashr`),approval policy = ask。
|
|
5
|
+
> 前置记录:2026-09-03 edit 工具同类 bug(session `8e966430`,"hashline edit escalation
|
|
6
|
+
> passthrough bug";dashr 插件侧修复 commit `e27fdec` + 报告 follow-up `419ca0e`——该修复仅覆盖
|
|
7
|
+
> vendored hashline edit 路径,本次 write 工具复发说明 harness 层文件工具透传问题未除根)。
|
|
8
|
+
|
|
9
|
+
## 场景
|
|
10
|
+
|
|
11
|
+
向 workspace 之外写文件:`/home/u1/workspaces/superd/docs/00-blueprint.md`(Super D 蓝图,
|
|
12
|
+
新建同级仓)。workspace-write 模式下首次尝试即被拒(`Read-only file system` / file access denied),
|
|
13
|
+
按规则走单次 `sandbox_permissions` 升级重试。
|
|
14
|
+
|
|
15
|
+
## 失败 1(已知 bug 复发):write 的升级字段被丢弃
|
|
16
|
+
|
|
17
|
+
首次 `write` 调用携带 `sandbox_permissions: "danger-full-access"` + justification,直接返回
|
|
18
|
+
`[sandbox: file access denied under workspace-write mode]`——**未弹出任何审批卡片**,升级字段
|
|
19
|
+
疑似在 harness 透传层被丢弃。与 2026-09-03 记录的 edit 工具症状同类:**文件类工具(write/edit)
|
|
20
|
+
的 sandbox_permissions 透传失效,bash 工具的升级正常**。
|
|
21
|
+
|
|
22
|
+
## 失败 2(新失败模式,更严重):升级后的 write 调用挂起至手动停止
|
|
23
|
+
|
|
24
|
+
agent 推理链(UI 可见):此前一次 bash 升级到 workspace-write 被拒,报错提示"升级必须严格宽于
|
|
25
|
+
当前模式"(当前已是 workspace-write);据此判断应改用 danger-full-access 重试 `write`。
|
|
26
|
+
该重试调用**表面上已发出,但此后 >5 分钟 agent 无任何活动**——非响应/挂起。用户在 UI 按
|
|
27
|
+
"stop" 并发新 prompt 才恢复。
|
|
28
|
+
|
|
29
|
+
即:升级版 write 不仅没有触发审批提示,还把整个 turn 挂死直到手动中断。两种可能根因待查:
|
|
30
|
+
(a) 审批提示从未渲染(透传丢失后 promise 悬空);(b) 调用 promise 永不 settle。
|
|
31
|
+
|
|
32
|
+
## 有效的 workaround
|
|
33
|
+
|
|
34
|
+
`bash` heredoc + `sandbox_permissions: "danger-full-access"` —— bash 的升级链路正常,审批弹出、
|
|
35
|
+
批准后写入成功(superd 仓蓝图 + git commit `1c1969a` 均经此路径完成)。
|
|
36
|
+
|
|
37
|
+
## 建议
|
|
38
|
+
|
|
39
|
+
1. **修复前**:跨 workspace 文件写入一律走 bash heredoc + 升级,不用 write/edit 的升级。
|
|
40
|
+
2. **根因排查**:write/edit 的 sandbox_permissions 透传丢失点(harness 文件工具包装层);
|
|
41
|
+
以及挂起问题——需区分"审批提示未渲染"与"promise 未 settle",二者修法不同。
|
|
42
|
+
3. 回归范围:至少覆盖 write / edit / undo_last_edit 三个文件工具 × workspace-write 与
|
|
43
|
+
read-only 两种基线模式下的升级路径。
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# v0.2.4 iOS Focus 放大抑制(zoomGuard)实测报告
|
|
2
|
+
|
|
3
|
+
- **Change**: `openspec/changes/2026-09-03-ios-focus-zoom-suppression`
|
|
4
|
+
- **版本**: better-dsh 0.2.2-a → **0.2.4**(canonical `dashr/package.json`;npm 已发布线 v0.2.3 顺延一档)
|
|
5
|
+
- **日期**: 2026-09-03
|
|
6
|
+
- **范围**: 纯 host 半(boot script 增量 + config 面 + payload 扩键);client 半零改动,无需 build-client
|
|
7
|
+
- **状态**: 单测/构建层自测完成;4999 实例验证(2.3)与真机观察(3.x)待做
|
|
8
|
+
- **状态**: 4999 实例验证(2.3)✅ 完成(含一轮 blocker 返修 commit `59870fc`);真机观察(3.x)待 user
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 一、实测范围与结果表
|
|
12
|
+
|
|
13
|
+
| # | 项目 | 层面 | 结果 | 证据 |
|
|
14
|
+
|---|------|------|------|------|
|
|
15
|
+
| 1 | vitest 全量(canonical) | 单测 | ✅ **460/460**(基线 428 + 新增 32;36 files) | `cd dashr && npm test` |
|
|
16
|
+
| 2 | tsc --noEmit(含新 spec) | 类型 | ✅ 0 错 | `npx tsc --noEmit` |
|
|
17
|
+
| 3 | schema:默认值 / off 透传 / font·bogus fail-loud | 单测 | ✅ | `test/zoom-guard.spec.ts` 'config schema' |
|
|
18
|
+
| 4 | 纯函数矩阵(UA × 视口 × config;幂等;还原;bare token;大小写) | 单测 | ✅ 32 tests | 同上 |
|
|
19
|
+
| 5 | 生成脚本形状(ES5、无 `</script`、断点派生 `(max-width:767.98px)`、payload 序列化) | 单测 | ✅ | 同上 'generated zoomGuard section' |
|
|
20
|
+
| 6 | **boot script 评估端到端**(stub DOM 执行真实脚本文本) | 单测 | ✅ 全矩阵 | 同上 'boot script evaluation'(12 场景) |
|
|
21
|
+
| 7 | 构建产物复核(lib/index.js 抽取嵌入函数源重组段 eval) | 构建 | ✅ ES5-ok + provisional/reconcile/还原/风暴全绿 | `.scratch/zg-built-check.ts` |
|
|
22
|
+
| 8 | monorepo 副本 rsync + tsdown(host 半) | 构建 | ✅ 9 files 528.31 kB | `pnpm --filter better-dsh exec tsdown` |
|
|
23
|
+
| 9 | dump-config 覆盖层渲染 `mobile.zoomGuard` | 集成 | ✅ `'off'` 逐字渲染于 dashr-repl 行 | `--patch .scratch/zg-overlay.yml` |
|
|
24
|
+
| 10 | 4999 实例(boot script 注入形状 + CDP 视口矩阵 + off 逃生门 + 桌面 user 复验) | 集成 | ✅ **9/9 矩阵 + 4 冒烟 + off PASS + 桌面 PASS**(经一轮 blocker 返修,见八) | §八;脚本 `.scratch/zg-verify/` |
|
|
25
|
+
| 11 | 真机 iOS PWA 观察(放大抑制/键盘自愈/回弹) | 真机 | ⏳ **待做**(user) | tasks 3.1–3.3 |
|
|
26
|
+
|
|
27
|
+
## 二、实现要点(含 design.md 偏差说明)
|
|
28
|
+
|
|
29
|
+
1. **纯函数 = 发布源**:`src/mobile/zoom-guard.ts` 的 `isIOSClassUA` / `mergeViewportTokens` / `shouldApplyZoomGuard` 以 ES5 自包含风格编写,boot script 经 `Function.prototype.toString()` 嵌入这三个函数 —— 单测覆盖的源码与页面执行的源码字节一致(构建后产物已复核该嵌入幸存于 tsdown bundle)。
|
|
30
|
+
2. **schema enum 形式**:本 fork schemastery 无 `choice()`;采用 `z.union(['meta','off']).default('meta')`(字面量 union 即 enum,非法值 config load 即抛)。**'font' 预留值位不进 enum**(决策:未实现值若被 schema 接受则运行期静默 no-op,fail-loud 优于静默;预留以注释标注)。
|
|
31
|
+
3. **⚠ 与 design D4 的重要发现级偏差(已按 D4 精神实现)**:webserver 的 head 注入 splice 点在 `<head>` 开标签**紧后**(`packages/host/webserver/src/injections.ts` `renderIndexInjections`),即 boot script 执行时上游 stock viewport meta(`apps/web/index.html:5`)**尚未解析**——"meta 缺失 → 创建"是常态路径而非边角。若照 D4 字面创建后不管,页面将永久双 meta,依赖引擎未定义的 multi-meta 合并语义,且 spec 的"还原 stock content"场景失义。实现为 **provisional meta + MutationObserver reconcile**:脚本执行时即建 provisional meta(抑制令牌先于一切 bundle 生效,时序要求满足)→ parser 插入 stock meta 时记录真 stock、token merge 改写 **stock meta 本体**、移除 provisional → 单 meta 文档;此后 resize 进出断口均以记录的 stock 为基线改写/还原(字节级还原)。无 MutationObserver 的引擎降级为 provisional 常驻(仍具抑制)。两路径(stock 先在场 = 直接改写;stock 后到 = reconcile)均有单测钉住。
|
|
32
|
+
4. **payload 面**:`zoomGuard` 与 breakpoint 等键同构(配置了才进 payload,schema 默认保证生产必带 'meta');`zoomGuard:'off'` 时整段不生成(逃生门 = 零脚本段,非运行期判断)。
|
|
33
|
+
5. **配置生命周期**:zoomGuard 随 mobile 腿(`mobile.enabled=false` 时整个 `__DASHR_MOBILE__` 不注入,zoomGuard 一并不生效)——design D5 "同路径同生命周期" 的字面执行。
|
|
34
|
+
|
|
35
|
+
## 三、环境事实
|
|
36
|
+
|
|
37
|
+
- canonical:`/home/u1/workspaces/dashr/dashr`(node_modules 在场,vitest/tsc 直接跑)
|
|
38
|
+
- monorepo 副本:`upstream/deepseek-harness/packages/better-dsh/better-dsh`(rsync + tsdown host 半)
|
|
39
|
+
- Node v22.22.1 / vitest 4.x / tsdown 0.15.x
|
|
40
|
+
- dump-config:`DSH_HOME=.dsh-test npm run dsh -- web --dump-config --patch .scratch/zg-overlay.yml`(dump-config 只渲染 patch 层,schema 默认由单测钉住)
|
|
41
|
+
|
|
42
|
+
## 四、boot script 评估测试明细(单测第 6 项)
|
|
43
|
+
|
|
44
|
+
stub DOM(El/Document/MutationObserver/live matchMedia/resize 记录),`new Function` 执行 `buildBootScript` 真实输出:
|
|
45
|
+
|
|
46
|
+
| 场景 | 断言 |
|
|
47
|
+
|------|------|
|
|
48
|
+
| iPhone ∧ 窄 | 脚本时 provisional meta(`maximum-scale=1, user-scalable=no`);stock 解析后单 meta、content = `width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no` |
|
|
49
|
+
| 非 viewport 解析插入 | observer 不误触发,继续守望 |
|
|
50
|
+
| 宽↔窄往返 + 5 连 resize 风暴 | 离带 = 字节级还原 stock;回带 = 合并;风暴零叠加 |
|
|
51
|
+
| stock 先在场(worker 形态) | 直接改写,无 provisional |
|
|
52
|
+
| 无 stock 页面 | provisional 常驻;离带移除自建 meta |
|
|
53
|
+
| iPad / iPadOS 桌面 UA+多点 | 改写生效;Mac 桌面(单/零触点)不生效 |
|
|
54
|
+
| Android ∧ 窄 / 桌面任意宽 | stock 原样、零监听器 |
|
|
55
|
+
| iPhone 宽载入后旋转入带 | 后补改写生效 |
|
|
56
|
+
| zoomGuard off | 无脚本段、零改写、零监听 |
|
|
57
|
+
| authorities + mobile 双腿 | `__DSH_TRANSPORT__` 与 zoomGuard 互不干扰 |
|
|
58
|
+
| 断点 900 | matchMedia 收到 `(max-width:899.98px)`,payload 带 `"breakpoint":900` |
|
|
59
|
+
|
|
60
|
+
## 五、真机观察(user,PWA standalone 主用法)—— TODO
|
|
61
|
+
|
|
62
|
+
> 以下三章按 design D6 留给 user 真机执行(4999 或真实入口,iOS 版本号请记录)。
|
|
63
|
+
|
|
64
|
+
### 5.1 放大抑制判定(tasks 3.1)— TODO
|
|
65
|
+
|
|
66
|
+
- [ ] Safari 态:focus composer / permission select / settings 各 input / 切会话自动聚焦 —— 无 115–120% 放大
|
|
67
|
+
- [ ] PWA standalone 态:同上全过
|
|
68
|
+
- [ ] 双指缩放仍可用(iOS 10+ 应忽略 user-scalable=no 对 pinch 的约束)
|
|
69
|
+
- [ ] `zoomGuard:'off'` 时放大复现(逃生门复验)
|
|
70
|
+
|
|
71
|
+
### 5.2 键盘遮蔽自愈判定(tasks 3.2)— TODO
|
|
72
|
+
|
|
73
|
+
- [ ] PWA 态 unzoomed 下 focus composer:键盘弹出后 composer 是否被引擎 resize 抬到键盘上方(innerHeight/100dvh 收缩联动)
|
|
74
|
+
- [ ] 键盘关闭后 viewport 回弹(对照 dev.to 2026-07 记录的 iOS 17/18 standalone 卡死 bug)
|
|
75
|
+
- [ ] Safari 态对照记录
|
|
76
|
+
|
|
77
|
+
### 5.3 观察结论回填(tasks 3.3)— TODO
|
|
78
|
+
|
|
79
|
+
- [ ] 研究文档 v2 §6 决策点 3/4 回填
|
|
80
|
+
- [ ] follow-up 取舍:visualViewport shim / display-flip 自愈 / focus gate 讨论输入
|
|
81
|
+
|
|
82
|
+
## 六、发现的瑕疵与去向
|
|
83
|
+
|
|
84
|
+
- **发现(非瑕疵)**:head 注入 splice 位于 stock meta 之前 → 促成 provisional+reconcile 设计(见二.3);已进 upstream-alignment S7 查表第 8 条(viewport 行形状 + splice 次序两查点)。
|
|
85
|
+
- dump-config 只渲染 patch 层、不显 schema 默认 —— zoomGuard 默认值的可观测面在 payload(4999 shell 页)与单测,不在 dump-config;如需 dump 可见需显式配置(本报告第 9 项即此形态)。
|
|
86
|
+
|
|
87
|
+
## 七、Open items
|
|
88
|
+
- ~~tasks 2.3(4999 实例验证)~~ ✅ 完成(见八;返修 `59870fc` 后 9/9)
|
|
89
|
+
- tasks 3.x 真机观察 —— user
|
|
90
|
+
- 发布/npm publish 与 prod 部署 —— 另按年龄门流程,不在本 change 内
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## 八、4999 实测记录与 blocker 返修轮(lead,2026-09-03 晚)
|
|
95
|
+
|
|
96
|
+
### 8.1 首轮矩阵与 blocker 发现
|
|
97
|
+
|
|
98
|
+
- 冒烟四断言全过:A)shell head 内联脚本含 zoomGuard 段;B)boot graph 含 `"id":"better-dsh"` 与 client URL;C)zoomGuard 段(HTML 偏移 25313)早于应用入口 module script(27056)——注:client-modules 的 combo 注册脚本(1286)先于我们执行,但纯工厂注册无 DOM/focus,行为层时序满足 S2;client.js 单包 URL 200 且与 `lib/client/index.js` 前缀字节一致(+84B sourceMappingURL,S7 惯例)。
|
|
99
|
+
- **CDP 视口矩阵首轮 7/9**:两个失败项均为**初始窄载入不 guard**(iOS+390、iPadOS 冒充+390);跨断口 resize 与事后 synthetic resize 均生效。
|
|
100
|
+
|
|
101
|
+
### 8.2 根因定位(诊断脚本 `diag.mjs` / `isolate.mjs`)
|
|
102
|
+
|
|
103
|
+
- 事件时序实测:boot script 执行于 head 解析期,此时布局视口为 viewport-meta 未应用前的默认宽 **980**(`docStart innerWidth=980, mq.matches=false`)→ 首评不 guard;引擎随后应用 `width=device-width` 收窄到 390,但**该转换在载入期间不向页面派发 `resize` 事件**(探针隔离三变体一致;我们的 resize 监听在位且事后手动 dispatch 立即生效)。
|
|
104
|
+
- 关键后果:deferred module script(React 挂载 → unlock effect 自动聚焦)在 DOMContentLoaded 前执行——依赖 resize 事件的补评可能晚于首次 auto-focus。**初始窄载入 = iPhone 用户打开 DSH 的主路径,blocker 级**。
|
|
105
|
+
|
|
106
|
+
### 8.3 返修(doer 第二轮,commit `59870fc`,tag `v0.2.4` 前移)
|
|
107
|
+
|
|
108
|
+
- **早期载入重评梯子**:首评未 applied 时启动 ~10ms poll 调 `re()`,三停条件 = applied / readyState complete / 200 tick(~2s);`apply(true)` 主动取消待发 tick,稳态零 timer。guard 不再依赖任何事件通知——事件缺失只影响"多快"发现,不影响"是否"发现;首次布局必然先于 deferred scripts,抑制仍落在任何输入可聚焦之前。
|
|
109
|
+
- **MQ change 监听兜底**(`mq.addEventListener('change', re)`,老引擎 `addListener` 形态)与 window resize 双通道。
|
|
110
|
+
- 测试 466/466(+6:晚翻无 resize、readyState 停止、非 iOS 零 setTimeout、MQ 通道、legacy addListener、梯子先于 stock 解析落 provisional);tsc 0。
|
|
111
|
+
|
|
112
|
+
### 8.4 返修后终验(全绿)
|
|
113
|
+
|
|
114
|
+
- **CDP 矩阵 9/9**:iOS+390 → guarded(单 meta、两 token 在位);iOS+1280 → stock;iPadOS 桌面冒充+touch+390 → guarded;Android+390 / 桌面+390 / 桌面+1280 → stock(零改写);跨断口三段(宽载入 stock → 入带 guard → 出带字节级还原 stock)。
|
|
115
|
+
- **off 逃生门 live PASS**:home 层 `.dsh-test/cordis.patch.yml` 覆盖(注:CLI `web` 子命令不认 `--patch`,该路仅 dump-config 可用)→ shell **整段不发射**、payload `zoomGuard:'off'`、iOS+390 live = stock。验后已删覆盖文件并还原默认态。
|
|
116
|
+
- **默认态恢复复验 + 桌面 user 复验 PASS**:还原后 iOS+390 → guarded;桌面(无 UA 伪装)meta stock 单条、app 完整渲染(root/composer/`[data-composer-card]`/sidebar/slots 在位,标题与占位文案正常)。
|
|
117
|
+
|
|
118
|
+
### 8.5 遗留观察(非阻塞)
|
|
119
|
+
|
|
120
|
+
- 真机 iOS Safari 上 8.2 竞态是否同样存在未证(无法本地复现 iOS 引擎);梯子设计使其无关化——真机观察(五)会给出实证。
|
|
121
|
+
- 探针实验中一次"全探针"运行曾捕捉到 resize 送达(时序非确定论),进一步佐证不能依赖事件通道。
|
|
122
|
+
- CDP 测试脚本存 `.scratch/zg-verify/`(matrix/diag/isolate/off/final 五件),可复跑。
|
|
123
|
+
|
|
124
|
+
## 九、v0.2.5 standalone 分流(change `2026-09-03-zoomguard-standalone-font-floor`)—— 骨架,4999/真机结论待回填
|
|
125
|
+
|
|
126
|
+
> 背景:真机首批观察(§五)——浏览器态 meta 改写完美(放大抑制 + pinch 保留),但 **Add to Home Screen PWA standalone 态引擎尊重 `user-scalable=no` → pinch 失效**(Discourse 注释未覆盖的形态;spec v1 的 "pinch remains engine-controlled" 括号在 standalone 不成立)。user 裁决:standalone 态完全不碰 viewport meta,改注入 16px 字号地板 CSS;browser 态维持 v0.2.4。user 主用法即 PWA。
|
|
127
|
+
|
|
128
|
+
### 9.1 实现摘要(doer,2026-09-03)
|
|
129
|
+
|
|
130
|
+
- 纯函数(`src/mobile/zoom-guard.ts`,ES5 自包含,toString 嵌入,`var ZD=`/`var ZF=` 与 ZI/ZM/ZS 同款):`isStandaloneDisplay(mqMatchesStandalone, navStandalone)` = 严格 `=== true` 双源 OR(MQ 主源 + `navigator.standalone` 老 iOS 兜底);`buildFontFloorCss(bp)` → `@media (max-width:{bp-0.02}px){ input,textarea,select,[contenteditable="true"]{ font-size:16px !important } }`(断口派生与 meta 腿一致:768→767.98、900→899.98)。
|
|
131
|
+
- boot script 分流(D1):iOS 判定后、任何 meta 逻辑前 —— `var sa=ZD((window.matchMedia&&window.matchMedia('(display-mode: standalone)').matches),(navigator.standalone===true));` standalone → `headEl()` 注入 `<style id="ios-zoom-font-floor" data-plugin="better-dsh" data-plugin-css="better-dsh/zoom-font-floor">`(textContent=ZF(rawBp))后 **return**:其后 provisional/reconcile/监听/梯子代码字面不可达,meta 字节不动。**style 注入不设宽度门**(宽度由 CSS media query 表达,宽屏 standalone 也注入、规则自然不匹配)。
|
|
132
|
+
- `'off'`:整段不发射(web-trust 门不变、零改动);`'meta'` 语义升级为自动双形态(enum 不动)。web-trust.ts / client 半零改动。
|
|
133
|
+
|
|
134
|
+
### 9.2 单测(canonical,doer)
|
|
135
|
+
|
|
136
|
+
- **478/478 + tsc 0**(v0.2.4 基线 466,+12):双源判定矩阵(含 undefined 单源兜底)、CSS 文本钉扎(selector 集合 / `!important` / 768→767.98 与 900→899.98)、standalone 矩阵(MQ 源:style 在场 + 三认领属性 + meta 字节不动 + 零 addEventListener/setTimeout/MutationObserver 痕迹 + 零窄带 MQ 探针;宽屏仍注入;navigator.standalone 单源;无 matchMedia 兜底;断口 900;browser 态显式无 style;off 双零)。
|
|
137
|
+
- stub 基建:`runBootScript` 增 `standaloneAtScriptTime`/`navStandalone` 参数,matchMedia 按查询串分流(display-mode 探针不再误读窄带裁定);browser 全矩阵零回归(mediaQueries 断言更新为含 display-mode 前导探针)。
|
|
138
|
+
|
|
139
|
+
### 9.3 4999 CDP 双形态(lead,2026-09-03 深夜)—— ✅ 8/8 全绿
|
|
140
|
+
|
|
141
|
+
**⚠ 测试方法学发现(重要)**:Chrome 的 `Emulation.setEmulatedMedia` **不支持 `display-mode` 特性**(实测 pre-nav/post-nav 设置后页面内 `matchMedia('(display-mode: standalone)').matches)` 恒 false —— Chrome 可仿真特性集不含它,DevTools Rendering 面板亦无此项)。改用 **matchMedia API 边界注入**:`Page.addScriptToEvaluateOnNewDocument` 在一切页面脚本(含 head 注入 boot script)之前包一层 `matchMedia`,仅对含 display-mode+standalone 的查询返回 `{matches:true}`,其余透传 —— boot script 全部分支逻辑照真运行(自检探针 truth=true 证实注入生效)。
|
|
142
|
+
|
|
143
|
+
| 场景 | 断言 | 结果 |
|
|
144
|
+
|---|---|---|
|
|
145
|
+
| standalone+iOS+390 | meta **字节不动**(`width=device-width, initial-scale=1` 原文)= pinch 恢复前提 | ✅ |
|
|
146
|
+
| standalone+iOS+390 | `#ios-zoom-font-floor` 在场,认领属性齐(data-plugin / data-plugin-css=better-dsh/zoom-font-floor),CSS 文本与 user 提案逐字一致 | ✅ |
|
|
147
|
+
| standalone+iOS+390 | composer computed font-size = **16px**(地板生效) | ✅ |
|
|
148
|
+
| browser+iOS+390 | meta guarded(两 token 在场)、无 font-floor | ✅ |
|
|
149
|
+
| browser+iOS+390 | **client 特性探针**:sidebar computed 首列 0px、认领样式 98 条(v0.2.1f 特性在位) | ✅ |
|
|
150
|
+
| standalone+iOS+1280 | floor 已注入(宽度无关)但 media query 不激活(composer 14px) | ✅ |
|
|
151
|
+
| standalone+Android+390 | iOS 门 → 无 floor、meta stock | ✅ |
|
|
152
|
+
| off | 双形态零发射(单测钉住;'off' 代码路径 v0.2.4 未动) | ✅ |
|
|
153
|
+
|
|
154
|
+
脚本 `.scratch/zg-verify/dual-mode.mjs`(含 matchMedia 边界注入实现);重启后 boot graph rev 已随 v0.2.5 变化,tsdown+build-client 双步完成、lib/client 在场(md5 `a88850ec…`)。
|
|
155
|
+
### 9.4 真机复验(user)—— TODO(tasks 3.1)
|
|
156
|
+
|
|
157
|
+
- [ ] PWA standalone:pinch 恢复 + focus 无放大(输入面 16px)。
|
|
158
|
+
- [ ] 浏览器态:维持 §五 结论(meta 改写 + pinch 保留)。
|