quality-loop 0.3.0__tar.gz → 0.4.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. {quality_loop-0.3.0/quality_loop.egg-info → quality_loop-0.4.1}/PKG-INFO +3 -1
  2. {quality_loop-0.3.0 → quality_loop-0.4.1}/README.md +2 -0
  3. {quality_loop-0.3.0 → quality_loop-0.4.1}/quality_loop/__init__.py +1 -1
  4. quality_loop-0.4.1/quality_loop/awake.py +85 -0
  5. quality_loop-0.4.1/quality_loop/guest/__init__.py +580 -0
  6. quality_loop-0.4.1/quality_loop/guest/boot/__init__.py +7 -0
  7. quality_loop-0.4.1/quality_loop/guest/boot/launch.py +35 -0
  8. quality_loop-0.4.1/quality_loop/guest/boot/screen_agent.py +54 -0
  9. quality_loop-0.4.1/quality_loop/guest/screens.py +138 -0
  10. {quality_loop-0.3.0 → quality_loop-0.4.1}/quality_loop/runner.py +3 -3
  11. {quality_loop-0.3.0 → quality_loop-0.4.1/quality_loop.egg-info}/PKG-INFO +3 -1
  12. {quality_loop-0.3.0 → quality_loop-0.4.1}/quality_loop.egg-info/SOURCES.txt +8 -0
  13. quality_loop-0.4.1/tests/test_awake.py +45 -0
  14. quality_loop-0.4.1/tests/test_guest.py +215 -0
  15. {quality_loop-0.3.0 → quality_loop-0.4.1}/LICENSE +0 -0
  16. {quality_loop-0.3.0 → quality_loop-0.4.1}/pyproject.toml +0 -0
  17. {quality_loop-0.3.0 → quality_loop-0.4.1}/quality_loop/OPERATOR.md +0 -0
  18. {quality_loop-0.3.0 → quality_loop-0.4.1}/quality_loop/__main__.py +0 -0
  19. {quality_loop-0.3.0 → quality_loop-0.4.1}/quality_loop/adapter.py +0 -0
  20. {quality_loop-0.3.0 → quality_loop-0.4.1}/quality_loop/audit.py +0 -0
  21. {quality_loop-0.3.0 → quality_loop-0.4.1}/quality_loop/cli.py +0 -0
  22. {quality_loop-0.3.0 → quality_loop-0.4.1}/quality_loop/compare.py +0 -0
  23. {quality_loop-0.3.0 → quality_loop-0.4.1}/quality_loop/config.py +0 -0
  24. {quality_loop-0.3.0 → quality_loop-0.4.1}/quality_loop/drive/__init__.py +0 -0
  25. {quality_loop-0.3.0 → quality_loop-0.4.1}/quality_loop/drive/_mac.py +0 -0
  26. {quality_loop-0.3.0 → quality_loop-0.4.1}/quality_loop/drive/_windows.py +0 -0
  27. {quality_loop-0.3.0 → quality_loop-0.4.1}/quality_loop/drive/keys.py +0 -0
  28. {quality_loop-0.3.0 → quality_loop-0.4.1}/quality_loop/drive/terminal.py +0 -0
  29. {quality_loop-0.3.0 → quality_loop-0.4.1}/quality_loop/install.py +0 -0
  30. {quality_loop-0.3.0 → quality_loop-0.4.1}/quality_loop/mcp.py +0 -0
  31. {quality_loop-0.3.0 → quality_loop-0.4.1}/quality_loop/operator.py +0 -0
  32. {quality_loop-0.3.0 → quality_loop-0.4.1}/quality_loop/pages.py +0 -0
  33. {quality_loop-0.3.0 → quality_loop-0.4.1}/quality_loop/processes.py +0 -0
  34. {quality_loop-0.3.0 → quality_loop-0.4.1}/quality_loop/progress.py +0 -0
  35. {quality_loop-0.3.0 → quality_loop-0.4.1}/quality_loop/report.py +0 -0
  36. {quality_loop-0.3.0 → quality_loop-0.4.1}/quality_loop/room.py +0 -0
  37. {quality_loop-0.3.0 → quality_loop-0.4.1}/quality_loop/scenario.py +0 -0
  38. {quality_loop-0.3.0 → quality_loop-0.4.1}/quality_loop/screens/__init__.py +0 -0
  39. {quality_loop-0.3.0 → quality_loop-0.4.1}/quality_loop/screens/_mac.py +0 -0
  40. {quality_loop-0.3.0 → quality_loop-0.4.1}/quality_loop/screens/_windows.py +0 -0
  41. {quality_loop-0.3.0 → quality_loop-0.4.1}/quality_loop/session.py +0 -0
  42. {quality_loop-0.3.0 → quality_loop-0.4.1}/quality_loop/skill/SKILL.md +0 -0
  43. {quality_loop-0.3.0 → quality_loop-0.4.1}/quality_loop/skill/references/filing.md +0 -0
  44. {quality_loop-0.3.0 → quality_loop-0.4.1}/quality_loop/skill/references/setup.md +0 -0
  45. {quality_loop-0.3.0 → quality_loop-0.4.1}/quality_loop/standin.py +0 -0
  46. {quality_loop-0.3.0 → quality_loop-0.4.1}/quality_loop/transcript.py +0 -0
  47. {quality_loop-0.3.0 → quality_loop-0.4.1}/quality_loop.egg-info/dependency_links.txt +0 -0
  48. {quality_loop-0.3.0 → quality_loop-0.4.1}/quality_loop.egg-info/entry_points.txt +0 -0
  49. {quality_loop-0.3.0 → quality_loop-0.4.1}/quality_loop.egg-info/requires.txt +0 -0
  50. {quality_loop-0.3.0 → quality_loop-0.4.1}/quality_loop.egg-info/top_level.txt +0 -0
  51. {quality_loop-0.3.0 → quality_loop-0.4.1}/setup.cfg +0 -0
  52. {quality_loop-0.3.0 → quality_loop-0.4.1}/tests/test_core.py +0 -0
  53. {quality_loop-0.3.0 → quality_loop-0.4.1}/tests/test_drive.py +0 -0
  54. {quality_loop-0.3.0 → quality_loop-0.4.1}/tests/test_loop.py +0 -0
  55. {quality_loop-0.3.0 → quality_loop-0.4.1}/tests/test_mcp.py +0 -0
  56. {quality_loop-0.3.0 → quality_loop-0.4.1}/tests/test_operator.py +0 -0
  57. {quality_loop-0.3.0 → quality_loop-0.4.1}/tests/test_scenario_report.py +0 -0
  58. {quality_loop-0.3.0 → quality_loop-0.4.1}/tests/test_screens.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quality-loop
3
- Version: 0.3.0
3
+ Version: 0.4.1
4
4
  Summary: Measure what a product teaches - its skill, tools or manual - with isolated agent runs of real tasks, reported by an operator agent
5
5
  Author: craftware
6
6
  License-Expression: MIT
@@ -110,6 +110,8 @@ under a policy the person sets. See [Running the loop](https://github.com/crftwr
110
110
  launch command and a redirected home up to evidence tools of its own.
111
111
  - [Reports](https://github.com/crftwr/quality-loop/blob/main/docs/reports.md): problems, severities, kinds and ids, the
112
112
  reproduction record, and `FINDINGS.md` / `findings.json`.
113
+ - [Running the app in a VM](https://github.com/crftwr/quality-loop/blob/main/docs/guests.md): one image per OS,
114
+ the guest over SSH, a clean machine every run, screens in the guest.
113
115
  - [Isolation and permissions](https://github.com/crftwr/quality-loop/blob/main/docs/isolation.md): what keeps the room, the
114
116
  operator and the conductor in bounds, why each rule exists, and the known limits.
115
117
  - [Development](https://github.com/crftwr/quality-loop/blob/main/docs/development.md): tests, live tests, releasing.
@@ -79,6 +79,8 @@ under a policy the person sets. See [Running the loop](https://github.com/crftwr
79
79
  launch command and a redirected home up to evidence tools of its own.
80
80
  - [Reports](https://github.com/crftwr/quality-loop/blob/main/docs/reports.md): problems, severities, kinds and ids, the
81
81
  reproduction record, and `FINDINGS.md` / `findings.json`.
82
+ - [Running the app in a VM](https://github.com/crftwr/quality-loop/blob/main/docs/guests.md): one image per OS,
83
+ the guest over SSH, a clean machine every run, screens in the guest.
82
84
  - [Isolation and permissions](https://github.com/crftwr/quality-loop/blob/main/docs/isolation.md): what keeps the room, the
83
85
  operator and the conductor in bounds, why each rule exists, and the known limits.
84
86
  - [Development](https://github.com/crftwr/quality-loop/blob/main/docs/development.md): tests, live tests, releasing.
@@ -12,4 +12,4 @@ room is handed, putting the user's machine back - is code here, not an
12
12
  agent's discretion.
13
13
  """
14
14
 
15
- __version__ = "0.3.0"
15
+ __version__ = "0.4.1"
@@ -0,0 +1,85 @@
1
+ """This machine kept from idle sleep while a run is going (issue #16).
2
+
3
+ A run is mostly waiting - on a session, on a VM - and a machine left alone
4
+ for it goes to sleep: a Mac on battery within minutes. Nothing fails; the
5
+ run just stops while it sleeps, and a VM guest stops with it. One run of
6
+ 43 minutes had spent 40 of them asleep.
7
+
8
+ So the runner holds the platform's "the system is busy" assertion for the
9
+ length of the run, and lets go when it ends. The display may still sleep,
10
+ and closing a laptop's lid still sleeps it: those are the person's to choose.
11
+
12
+ - macOS: ``caffeinate -i -w <this pid>``, which also ends with this process
13
+ if it dies without letting go.
14
+ - Windows: ``SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED)``
15
+ from the runner's thread, cleared at the end (and by Windows when the
16
+ process exits).
17
+ - Elsewhere nothing is held.
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ import contextlib
23
+ import os
24
+ import shutil
25
+ import subprocess
26
+ import sys
27
+
28
+ ES_CONTINUOUS = 0x80000000
29
+ ES_SYSTEM_REQUIRED = 0x00000001
30
+
31
+
32
+ @contextlib.contextmanager
33
+ def held(say=print):
34
+ """Idle sleep held off for the ``with`` block, where the platform allows."""
35
+ release = _hold()
36
+ if release is None:
37
+ yield False
38
+ return
39
+ say("awake: idle sleep is held off for the run (closing the lid still sleeps it)")
40
+ try:
41
+ yield True
42
+ finally:
43
+ release()
44
+
45
+
46
+ def _hold():
47
+ if sys.platform == "darwin":
48
+ return _hold_mac()
49
+ if os.name == "nt":
50
+ return _hold_windows()
51
+ return None
52
+
53
+
54
+ def _hold_mac():
55
+ caffeinate = shutil.which("caffeinate")
56
+ if caffeinate is None:
57
+ return None
58
+ try:
59
+ process = subprocess.Popen([caffeinate, "-i", "-w", str(os.getpid())],
60
+ stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL,
61
+ stderr=subprocess.DEVNULL)
62
+ except OSError:
63
+ return None
64
+
65
+ def release():
66
+ process.terminate()
67
+ try:
68
+ process.wait(5)
69
+ except subprocess.TimeoutExpired:
70
+ process.kill()
71
+
72
+ return release
73
+
74
+
75
+ def _hold_windows():
76
+ import ctypes
77
+ try:
78
+ kernel32 = ctypes.WinDLL("kernel32")
79
+ except OSError:
80
+ return None
81
+ kernel32.SetThreadExecutionState.restype = ctypes.c_uint
82
+ kernel32.SetThreadExecutionState.argtypes = [ctypes.c_uint]
83
+ if not kernel32.SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED):
84
+ return None
85
+ return lambda: kernel32.SetThreadExecutionState(ES_CONTINUOUS)