workjig 0.1.0__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 (92) hide show
  1. workjig-0.1.0/LICENSE +21 -0
  2. workjig-0.1.0/MANIFEST.in +6 -0
  3. workjig-0.1.0/PKG-INFO +171 -0
  4. workjig-0.1.0/README.md +140 -0
  5. workjig-0.1.0/adr/001-name-physical-launch-records.md +58 -0
  6. workjig-0.1.0/adr/002-classify-terminal-harness-failures.md +88 -0
  7. workjig-0.1.0/adr/003-separate-execution-controls-from-work-identity.md +101 -0
  8. workjig-0.1.0/adr/README.md +93 -0
  9. workjig-0.1.0/adr/evidence/README.md +48 -0
  10. workjig-0.1.0/adr/evidence/execution-controls/README.md +73 -0
  11. workjig-0.1.0/adr/evidence/execution-policy-and-continuation-proposal.md +125 -0
  12. workjig-0.1.0/adr/evidence/terminal-failures/README.md +254 -0
  13. workjig-0.1.0/adr/evidence/terminal-failures/captures/claude-auth/result.json +48 -0
  14. workjig-0.1.0/adr/evidence/terminal-failures/captures/claude-auth/stderr.log +0 -0
  15. workjig-0.1.0/adr/evidence/terminal-failures/captures/claude-auth/stdout.jsonl +4 -0
  16. workjig-0.1.0/adr/evidence/terminal-failures/captures/claude-billing/result.json +48 -0
  17. workjig-0.1.0/adr/evidence/terminal-failures/captures/claude-billing/stderr.log +0 -0
  18. workjig-0.1.0/adr/evidence/terminal-failures/captures/claude-billing/stdout.jsonl +4 -0
  19. workjig-0.1.0/adr/evidence/terminal-failures/captures/claude-quota/result.json +48 -0
  20. workjig-0.1.0/adr/evidence/terminal-failures/captures/claude-quota/stderr.log +0 -0
  21. workjig-0.1.0/adr/evidence/terminal-failures/captures/claude-quota/stdout.jsonl +4 -0
  22. workjig-0.1.0/adr/evidence/terminal-failures/captures/claude-recover/result.json +52 -0
  23. workjig-0.1.0/adr/evidence/terminal-failures/captures/claude-recover/stderr.log +0 -0
  24. workjig-0.1.0/adr/evidence/terminal-failures/captures/claude-recover/stdout.jsonl +5 -0
  25. workjig-0.1.0/adr/evidence/terminal-failures/captures/claude-success/result.json +37 -0
  26. workjig-0.1.0/adr/evidence/terminal-failures/captures/claude-success/stderr.log +0 -0
  27. workjig-0.1.0/adr/evidence/terminal-failures/captures/claude-success/stdout.jsonl +4 -0
  28. workjig-0.1.0/adr/evidence/terminal-failures/captures/codex-auth/result.json +36 -0
  29. workjig-0.1.0/adr/evidence/terminal-failures/captures/codex-auth/stderr.log +2 -0
  30. workjig-0.1.0/adr/evidence/terminal-failures/captures/codex-auth/stdout.jsonl +5 -0
  31. workjig-0.1.0/adr/evidence/terminal-failures/captures/codex-exhausted/result.json +51 -0
  32. workjig-0.1.0/adr/evidence/terminal-failures/captures/codex-exhausted/stderr.log +2 -0
  33. workjig-0.1.0/adr/evidence/terminal-failures/captures/codex-exhausted/stdout.jsonl +6 -0
  34. workjig-0.1.0/adr/evidence/terminal-failures/captures/codex-quota/result.json +36 -0
  35. workjig-0.1.0/adr/evidence/terminal-failures/captures/codex-quota/stderr.log +2 -0
  36. workjig-0.1.0/adr/evidence/terminal-failures/captures/codex-quota/stdout.jsonl +5 -0
  37. workjig-0.1.0/adr/evidence/terminal-failures/captures/codex-recover/result.json +40 -0
  38. workjig-0.1.0/adr/evidence/terminal-failures/captures/codex-recover/stderr.log +2 -0
  39. workjig-0.1.0/adr/evidence/terminal-failures/captures/codex-recover/stdout.jsonl +6 -0
  40. workjig-0.1.0/adr/evidence/terminal-failures/captures/codex-success/result.json +25 -0
  41. workjig-0.1.0/adr/evidence/terminal-failures/captures/codex-success/stderr.log +2 -0
  42. workjig-0.1.0/adr/evidence/terminal-failures/captures/codex-success/stdout.jsonl +5 -0
  43. workjig-0.1.0/adr/evidence/terminal-failures/captures/pi-auth/result.json +48 -0
  44. workjig-0.1.0/adr/evidence/terminal-failures/captures/pi-auth/stderr.log +0 -0
  45. workjig-0.1.0/adr/evidence/terminal-failures/captures/pi-auth/stdout.jsonl +12 -0
  46. workjig-0.1.0/adr/evidence/terminal-failures/captures/pi-exhausted/result.json +63 -0
  47. workjig-0.1.0/adr/evidence/terminal-failures/captures/pi-exhausted/stderr.log +0 -0
  48. workjig-0.1.0/adr/evidence/terminal-failures/captures/pi-exhausted/stdout.jsonl +21 -0
  49. workjig-0.1.0/adr/evidence/terminal-failures/captures/pi-quota/result.json +48 -0
  50. workjig-0.1.0/adr/evidence/terminal-failures/captures/pi-quota/stderr.log +0 -0
  51. workjig-0.1.0/adr/evidence/terminal-failures/captures/pi-quota/stdout.jsonl +12 -0
  52. workjig-0.1.0/adr/evidence/terminal-failures/captures/pi-recover/result.json +52 -0
  53. workjig-0.1.0/adr/evidence/terminal-failures/captures/pi-recover/stderr.log +0 -0
  54. workjig-0.1.0/adr/evidence/terminal-failures/captures/pi-recover/stdout.jsonl +24 -0
  55. workjig-0.1.0/adr/evidence/terminal-failures/captures/pi-success/result.json +37 -0
  56. workjig-0.1.0/adr/evidence/terminal-failures/captures/pi-success/stderr.log +0 -0
  57. workjig-0.1.0/adr/evidence/terminal-failures/captures/pi-success/stdout.jsonl +15 -0
  58. workjig-0.1.0/adr/evidence/terminal-failures/local-probes.md +168 -0
  59. workjig-0.1.0/adr/evidence/terminal-failures/local_probe.py +445 -0
  60. workjig-0.1.0/docs/design-rationale.md +175 -0
  61. workjig-0.1.0/docs/design.md +144 -0
  62. workjig-0.1.0/docs/harness-evidence.md +208 -0
  63. workjig-0.1.0/docs/usage.md +256 -0
  64. workjig-0.1.0/examples/README.md +25 -0
  65. workjig-0.1.0/examples/ordinary_files.py +145 -0
  66. workjig-0.1.0/examples/ordinary_worker.py +71 -0
  67. workjig-0.1.0/examples/smoke_harness.py +104 -0
  68. workjig-0.1.0/pyproject.toml +33 -0
  69. workjig-0.1.0/setup.cfg +4 -0
  70. workjig-0.1.0/src/workjig/__init__.py +16 -0
  71. workjig-0.1.0/src/workjig/adapters.py +224 -0
  72. workjig-0.1.0/src/workjig/harness.py +255 -0
  73. workjig-0.1.0/src/workjig/models.py +229 -0
  74. workjig-0.1.0/src/workjig/outcomes.py +135 -0
  75. workjig-0.1.0/src/workjig/run.py +727 -0
  76. workjig-0.1.0/src/workjig.egg-info/PKG-INFO +171 -0
  77. workjig-0.1.0/src/workjig.egg-info/SOURCES.txt +90 -0
  78. workjig-0.1.0/src/workjig.egg-info/dependency_links.txt +1 -0
  79. workjig-0.1.0/src/workjig.egg-info/top_level.txt +1 -0
  80. workjig-0.1.0/tests/README.md +62 -0
  81. workjig-0.1.0/tests/conftest.py +12 -0
  82. workjig-0.1.0/tests/test_acceptance.py +587 -0
  83. workjig-0.1.0/tests/test_adapters.py +251 -0
  84. workjig-0.1.0/tests/test_continuation.py +299 -0
  85. workjig-0.1.0/tests/test_continuation_validation.py +118 -0
  86. workjig-0.1.0/tests/test_core.py +478 -0
  87. workjig-0.1.0/tests/test_metadata.py +112 -0
  88. workjig-0.1.0/tests/test_outcomes.py +378 -0
  89. workjig-0.1.0/tests/test_record_validation.py +201 -0
  90. workjig-0.1.0/tests/test_review.py +85 -0
  91. workjig-0.1.0/tests/test_smoke.py +85 -0
  92. workjig-0.1.0/uv.lock +112 -0
workjig-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Zbigniew Lukasiak
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,6 @@
1
+ recursive-include docs *.md
2
+ recursive-include adr *.md
3
+ recursive-include adr/evidence/terminal-failures *.py *.json *.jsonl *.log
4
+ recursive-include examples *.py *.md
5
+ recursive-include tests *.py *.md
6
+ include uv.lock
workjig-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,171 @@
1
+ Metadata-Version: 2.4
2
+ Name: workjig
3
+ Version: 0.1.0
4
+ Summary: Run headless LLM coding harnesses as subprocess workers from Python workflows
5
+ License: MIT License
6
+
7
+ Copyright (c) 2026 Zbigniew Lukasiak
8
+
9
+ Permission is hereby granted, free of charge, to any person obtaining a copy
10
+ of this software and associated documentation files (the "Software"), to deal
11
+ in the Software without restriction, including without limitation the rights
12
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
+ copies of the Software, and to permit persons to whom the Software is
14
+ furnished to do so, subject to the following conditions:
15
+
16
+ The above copyright notice and this permission notice shall be included in all
17
+ copies or substantial portions of the Software.
18
+
19
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
+ SOFTWARE.
26
+
27
+ Requires-Python: >=3.11
28
+ Description-Content-Type: text/markdown
29
+ License-File: LICENSE
30
+ Dynamic: license-file
31
+
32
+ # Workjig
33
+
34
+ A Python workflow runtime that orchestrates headless LLM harnesses.
35
+
36
+ Run Claude Code, Codex and Pi as subprocess workers. You write the workflow
37
+ in Python; Workjig handles launches, separate output
38
+ paths, bounded execution and durable run records.
39
+
40
+ ## Why Workjig?
41
+
42
+ **Use existing harnesses, not another model API integration.** Workjig delegates
43
+ work to coding harnesses through their command-line interfaces, using their
44
+ tools, configuration and authentication. An important motivation is letting
45
+ people use Claude Code with an eligible Anthropic subscription rather than
46
+ requiring separate API billing. Availability depends on the harness, account,
47
+ provider terms and usage limits; Workjig does not supply or bypass access.
48
+
49
+ **Keep the whole workflow in Python.** Branches, parallel work, checks and
50
+ correction loops are ordinary Python, not a separate graph or workflow language.
51
+ Unlike libraries centered on direct model API calls, Workjig treats the harness
52
+ as the worker. [mdflow](https://github.com/johnlindquist/mdflow) is another
53
+ harness-based approach: it turns Markdown prompts into executable CLI commands
54
+ that can be composed through pipes. For Commonplace, where most of the system
55
+ is already Python, keeping workflow control in Python lets us reuse its code
56
+ and tests without introducing another way to express control flow.
57
+
58
+ **Keep the runtime small and independent.** Workjig's primary purpose is to
59
+ support [Commonplace](https://github.com/zby/commonplace) as the foundation of
60
+ its workflow engine. A separate package gives agents a focused codebase and a
61
+ clear execution contract. Commonplace drives priorities, but its domain rules
62
+ stay outside the runtime. Workjig requires neither Commonplace nor Git.
63
+ Initial platform: Linux, Python 3.11 or newer.
64
+
65
+ ### Compatibility
66
+
67
+ Workjig is in early development. We do not maintain backward
68
+ compatibility for the public API or persisted run-record format. Changes may
69
+ require updating callers and starting fresh run directories; no migration of
70
+ older records is promised.
71
+
72
+ ### Code-steered or agent-steered?
73
+
74
+ Agent-led delegation is useful for exploratory work where the process changes
75
+ as we learn; our [lightweight review workflow](instructions/lightweight-review.md)
76
+ uses that approach. Workjig is for code-steered work: execution rules live in
77
+ Python, completed calls can be reused after restart, and interruptions require
78
+ explicit recovery. We are trying both, not claiming one is always better.
79
+
80
+ In [Treadle](https://github.com/zby/treadle), code controlled the workflow but an
81
+ agent carried prompts and answers between it and workers. Workjig launches
82
+ harnesses directly to remove that transmission and coordination layer.
83
+
84
+ The supporting research lives in the [Commonplace KB](https://zby.github.io/commonplace/),
85
+ including notes on [orchestration design dimensions](https://github.com/zby/commonplace/blob/main/kb/notes/agent-orchestration-occupies-a-multi-dimensional-design-space.md)
86
+ and [coordination guarantees versus channels](https://github.com/zby/commonplace/blob/main/kb/notes/agent-orchestration-needs-coordination-guarantees-not-just.md).
87
+ These inform the design; they are not evidence of Workjig's tested behavior.
88
+ Our [design rationale](docs/design-rationale.md) explains the local tradeoffs.
89
+
90
+ ## A call
91
+
92
+ ```python
93
+ import sys
94
+ from pathlib import Path
95
+
96
+ from workjig import CommandWorker, Request, Run
97
+
98
+ worker = CommandWorker(argv=(sys.executable, str(Path("worker.py").resolve()), "{prompt}"))
99
+ request = Request(
100
+ call_id="summarize-v1",
101
+ task="Read the supplied source and write the requested summary file.",
102
+ inputs={"source.txt": "Material to summarize."},
103
+ required_outputs=("summary.txt",),
104
+ worker=worker,
105
+ timeout_seconds=60,
106
+ )
107
+
108
+ with Run(Path("run-records"), Path.cwd()) as run:
109
+ result = run.call(request)
110
+ if result.status == "delivered":
111
+ print(result.outputs["summary.txt"])
112
+ else:
113
+ print(result.status)
114
+ ```
115
+
116
+ Supply your own `worker.py`: it receives a generated prompt with absolute input
117
+ and output paths, reads the inputs and writes the named files. Standard output
118
+ is execution evidence, not the file result. For real harnesses, use the
119
+ `claude_worker`, `codex_worker` or `pi_worker` factories instead of a generic
120
+ `CommandWorker`; see [usage and adapter configuration](docs/usage.md).
121
+ The [ordinary-file example](examples/README.md) provides a runnable workflow
122
+ with checks, corrections and resume.
123
+
124
+ ## Guarantees and boundaries
125
+
126
+ - **Reuse:** reopening a run reuses committed results only when the stable call
127
+ identity and declared work match. Timeout, launch ceiling, and retry policy
128
+ are not part of work identity. Ambient environment changes are not detected
129
+ automatically.
130
+ - **Bounded execution:** one launch by default; retries and larger allowances
131
+ are explicit. Launches are reserved durably, and restart does not refund them.
132
+ A caller may raise the total ceiling to continue eligible failed work; usage
133
+ stops require explicit approval of the particular stop.
134
+ - **Explicit recovery:** unresolved launches block automatic work until you
135
+ establish that the old worker has stopped and acknowledge it.
136
+ - **Delivery is not acceptance:** your workflow checks content and bounds
137
+ correction loops. Reused results still need those checks.
138
+ - **Separate files are not a sandbox:** callers own permissions, credentials,
139
+ environment compatibility and recovery of external effects.
140
+
141
+ ## Documentation
142
+
143
+ - [Usage and recovery](docs/usage.md) — adapters, retries, outcomes, parallel calls
144
+ and recovery procedures.
145
+ - [Execution contract](docs/design.md) — guarantees and limits.
146
+ - [Design rationale](docs/design-rationale.md) and [decision records](adr/README.md)
147
+ — reasons, alternatives and tradeoffs.
148
+ - [Harness evidence](docs/harness-evidence.md) — tested adapters and versions,
149
+ distinguishing deterministic tests from actual model invocations.
150
+
151
+ ## Development
152
+
153
+ Run from this package directory:
154
+
155
+ ```sh
156
+ uv sync --locked
157
+ uv run pytest
158
+ uv run ruff check .
159
+ uv run ruff format --check .
160
+ uv build
161
+ ```
162
+
163
+ Normal tests use direct parser checks and deterministic subprocess workers;
164
+ they do not call model APIs or require credentials. See [test organization](tests/README.md)
165
+ for selective runs and GitHub CI. Real-harness probes remain
166
+ [manual operations](docs/usage.md#examples-and-real-harness-probes), outside CI.
167
+ Building creates local artifacts; publishing is a separate step.
168
+
169
+ ## License
170
+
171
+ [MIT](LICENSE).
@@ -0,0 +1,140 @@
1
+ # Workjig
2
+
3
+ A Python workflow runtime that orchestrates headless LLM harnesses.
4
+
5
+ Run Claude Code, Codex and Pi as subprocess workers. You write the workflow
6
+ in Python; Workjig handles launches, separate output
7
+ paths, bounded execution and durable run records.
8
+
9
+ ## Why Workjig?
10
+
11
+ **Use existing harnesses, not another model API integration.** Workjig delegates
12
+ work to coding harnesses through their command-line interfaces, using their
13
+ tools, configuration and authentication. An important motivation is letting
14
+ people use Claude Code with an eligible Anthropic subscription rather than
15
+ requiring separate API billing. Availability depends on the harness, account,
16
+ provider terms and usage limits; Workjig does not supply or bypass access.
17
+
18
+ **Keep the whole workflow in Python.** Branches, parallel work, checks and
19
+ correction loops are ordinary Python, not a separate graph or workflow language.
20
+ Unlike libraries centered on direct model API calls, Workjig treats the harness
21
+ as the worker. [mdflow](https://github.com/johnlindquist/mdflow) is another
22
+ harness-based approach: it turns Markdown prompts into executable CLI commands
23
+ that can be composed through pipes. For Commonplace, where most of the system
24
+ is already Python, keeping workflow control in Python lets us reuse its code
25
+ and tests without introducing another way to express control flow.
26
+
27
+ **Keep the runtime small and independent.** Workjig's primary purpose is to
28
+ support [Commonplace](https://github.com/zby/commonplace) as the foundation of
29
+ its workflow engine. A separate package gives agents a focused codebase and a
30
+ clear execution contract. Commonplace drives priorities, but its domain rules
31
+ stay outside the runtime. Workjig requires neither Commonplace nor Git.
32
+ Initial platform: Linux, Python 3.11 or newer.
33
+
34
+ ### Compatibility
35
+
36
+ Workjig is in early development. We do not maintain backward
37
+ compatibility for the public API or persisted run-record format. Changes may
38
+ require updating callers and starting fresh run directories; no migration of
39
+ older records is promised.
40
+
41
+ ### Code-steered or agent-steered?
42
+
43
+ Agent-led delegation is useful for exploratory work where the process changes
44
+ as we learn; our [lightweight review workflow](instructions/lightweight-review.md)
45
+ uses that approach. Workjig is for code-steered work: execution rules live in
46
+ Python, completed calls can be reused after restart, and interruptions require
47
+ explicit recovery. We are trying both, not claiming one is always better.
48
+
49
+ In [Treadle](https://github.com/zby/treadle), code controlled the workflow but an
50
+ agent carried prompts and answers between it and workers. Workjig launches
51
+ harnesses directly to remove that transmission and coordination layer.
52
+
53
+ The supporting research lives in the [Commonplace KB](https://zby.github.io/commonplace/),
54
+ including notes on [orchestration design dimensions](https://github.com/zby/commonplace/blob/main/kb/notes/agent-orchestration-occupies-a-multi-dimensional-design-space.md)
55
+ and [coordination guarantees versus channels](https://github.com/zby/commonplace/blob/main/kb/notes/agent-orchestration-needs-coordination-guarantees-not-just.md).
56
+ These inform the design; they are not evidence of Workjig's tested behavior.
57
+ Our [design rationale](docs/design-rationale.md) explains the local tradeoffs.
58
+
59
+ ## A call
60
+
61
+ ```python
62
+ import sys
63
+ from pathlib import Path
64
+
65
+ from workjig import CommandWorker, Request, Run
66
+
67
+ worker = CommandWorker(argv=(sys.executable, str(Path("worker.py").resolve()), "{prompt}"))
68
+ request = Request(
69
+ call_id="summarize-v1",
70
+ task="Read the supplied source and write the requested summary file.",
71
+ inputs={"source.txt": "Material to summarize."},
72
+ required_outputs=("summary.txt",),
73
+ worker=worker,
74
+ timeout_seconds=60,
75
+ )
76
+
77
+ with Run(Path("run-records"), Path.cwd()) as run:
78
+ result = run.call(request)
79
+ if result.status == "delivered":
80
+ print(result.outputs["summary.txt"])
81
+ else:
82
+ print(result.status)
83
+ ```
84
+
85
+ Supply your own `worker.py`: it receives a generated prompt with absolute input
86
+ and output paths, reads the inputs and writes the named files. Standard output
87
+ is execution evidence, not the file result. For real harnesses, use the
88
+ `claude_worker`, `codex_worker` or `pi_worker` factories instead of a generic
89
+ `CommandWorker`; see [usage and adapter configuration](docs/usage.md).
90
+ The [ordinary-file example](examples/README.md) provides a runnable workflow
91
+ with checks, corrections and resume.
92
+
93
+ ## Guarantees and boundaries
94
+
95
+ - **Reuse:** reopening a run reuses committed results only when the stable call
96
+ identity and declared work match. Timeout, launch ceiling, and retry policy
97
+ are not part of work identity. Ambient environment changes are not detected
98
+ automatically.
99
+ - **Bounded execution:** one launch by default; retries and larger allowances
100
+ are explicit. Launches are reserved durably, and restart does not refund them.
101
+ A caller may raise the total ceiling to continue eligible failed work; usage
102
+ stops require explicit approval of the particular stop.
103
+ - **Explicit recovery:** unresolved launches block automatic work until you
104
+ establish that the old worker has stopped and acknowledge it.
105
+ - **Delivery is not acceptance:** your workflow checks content and bounds
106
+ correction loops. Reused results still need those checks.
107
+ - **Separate files are not a sandbox:** callers own permissions, credentials,
108
+ environment compatibility and recovery of external effects.
109
+
110
+ ## Documentation
111
+
112
+ - [Usage and recovery](docs/usage.md) — adapters, retries, outcomes, parallel calls
113
+ and recovery procedures.
114
+ - [Execution contract](docs/design.md) — guarantees and limits.
115
+ - [Design rationale](docs/design-rationale.md) and [decision records](adr/README.md)
116
+ — reasons, alternatives and tradeoffs.
117
+ - [Harness evidence](docs/harness-evidence.md) — tested adapters and versions,
118
+ distinguishing deterministic tests from actual model invocations.
119
+
120
+ ## Development
121
+
122
+ Run from this package directory:
123
+
124
+ ```sh
125
+ uv sync --locked
126
+ uv run pytest
127
+ uv run ruff check .
128
+ uv run ruff format --check .
129
+ uv build
130
+ ```
131
+
132
+ Normal tests use direct parser checks and deterministic subprocess workers;
133
+ they do not call model APIs or require credentials. See [test organization](tests/README.md)
134
+ for selective runs and GitHub CI. Real-harness probes remain
135
+ [manual operations](docs/usage.md#examples-and-real-harness-probes), outside CI.
136
+ Building creates local artifacts; publishing is a separate step.
137
+
138
+ ## License
139
+
140
+ [MIT](LICENSE).
@@ -0,0 +1,58 @@
1
+ # 001 — Name physical launch records explicitly
2
+
3
+ Status: accepted
4
+ Decision date: 2026-10-20
5
+ Recorded: 2026-10-20
6
+
7
+ ## Context
8
+
9
+ Workjig records each physical launch reservation, including reservations for
10
+ which no process started. Commonplace uses “attempt” for a larger unit of work
11
+ that includes content checking and correction. Using the same word at both
12
+ levels obscures which lifecycle and budget a caller is discussing.
13
+ Commonplace's integration review identified this problem.
14
+
15
+ ## Decision
16
+
17
+ Expose `LaunchRecord` and `CallResult.launches` in the Python API. Callers use
18
+ these names when inspecting execution evidence and spent launch reservations.
19
+ They align with `launch_id`, `launch_dir`, `max_launches`, and
20
+ `unresolved_launches()`.
21
+
22
+ Keep the version-1 persisted JSON field `attempts`. The public vocabulary change
23
+ does not require a storage migration. This records the implemented choice
24
+ retrospectively.
25
+
26
+ ## Considered alternatives
27
+
28
+ **Keep `Attempt` and `CallResult.attempts`.** “Attempt” can reasonably include an
29
+ effort to start a process. However, the consumer's different meaning creates
30
+ avoidable ambiguity at the integration boundary.
31
+
32
+ **Use `result.launch_records`.** This is explicit but unnecessarily repeats the
33
+ record representation. `result.launches` reads naturally and matches the other
34
+ launch-oriented API names.
35
+
36
+ **Rename stored fields too.** Consistent spelling across Python and JSON would
37
+ be tidier, but would introduce migration work unrelated to the consumer-facing
38
+ problem. Existing records should remain readable without rewriting them.
39
+
40
+ ## Consequences
41
+
42
+ The runtime and consumer can describe physical launches and workflow attempts
43
+ without conflating their budgets. `LaunchRecord` also accommodates a reservation
44
+ that never reached process start.
45
+
46
+ Python consumers must use the new names; no compatibility aliases were added.
47
+ Persisted records retain their format and existing execution/recovery behavior.
48
+ Internal storage helpers can continue to use the old field terminology.
49
+
50
+ The rename was checked by the functional suite and a separate verification
51
+ session that compared old and new codecs across all 11 lifecycle statuses.
52
+ This supports storage compatibility; it does not establish new harness behavior.
53
+
54
+ ## Reevaluate when
55
+
56
+ Consumer integration reveals another concrete ambiguity in the launch lifecycle.
57
+ Revisit the stored field name only if a separately justified record-format change
58
+ makes migration worthwhile.
@@ -0,0 +1,88 @@
1
+ # 002 — Classify terminal harness failures, not whole logs
2
+
3
+ Status: accepted
4
+ Decision date: 2026-10-20
5
+ Recorded: 2026-10-20
6
+
7
+ ## Context
8
+
9
+ The original `usage limit` scan could mistake quoted task content for a quota
10
+ stop and suppress a configured retry. It also missed every quota failure in
11
+ our local fixtures. Pi can report an assistant failure while exiting zero, and
12
+ all three harnesses can recover after an intermediate error.
13
+
14
+ [Protocol inspection](evidence/terminal-failures/README.md) and
15
+ [15 real-CLI fixture probes](evidence/terminal-failures/local-probes.md) establish
16
+ these distinctions for Codex 0.162.1, Claude Code 2.1.303 and Pi 1.3.0. The probes
17
+ used isolated loopback providers, not live quota exhaustion. Subscription OAuth
18
+ and other Pi provider transports remain outside the observed evidence.
19
+
20
+ ## Decision
21
+
22
+ Select the final harness outcome first, then classify its cause. Adapter
23
+ factories declare `CommandWorker.output_protocol`; worker names remain
24
+ labels. Generic commands default to no stream interpretation. Protocol selection
25
+ belongs to declared request identity, with the absent default preserving the
26
+ canonical material of existing records. Committed outcomes are not reinterpreted.
27
+
28
+ | Protocol | Terminal selection | Usage-stop signals |
29
+ |---|---|---|
30
+ | `codex-json` | Last `turn.completed` or `turn.failed` in the current turn | Known text in `turn.failed.error.message` |
31
+ | `claude-stream-json` | Main-agent `result`; `subtype: success` still requires `is_error: false` | On an error result of that variant: last main-agent assistant `rate_limit`/`billing_error`, final HTTP 429, or known terminal error text |
32
+ | `pi-json` | Last completed assistant message, read through non-aborted `agent_settled`; `stopReason: stop` succeeds | Known text in the final assistant's `errorMessage` when `stopReason: error` |
33
+
34
+ Text matching is limited to selected error fields: usage-limit and quota-exceeded
35
+ phrases, `insufficient_quota`, rate-limit wording/codes, credit-balance failure
36
+ wording and terminal HTTP 429 forms. Ordinary messages, tool errors, retry
37
+ notifications and stderr do not establish quota failure. Descriptive
38
+ `parse_evidence` remains separate from outcome classification.
39
+
40
+ Recognized quota, billing and **unrecovered transient rate limits** become
41
+ nonretryable `usage_limit`. Harnesses already handle short retries; after they
42
+ give up, workflow code decides whether and when to resume. Workjig does not add
43
+ a second implicit backoff loop. Worker-written `usage_limit` problems remain
44
+ independent explicit stop signals, not claims of verified provider exhaustion.
45
+
46
+ Handled timeout wins. Either usage-stop signal wins over other completed-process
47
+ failures, including lingering children, after process-group cleanup. Otherwise,
48
+ existing cleanup and worker-problem precedence remains. Other terminal failures
49
+ and missing or malformed completion evidence become `process_error`; a caller
50
+ may explicitly retry that outcome within the launch allowance. A successful
51
+ terminal event does not override a nonzero exit or certify delivered content:
52
+ file collection and workflow checks still apply.
53
+
54
+ ## Considered alternatives
55
+
56
+ - **Keep whole-log matching, or scan only stderr:** both retain quotation false
57
+ positives; stderr-only also misses the observed stdout failure channels.
58
+ - **Use exit status alone:** sufficient for generic commands, but would accept
59
+ Pi's exit-zero errors if required files were already present.
60
+ - **Treat any error event as terminal:** would stop successfully recovered runs.
61
+ - **Automatically retry exhausted throttling:** duplicates harness retry policy
62
+ and can spend launches while an account or provider remains blocked. Keep the
63
+ resume decision in the workflow instead.
64
+ - **Stop on every unknown harness failure:** would suppress intentional bounded
65
+ retries of ordinary process failures. Unknown failures use `process_error`.
66
+
67
+ ## Consequences
68
+
69
+ The small version-scoped parsers cover retained failure/recovery streams without
70
+ requiring harness installations in the normal tests. They are not universal
71
+ provider-error classifiers. Unknown quota wording can still become a retryable
72
+ `process_error`; missing completion cannot become delivery merely because the
73
+ process exited zero. Authentication and local harness budget failures also use
74
+ that generic outcome rather than new runtime statuses.
75
+
76
+ Subscription limit notifications and Pi retry summaries are not independently
77
+ used as quota evidence. The supported final outcome must establish the stop.
78
+ Raw streams remain available for diagnosis; adding new protocol signals needs
79
+ evidence rather than broadening a whole-log keyword list.
80
+
81
+ ## Reevaluate when
82
+
83
+ - A supported CLI changes terminal framing, or retained real failures show a
84
+ different final-message sequence or quota representation.
85
+ - Subscription OAuth or another provider transport needs a reliably classified
86
+ stop that these signals miss.
87
+ - Actual workflows show that delegating exhausted throttling to callers is
88
+ impractical, with evidence for a bounded runtime scheduling policy.
@@ -0,0 +1,101 @@
1
+ # 003 — Separate execution controls from work identity
2
+
3
+ Status: accepted
4
+ Decision date: 2026-10-20
5
+ Recorded: 2026-10-20
6
+
7
+ ## Context
8
+
9
+ Commonplace needs to increase timeouts without relaunching delivered work, extend
10
+ failed calls' launch ceilings, and explicitly continue after resolving usage
11
+ limits. Previously, timeout, ceiling, and retry policy were part of request
12
+ identity. Changing any of them created a separate history and fresh allowance,
13
+ rather than continuing the existing work.
14
+
15
+ The [review proposal](evidence/execution-policy-and-continuation-proposal.md)
16
+ explored these changes. The maintainer confirmed there are no saved runs to
17
+ preserve, backward compatibility is not required, and run migration across code
18
+ changes is out of scope. Resume supports recovery with compatible workflow and
19
+ runtime code and environment, not upgrades across incompatible changes; changing
20
+ execution controls is compatible by design.
21
+
22
+ ## Decision
23
+
24
+ Exclude `timeout_seconds`, `max_launches`, and `retry` from work identity. Keep
25
+ all other declared material identity-bearing. Matching delivered results replay
26
+ under changed execution controls, with workflow checks still caller-owned.
27
+ `CallResult.request_hash` now hashes work material, not execution controls.
28
+
29
+ Evaluate the current call's controls against its existing history. The ceiling
30
+ is total reservations, not additional launches: increasing two to three permits
31
+ one further launch after two were spent. Retry eligibility and backoff are
32
+ independent of the ceiling. Increasing timeout alone authorizes no retry. Lower
33
+ ceilings stop further work when reached but do not invalidate historical counts.
34
+ `failed_at_launch_limit` uses the currently supplied ceiling.
35
+
36
+ Persist effective timeout, ceiling, and retry policy with every reservation
37
+ before process start. Validate each launch against its recorded ceiling and
38
+ continuation permission rather than the controls supplied today.
39
+
40
+ Keep usage stops outside ordinary retry policy. `Run.call(request,
41
+ resume_usage_stop=launch_id)` explicitly approves the named usage stop in that
42
+ exact history. Approval overrides only the latest stop, while allowance remains.
43
+ The next reservation records `resumed_usage_stop`; once reserved, the approved
44
+ stop is no longer latest. Repeating approval after continuation or delivery is
45
+ harmless. A new usage stop requires new approval. Other subsequent failures
46
+ follow ordinary bounded retry policy. Current backoff also applies before an
47
+ approved continuation. Invalid, unknown, wrong-entry, and non-usage references
48
+ raise `ValueError` once unresolved-launch recovery permits inspecting the call.
49
+
50
+ Unresolved reservations still require establishing worker termination and
51
+ explicit acknowledgment. No allowance is refunded and no unrecorded output is
52
+ adopted. Fresh launch paths, process cleanup, and parallel settlement are
53
+ unchanged. `protocol_error` remains nonretryable.
54
+
55
+ Use record format version 2 and reject older formats. Do not provide migration
56
+ or a legacy mode. Keep the stored `attempts` field name; renaming it is unrelated
57
+ to this change. ADR 001's historical format-preservation rationale does not imply
58
+ a continuing compatibility commitment. Always store `output_protocol`, including
59
+ an absent value; ADR 002's omission of that default to preserve existing records
60
+ no longer applies.
61
+
62
+ ## Considered alternatives
63
+
64
+ - **Keep execution controls in identity:** avoids changing the record contract,
65
+ but cannot preserve delivered work while updating controls and gives policy
66
+ variants separate budgets. Rejected for Commonplace's use case.
67
+ - **Reset the budget or treat an increased ceiling as extra launches:** could
68
+ replenish allowance on repeated calls or restart. Rejected; spent reservations
69
+ always count against the supplied total.
70
+ - **Separate durable budget grants or pending approval ledger:** adds state and
71
+ crash/replay accounting. Unnecessary: the supplied total ceiling plus durable
72
+ reservations tracks budget, and the next reservation records stop approval.
73
+ - **Let a longer timeout imply retry permission:** conflates execution settings
74
+ with authorization. Require explicit ceiling and eligible retry outcome.
75
+ - **Permanent usage-resume boolean or `usage_limit` in retry policy:** could
76
+ relaunch repeatedly while usage remains blocked. Bind permission to one stop.
77
+ - **Exactly one launch after approval, even for other retryable failures:** adds
78
+ special retry semantics. Clear only the named usage stop, then use ordinary
79
+ bounded retries; any later usage stop blocks again.
80
+ - **Separate continuation operation:** not needed for current callers. Use
81
+ per-call controls and one stop-ID argument rather than a second lifecycle API.
82
+ - **Legacy mode or merging policy-variant histories:** adds unsupported migration
83
+ work and ambiguous result/budget selection. No saved runs require it.
84
+
85
+ ## Consequences
86
+
87
+ Execution authorization can evolve without discarding delivered answers or
88
+ launch evidence. Changing work material still creates a separate history.
89
+ An answer delivered under old controls is not a promise about the hypothetical
90
+ answer under new controls; callers wanting new work choose a new identity/run.
91
+ External effects can repeat, and their recovery remains caller-owned.
92
+
93
+ The record format and public hash meaning change without compatibility shims.
94
+ Supporting [evidence](evidence/execution-controls/README.md) records deterministic
95
+ checks, independent verification, and pre/post source-size measurements. These
96
+ checks do not establish new real-harness support claims.
97
+
98
+ ## Reevaluate when
99
+
100
+ Commonplace demonstrates a need not covered by per-call controls and stop-ID
101
+ approval, or deterministic recovery tests reveal ambiguous authorization.