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
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

## Table of Contents

- [What's new (2026-06-19) — SBOM & Suite Sharding](#whats-new-2026-06-19--sbom--suite-sharding)
- [What's new (2026-06-19) — Reactive Observer](#whats-new-2026-06-19--reactive-observer)
- [What's new (2026-06-19) — WCAG 2.2 Audit](#whats-new-2026-06-19--wcag-22-audit)
- [What's new (2026-06-19) — Memory & Determinism](#whats-new-2026-06-19--memory--determinism)
Expand Down Expand Up @@ -70,6 +71,13 @@

---

## What's new (2026-06-19) — SBOM & Suite Sharding

Two pure-stdlib ops tools (security + scale research angles), full stack. Full reference: [`docs/source/Eng/doc/new_features/v18_features_doc.rst`](docs/source/Eng/doc/new_features/v18_features_doc.rst).

- **CycloneDX SBOM** — `build_sbom` / `write_sbom` (`AC_generate_sbom`, `ac_generate_sbom`): emit a CycloneDX 1.6 dependency SBOM (name/version/purl/license) for supply-chain compliance (EU CRA / EO 14028); `root` limits to a package's closure, `extra_components` inventories action files. No third-party dependency.
- **Duration-aware suite sharding** — `shard_flows` / `merge_results` (`AC_shard_suite` / `AC_merge_results`): bin-pack flows into N shards balanced by historical per-flow duration (so the slowest worker, not test count, defines runtime), then merge per-shard reports into one rollup.

## What's new (2026-06-19) — Reactive Observer

A non-blocking screen observer (SikuliX `observe` model), full stack (facade, `AC_*`, MCP, Script Builder). Full reference: [`docs/source/Eng/doc/new_features/v17_features_doc.rst`](docs/source/Eng/doc/new_features/v17_features_doc.rst).
Expand Down
8 changes: 8 additions & 0 deletions README/README_zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

## 目录

- [本次更新 (2026-06-19) — SBOM 与测试分片](#本次更新-2026-06-19--sbom-与测试分片)
- [本次更新 (2026-06-19) — 反应式观察器](#本次更新-2026-06-19--反应式观察器)
- [本次更新 (2026-06-19) — WCAG 2.2 审计](#本次更新-2026-06-19--wcag-22-审计)
- [本次更新 (2026-06-19) — 记忆与确定性](#本次更新-2026-06-19--记忆与确定性)
Expand Down Expand Up @@ -69,6 +70,13 @@

---

## 本次更新 (2026-06-19) — SBOM 与测试分片

来自安全与规模研究角度的两项纯标准库运维工具,走完整五层。完整参考:[`docs/source/Zh/doc/new_features/v18_features_doc.rst`](../docs/source/Zh/doc/new_features/v18_features_doc.rst)。

- **CycloneDX SBOM** — `build_sbom` / `write_sbom`(`AC_generate_sbom`、`ac_generate_sbom`):为供应链合规(欧盟 CRA / EO 14028)输出 CycloneDX 1.6 依赖 SBOM(name/version/purl/许可证);`root` 限定某包的闭包,`extra_components` 可纳入 action 文件。无第三方依赖。
- **时长感知套件分片** — `shard_flows` / `merge_results`(`AC_shard_suite` / `AC_merge_results`):依每个流程历史时长把流程装箱成 N 片(让最慢的 worker 而非测试数量决定总时长),再把各分片报告合并为一份汇总。

## 本次更新 (2026-06-19) — 反应式观察器

非阻塞的屏幕观察器(SikuliX `observe` 模型),走完整五层(facade、`AC_*`、MCP、Script Builder)。完整参考:[`docs/source/Zh/doc/new_features/v17_features_doc.rst`](../docs/source/Zh/doc/new_features/v17_features_doc.rst)。
Expand Down
8 changes: 8 additions & 0 deletions README/README_zh-TW.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

## 目錄

- [本次更新 (2026-06-19) — SBOM 與測試分片](#本次更新-2026-06-19--sbom-與測試分片)
- [本次更新 (2026-06-19) — 反應式觀察器](#本次更新-2026-06-19--反應式觀察器)
- [本次更新 (2026-06-19) — WCAG 2.2 稽核](#本次更新-2026-06-19--wcag-22-稽核)
- [本次更新 (2026-06-19) — 記憶與決定性](#本次更新-2026-06-19--記憶與決定性)
Expand Down Expand Up @@ -69,6 +70,13 @@

---

## 本次更新 (2026-06-19) — SBOM 與測試分片

來自安全與規模研究角度的兩項純標準庫維運工具,走完整五層。完整參考:[`docs/source/Zh/doc/new_features/v18_features_doc.rst`](../docs/source/Zh/doc/new_features/v18_features_doc.rst)。

- **CycloneDX SBOM** — `build_sbom` / `write_sbom`(`AC_generate_sbom`、`ac_generate_sbom`):為供應鏈合規(歐盟 CRA / EO 14028)輸出 CycloneDX 1.6 相依 SBOM(name/version/purl/授權);`root` 限定某套件的封閉集,`extra_components` 可納入 action 檔。不需第三方相依。
- **時長感知套件分片** — `shard_flows` / `merge_results`(`AC_shard_suite` / `AC_merge_results`):依每個流程歷史時長把流程裝箱成 N 片(讓最慢的 worker 而非測試數量決定總時長),再把各分片報告合併為一份彙總。

## 本次更新 (2026-06-19) — 反應式觀察器

非阻塞的螢幕觀察器(SikuliX `observe` 模型),走完整五層(facade、`AC_*`、MCP、Script Builder)。完整參考:[`docs/source/Zh/doc/new_features/v17_features_doc.rst`](../docs/source/Zh/doc/new_features/v17_features_doc.rst)。
Expand Down
55 changes: 55 additions & 0 deletions docs/source/Eng/doc/new_features/v18_features_doc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
==================================================
New Features (2026-06-19) — SBOM & Suite Sharding
==================================================

Two pure-standard-library ops tools from the security and scale research
angles, wired through the full stack (facade, ``AC_*`` executor commands,
MCP tools, Script Builder): a **CycloneDX SBOM generator** and a
**duration-aware suite sharder** (with shard-result merge).

.. contents::
:local:
:depth: 2


CycloneDX SBOM
=============

Supply-chain regulation (EU Cyber Resilience Act, US EO 14028) increasingly
requires a machine-readable Software Bill of Materials. ``build_sbom`` walks
the installed Python distributions and emits a **CycloneDX 1.6** JSON
document — no third-party dependency::

from je_auto_control import build_sbom, write_sbom

sbom = build_sbom("je_auto_control") # dependency closure of the pkg
sbom = build_sbom(None) # every installed distribution
write_sbom("sbom.cdx.json", "je_auto_control",
extra_components=[{"type": "file", "name": "login.json",
"version": "1"}])

Each component carries ``name`` / ``version`` / ``purl`` (``pkg:pypi/...``)
and, when available, its license. ``extra_components`` lets you inventory
action files alongside code. Exposed as ``AC_generate_sbom`` /
``ac_generate_sbom``.


Duration-aware suite sharding
============================

Splitting a suite across N workers by *count* wastes time when tests differ
in duration — the slowest worker defines wall-clock. ``shard_flows`` balances
shards by **historical per-flow duration** from the run-history store using
greedy bin-packing::

from je_auto_control import shard_flows, merge_results

shards = shard_flows(all_flows, shards=4) # ~equal time per shard
# ... each worker runs its shard, produces a report ...
report = merge_results([shard_report_1, shard_report_2, ...])

Flows with no history fall back to the mean of known flows (so new tests
spread evenly). ``merge_results`` recombines per-shard report dicts — summing
``total`` / ``passed`` / ``failed`` / ``skipped`` / ``errors`` and
concatenating ``results``. Exposed as ``AC_shard_suite`` / ``AC_merge_results``
(and ``ac_shard_suite`` / ``ac_merge_results``).
1 change: 1 addition & 0 deletions docs/source/Eng/eng_index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ Comprehensive guides for all AutoControl features.
doc/new_features/v15_features_doc
doc/new_features/v16_features_doc
doc/new_features/v17_features_doc
doc/new_features/v18_features_doc
doc/ocr_backends/ocr_backends_doc
doc/observability/observability_doc
doc/operations_layer/operations_layer_doc
Expand Down
50 changes: 50 additions & 0 deletions docs/source/Zh/doc/new_features/v18_features_doc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
==============================================
新功能 (2026-06-19) — SBOM 與測試分片
==============================================

來自安全與規模研究角度的兩項純標準庫維運工具,走完整五層(facade、
``AC_*`` 執行器指令、MCP 工具、Script Builder):**CycloneDX SBOM 產生器**
與**時長感知的測試套件分片**(含分片結果合併)。

.. contents::
:local:
:depth: 2


CycloneDX SBOM
==============

供應鏈法規(歐盟網路韌性法 CRA、美國 EO 14028)日益要求可機讀的軟體物料
清單(SBOM)。``build_sbom`` 會走訪已安裝的 Python 發行套件並輸出
**CycloneDX 1.6** JSON 文件——不需任何第三方相依::

from je_auto_control import build_sbom, write_sbom

sbom = build_sbom("je_auto_control") # 該套件的相依封閉集
sbom = build_sbom(None) # 所有已安裝發行套件
write_sbom("sbom.cdx.json", "je_auto_control",
extra_components=[{"type": "file", "name": "login.json",
"version": "1"}])

每個元件帶有 ``name`` / ``version`` / ``purl``(``pkg:pypi/...``),有提供時
也帶授權。``extra_components`` 讓你把 action 檔與程式碼一併納入清單。對應
``AC_generate_sbom`` / ``ac_generate_sbom``。


時長感知的套件分片
==================

把套件依*數量*分到 N 個 worker,在測試時長不均時會浪費時間——最慢的
worker 決定總時長。``shard_flows`` 以 run-history 中的**每個流程歷史時長**
用貪婪裝箱法平衡各分片::

from je_auto_control import shard_flows, merge_results

shards = shard_flows(all_flows, shards=4) # 每片時間約略相等
# ... 各 worker 跑自己的分片,產生一份報告 ...
report = merge_results([shard_report_1, shard_report_2, ...])

沒有歷史的流程退回為已知流程的平均(讓新測試平均分散)。``merge_results``
會重新合併各分片報告 dict——加總 ``total`` / ``passed`` / ``failed`` /
``skipped`` / ``errors`` 並串接 ``results``。對應 ``AC_shard_suite`` /
``AC_merge_results``(以及 ``ac_shard_suite`` / ``ac_merge_results``)。
1 change: 1 addition & 0 deletions docs/source/Zh/zh_index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ AutoControl 所有功能的完整使用指南。
doc/new_features/v15_features_doc
doc/new_features/v16_features_doc
doc/new_features/v17_features_doc
doc/new_features/v18_features_doc
doc/ocr_backends/ocr_backends_doc
doc/observability/observability_doc
doc/operations_layer/operations_layer_doc
Expand Down
6 changes: 6 additions & 0 deletions je_auto_control/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,10 @@
ScreenObserver, WatchRule, default_observer,
image_predicate, pixel_predicate, text_predicate,
)
# CycloneDX SBOM generation (supply-chain compliance)
from je_auto_control.utils.sbom import build_sbom, write_sbom
# Duration-aware suite sharding + shard-result merge
from je_auto_control.utils.test_shard import merge_results, shard_flows
# Background popup/interrupt watchdog (unattended automation)
from je_auto_control.utils.watchdog import (
PopupWatchdog, WatchdogRule, default_popup_watchdog,
Expand Down Expand Up @@ -566,6 +570,8 @@ def start_autocontrol_gui(*args, **kwargs):
"DeterministicRun", "seed_everything",
"ScreenObserver", "WatchRule", "default_observer",
"image_predicate", "pixel_predicate", "text_predicate",
"build_sbom", "write_sbom",
"merge_results", "shard_flows",
# MCP server
"AuditLogger", "HttpMCPServer", "MCPContent", "MCPPrompt",
"MCPPromptArgument", "MCPResource", "MCPServer", "MCPTool",
Expand Down
27 changes: 27 additions & 0 deletions je_auto_control/gui/script_builder/command_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,33 @@ def _add_misc_specs(specs: List[CommandSpec]) -> None:
description="WCAG 2.2 audit: SC-tagged findings + Target Size 2.5.8.",
))
_add_observer_specs(specs)
_add_ops_specs(specs)


def _add_ops_specs(specs: List[CommandSpec]) -> None:
specs.append(CommandSpec(
"AC_generate_sbom", "Tools", "Generate SBOM (CycloneDX)",
fields=(
FieldSpec("path", FieldType.FILE_PATH, optional=True,
default="sbom.cdx.json"),
FieldSpec("root", FieldType.STRING, optional=True,
default="je_auto_control"),
),
description="Emit a CycloneDX 1.6 dependency SBOM for the project.",
))
specs.append(CommandSpec(
"AC_shard_suite", "Testing", "Shard Suite (duration-aware)",
fields=(
FieldSpec("shards", FieldType.INT, default=2),
FieldSpec("history_path", FieldType.FILE_PATH, optional=True),
FieldSpec("window", FieldType.INT, optional=True, default=20),
),
description="Balance 'flows' (JSON view) into N shards by duration.",
))
specs.append(CommandSpec(
"AC_merge_results", "Testing", "Merge Shard Results",
description="Merge per-shard 'reports' (JSON view) into one report.",
))


def _add_observer_specs(specs: List[CommandSpec]) -> None:
Expand Down
29 changes: 29 additions & 0 deletions je_auto_control/utils/executor/action_executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -2637,6 +2637,32 @@ def _observe_stop() -> Dict[str, Any]:
return {"running": default_observer.running}


def _generate_sbom(path: Optional[str] = None,
root: str = "je_auto_control") -> Dict[str, Any]:
"""Adapter: build (or write) a CycloneDX SBOM for the project."""
from je_auto_control.utils.sbom import build_sbom, write_sbom
root_arg = root or None
if path:
return {"path": write_sbom(path, root_arg)}
return {"sbom": build_sbom(root_arg)}


def _shard_suite(flows: List[str], shards: int = 2,
history_path: Optional[str] = None,
window: int = 20) -> Dict[str, Any]:
"""Adapter: balance flows into duration-aware shards."""
from je_auto_control.utils.test_shard import shard_flows
return {"shards": shard_flows(flows, int(shards),
history_path=history_path,
window=int(window))}


def _merge_results(reports: List[Dict[str, Any]]) -> Dict[str, Any]:
"""Adapter: merge per-shard report dicts into one report."""
from je_auto_control.utils.test_shard import merge_results
return merge_results(reports)


class Executor:
"""
Executor
Expand Down Expand Up @@ -2835,6 +2861,9 @@ def __init__(self):
"AC_observe_poll": _observe_poll,
"AC_observe_start": _observe_start,
"AC_observe_stop": _observe_stop,
"AC_generate_sbom": _generate_sbom,
"AC_shard_suite": _shard_suite,
"AC_merge_results": _merge_results,
"AC_a11y_record_start": _a11y_record_start,
"AC_a11y_record_stop": _a11y_record_stop,
"AC_a11y_record_events": _a11y_record_events,
Expand Down
48 changes: 48 additions & 0 deletions je_auto_control/utils/mcp_server/tools/_factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -2134,6 +2134,53 @@ def observer_tools() -> List[MCPTool]:
]


def sbom_tools() -> List[MCPTool]:
return [
MCPTool(
name="ac_generate_sbom",
description=("Generate a CycloneDX 1.6 SBOM of the project's "
"dependencies (supply-chain compliance). 'root' "
"limits to a distribution's closure (empty = all "
"installed). Writes to 'path' or returns the SBOM."),
input_schema=schema({"path": {"type": "string"},
"root": {"type": "string"}}),
handler=h.generate_sbom,
annotations=SIDE_EFFECT_ONLY,
),
]


def sharding_tools() -> List[MCPTool]:
return [
MCPTool(
name="ac_shard_suite",
description=("Split 'flows' into 'shards' balanced lists using "
"historical per-flow duration from run history "
"(greedy bin-pack), so each worker takes ~equal "
"time. Returns the shard lists."),
input_schema=schema({
"flows": {"type": "array", "items": {"type": "string"}},
"shards": {"type": "integer"},
"history_path": {"type": "string"},
"window": {"type": "integer"},
}, required=["flows"]),
handler=h.shard_suite,
annotations=READ_ONLY,
),
MCPTool(
name="ac_merge_results",
description=("Merge per-shard report dicts into one consolidated "
"report (sums total/passed/failed/skipped/errors, "
"concatenates results)."),
input_schema=schema({
"reports": {"type": "array", "items": {"type": "object"}},
}, required=["reports"]),
handler=h.merge_results,
annotations=READ_ONLY,
),
]


def unattended_tools() -> List[MCPTool]:
return [
MCPTool(
Expand Down Expand Up @@ -3187,6 +3234,7 @@ def media_assert_tools() -> List[MCPTool]:
element_repository_tools, flow_debugger_tools,
skill_library_tools, guardrail_tools, a2a_tools, office_tools,
agent_memory_tools, determinism_tools, observer_tools,
sbom_tools, sharding_tools,
screen_record_tools,
process_and_shell_tools, remote_desktop_tools, gamepad_tools,
usb_passthrough_tools, assertion_tools, data_source_tools,
Expand Down
20 changes: 20 additions & 0 deletions je_auto_control/utils/mcp_server/tools/_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1054,6 +1054,26 @@ def observe_stop():
return {"running": default_observer.running}


def generate_sbom(path=None, root="je_auto_control"):
from je_auto_control.utils.sbom import build_sbom, write_sbom
root_arg = root or None
if path:
return {"path": write_sbom(path, root_arg)}
return {"sbom": build_sbom(root_arg)}


def shard_suite(flows, shards=2, history_path=None, window=20):
from je_auto_control.utils.test_shard import shard_flows
return {"shards": shard_flows(flows, int(shards),
history_path=history_path,
window=int(window))}


def merge_results(reports):
from je_auto_control.utils.test_shard import merge_results as _merge
return _merge(reports)


def vlm_locate(description: str,
screen_region: Optional[List[int]] = None,
model: Optional[str] = None) -> Optional[List[int]]:
Expand Down
4 changes: 4 additions & 0 deletions je_auto_control/utils/sbom/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
"""Software Bill of Materials (CycloneDX) generation for automation projects."""
from je_auto_control.utils.sbom.sbom import build_sbom, write_sbom

__all__ = ["build_sbom", "write_sbom"]
Loading
Loading