Skip to content

Commit bf76653

Browse files
committed
Add CycloneDX SBOM generator and duration-aware suite sharding
1 parent 27d5d0b commit bf76653

17 files changed

Lines changed: 545 additions & 0 deletions

File tree

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
## Table of Contents
1515

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

7172
---
7273

74+
## What's new (2026-06-19) — SBOM & Suite Sharding
75+
76+
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).
77+
78+
- **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.
79+
- **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.
80+
7381
## What's new (2026-06-19) — Reactive Observer
7482

7583
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).

README/README_zh-CN.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
## 目录
1414

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

7071
---
7172

73+
## 本次更新 (2026-06-19) — SBOM 与测试分片
74+
75+
来自安全与规模研究角度的两项纯标准库运维工具,走完整五层。完整参考:[`docs/source/Zh/doc/new_features/v18_features_doc.rst`](../docs/source/Zh/doc/new_features/v18_features_doc.rst)
76+
77+
- **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 文件。无第三方依赖。
78+
- **时长感知套件分片**`shard_flows` / `merge_results`(`AC_shard_suite` / `AC_merge_results`):依每个流程历史时长把流程装箱成 N 片(让最慢的 worker 而非测试数量决定总时长),再把各分片报告合并为一份汇总。
79+
7280
## 本次更新 (2026-06-19) — 反应式观察器
7381

7482
非阻塞的屏幕观察器(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)

README/README_zh-TW.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
## 目錄
1414

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

7071
---
7172

73+
## 本次更新 (2026-06-19) — SBOM 與測試分片
74+
75+
來自安全與規模研究角度的兩項純標準庫維運工具,走完整五層。完整參考:[`docs/source/Zh/doc/new_features/v18_features_doc.rst`](../docs/source/Zh/doc/new_features/v18_features_doc.rst)
76+
77+
- **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 檔。不需第三方相依。
78+
- **時長感知套件分片**`shard_flows` / `merge_results`(`AC_shard_suite` / `AC_merge_results`):依每個流程歷史時長把流程裝箱成 N 片(讓最慢的 worker 而非測試數量決定總時長),再把各分片報告合併為一份彙總。
79+
7280
## 本次更新 (2026-06-19) — 反應式觀察器
7381

7482
非阻塞的螢幕觀察器(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)
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
==================================================
2+
New Features (2026-06-19) — SBOM & Suite Sharding
3+
==================================================
4+
5+
Two pure-standard-library ops tools from the security and scale research
6+
angles, wired through the full stack (facade, ``AC_*`` executor commands,
7+
MCP tools, Script Builder): a **CycloneDX SBOM generator** and a
8+
**duration-aware suite sharder** (with shard-result merge).
9+
10+
.. contents::
11+
:local:
12+
:depth: 2
13+
14+
15+
CycloneDX SBOM
16+
=============
17+
18+
Supply-chain regulation (EU Cyber Resilience Act, US EO 14028) increasingly
19+
requires a machine-readable Software Bill of Materials. ``build_sbom`` walks
20+
the installed Python distributions and emits a **CycloneDX 1.6** JSON
21+
document — no third-party dependency::
22+
23+
from je_auto_control import build_sbom, write_sbom
24+
25+
sbom = build_sbom("je_auto_control") # dependency closure of the pkg
26+
sbom = build_sbom(None) # every installed distribution
27+
write_sbom("sbom.cdx.json", "je_auto_control",
28+
extra_components=[{"type": "file", "name": "login.json",
29+
"version": "1"}])
30+
31+
Each component carries ``name`` / ``version`` / ``purl`` (``pkg:pypi/...``)
32+
and, when available, its license. ``extra_components`` lets you inventory
33+
action files alongside code. Exposed as ``AC_generate_sbom`` /
34+
``ac_generate_sbom``.
35+
36+
37+
Duration-aware suite sharding
38+
============================
39+
40+
Splitting a suite across N workers by *count* wastes time when tests differ
41+
in duration — the slowest worker defines wall-clock. ``shard_flows`` balances
42+
shards by **historical per-flow duration** from the run-history store using
43+
greedy bin-packing::
44+
45+
from je_auto_control import shard_flows, merge_results
46+
47+
shards = shard_flows(all_flows, shards=4) # ~equal time per shard
48+
# ... each worker runs its shard, produces a report ...
49+
report = merge_results([shard_report_1, shard_report_2, ...])
50+
51+
Flows with no history fall back to the mean of known flows (so new tests
52+
spread evenly). ``merge_results`` recombines per-shard report dicts — summing
53+
``total`` / ``passed`` / ``failed`` / ``skipped`` / ``errors`` and
54+
concatenating ``results``. Exposed as ``AC_shard_suite`` / ``AC_merge_results``
55+
(and ``ac_shard_suite`` / ``ac_merge_results``).

docs/source/Eng/eng_index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ Comprehensive guides for all AutoControl features.
4040
doc/new_features/v15_features_doc
4141
doc/new_features/v16_features_doc
4242
doc/new_features/v17_features_doc
43+
doc/new_features/v18_features_doc
4344
doc/ocr_backends/ocr_backends_doc
4445
doc/observability/observability_doc
4546
doc/operations_layer/operations_layer_doc
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
==============================================
2+
新功能 (2026-06-19) — SBOM 與測試分片
3+
==============================================
4+
5+
來自安全與規模研究角度的兩項純標準庫維運工具,走完整五層(facade、
6+
``AC_*`` 執行器指令、MCP 工具、Script Builder):**CycloneDX SBOM 產生器**
7+
與**時長感知的測試套件分片**(含分片結果合併)。
8+
9+
.. contents::
10+
:local:
11+
:depth: 2
12+
13+
14+
CycloneDX SBOM
15+
==============
16+
17+
供應鏈法規(歐盟網路韌性法 CRA、美國 EO 14028)日益要求可機讀的軟體物料
18+
清單(SBOM)。``build_sbom`` 會走訪已安裝的 Python 發行套件並輸出
19+
**CycloneDX 1.6** JSON 文件——不需任何第三方相依::
20+
21+
from je_auto_control import build_sbom, write_sbom
22+
23+
sbom = build_sbom("je_auto_control") # 該套件的相依封閉集
24+
sbom = build_sbom(None) # 所有已安裝發行套件
25+
write_sbom("sbom.cdx.json", "je_auto_control",
26+
extra_components=[{"type": "file", "name": "login.json",
27+
"version": "1"}])
28+
29+
每個元件帶有 ``name`` / ``version`` / ``purl``(``pkg:pypi/...``),有提供時
30+
也帶授權。``extra_components`` 讓你把 action 檔與程式碼一併納入清單。對應
31+
``AC_generate_sbom`` / ``ac_generate_sbom``。
32+
33+
34+
時長感知的套件分片
35+
==================
36+
37+
把套件依*數量*分到 N 個 worker,在測試時長不均時會浪費時間——最慢的
38+
worker 決定總時長。``shard_flows`` 以 run-history 中的**每個流程歷史時長**
39+
用貪婪裝箱法平衡各分片::
40+
41+
from je_auto_control import shard_flows, merge_results
42+
43+
shards = shard_flows(all_flows, shards=4) # 每片時間約略相等
44+
# ... 各 worker 跑自己的分片,產生一份報告 ...
45+
report = merge_results([shard_report_1, shard_report_2, ...])
46+
47+
沒有歷史的流程退回為已知流程的平均(讓新測試平均分散)。``merge_results``
48+
會重新合併各分片報告 dict——加總 ``total`` / ``passed`` / ``failed`` /
49+
``skipped`` / ``errors`` 並串接 ``results``。對應 ``AC_shard_suite`` /
50+
``AC_merge_results``(以及 ``ac_shard_suite`` / ``ac_merge_results``)。

docs/source/Zh/zh_index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ AutoControl 所有功能的完整使用指南。
4040
doc/new_features/v15_features_doc
4141
doc/new_features/v16_features_doc
4242
doc/new_features/v17_features_doc
43+
doc/new_features/v18_features_doc
4344
doc/ocr_backends/ocr_backends_doc
4445
doc/observability/observability_doc
4546
doc/operations_layer/operations_layer_doc

je_auto_control/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,10 @@
149149
ScreenObserver, WatchRule, default_observer,
150150
image_predicate, pixel_predicate, text_predicate,
151151
)
152+
# CycloneDX SBOM generation (supply-chain compliance)
153+
from je_auto_control.utils.sbom import build_sbom, write_sbom
154+
# Duration-aware suite sharding + shard-result merge
155+
from je_auto_control.utils.test_shard import merge_results, shard_flows
152156
# Background popup/interrupt watchdog (unattended automation)
153157
from je_auto_control.utils.watchdog import (
154158
PopupWatchdog, WatchdogRule, default_popup_watchdog,
@@ -566,6 +570,8 @@ def start_autocontrol_gui(*args, **kwargs):
566570
"DeterministicRun", "seed_everything",
567571
"ScreenObserver", "WatchRule", "default_observer",
568572
"image_predicate", "pixel_predicate", "text_predicate",
573+
"build_sbom", "write_sbom",
574+
"merge_results", "shard_flows",
569575
# MCP server
570576
"AuditLogger", "HttpMCPServer", "MCPContent", "MCPPrompt",
571577
"MCPPromptArgument", "MCPResource", "MCPServer", "MCPTool",

je_auto_control/gui/script_builder/command_schema.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -670,6 +670,33 @@ def _add_misc_specs(specs: List[CommandSpec]) -> None:
670670
description="WCAG 2.2 audit: SC-tagged findings + Target Size 2.5.8.",
671671
))
672672
_add_observer_specs(specs)
673+
_add_ops_specs(specs)
674+
675+
676+
def _add_ops_specs(specs: List[CommandSpec]) -> None:
677+
specs.append(CommandSpec(
678+
"AC_generate_sbom", "Tools", "Generate SBOM (CycloneDX)",
679+
fields=(
680+
FieldSpec("path", FieldType.FILE_PATH, optional=True,
681+
default="sbom.cdx.json"),
682+
FieldSpec("root", FieldType.STRING, optional=True,
683+
default="je_auto_control"),
684+
),
685+
description="Emit a CycloneDX 1.6 dependency SBOM for the project.",
686+
))
687+
specs.append(CommandSpec(
688+
"AC_shard_suite", "Testing", "Shard Suite (duration-aware)",
689+
fields=(
690+
FieldSpec("shards", FieldType.INT, default=2),
691+
FieldSpec("history_path", FieldType.FILE_PATH, optional=True),
692+
FieldSpec("window", FieldType.INT, optional=True, default=20),
693+
),
694+
description="Balance 'flows' (JSON view) into N shards by duration.",
695+
))
696+
specs.append(CommandSpec(
697+
"AC_merge_results", "Testing", "Merge Shard Results",
698+
description="Merge per-shard 'reports' (JSON view) into one report.",
699+
))
673700

674701

675702
def _add_observer_specs(specs: List[CommandSpec]) -> None:

je_auto_control/utils/executor/action_executor.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2637,6 +2637,32 @@ def _observe_stop() -> Dict[str, Any]:
26372637
return {"running": default_observer.running}
26382638

26392639

2640+
def _generate_sbom(path: Optional[str] = None,
2641+
root: str = "je_auto_control") -> Dict[str, Any]:
2642+
"""Adapter: build (or write) a CycloneDX SBOM for the project."""
2643+
from je_auto_control.utils.sbom import build_sbom, write_sbom
2644+
root_arg = root or None
2645+
if path:
2646+
return {"path": write_sbom(path, root_arg)}
2647+
return {"sbom": build_sbom(root_arg)}
2648+
2649+
2650+
def _shard_suite(flows: List[str], shards: int = 2,
2651+
history_path: Optional[str] = None,
2652+
window: int = 20) -> Dict[str, Any]:
2653+
"""Adapter: balance flows into duration-aware shards."""
2654+
from je_auto_control.utils.test_shard import shard_flows
2655+
return {"shards": shard_flows(flows, int(shards),
2656+
history_path=history_path,
2657+
window=int(window))}
2658+
2659+
2660+
def _merge_results(reports: List[Dict[str, Any]]) -> Dict[str, Any]:
2661+
"""Adapter: merge per-shard report dicts into one report."""
2662+
from je_auto_control.utils.test_shard import merge_results
2663+
return merge_results(reports)
2664+
2665+
26402666
class Executor:
26412667
"""
26422668
Executor
@@ -2835,6 +2861,9 @@ def __init__(self):
28352861
"AC_observe_poll": _observe_poll,
28362862
"AC_observe_start": _observe_start,
28372863
"AC_observe_stop": _observe_stop,
2864+
"AC_generate_sbom": _generate_sbom,
2865+
"AC_shard_suite": _shard_suite,
2866+
"AC_merge_results": _merge_results,
28382867
"AC_a11y_record_start": _a11y_record_start,
28392868
"AC_a11y_record_stop": _a11y_record_stop,
28402869
"AC_a11y_record_events": _a11y_record_events,

0 commit comments

Comments
 (0)