Card: 2.0 - measured test counts, gallery coverage, watchdog and audit
Browse files
README.md
CHANGED
|
@@ -94,7 +94,7 @@ X server. First the suite, headless:
|
|
| 94 |
```bash
|
| 95 |
python -m pip install -e ".[desktop,ocr,dev]"
|
| 96 |
pytest tests -q
|
| 97 |
-
# 227 passed, 2 skipped in 18.7s (that run was 0.3.0; 2.0 has grown this to
|
| 98 |
```
|
| 99 |
|
| 100 |
The two skips are the live-desktop tests. Give them a display and they run for real:
|
|
@@ -266,7 +266,7 @@ open problem: it is reliable on a clean screenshot and unreliable on a busy live
|
|
| 266 |
`warning: no text was found on screen, so the index is 96 numbered grid cells and the
|
| 267 |
agent is choosing blind` — instead of silently guessing.
|
| 268 |
|
| 269 |
-
**
|
| 270 |
that click a real button.
|
| 271 |
|
| 272 |
## Verified on real Windows
|
|
@@ -314,7 +314,7 @@ the harness itself, installed into site-packages
|
|
| 314 |
|
| 315 |
None of the three failures in the first row is a harness defect: two are
|
| 316 |
`ModuleNotFoundError: numpy` and the third is the guard bug documented below. With the
|
| 317 |
-
OCR engine installed, the same suite exits **0**. (Those Windows counts predate `trace.py` and 2.0; the harness suite has since grown to
|
| 318 |
|
| 319 |
**The emulated-OCR caveat.** onnxruntime requires AVX2, and QEMU's software emulation of
|
| 320 |
AVX2 is roughly two orders of magnitude slower than native. A suite that finishes in 17
|
|
@@ -340,7 +340,7 @@ exactly the kind of thing that only surfaces on a machine without an OCR engine.
|
|
| 340 |
|
| 341 |
## The test suite, in full
|
| 342 |
|
| 343 |
-
`pytest tests` collects **
|
| 344 |
weights. The last 2 are the opt-in live-desktop tests, skipped unless
|
| 345 |
`TINY_COMPUTER_AGENT_LIVE_TEST=1`. On a box without PyGObject, two of the 282 *fail* rather
|
| 346 |
than skip: they assert the AT-SPI reader is reachable, and that is the honest outcome.
|
|
@@ -356,19 +356,22 @@ than skip: they assert the AT-SPI reader is reachable, and that is the honest ou
|
|
| 356 |
| `tests/test_verify.py` | 7 | success is only claimed when a verifier can actually see it |
|
| 357 |
| `tests/test_safety.py` | 11 | deny lists, the kill switch, the blind-grid-click guard |
|
| 358 |
| `tests/test_grid_fallback.py` | 11 | the fallback index announces itself instead of guessing |
|
| 359 |
-
| `tests/test_desktop.py` |
|
| 360 |
-
| `tests/test_cli.py` |
|
| 361 |
| `tests/test_trace.py` | 28 | traces: a run recorded with no pixels, replayed against another brain, and scored against ground truth |
|
| 362 |
| `tests/test_models.py` | 7 | the model backends: lazy loading, device choice, honest refusals |
|
| 363 |
| `tests/test_api_surface.py` | 4 | the public API is a promise: everything advertised is importable, and nothing public is left out |
|
| 364 |
| `tests/test_decoding.py` | 24 | the mask, the answer space, the retry ladder, the vote - and that an echo is refused |
|
| 365 |
-
| `tests/test_accessibility.py` |
|
| 366 |
| `tests/test_diff.py` | 8 | what changed between two frames: added, removed, moved, and the verifier that remembers |
|
| 367 |
| `tests/test_stepwise.py` | 11 | begin / step / pause / resume, and that it equals the batch loop |
|
| 368 |
| `tests/test_ocr_guard.py` | 4 | the OCR guard really skips with no engine - the guard that never fired |
|
| 369 |
-
| `tests/test_gates.py` |
|
| 370 |
| `tests/test_skills.py` | 17 | only a verified run becomes a skill; re-grounding by label; aiming at nothing raises |
|
| 371 |
| `tests/test_runner.py` | 10 | a queue, a JSONL resume file, and a report counts what was verified |
|
|
|
|
|
|
|
|
|
|
| 372 |
| `tests/test_live_desktop.py` | 2 | **needs a display, opt-in**: OCR finds a real button at the pixels it was drawn at, and a real click makes the app react |
|
| 373 |
|
| 374 |
```bash
|
|
@@ -383,7 +386,7 @@ TINY_COMPUTER_AGENT_LIVE_TEST=1 pytest tests -q # + the 2 live-desktop tests, o
|
|
| 383 |
pip install -e ".[ocr]" # labels from pixels (RapidOCR: no system binary)
|
| 384 |
pip install -e ".[desktop,ocr]" # + real capture and real input
|
| 385 |
pip install -e ".[vision,ocr]" # + the tiny model brains
|
| 386 |
-
pytest #
|
| 387 |
```
|
| 388 |
|
| 389 |
The accessibility detector needs system packages rather than an extra:
|
|
@@ -499,6 +502,8 @@ this: an answer file with non-canonical XML namespaces is **silently ignored** b
|
|
| 499 |
| `skills.py` | **2.0** - Skills: a *verified* run written down once, replayed by `SkillBrain` with no model. Labels, never coordinates |
|
| 500 |
| `gates.py` | **2.0** - Approval gates: `AllowOnlyLabels`, `InteractiveGate` (yes / no / always / never), `AllOf` / `AnyOf`. A refusal is a step |
|
| 501 |
| `runner.py` | **2.0** - `TaskRunner`: a queue of goals, a JSONL resume file, and a `RunReport` that counts what was *verified* |
|
|
|
|
|
|
|
| 502 |
| `showcase.py` | The capability gallery: one real screenshot per capability, checked against the public API |
|
| 503 |
| `measure_vlm.py` | Measures the four ways to ask a model, on a real screen, and prints the table |
|
| 504 |
| `evaluate_daily.py` | A day's tasks against a real app, verified by the app's own log |
|
|
@@ -549,9 +554,10 @@ That click was found **without reading a single pixel**: the window's own status
|
|
| 549 |
reads `last action: Save` afterwards, and the app wrote `Save` to its marker file.
|
| 550 |
`--detector best` tries the tree first, then OCR, then the numbered grid.
|
| 551 |
|
| 552 |
-
**Honest limits.** AT-SPI is Linux-only here
|
| 553 |
-
`
|
| 554 |
-
|
|
|
|
| 555 |
which is why this is a detector and not a replacement.
|
| 556 |
|
| 557 |
## Can it do a day's work?
|
|
@@ -605,7 +611,7 @@ eleven tasks are a sample, not a survey.
|
|
| 605 |
|
| 606 |
## Capability gallery
|
| 607 |
|
| 608 |
-
**74 capabilities, one screenshot each - and
|
| 609 |
|
| 610 |
Nothing here is a mock-up. Each picture is produced by [`showcase.py`](showcase.py): a command or a call is run for real, and its actual output - or the real desktop it acted on - is what you see. The desktop shots are real screen captures on a real X server (Xvfb, 1280x800), including a real mouse click verified by the target application's own reaction.
|
| 611 |
|
|
@@ -1233,15 +1239,15 @@ success.
|
|
| 1233 |
**Works:** real capture, a real OCR index with coordinates that land on the right pixel,
|
| 1234 |
a real click an app reacts to, verified success, the closed vocabulary, the policy gates,
|
| 1235 |
the no-progress guard, echo detection, the doctor, the CLI, portable traces that score a brain's choices offline, skills learned from verified runs,
|
| 1236 |
-
a resumable task runner, operator approval gates,
|
| 1237 |
reads a button label from pixels on a clean screenshot — and the same suite passing inside
|
| 1238 |
a real Windows Server 2022 guest with no KVM (109 passed, exit 0, with the OCR-inference
|
| 1239 |
tests excluded as impractically slow under emulation).
|
| 1240 |
|
| 1241 |
**Does not work yet:** reliability on a *live* screen. The 256M VLM echoed the prompt
|
| 1242 |
-
there; the 0.5B text planner picks wrong actions;
|
| 1243 |
-
|
| 1244 |
-
when no verifier is given.
|
| 1245 |
|
| 1246 |
## Roadmap
|
| 1247 |
|
|
@@ -1266,7 +1272,15 @@ when no verifier is given.
|
|
| 1266 |
- [x] **2.0** Operator approval gates - a label whitelist, or ask before every action
|
| 1267 |
- [x] **2.0** A resumable task queue with a report, and structural verification of "it worked"
|
| 1268 |
- [x] **2.0** `skills` and `batch` subcommands, and an example queue at `tasks/day.json`
|
| 1269 |
-
- [
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1270 |
- [ ] **Verify UI Automation** (Windows) against a real Windows desktop. The reader and the
|
| 1271 |
detector are implemented and unit-tested against a synthetic control tree, and
|
| 1272 |
`accessibility_available("uia")` reports the truth on any machine - but no Windows
|
|
|
|
| 94 |
```bash
|
| 95 |
python -m pip install -e ".[desktop,ocr,dev]"
|
| 96 |
pytest tests -q
|
| 97 |
+
# 227 passed, 2 skipped in 18.7s (that run was 0.3.0; 2.0 has grown this to 327)
|
| 98 |
```
|
| 99 |
|
| 100 |
The two skips are the live-desktop tests. Give them a display and they run for real:
|
|
|
|
| 266 |
`warning: no text was found on screen, so the index is 96 numbered grid cells and the
|
| 267 |
agent is choosing blind` — instead of silently guessing.
|
| 268 |
|
| 269 |
+
**327 tests collect** - 315 pass, 12 skip for want of a display or an AT-SPI typelib - including real OCR over a real image and (opt-in) live-desktop tests
|
| 270 |
that click a real button.
|
| 271 |
|
| 272 |
## Verified on real Windows
|
|
|
|
| 314 |
|
| 315 |
None of the three failures in the first row is a harness defect: two are
|
| 316 |
`ModuleNotFoundError: numpy` and the third is the guard bug documented below. With the
|
| 317 |
+
OCR engine installed, the same suite exits **0**. (Those Windows counts predate `trace.py` and 2.0; the harness suite has since grown to 327 tests.)
|
| 318 |
|
| 319 |
**The emulated-OCR caveat.** onnxruntime requires AVX2, and QEMU's software emulation of
|
| 320 |
AVX2 is roughly two orders of magnitude slower than native. A suite that finishes in 17
|
|
|
|
| 340 |
|
| 341 |
## The test suite, in full
|
| 342 |
|
| 343 |
+
`pytest tests` collects **327 tests**. 325 run headless — no display, no network, no
|
| 344 |
weights. The last 2 are the opt-in live-desktop tests, skipped unless
|
| 345 |
`TINY_COMPUTER_AGENT_LIVE_TEST=1`. On a box without PyGObject, two of the 282 *fail* rather
|
| 346 |
than skip: they assert the AT-SPI reader is reachable, and that is the honest outcome.
|
|
|
|
| 356 |
| `tests/test_verify.py` | 7 | success is only claimed when a verifier can actually see it |
|
| 357 |
| `tests/test_safety.py` | 11 | deny lists, the kill switch, the blind-grid-click guard |
|
| 358 |
| `tests/test_grid_fallback.py` | 11 | the fallback index announces itself instead of guessing |
|
| 359 |
+
| `tests/test_desktop.py` | 12 | display detection, the preflight checks, `doctor` |
|
| 360 |
+
| `tests/test_cli.py` | 22 | the command line: `--doctor`, `--image`, `--live`, and the 2.0 `skills` / `batch` subcommands |
|
| 361 |
| `tests/test_trace.py` | 28 | traces: a run recorded with no pixels, replayed against another brain, and scored against ground truth |
|
| 362 |
| `tests/test_models.py` | 7 | the model backends: lazy loading, device choice, honest refusals |
|
| 363 |
| `tests/test_api_surface.py` | 4 | the public API is a promise: everything advertised is importable, and nothing public is left out |
|
| 364 |
| `tests/test_decoding.py` | 24 | the mask, the answer space, the retry ladder, the vote - and that an echo is refused |
|
| 365 |
+
| `tests/test_accessibility.py` | 27 | role filtering, the coordinate contract, the fallback chain, and real AT-SPI |
|
| 366 |
| `tests/test_diff.py` | 8 | what changed between two frames: added, removed, moved, and the verifier that remembers |
|
| 367 |
| `tests/test_stepwise.py` | 11 | begin / step / pause / resume, and that it equals the batch loop |
|
| 368 |
| `tests/test_ocr_guard.py` | 4 | the OCR guard really skips with no engine - the guard that never fired |
|
| 369 |
+
| `tests/test_gates.py` | 14 | the operator's yes: whitelists, ask-once gates, and a refusal that is a step |
|
| 370 |
| `tests/test_skills.py` | 17 | only a verified run becomes a skill; re-grounding by label; aiming at nothing raises |
|
| 371 |
| `tests/test_runner.py` | 10 | a queue, a JSONL resume file, and a report counts what was verified |
|
| 372 |
+
| `tests/test_watchdog.py` | 12 | a hung call becomes a named step instead of a stall - and the thread it leaves behind is admitted |
|
| 373 |
+
| `tests/test_audit.py` | 12 | the chain verifies, and editing, deleting or reordering a line is caught by number |
|
| 374 |
+
| `tests/test_atspi_guard.py` | 4 | the AT-SPI guard really skips without the typelib - the same trap as the OCR guard |
|
| 375 |
| `tests/test_live_desktop.py` | 2 | **needs a display, opt-in**: OCR finds a real button at the pixels it was drawn at, and a real click makes the app react |
|
| 376 |
|
| 377 |
```bash
|
|
|
|
| 386 |
pip install -e ".[ocr]" # labels from pixels (RapidOCR: no system binary)
|
| 387 |
pip install -e ".[desktop,ocr]" # + real capture and real input
|
| 388 |
pip install -e ".[vision,ocr]" # + the tiny model brains
|
| 389 |
+
pytest # 327 tests, no display or network needed
|
| 390 |
```
|
| 391 |
|
| 392 |
The accessibility detector needs system packages rather than an extra:
|
|
|
|
| 502 |
| `skills.py` | **2.0** - Skills: a *verified* run written down once, replayed by `SkillBrain` with no model. Labels, never coordinates |
|
| 503 |
| `gates.py` | **2.0** - Approval gates: `AllowOnlyLabels`, `InteractiveGate` (yes / no / always / never), `AllOf` / `AnyOf`. A refusal is a step |
|
| 504 |
| `runner.py` | **2.0** - `TaskRunner`: a queue of goals, a JSONL resume file, and a `RunReport` that counts what was *verified* |
|
| 505 |
+
| `watchdog.py` | **2.0** - `Watchdog`: a deadline on `decide` / `execute` / `capture`, so a hung call is a step with a reason, not a stall |
|
| 506 |
+
| `audit.py` | **2.0** - `AuditLog`: an append-only, hash-chained record of a run. Edit, delete or reorder a line and `verify_audit` names it |
|
| 507 |
| `showcase.py` | The capability gallery: one real screenshot per capability, checked against the public API |
|
| 508 |
| `measure_vlm.py` | Measures the four ways to ask a model, on a real screen, and prints the table |
|
| 509 |
| `evaluate_daily.py` | A day's tasks against a real app, verified by the app's own log |
|
|
|
|
| 554 |
reads `last action: Save` afterwards, and the app wrote `Save` to its marker file.
|
| 555 |
`--detector best` tries the tree first, then OCR, then the numbered grid.
|
| 556 |
|
| 557 |
+
**Honest limits.** AT-SPI is Linux-only here. UI Automation *is* implemented - `UiaReader` and
|
| 558 |
+
`UiaDetector`, unit-tested against a synthetic control tree - but it has never run against a
|
| 559 |
+
real Windows desktop, and `accessibility_available("uia")` reports the truth on any machine
|
| 560 |
+
rather than pretending, so an agent on Windows still falls back to OCR. Tk barely publishes a tree and a canvas publishes none,
|
| 561 |
which is why this is a detector and not a replacement.
|
| 562 |
|
| 563 |
## Can it do a day's work?
|
|
|
|
| 611 |
|
| 612 |
## Capability gallery
|
| 613 |
|
| 614 |
+
**74 capabilities, one screenshot each - and 163 of the package's 270 public callables (functions, methods, classes and protocols) appear in at least one of them**, together with 8 of its 26 public constants. **Not yet pictured: 107 callables** - 103 in the 2.0 operator layer (skills 29, the batch runner 20, approval gates 19, the structural diff 14, the audit log 13, the watchdog 8) and four CLI entry points. They are covered by tests rather than by pictures, and `showcase.py` prints the exact list. The counts are read off `gallery/index.json`, which records what each picture shows, and checked against the package by `showcase.api_surface()`.
|
| 615 |
|
| 616 |
Nothing here is a mock-up. Each picture is produced by [`showcase.py`](showcase.py): a command or a call is run for real, and its actual output - or the real desktop it acted on - is what you see. The desktop shots are real screen captures on a real X server (Xvfb, 1280x800), including a real mouse click verified by the target application's own reaction.
|
| 617 |
|
|
|
|
| 1239 |
**Works:** real capture, a real OCR index with coordinates that land on the right pixel,
|
| 1240 |
a real click an app reacts to, verified success, the closed vocabulary, the policy gates,
|
| 1241 |
the no-progress guard, echo detection, the doctor, the CLI, portable traces that score a brain's choices offline, skills learned from verified runs,
|
| 1242 |
+
a resumable task runner, operator approval gates, a watchdog that refuses to wait forever rather than stalling silently, a hash-chained audit log, 327 tests, a 256M VLM that
|
| 1243 |
reads a button label from pixels on a clean screenshot — and the same suite passing inside
|
| 1244 |
a real Windows Server 2022 guest with no KVM (109 passed, exit 0, with the OCR-inference
|
| 1245 |
tests excluded as impractically slow under emulation).
|
| 1246 |
|
| 1247 |
**Does not work yet:** reliability on a *live* screen. The 256M VLM echoed the prompt
|
| 1248 |
+
there; the 0.5B text planner picks wrong actions; UI Automation has never been exercised
|
| 1249 |
+
against a real Windows desktop, so an agent there still reads labels from OCR; and the
|
| 1250 |
+
harness still trusts a model's `done` when no verifier is given.
|
| 1251 |
|
| 1252 |
## Roadmap
|
| 1253 |
|
|
|
|
| 1272 |
- [x] **2.0** Operator approval gates - a label whitelist, or ask before every action
|
| 1273 |
- [x] **2.0** A resumable task queue with a report, and structural verification of "it worked"
|
| 1274 |
- [x] **2.0** `skills` and `batch` subcommands, and an example queue at `tasks/day.json`
|
| 1275 |
+
- [x] **2.0** A watchdog: a per-call and whole-episode deadline, so a call that never returns
|
| 1276 |
+
becomes a named step instead of a console that looks like it is thinking
|
| 1277 |
+
- [x] **2.0** An audit log: an append-only, hash-chained record of a run, and `--verify-audit`
|
| 1278 |
+
to check it - so the evidence survives the container that produced it
|
| 1279 |
+
- [x] **2.0** `--gate`, `--audit` and `--verify-audit` on the command line, and the shared
|
| 1280 |
+
AT-SPI guard, so a machine without the typelib skips rather than failing
|
| 1281 |
+
- [ ] A gallery run that pictures the 2.0 additions: **107 of the package's 270 public callables**
|
| 1282 |
+
have no screenshot yet - 103 in the operator layer and four CLI entry points. Adding the
|
| 1283 |
+
entries to `showcase_catalog.py` is what closes it
|
| 1284 |
- [ ] **Verify UI Automation** (Windows) against a real Windows desktop. The reader and the
|
| 1285 |
detector are implemented and unit-tested against a synthetic control tree, and
|
| 1286 |
`accessibility_available("uia")` reports the truth on any machine - but no Windows
|