Skip to content

Commit 61d68f2

Browse files
authored
Merge pull request #389 from Integration-Automation/feat/settle-detector-batch
Add settle_detector: settle decision as a pure seam over a churn series
2 parents 49e8731 + e362810 commit 61d68f2

15 files changed

Lines changed: 282 additions & 0 deletions

File tree

README/WHATS_NEW_zh-CN.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# 本次更新 — AutoControl
22

3+
## 本次更新 (2026-06-24) — 变化量序列的稳定检测
4+
5+
判断 UI 何时安定下来——以纯粹、可测试的函数作用于变化序列。完整参考:[`docs/source/Zh/doc/new_features/v175_features_doc.rst`](../docs/source/Zh/doc/new_features/v175_features_doc.rst)
6+
7+
- **`settle_point` / `is_settled` / `SettleTracker`**(`AC_settle_point`):`smart_waits.wait_until_screen_stable` 把稳定逻辑包在 `time.sleep` 循环内、作用于实时帧——你无法喂记录好的序列,也无法单元测试该决策。本功能把它抽离:给定一串*变化量*(像素差 / 元素数差 / 0-1 digest 是否变),在变化量连续 `quiet_samples` 次维持 ≤ `max_churn` 时报告稳定(尖峰重置 run)。`settle_point` 返回稳定索引,`SettleTracker` 为供实时循环的增量形式。纯标准库,不需时钟、不需捕获;不导入 `PySide6`
8+
39
## 本次更新 (2026-06-24) — OCR 行的段落与列表分组
410

511
把 OCR 行分组成段落,并检测项目符号 / 编号列表。完整参考:[`docs/source/Zh/doc/new_features/v174_features_doc.rst`](../docs/source/Zh/doc/new_features/v174_features_doc.rst)

README/WHATS_NEW_zh-TW.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# 本次更新 — AutoControl
22

3+
## 本次更新 (2026-06-24) — 變化量序列的穩定偵測
4+
5+
判斷 UI 何時安定下來——以純粹、可測試的函式作用於變化序列。完整參考:[`docs/source/Zh/doc/new_features/v175_features_doc.rst`](../docs/source/Zh/doc/new_features/v175_features_doc.rst)
6+
7+
- **`settle_point` / `is_settled` / `SettleTracker`**(`AC_settle_point`):`smart_waits.wait_until_screen_stable` 把穩定邏輯包在 `time.sleep` 迴圈內、作用於即時幀——你無法餵記錄好的序列,也無法單元測試該決策。本功能把它抽離:給定一串*變化量*(像素差 / 元素數差 / 0-1 digest 是否變),在變化量連續 `quiet_samples` 次維持 ≤ `max_churn` 時回報穩定(尖峰重置 run)。`settle_point` 回傳穩定索引,`SettleTracker` 為供即時迴圈的增量形式。純標準函式庫,不需時鐘、不需擷取;不匯入 `PySide6`
8+
39
## 本次更新 (2026-06-24) — OCR 行的段落與清單分組
410

511
把 OCR 行分組成段落,並偵測項目符號 / 編號清單。完整參考:[`docs/source/Zh/doc/new_features/v174_features_doc.rst`](../docs/source/Zh/doc/new_features/v174_features_doc.rst)

WHATS_NEW.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# What's New — AutoControl
22

3+
## What's new (2026-06-24) — Settle Detection Over a Churn Series
4+
5+
Decide when the UI has gone quiet — as a pure, testable function over a change series. Full reference: [`docs/source/Eng/doc/new_features/v175_features_doc.rst`](docs/source/Eng/doc/new_features/v175_features_doc.rst).
6+
7+
- **`settle_point` / `is_settled` / `SettleTracker`** (`AC_settle_point`): `smart_waits.wait_until_screen_stable` bakes the settle logic inside a `time.sleep` loop over live frames — you can't feed it a recorded series or unit-test the decision. This extracts it: given a stream of *churn* values (pixel delta / element-count delta / 0-1 digest-changed), it reports when churn stayed ≤ `max_churn` for `quiet_samples` in a row (a spike resets the run). `settle_point` returns the settle index, `SettleTracker` is the incremental form for a live loop. Pure-stdlib, no clock, no capture; no `PySide6`.
8+
39
## What's new (2026-06-24) — Paragraph & List Grouping of OCR Lines
410

511
Group OCR lines into paragraphs and detect bulleted / numbered lists. Full reference: [`docs/source/Eng/doc/new_features/v174_features_doc.rst`](docs/source/Eng/doc/new_features/v174_features_doc.rst).
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
Settle Detection Over a Churn Series
2+
====================================
3+
4+
``smart_waits.wait_until_screen_stable`` and ``actionability``'s stability check bake the
5+
settle logic *inside* a ``time.sleep`` polling loop over live pixel frames — you cannot feed
6+
them a recorded series of a11y-element counts or screen-diff metrics, and you cannot unit-test
7+
the *decision* independently of capture. ``settle_detector`` extracts that decision: it takes a
8+
stream of *churn* values (how much changed each sample — a pixel delta, an element-count delta,
9+
a digest-changed 0/1, anything) and reports when the churn has stayed at or below ``max_churn``
10+
for ``quiet_samples`` in a row. A spike resets the quiet run, so "settled then changed again"
11+
is handled.
12+
13+
Pure-stdlib; deterministic and unit-testable on an injected series with no capture and no
14+
clock. Imports no ``PySide6``.
15+
16+
Headless API
17+
------------
18+
19+
.. code-block:: python
20+
21+
from je_auto_control import settle_point, is_settled, SettleTracker
22+
23+
churns = [5, 4, 0.5, 0.3, 0.2] # per-frame change metric
24+
settle_point(churns, quiet_samples=3, max_churn=1.0) # -> 4
25+
is_settled(churns, quiet_samples=3, max_churn=1.0) # -> True
26+
27+
# incremental, for a live loop (you supply the churn each tick)
28+
tracker = SettleTracker(quiet_samples=3, max_churn=1.0)
29+
state = tracker.update(current_churn)
30+
if state.settled:
31+
observe_now()
32+
33+
``settle_point`` returns the index at which the series first settles (or ``None``);
34+
``is_settled`` is the boolean. ``SettleTracker`` is the incremental form: ``update(churn)``
35+
returns a ``SettleState`` (``settled`` / ``quiet_run`` / ``churn``); ``reset`` clears the run
36+
(e.g. right after acting again).
37+
38+
Executor command
39+
----------------
40+
41+
``AC_settle_point`` (``churns`` / ``quiet_samples`` / ``max_churn`` → ``{settled, index}``) is
42+
exposed as the MCP tool ``ac_settle_point`` (read-only) and as the Script Builder command
43+
**Settle Point (churn series)** under **Flow**.

docs/source/Eng/eng_index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ Comprehensive guides for all AutoControl features.
197197
doc/new_features/v172_features_doc
198198
doc/new_features/v173_features_doc
199199
doc/new_features/v174_features_doc
200+
doc/new_features/v175_features_doc
200201
doc/ocr_backends/ocr_backends_doc
201202
doc/observability/observability_doc
202203
doc/operations_layer/operations_layer_doc
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
變化量序列的穩定偵測
2+
====================
3+
4+
``smart_waits.wait_until_screen_stable`` 與 ``actionability`` 的穩定檢查把穩定邏輯包在
5+
``time.sleep`` 輪詢迴圈內、作用於即時像素幀——你無法餵給它一段記錄好的 a11y 元素數或畫面
6+
差異指標序列,也無法獨立於擷取去單元測試那個*決策*。``settle_detector`` 把該決策抽離:它接收
7+
一串*變化量*(churn,每個樣本變了多少——像素差、元素數差、digest 是否變的 0/1,皆可),並在
8+
變化量連續 ``quiet_samples`` 次維持在 ``max_churn`` 以下時回報穩定。尖峰會重置 quiet run,因此
9+
「穩定後又變動」也能處理。
10+
11+
純標準函式庫;確定性、可在注入序列上單元測試,不需擷取、不需時鐘。不匯入 ``PySide6``。
12+
13+
無頭 API
14+
--------
15+
16+
.. code-block:: python
17+
18+
from je_auto_control import settle_point, is_settled, SettleTracker
19+
20+
churns = [5, 4, 0.5, 0.3, 0.2] # 每幀變化量指標
21+
settle_point(churns, quiet_samples=3, max_churn=1.0) # -> 4
22+
is_settled(churns, quiet_samples=3, max_churn=1.0) # -> True
23+
24+
# 增量版,供即時迴圈(你每 tick 提供 churn)
25+
tracker = SettleTracker(quiet_samples=3, max_churn=1.0)
26+
state = tracker.update(current_churn)
27+
if state.settled:
28+
observe_now()
29+
30+
``settle_point`` 回傳序列首次穩定的索引(或 ``None``);``is_settled`` 為布林。``SettleTracker``
31+
為增量形式:``update(churn)`` 回傳 ``SettleState``(``settled`` / ``quiet_run`` / ``churn``);
32+
``reset`` 清除 run(例如在再次動作後)。
33+
34+
執行器指令
35+
----------
36+
37+
``AC_settle_point``(``churns`` / ``quiet_samples`` / ``max_churn`` → ``{settled, index}``)
38+
以 MCP 工具 ``ac_settle_point``(唯讀)及 Script Builder 指令 **Settle Point (churn series)**
39+
(位於 **Flow** 分類下)形式提供。

docs/source/Zh/zh_index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ AutoControl 所有功能的完整使用指南。
197197
doc/new_features/v172_features_doc
198198
doc/new_features/v173_features_doc
199199
doc/new_features/v174_features_doc
200+
doc/new_features/v175_features_doc
200201
doc/ocr_backends/ocr_backends_doc
201202
doc/observability/observability_doc
202203
doc/operations_layer/operations_layer_doc

je_auto_control/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,10 @@
343343
from je_auto_control.utils.grounding_consensus import (
344344
ConsensusResult, consensus_element, consensus_point, is_confident,
345345
)
346+
# Decide when a UI has settled, as a pure seam over a churn series
347+
from je_auto_control.utils.settle_detector import (
348+
SettleState, SettleTracker, is_settled, settle_point,
349+
)
346350
# Locate on-screen regions by colour (mask + connected components)
347351
from je_auto_control.utils.color_region import (
348352
find_color_region, find_color_regions,
@@ -1304,6 +1308,10 @@ def start_autocontrol_gui(*args, **kwargs):
13041308
"consensus_point",
13051309
"consensus_element",
13061310
"is_confident",
1311+
"SettleState",
1312+
"SettleTracker",
1313+
"settle_point",
1314+
"is_settled",
13071315
"find_color_region",
13081316
"find_color_regions",
13091317
"ssim_compare",

je_auto_control/gui/script_builder/command_schema.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3273,6 +3273,16 @@ def _add_set_of_marks_specs(specs: List[CommandSpec]) -> None:
32733273
),
32743274
description="Agreed target point from clustered grounding proposals.",
32753275
))
3276+
specs.append(CommandSpec(
3277+
"AC_settle_point", "Flow", "Settle Point (churn series)",
3278+
fields=(
3279+
FieldSpec("churns", FieldType.STRING,
3280+
placeholder="[5, 4, 0.5, 0.3, 0.2]"),
3281+
FieldSpec("quiet_samples", FieldType.INT, optional=True, default=3),
3282+
FieldSpec("max_churn", FieldType.FLOAT, optional=True, default=1.0),
3283+
),
3284+
description="Index where a churn series first settles (offline settle check).",
3285+
))
32763286
specs.append(CommandSpec(
32773287
"AC_consensus_element", "Native UI", "Grounding Consensus Element",
32783288
fields=(

je_auto_control/utils/executor/action_executor.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4232,6 +4232,19 @@ def _consensus_element(candidates: Any, elements: Any) -> Dict[str, Any]:
42324232
"agreement": winner[1] if winner else 0.0}
42334233

42344234

4235+
def _settle_point(churns: Any, quiet_samples: Any = 3,
4236+
max_churn: Any = 1.0) -> Dict[str, Any]:
4237+
"""Adapter: index at which a churn series first settles (or settled=False)."""
4238+
import json
4239+
from je_auto_control.utils.settle_detector import settle_point
4240+
if isinstance(churns, str):
4241+
churns = json.loads(churns)
4242+
index = settle_point([float(c) for c in churns],
4243+
quiet_samples=int(quiet_samples),
4244+
max_churn=float(max_churn))
4245+
return {"settled": index is not None, "index": index}
4246+
4247+
42354248
def _validate_action(action: Any, screen: Any = None,
42364249
targets: Any = None) -> Dict[str, Any]:
42374250
"""Adapter: validate a coordinate action (bounds + optional snap-to-target)."""
@@ -6121,6 +6134,7 @@ def __init__(self):
61216134
"AC_plan_repair": _plan_repair,
61226135
"AC_consensus_point": _consensus_point,
61236136
"AC_consensus_element": _consensus_element,
6137+
"AC_settle_point": _settle_point,
61246138
"AC_validate_action": _validate_action,
61256139
"AC_replay_trace": _replay_trace,
61266140
"AC_match_elements": _match_elements,

0 commit comments

Comments
 (0)