MIL-013: Debian container and Windows Sandbox checks

A Debian image (python:3.13-slim-bookworm with Xvfb and GNOME's window
manager) runs the linters, the whole test suite, the screenshots and the
smoke test of a PyInstaller Linux binary and of the source program, so no
window opens on the development machine. The screenshot tool works on
Linux, a smoke driver presses the known button with the virtual mouse, and
Windows Sandbox scripts are written (not run, they open a window on the
host). README and the wiki Install pages describe it. Review records
RC-077 (Python) and RC-078 (shell).

Task: MIL-013#2
Task: MIL-013#3
Task: MIL-013#4
Task: MIL-013#5
Task: MIL-013#6
Task: MIL-013#7
Task: MIL-013#9
Task: MIL-013#10
This commit is contained in:
2026-10-15 10:09:48 +08:00
parent a72ddb9175
commit f8ccfca02e
24 changed files with 1205 additions and 10 deletions
+63
View File
@@ -0,0 +1,63 @@
# RC-077: Review of PY-MIL-013
## Metadata
| Key | Value |
| --- | --- |
| ID | RC-077 |
| CrossReference | [PY-MIL-013], [QC-PY-001] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-15 | Accepted | Jens Tirsvad Nielsen | AI as stand-in for S01 | Initial version | pending |
---
## Artifact Under Review
- Instance reviewed: [PY-MIL-013]
- Checklist used: [QC-PY-001]
- Scope: full review
- Language and domain: n/a (technical type)
- Language reviewer: none
- Mode: autonomous, not independent (reviewer is `AI as stand-in for S01`)
## Checklist Results
| # | Criterion | Status | Evidence/Notes |
| --- | --- | --- | --- |
| 1 | Packages, modules, functions, variables, classes and constants follow PEP 8 casing (`snake_case`, `PascalCase`, `UPPER_SNAKE`) | Pass | PEP 8 casing holds in `smoke_driver.py`, `capture_screenshots.py` and the new tests; ruff rule set N passes. |
| 2 | Names state purpose in the domain's language; no unexplained abbreviations, no single-letter names outside tiny scopes | Pass | Names state purpose (`known_button_point`, `parse_geometry`, `platform_kind`, `window_box`, `progress_ok`). |
| 3 | Code is produced by the project's formatter and passes its linter with no unexplained suppressions | Pass | `ruff check` and `ruff format --check` pass on 83 files; the one `type: ignore` for `os.killpg` names the errors it silences. |
| 4 | Every function and method signature is type-annotated, including `-> None` | Pass | Every signature is annotated; `mypy --strict` passes on 49 source files and on the four tool scripts. |
| 5 | No bare `except:`, no swallowed exceptions; specific exceptions are raised and the cause is kept (`raise ... from`) | Pass | Process start and stop failures are turned into a failed run with its reason, not swallowed; no bare `except`. |
| 6 | No mutable default arguments and no shadowed builtins | Pass | No mutable defaults, no builtin shadowed. |
| 7 | Files, locks and connections are managed with context managers | Pass | Temporary folders are context-managed or removed in `finally`; processes are stopped as a group. |
| 8 | Public modules, classes and functions have docstrings that say what, not how | Pass | Public modules, functions and constants have Doxygen docstrings; `doxygen Doxyfile` covers `src` and ends with 0 warnings. |
| 9 | Logging uses `logging`, not `print`; no secrets or personal data in log output | Pass | No `print` in `src`; the tools write results with `sys.stdout.write` as JSON lines. |
| 10 | Classes and operations trace to the Design Class Diagram they implement; deviations are recorded | Pass | No program class changed; the tools are packaging and check support outside the design classes (ADR-0005). |
| 11 | Tests exist for new behaviour, are named for the behaviour, and do not depend on order or the network | Pass | 272 tests; the pure parts of the smoke driver and the capture tool are unit tested with fakes (no xdotool, no display); the whole suite passed twice in the Debian container including the real-window e2e tests. |
| 12 | Type checker runs in strict mode without errors; `Any` is justified in a comment | Pass | `mypy --strict` reports no issue; no `Any`. |
| 13 | Dependencies are declared and pinned in the project's dependency file, none unused | Pass | `dependencies` stays empty; the container image installs the extras `dev`, `speech`, `tools` and `build`. |
| 14 | Each piece of knowledge (a business rule, constant, format, validation or query) is defined in one place; code is merged only where it expresses the same knowledge, not where it merely looks alike | Pass | The click point, the geometry parsing and the success rule are one function each in `smoke_driver.py`. |
| 15 | Imports point inward: business-rule modules import no framework, database, UI or delivery-mechanism code, and there are no import cycles | Pass | `lint-imports` reports `Layers point inward` kept; the tools import only the composition root. |
| 16 | SOLID holds: a class or module has one reason to change; new behaviour is added by extension, not by editing a type test in several places; subclasses honour the contract of their base; protocols are narrow; high-level code depends on protocols or abstract types and receives its implementations | Pass | Pure functions with injected `run`, `start` and `sleep`; no function mixes the platform choice with drawing. |
| 17 | Source is laid out in the layers and folders of the clean architecture folder structure (`domain`, `application`, `infrastructure`, `presentation` packages under `src/`, and `unit`, `integration` and `e2e` under `tests/`), in `snake_case`, and imports between the layers point inward | Pass | `tools/docker/` holds the container scripts and the driver; the layers and the test folders are unchanged. |
| 18 | File and folder names are English, contain no space and no character outside letters, digits, `-`, `_` and `.`, and follow the casing of the language for files | Pass | File and folder names follow the rule. |
## Overall Verdict
Go — every Mandatory criterion passes. Reviewed by AI as stand-in for S01; not independent of the author.
Reviewed in the Debian container (python:3.13-slim-bookworm, Python 3.13.16).
## Action Items
| Action | Owner | Due |
| --- | --- | --- |
| none | - | - |
---
[PY-MIL-013]: ../../../tools
[QC-PY-001]: ../../../framework/qc/code/qc-programming-python.md
+60
View File
@@ -0,0 +1,60 @@
# RC-078: Review of SH-MIL-013
## Metadata
| Key | Value |
| --- | --- |
| ID | RC-078 |
| CrossReference | [SH-MIL-013], [QC-SH-001] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-15 | Accepted | Jens Tirsvad Nielsen | AI as stand-in for S01 | Initial version | pending |
---
## Artifact Under Review
- Instance reviewed: [SH-MIL-013]
- Checklist used: [QC-SH-001]
- Scope: full review
- Language and domain: n/a (technical type)
- Language reviewer: none
- Mode: autonomous, not independent (reviewer is `AI as stand-in for S01`)
## Checklist Results
| # | Criterion | Status | Evidence/Notes |
| --- | --- | --- | --- |
| 1 | Starts with `#!/usr/bin/env bash` and `set -euo pipefail` (or a comment explains the exception) | Pass | Every executable script starts with `#!/usr/bin/env bash` and `set -euo pipefail`; `start-desktop.sh` is sourced, says so in its header and sets no shell options in the caller. |
| 2 | Every expansion is quoted; lists are arrays; tests use `[[ ]]` and `$(...)` | Pass | Expansions are quoted, tests use `[[ ]]`, command substitutions use `$(...)`; shellcheck reports nothing. |
| 3 | Names follow the conventions: `kebab-case.sh` files, `snake_case` functions and variables, `UPPER_SNAKE` constants and environment variables | Pass | File names are kebab-case; variables are snake_case, environment variables UPPER_SNAKE. |
| 4 | Passes `shellcheck` and `bash -n` with no unexplained `disable` comments | Pass | `shellcheck -x` and `bash -n` pass on all six scripts with no `disable` comment. |
| 5 | Errors go to standard error with an `error:` message and a non-zero exit code; bad or missing arguments print a usage line | Pass | Errors go to standard error with `error:`, a usage line is printed, and bad arguments exit with 2 (`selftest.sh` checks it). |
| 6 | Temporary files use `mktemp` with a `trap ... EXIT` cleanup; no fixed `/tmp` names | Pass | `start-desktop.sh` uses `mktemp -d` with a `trap ... EXIT`; no fixed `/tmp` names remain. |
| 7 | No secret is written in the script, echoed, or put on a command line; secrets come from the environment or a gitignored file | Pass | No secret is used or written. |
| 8 | A script that changes state outside its own directory defaults to a dry run or needs an explicit flag, and says so in its header | Pass | `in-container.sh` is the only script that deletes a folder; its header says so and it refuses the repository and `/` as work folder. |
| 9 | A header comment states purpose, usage, options, environment variables and exit codes | Pass | Each header gives purpose, usage, environment, tools and exit codes. |
| 10 | The script implements a task or design it cites; deviations are recorded | Pass | The scripts implement tasks 3, 4 and 6 of MIL-013 and ADR-0005; the fallback from GNOME Shell to mutter is recorded in the output. |
| 11 | Behaviour is tested for success, failure and any disabled or bypass path | Pass | `selftest.sh` tests the refusals and `bash -n`; the success paths are the container runs of `run-checks.sh`, `screenshots.sh` and `smoke-binary.sh` (recorded in RC-079). |
| 12 | Formatted with `shfmt` (or the project's formatter) | Fail | `shfmt` was not run; Optional, deferred. |
| 13 | Safe to re-run: a second run does not duplicate or corrupt what the first did | Pass | The second container run gave the same result as the first (272 passed, 5 of 5 smoke runs). |
| 14 | Bash version and external tools it needs are stated; GNU-only options are named | Pass | Bash and the external tools are listed under `Needs`; GNU `tar --exclude` is named by the tool list. |
| 15 | A command sequence, constant, path or message used more than once is defined once (a function, a variable or a sourced helper); a helper is shared between scripts only where it expresses the same knowledge | Pass | The desktop start is defined once in `start-desktop.sh` and sourced by the three scripts. |
## Overall Verdict
Go — every Mandatory criterion passes; the failed Optional criteria are deferred with an action item. Reviewed by AI as stand-in for S01; not independent of the author.
Container run of the self test: 11 cases ok.
## Action Items
| Action | Owner | Due |
| --- | --- | --- |
| Criterion 12 (Optional) failed: Formatted with `shfmt` (or the project's formatter); fix or accept the deferral | S01 | 2026-10-21 |
---
[SH-MIL-013]: ../../../tools/docker
[QC-SH-001]: ../../../framework/qc/code/qc-programming-shell.md
+6
View File
@@ -72,6 +72,8 @@ updated whenever an artifact instance is created or reviewed.
| PY-MIL-011 | Python Source Code | - | - | DCD-001, ADR-0002 | - | [RC-066] |
| PY-FIX-001 | Python Source Code | - | - | PY-MIL-008 | - | [RC-049] |
| PY-MIL-012 | Python Source Code | - | - | MIL-012 | - | [RC-071] |
| PY-MIL-013 | Python Source Code | - | - | MIL-013 | - | [RC-077] |
| SH-MIL-013 | Shell Script | - | - | MIL-013 | - | [RC-078] |
## Coverage Notes
@@ -207,3 +209,7 @@ updated whenever an artifact instance is created or reviewed.
[MIL-013]: ../milestones/mil-013-container-checks.md
[ADR-0005]: ../adr/adr-0005-container-and-sandbox-checks.md
[f86bc91]: https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/031-flash-card/commit/f86bc9136e7719fbcd049a4f803959019b7d2df3
[RC-077]: ./reviews/rc-077-py-mil-013.md
[RC-078]: ./reviews/rc-078-sh-mil-013.md
[PY-MIL-013]: ../../tools
[SH-MIL-013]: ../../tools/docker