Skip to content

feat(record): capture Trace v3 states after pages settle - #103

Draft
shnpd wants to merge 1 commit into
feat/trace-v3-protocolfrom
feat/record-settled-states
Draft

feat(record): capture Trace v3 states after pages settle#103
shnpd wants to merge 1 commit into
feat/trace-v3-protocolfrom
feat/record-settled-states

Conversation

@shnpd

@shnpd shnpd commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator

背景

本 PR 基于 feat/trace-v3-protocol(PR #101),落地 Trace v3 录制侧的完整 observation 库,覆盖:

  1. 页面 settle 等待(DOM 安静后再观察)
  2. 语义化 VOM 捕获与 state 去重
  3. 几何匹配:把用户操作目标对齐到 VOM ref
  4. Redirect 合并与 settle 队列
  5. Observation 文件序列化与行内 step 注解
  6. Trace v3 reducer / buildTraceV3
  7. Trace v2 reducer 拆分,保证默认录制仍可出 v2
  8. 类型与 capture 基础设施CaptureGeometrydocumentRect 等)

尚未把线上 record 工具完整切到 Trace v3 产出。record.ts 结束录制时仍走 buildTraceV2;本改动先搭好管线(settle → observe → 几何匹配 → reduce → buildTraceV3),后续再接线。

动机

固定延时既不适合一帧内出现的弹窗,也不适合慢路由。本方案改为:页面 DOM 安静后再拍 observation,并把点击/填写等目标通过 几何信息 对齐到该 observation 中的 VOM ref,使 Trace v3 同时描述「做了什么」和「在哪个控件 / 哪个页面状态上做的」。

Trace v3 形态:

  • states[] — 去重后的页面观察(s1s2、…)
  • steps[] — 每步绑定 state(操作前)与 result.state(操作后),目标优先带 VOM ref

架构 / 数据流

用户操作(draft:captureTarget + geometry)
        │
        ▼
 waitForPageSettled()                ← MutationObserver 安静探测
        │
        ▼
 captureAndRegisterObservation()     ← VOM 捕获 + state 去重(含 documentRect)
        │
        ├─ applyTargetMatching()
        │     └─ matchTarget(geometry ↔ captured/refs)
        │           ├─ 唯一命中 → TargetDescriptorV3 { ref, role, name, ctx }
        │           └─ 0/多命中 → fallback(保留 capture 的 role/name + unmatched)
        ├─ rememberStepOnPage()      ← steps_here + 行内注解
        └─ scheduleDraftSettle()     ← 串行 settle 队列(保序 / 可取消)
                │
                ▼
         draft.preStateId / postStateId / target 就绪
                │
                ▼
         reduceTraceSteps() / buildTraceV3()
                │
                ▼
         TraceV3 { states, steps, entry, recorder, ... }

改动清单(本 PR 全部内容)

1. 页面 settle — page-settled.ts + record-constants.ts

  • 向页面注入 MutationObserver,记录最近一次 DOM 变化时间
  • 轮询 idleMs + readyState
  • 预算:最少等待 150ms,安静 250ms,最长 2s,轮询 60ms
  • 结果:quiet / timeout / cancelled
  • 常量集中:settle/捕获间隔、FNV-1a、sN id、导航 cause 映射、fill 默认 commit=blur、几何容差 2px

2. Observation 管线 — record-observation.ts

  • createObservationState:state registry、settle 队列、redirect 合并状态、注解表
  • captureAndRegisterObservation:节流捕获、VOM 获取、state 去重注册、更新 lastSettled
  • applyTargetMatching几何匹配入口;无 settled 观察时保留 capture 语义,不写成空 unmatched;若上一动作仍在 settle 且新控件匹配失败,不绑定陈旧 origin state
  • scheduleDraftSettle / flushPendingSettles / cancelPendingSettles:串行 settle、可被更新动作取消
  • Redirect coalescing:scheduleRedirectLandingFlush / flushPendingRedirectLanding(中间跳只更新 URL + generation)
  • inferMissingPostStatessettleUnsettledDrafts
  • buildTraceV3:reduce → 只发布被引用的 states → 重编号 s1..sN → 写入带注解的 state body

3. 几何匹配(本 PR 核心能力之一)

3.1 match-target.ts

用 content script 上报的 CaptureGeometry,在最近一次 settled observation 的 CapturedNode[] + RenderedRef[] 中定位控件:

规则 行为
Frame 过滤 ownerFrameBackendNodeId 必须一致
Tag 过滤 geometry.tag 与 node.tag 忽略大小写相等
矩形匹配 容差 GEOM_MATCH_TOLERANCE_PX = 2
坐标系 顶层 + 非 fixed/sticky 且存在 documentRect文档坐标rect + scrolldocumentRect);否则 → 视口坐标localRect/rect
唯一性 恰好 1 个候选才算命中;0 个或多于 1 个 → unmatched
命中结果 { ref, role?, name?, ctx? }(从 VOM refs 取)
未命中 fallbackDescriptor:尽量保留 capture 的 role/name,并标 unmatched: true

3.2 Capture 侧几何数据 — vom/capture.ts

  • CapturedNode 新增 documentRect(DOMSnapshot 文档坐标)
  • localRectdocumentRect - scroll 得到,供视口匹配使用
  • 这是几何匹配在「观察后页面发生滚动」时仍能对准控件的基础

3.3 描述符分层 — describe-target.ts

  • 内容脚本捕获描述符改名为 CaptureTargetDescriptor(操作瞬间的 role/name/tag…)
  • 与最终 Trace 目标 TargetDescriptorV3(可带 ref / unmatched)分离
  • 保留 TargetDescriptor 别名,兼容迁移期

4. Observation 文件 — format-observation-file.ts

  • 序列化格式:# bsk-observation 1 front matter + VOM body
  • 支持 steps_here 与行内注解(如 ⟵ step 3: fill: "..."
  • 哈希输入使用注解插入前的纯 VOM body

5. Trace v3 reducer — trace-reducer.ts

  • registerObservation / stateDedupKeyfnv1a64(body):url
  • 过滤噪声 press(修饰键 / 剪贴板 / 裸字符)
  • Redirect 链折叠:保留第一跳 origin/cause + 最后一跳 destination
  • Draft → StepV3state / result.state;navigate cause;fill commit
  • 缺一端观察时用另一端兜底;两端都没有才丢步
  • 输出 stepIdByDraftId,供注解 / steps_here 重映射
  • resolveTraceStartUrl

6. Trace v2 拆分 — trace-reducer-v2.ts + record.ts

  • 原 v2 归约逻辑迁到 trace-reducer-v2.tsbuildTraceV2
  • record.ts 当前仍调用 buildTraceV2,默认录制路径不回归
  • v3 库已就绪,但 recorder 生命周期尚未消费 buildTraceV3

7. 协议类型 — transport/types.ts

新增/扩展:

  • CaptureGeometry(rect / scrollX/Y / position / tag / ownerFrameBackendNodeId)
  • TargetDescriptorV3(ref / role / name / ctx / unmatched)
  • DraftTraceStep 增加:captureTargetgeometrypreStateIdpostStateId、observation 元数据、navigate transition 元数据、fill commit
  • TraceState / StepV3 / TraceV3 / RecordedTrace 等 v3 wire 形状(与协议 PR 对齐、供本库使用)

8. 测试

文件 覆盖内容
match-target.test.ts 视口唯一匹配;滚动后非 fixed 匹配;观察后滚动用文档坐标;多候选 → unmatched;fixed 用视口坐标
page-settled.test.ts 最短安静等待;探测前取消
record-observation.test.ts 脱敏注解;无 settled 时保留 capture 语义;settle 中不绑陈旧 state;redirect 合并不吞更新 hop
trace-reducer.test.ts v3 state/result、去重、用户连续导航、redirect origin/cause、draft→step id 映射、空 fill 保留
trace-reducer-v2.test.ts v2 拆分后行为
vom/capture.test.ts documentRect / 录制复用相关 capture

建议命令:

pnpm --filter @browser-skill/extension exec vitest run \
  src/lib/__tests__/page-settled.test.ts \
  src/lib/__tests__/match-target.test.ts \
  src/lib/__tests__/record-observation.test.ts \
  src/lib/__tests__/trace-reducer.test.ts \
  src/lib/__tests__/trace-reducer-v2.test.ts \
  src/tools/vom/__tests__/capture.test.ts

pnpm --filter @browser-skill/extension compile

兼容性 / 非目标

  • 默认录制仍产出 Trace v2
  • 不改 CLI bundle / trace_version=3 协商(协议 PR)
  • 端到端「录流程 → Trace v3 JSON」需后续把 observation 管线接到 record.ts

Review 关注点

  1. 几何匹配:文档坐标 vs 视口坐标分支、2px 容差、多候选 unmatched 是否合理?
  2. documentRect 从 DOMSnapshot 推导是否覆盖 iframe / 变换场景?
  3. Settle 预算对 SPA / 动画是否过激或过慢?
  4. Redirect 合并 + generation 取消是否会丢应保留的中间跳?
  5. Reducer pre ?? post 兜底相对丢步是否可接受?
  6. 先合 library、后接线 buildTraceV3 是否 OK?

@shnpd
shnpd requested review from Ljy-0827 and iuyo5678 and a lite review from Copilot August 18, 2026 06:50

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR lays down the Trace v3 recording-side observation pipeline in the extension (page settle → capture VOM observation → target geometry matching → v3 reduction → TraceV3 build), while keeping the current default recorder export on Trace v2 by splitting out the v2 reducer.

Changes:

  • Add page “settle” detection via an injected MutationObserver probe (waitForPageSettled) and centralize recording constants (settle budgets, hashing, id generation, defaults).
  • Introduce observation/state registry + target matching utilities (record-observation.ts, match-target.ts, format-observation-file.ts) to capture and dedupe states and attach step annotations.
  • Split Trace v2 reduction into trace-reducer-v2.ts and switch record.ts to buildTraceV2, while evolving the existing trace-reducer.ts into a Trace v3 reducer that emits state-linked steps.

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
apps/extension/src/transport/types.ts Expand draft step payloads with v3 fields (state ids, geometry, captureTarget) and add v3 types.
apps/extension/src/tools/vom/capture.ts Add documentRect and derive viewport localRect from document coordinates for robust matching after scroll.
apps/extension/src/tools/vom/tests/capture.test.ts Update tests to validate documentRect preservation and derived viewport rects.
apps/extension/src/tools/record.ts Keep default recording output on v2 by switching to buildTraceV2.
apps/extension/src/lib/trace-reducer.ts Implement v3 reducer producing states[] + steps[] with state/result references and redirect collapsing semantics.
apps/extension/src/lib/trace-reducer-v2.ts New v2-only reducer extracted to preserve legacy output behavior.
apps/extension/src/lib/record-observation.ts New observation pipeline: throttled capture, state dedupe, settle queueing, redirect coalescing, annotation + TraceV3 builder.
apps/extension/src/lib/record-constants.ts New centralized constants + FNV-1a hash + state id generator + navigation cause mapping defaults.
apps/extension/src/lib/page-settled.ts New “settle” implementation based on DOM quiet probing and readyState polling.
apps/extension/src/lib/match-target.ts New geometry-based matching from content-script capture to VOM refs (viewport vs document coordinates).
apps/extension/src/lib/format-observation-file.ts New observation file serialization (front matter + inline step annotations).
apps/extension/src/lib/describe-target.ts Rename content-script descriptor to CaptureTargetDescriptor (keep deprecated alias).
apps/extension/src/lib/tests/trace-reducer.test.ts Update tests to validate v3 reducer semantics (states, redirects, step id mapping).
apps/extension/src/lib/tests/trace-reducer-v2.test.ts New tests to validate extracted v2 reducer behavior.
apps/extension/src/lib/tests/record-observation.test.ts New tests covering annotation redaction behavior, target matching fallback, redirect coalescing.
apps/extension/src/lib/tests/page-settled.test.ts New tests for settle floor and cancellation behavior.
apps/extension/src/lib/tests/match-target.test.ts New tests for geometry matching and fallback/unmatched rules.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread apps/extension/src/lib/page-settled.ts
Comment thread apps/extension/src/lib/format-observation-file.ts
@shnpd
shnpd requested a lite review from Copilot August 18, 2026 07:05

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Wait for DOM quiet after actions, match targets to VOM refs, and reduce
drafts into deduped state-linked Trace v3 while keeping the v2 reducer.

Co-authored-by: Cursor <cursoragent@cursor.com>
@shnpd
shnpd force-pushed the feat/record-settled-states branch from e7a4b48 to 1afd217 Compare August 18, 2026 07:36
@shnpd
shnpd marked this pull request as draft August 18, 2026 08:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants