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.
- workjig-0.1.0/LICENSE +21 -0
- workjig-0.1.0/MANIFEST.in +6 -0
- workjig-0.1.0/PKG-INFO +171 -0
- workjig-0.1.0/README.md +140 -0
- workjig-0.1.0/adr/001-name-physical-launch-records.md +58 -0
- workjig-0.1.0/adr/002-classify-terminal-harness-failures.md +88 -0
- workjig-0.1.0/adr/003-separate-execution-controls-from-work-identity.md +101 -0
- workjig-0.1.0/adr/README.md +93 -0
- workjig-0.1.0/adr/evidence/README.md +48 -0
- workjig-0.1.0/adr/evidence/execution-controls/README.md +73 -0
- workjig-0.1.0/adr/evidence/execution-policy-and-continuation-proposal.md +125 -0
- workjig-0.1.0/adr/evidence/terminal-failures/README.md +254 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/claude-auth/result.json +48 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/claude-auth/stderr.log +0 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/claude-auth/stdout.jsonl +4 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/claude-billing/result.json +48 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/claude-billing/stderr.log +0 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/claude-billing/stdout.jsonl +4 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/claude-quota/result.json +48 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/claude-quota/stderr.log +0 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/claude-quota/stdout.jsonl +4 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/claude-recover/result.json +52 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/claude-recover/stderr.log +0 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/claude-recover/stdout.jsonl +5 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/claude-success/result.json +37 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/claude-success/stderr.log +0 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/claude-success/stdout.jsonl +4 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/codex-auth/result.json +36 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/codex-auth/stderr.log +2 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/codex-auth/stdout.jsonl +5 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/codex-exhausted/result.json +51 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/codex-exhausted/stderr.log +2 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/codex-exhausted/stdout.jsonl +6 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/codex-quota/result.json +36 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/codex-quota/stderr.log +2 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/codex-quota/stdout.jsonl +5 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/codex-recover/result.json +40 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/codex-recover/stderr.log +2 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/codex-recover/stdout.jsonl +6 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/codex-success/result.json +25 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/codex-success/stderr.log +2 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/codex-success/stdout.jsonl +5 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/pi-auth/result.json +48 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/pi-auth/stderr.log +0 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/pi-auth/stdout.jsonl +12 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/pi-exhausted/result.json +63 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/pi-exhausted/stderr.log +0 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/pi-exhausted/stdout.jsonl +21 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/pi-quota/result.json +48 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/pi-quota/stderr.log +0 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/pi-quota/stdout.jsonl +12 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/pi-recover/result.json +52 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/pi-recover/stderr.log +0 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/pi-recover/stdout.jsonl +24 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/pi-success/result.json +37 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/pi-success/stderr.log +0 -0
- workjig-0.1.0/adr/evidence/terminal-failures/captures/pi-success/stdout.jsonl +15 -0
- workjig-0.1.0/adr/evidence/terminal-failures/local-probes.md +168 -0
- workjig-0.1.0/adr/evidence/terminal-failures/local_probe.py +445 -0
- workjig-0.1.0/docs/design-rationale.md +175 -0
- workjig-0.1.0/docs/design.md +144 -0
- workjig-0.1.0/docs/harness-evidence.md +208 -0
- workjig-0.1.0/docs/usage.md +256 -0
- workjig-0.1.0/examples/README.md +25 -0
- workjig-0.1.0/examples/ordinary_files.py +145 -0
- workjig-0.1.0/examples/ordinary_worker.py +71 -0
- workjig-0.1.0/examples/smoke_harness.py +104 -0
- workjig-0.1.0/pyproject.toml +33 -0
- workjig-0.1.0/setup.cfg +4 -0
- workjig-0.1.0/src/workjig/__init__.py +16 -0
- workjig-0.1.0/src/workjig/adapters.py +224 -0
- workjig-0.1.0/src/workjig/harness.py +255 -0
- workjig-0.1.0/src/workjig/models.py +229 -0
- workjig-0.1.0/src/workjig/outcomes.py +135 -0
- workjig-0.1.0/src/workjig/run.py +727 -0
- workjig-0.1.0/src/workjig.egg-info/PKG-INFO +171 -0
- workjig-0.1.0/src/workjig.egg-info/SOURCES.txt +90 -0
- workjig-0.1.0/src/workjig.egg-info/dependency_links.txt +1 -0
- workjig-0.1.0/src/workjig.egg-info/top_level.txt +1 -0
- workjig-0.1.0/tests/README.md +62 -0
- workjig-0.1.0/tests/conftest.py +12 -0
- workjig-0.1.0/tests/test_acceptance.py +587 -0
- workjig-0.1.0/tests/test_adapters.py +251 -0
- workjig-0.1.0/tests/test_continuation.py +299 -0
- workjig-0.1.0/tests/test_continuation_validation.py +118 -0
- workjig-0.1.0/tests/test_core.py +478 -0
- workjig-0.1.0/tests/test_metadata.py +112 -0
- workjig-0.1.0/tests/test_outcomes.py +378 -0
- workjig-0.1.0/tests/test_record_validation.py +201 -0
- workjig-0.1.0/tests/test_review.py +85 -0
- workjig-0.1.0/tests/test_smoke.py +85 -0
- 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.
|
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).
|
workjig-0.1.0/README.md
ADDED
|
@@ -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.
|