checkpointflow 1.0.4__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.
- checkpointflow-1.0.4/PKG-INFO +342 -0
- checkpointflow-1.0.4/README.md +330 -0
- checkpointflow-1.0.4/pyproject.toml +50 -0
- checkpointflow-1.0.4/src/checkpointflow/__init__.py +6 -0
- checkpointflow-1.0.4/src/checkpointflow/__main__.py +6 -0
- checkpointflow-1.0.4/src/checkpointflow/cli.py +311 -0
- checkpointflow-1.0.4/src/checkpointflow/docs/__init__.py +0 -0
- checkpointflow-1.0.4/src/checkpointflow/docs/guide.md +333 -0
- checkpointflow-1.0.4/src/checkpointflow/engine/__init__.py +1 -0
- checkpointflow-1.0.4/src/checkpointflow/engine/evaluator.py +95 -0
- checkpointflow-1.0.4/src/checkpointflow/engine/queries.py +163 -0
- checkpointflow-1.0.4/src/checkpointflow/engine/runner.py +566 -0
- checkpointflow-1.0.4/src/checkpointflow/engine/steps/__init__.py +1 -0
- checkpointflow-1.0.4/src/checkpointflow/engine/steps/await_event_step.py +8 -0
- checkpointflow-1.0.4/src/checkpointflow/engine/steps/cli_step.py +131 -0
- checkpointflow-1.0.4/src/checkpointflow/engine/steps/end_step.py +38 -0
- checkpointflow-1.0.4/src/checkpointflow/models/__init__.py +7 -0
- checkpointflow-1.0.4/src/checkpointflow/models/envelope.py +95 -0
- checkpointflow-1.0.4/src/checkpointflow/models/errors.py +35 -0
- checkpointflow-1.0.4/src/checkpointflow/models/state.py +24 -0
- checkpointflow-1.0.4/src/checkpointflow/models/workflow.py +142 -0
- checkpointflow-1.0.4/src/checkpointflow/persistence/__init__.py +1 -0
- checkpointflow-1.0.4/src/checkpointflow/persistence/store.py +223 -0
- checkpointflow-1.0.4/src/checkpointflow/py.typed +1 -0
- checkpointflow-1.0.4/src/checkpointflow/schema.py +30 -0
- checkpointflow-1.0.4/src/checkpointflow/schemas/__init__.py +0 -0
- checkpointflow-1.0.4/src/checkpointflow/schemas/checkpointflow-run-envelope.schema.json +187 -0
- checkpointflow-1.0.4/src/checkpointflow/schemas/checkpointflow.schema.json +533 -0
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: checkpointflow
|
|
3
|
+
Version: 1.0.4
|
|
4
|
+
Summary: CLI toolchain for authoring and running deterministic, resumable agent workflows.
|
|
5
|
+
Requires-Dist: jsonschema>=4.26.0
|
|
6
|
+
Requires-Dist: pydantic>=2.12.5
|
|
7
|
+
Requires-Dist: pyyaml>=6.0.3
|
|
8
|
+
Requires-Dist: rich>=14.3.3
|
|
9
|
+
Requires-Dist: typer>=0.24.1
|
|
10
|
+
Requires-Python: >=3.13
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
|
|
13
|
+
# checkpointflow
|
|
14
|
+
|
|
15
|
+
Deterministic, resumable workflows for agents, operators, and shells.
|
|
16
|
+
|
|
17
|
+
`checkpointflow` lets you define a workflow as a portable state machine in YAML, validate it before execution, run it through a stable CLI, pause for explicit external input, and resume later without relying on hidden chat history or process-local state.
|
|
18
|
+
|
|
19
|
+
It is built for workflows that mix:
|
|
20
|
+
|
|
21
|
+
- local CLI steps
|
|
22
|
+
- structured branching
|
|
23
|
+
- human approval
|
|
24
|
+
- bounded agent judgment
|
|
25
|
+
- durable handoff across shells, tools, and operators
|
|
26
|
+
|
|
27
|
+
## Why checkpointflow
|
|
28
|
+
|
|
29
|
+
Most agent workflows break down in the same places:
|
|
30
|
+
|
|
31
|
+
- important state lives only in a chat thread
|
|
32
|
+
- pause and resume are implicit instead of modeled
|
|
33
|
+
- outputs are hard to validate before taking the next step
|
|
34
|
+
- another agent or operator cannot reliably continue the work
|
|
35
|
+
|
|
36
|
+
`checkpointflow` solves that by making the workflow contract explicit:
|
|
37
|
+
|
|
38
|
+
- workflows are written as versioned YAML documents
|
|
39
|
+
- inputs, outputs, and resume events are validated with JSON Schema
|
|
40
|
+
- stdout returns stable machine-readable JSON envelopes
|
|
41
|
+
- waiting is explicit and durable through `await_event`
|
|
42
|
+
- state is persisted under `~/.checkpointflow/` by default
|
|
43
|
+
- workflow files can live anywhere on disk
|
|
44
|
+
|
|
45
|
+
## What You Get
|
|
46
|
+
|
|
47
|
+
- A narrow, predictable runtime contract that is easy for agents to drive.
|
|
48
|
+
- Stable CLI commands for validate, run, resume, inspect, status, logs, cancel, and guide.
|
|
49
|
+
- Explicit pause/resume semantics instead of hidden conversational continuation.
|
|
50
|
+
- Deterministic control flow driven by persisted inputs and step outputs.
|
|
51
|
+
- A default user-scoped state store at `~/.checkpointflow/`.
|
|
52
|
+
- A workflow format that is simple enough to derive from human-agent conversation.
|
|
53
|
+
|
|
54
|
+
## Installation
|
|
55
|
+
|
|
56
|
+
Install with `uv`:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
uv tool install checkpointflow
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Then verify the CLI is available:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
cpf --help
|
|
66
|
+
cpf guide
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
If you want to use it inside an existing Python project:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
uv add checkpointflow
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Quick Start
|
|
76
|
+
|
|
77
|
+
Create a workflow file anywhere you want, for example `workflow.yaml`:
|
|
78
|
+
|
|
79
|
+
```yaml
|
|
80
|
+
schema_version: checkpointflow/v1
|
|
81
|
+
workflow:
|
|
82
|
+
id: publish_confluence_change
|
|
83
|
+
name: Publish Confluence change
|
|
84
|
+
version: 1.0.0
|
|
85
|
+
inputs:
|
|
86
|
+
type: object
|
|
87
|
+
required: [page_id, source_file]
|
|
88
|
+
properties:
|
|
89
|
+
page_id:
|
|
90
|
+
type: string
|
|
91
|
+
source_file:
|
|
92
|
+
type: string
|
|
93
|
+
|
|
94
|
+
steps:
|
|
95
|
+
- id: plan
|
|
96
|
+
kind: cli
|
|
97
|
+
command: confpub plan --page-id ${inputs.page_id} --file ${inputs.source_file} --format json
|
|
98
|
+
outputs:
|
|
99
|
+
type: object
|
|
100
|
+
required: [plan_file, summary]
|
|
101
|
+
properties:
|
|
102
|
+
plan_file: { type: string }
|
|
103
|
+
summary: { type: string }
|
|
104
|
+
|
|
105
|
+
- id: approval
|
|
106
|
+
kind: await_event
|
|
107
|
+
audience: user
|
|
108
|
+
event_name: change_approval
|
|
109
|
+
prompt: Review the proposed page update and approve or reject it.
|
|
110
|
+
input_schema:
|
|
111
|
+
type: object
|
|
112
|
+
required: [decision]
|
|
113
|
+
properties:
|
|
114
|
+
decision:
|
|
115
|
+
type: string
|
|
116
|
+
enum: [approve, reject]
|
|
117
|
+
comment:
|
|
118
|
+
type: string
|
|
119
|
+
transitions:
|
|
120
|
+
- when: ${event.decision == "approve"}
|
|
121
|
+
next: apply
|
|
122
|
+
- when: ${event.decision == "reject"}
|
|
123
|
+
next: rejected
|
|
124
|
+
|
|
125
|
+
- id: apply
|
|
126
|
+
kind: cli
|
|
127
|
+
command: confpub apply --plan ${steps.plan.outputs.plan_file} --format json
|
|
128
|
+
outputs:
|
|
129
|
+
type: object
|
|
130
|
+
required: [page_url]
|
|
131
|
+
properties:
|
|
132
|
+
page_url: { type: string }
|
|
133
|
+
|
|
134
|
+
- id: rejected
|
|
135
|
+
kind: end
|
|
136
|
+
result:
|
|
137
|
+
status: rejected
|
|
138
|
+
|
|
139
|
+
outputs:
|
|
140
|
+
type: object
|
|
141
|
+
properties:
|
|
142
|
+
page_url:
|
|
143
|
+
from: steps.apply.outputs.page_url
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Create an input file:
|
|
147
|
+
|
|
148
|
+
```json
|
|
149
|
+
{
|
|
150
|
+
"page_id": "123456",
|
|
151
|
+
"source_file": "docs/architecture.md"
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Validate the workflow:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
cpf validate -f workflow.yaml
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Run it:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
cpf run -f workflow.yaml --input @input.json
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
If the workflow reaches a wait point, `checkpointflow` exits with code `40` and prints a waiting envelope on stdout:
|
|
168
|
+
|
|
169
|
+
```json
|
|
170
|
+
{
|
|
171
|
+
"schema_version": "checkpointflow-run/v1",
|
|
172
|
+
"ok": true,
|
|
173
|
+
"command": "run",
|
|
174
|
+
"status": "waiting",
|
|
175
|
+
"exit_code": 40,
|
|
176
|
+
"run_id": "01JQEXAMPLE00000000000002",
|
|
177
|
+
"current_step_id": "approval",
|
|
178
|
+
"wait": {
|
|
179
|
+
"audience": "user",
|
|
180
|
+
"event_name": "change_approval",
|
|
181
|
+
"prompt": "Review the proposed page update and approve or reject it.",
|
|
182
|
+
"input_schema": {
|
|
183
|
+
"type": "object",
|
|
184
|
+
"required": ["decision"],
|
|
185
|
+
"properties": {
|
|
186
|
+
"decision": {
|
|
187
|
+
"type": "string",
|
|
188
|
+
"enum": ["approve", "reject"]
|
|
189
|
+
},
|
|
190
|
+
"comment": {
|
|
191
|
+
"type": "string"
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
"resume": {
|
|
196
|
+
"command": "cpf resume --run-id 01JQEXAMPLE00000000000002 --event change_approval --input @response.json"
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Resume with explicit input:
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
cpf resume --run-id 01JQEXAMPLE00000000000002 --event change_approval --input @response.json
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Core Ideas
|
|
209
|
+
|
|
210
|
+
### Explicit pause and resume
|
|
211
|
+
|
|
212
|
+
`checkpointflow` uses a single pause primitive:
|
|
213
|
+
|
|
214
|
+
- `await_event`
|
|
215
|
+
|
|
216
|
+
That same primitive covers:
|
|
217
|
+
|
|
218
|
+
- user approval
|
|
219
|
+
- agent decision
|
|
220
|
+
- callback from another system
|
|
221
|
+
- manual operator continuation
|
|
222
|
+
|
|
223
|
+
This keeps the runtime small and the resume contract uniform.
|
|
224
|
+
|
|
225
|
+
### Deterministic orchestration
|
|
226
|
+
|
|
227
|
+
Control flow is driven by persisted workflow inputs, validated event payloads, and step outputs. The orchestrator does not depend on hidden prompt state, wall-clock reads during evaluation, or direct network results inside expressions.
|
|
228
|
+
|
|
229
|
+
### Agent-agnostic execution
|
|
230
|
+
|
|
231
|
+
A workflow can be driven by:
|
|
232
|
+
|
|
233
|
+
- Codex
|
|
234
|
+
- Claude Code
|
|
235
|
+
- GitHub Copilot agents
|
|
236
|
+
- CI jobs
|
|
237
|
+
- shell scripts
|
|
238
|
+
- a human operator
|
|
239
|
+
|
|
240
|
+
The workflow remains the same because the runtime contract remains the same.
|
|
241
|
+
|
|
242
|
+
## Stable CLI Surface
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
cpf init
|
|
246
|
+
cpf guide
|
|
247
|
+
cpf validate -f workflow.yaml
|
|
248
|
+
cpf run -f workflow.yaml --input @input.json
|
|
249
|
+
cpf resume --run-id <run_id> --event <event_name> --input @event.json
|
|
250
|
+
cpf status --run-id <run_id>
|
|
251
|
+
cpf inspect --run-id <run_id>
|
|
252
|
+
cpf logs --run-id <run_id>
|
|
253
|
+
cpf cancel --run-id <run_id> --reason "..."
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Workflow Model
|
|
257
|
+
|
|
258
|
+
The runtime is intentionally opinionated.
|
|
259
|
+
|
|
260
|
+
For most production workflows, teams start with:
|
|
261
|
+
|
|
262
|
+
- `cli`
|
|
263
|
+
- `await_event`
|
|
264
|
+
- `end`
|
|
265
|
+
|
|
266
|
+
That narrow core covers a surprising amount of real work while keeping the execution model understandable and replay-safe.
|
|
267
|
+
|
|
268
|
+
## Storage Model
|
|
269
|
+
|
|
270
|
+
Workflow location and runtime state are separate concerns.
|
|
271
|
+
|
|
272
|
+
- Workflow files can live anywhere on disk.
|
|
273
|
+
- Runtime state defaults to `~/.checkpointflow/`.
|
|
274
|
+
- Run metadata lives in `~/.checkpointflow/runs.db`.
|
|
275
|
+
- Per-run artifacts live under `~/.checkpointflow/runs/<run_id>/`.
|
|
276
|
+
- The workflow source path is recorded as metadata, but it does not determine where state is stored.
|
|
277
|
+
|
|
278
|
+
This makes workflows easy to keep in repos, temp directories, shared folders, or generated output locations without coupling them to runtime storage.
|
|
279
|
+
|
|
280
|
+
## Exit Codes
|
|
281
|
+
|
|
282
|
+
```text
|
|
283
|
+
0 Success
|
|
284
|
+
10 Validation error
|
|
285
|
+
20 Runtime error
|
|
286
|
+
30 Step failed
|
|
287
|
+
40 Waiting for external event
|
|
288
|
+
50 Cancelled
|
|
289
|
+
60 Persistence error
|
|
290
|
+
70 Concurrency or lock error
|
|
291
|
+
80 Unsupported feature
|
|
292
|
+
90 Internal error
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
`40` is not a failure. It means the workflow is paused and ready to be resumed with an explicit event payload.
|
|
296
|
+
|
|
297
|
+
## Who It Is For
|
|
298
|
+
|
|
299
|
+
`checkpointflow` is a good fit if you want to:
|
|
300
|
+
|
|
301
|
+
- turn a conversation into a durable workflow file
|
|
302
|
+
- run mixed human-agent workflows without hidden continuation
|
|
303
|
+
- encode operational runbooks that can be resumed later
|
|
304
|
+
- keep the reliable parts of an agent workflow outside the prompt
|
|
305
|
+
- hand work from one agent or operator to another safely
|
|
306
|
+
|
|
307
|
+
It is not trying to be a general-purpose programming language or a heavyweight distributed orchestration platform.
|
|
308
|
+
|
|
309
|
+
## Documentation
|
|
310
|
+
|
|
311
|
+
- [Guide](./checkpointflow-guide.md)
|
|
312
|
+
- [Product and Technical Spec](./checkpointflow-prd.md)
|
|
313
|
+
- [Phase 1 Implementation Plan](./checkpointflow-phase1-implementation-plan.md)
|
|
314
|
+
- [Workflow Schema](./schemas/checkpointflow.schema.json)
|
|
315
|
+
- [Run Envelope Schema](./schemas/checkpointflow-run-envelope.schema.json)
|
|
316
|
+
- [Example Workflow](./examples/publish-confluence-change.yaml)
|
|
317
|
+
- [Contributing](./CONTRIBUTING.md)
|
|
318
|
+
|
|
319
|
+
## Development
|
|
320
|
+
|
|
321
|
+
This project is developed with strict TDD and uses `uv` as the package manager.
|
|
322
|
+
|
|
323
|
+
```bash
|
|
324
|
+
uv sync --group dev
|
|
325
|
+
uv run python scripts/install_git_hooks.py
|
|
326
|
+
uv run pytest
|
|
327
|
+
uv run ruff check .
|
|
328
|
+
uv run mypy
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
GitHub Actions enforces the same quality gate in CI:
|
|
332
|
+
|
|
333
|
+
- `uv lock --check`
|
|
334
|
+
- `ruff format --check`
|
|
335
|
+
- `ruff check`
|
|
336
|
+
- `mypy`
|
|
337
|
+
- cross-platform `pytest`
|
|
338
|
+
- `uv build` plus a built-wheel smoke test
|
|
339
|
+
|
|
340
|
+
## License
|
|
341
|
+
|
|
342
|
+
Private repository.
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
# checkpointflow
|
|
2
|
+
|
|
3
|
+
Deterministic, resumable workflows for agents, operators, and shells.
|
|
4
|
+
|
|
5
|
+
`checkpointflow` lets you define a workflow as a portable state machine in YAML, validate it before execution, run it through a stable CLI, pause for explicit external input, and resume later without relying on hidden chat history or process-local state.
|
|
6
|
+
|
|
7
|
+
It is built for workflows that mix:
|
|
8
|
+
|
|
9
|
+
- local CLI steps
|
|
10
|
+
- structured branching
|
|
11
|
+
- human approval
|
|
12
|
+
- bounded agent judgment
|
|
13
|
+
- durable handoff across shells, tools, and operators
|
|
14
|
+
|
|
15
|
+
## Why checkpointflow
|
|
16
|
+
|
|
17
|
+
Most agent workflows break down in the same places:
|
|
18
|
+
|
|
19
|
+
- important state lives only in a chat thread
|
|
20
|
+
- pause and resume are implicit instead of modeled
|
|
21
|
+
- outputs are hard to validate before taking the next step
|
|
22
|
+
- another agent or operator cannot reliably continue the work
|
|
23
|
+
|
|
24
|
+
`checkpointflow` solves that by making the workflow contract explicit:
|
|
25
|
+
|
|
26
|
+
- workflows are written as versioned YAML documents
|
|
27
|
+
- inputs, outputs, and resume events are validated with JSON Schema
|
|
28
|
+
- stdout returns stable machine-readable JSON envelopes
|
|
29
|
+
- waiting is explicit and durable through `await_event`
|
|
30
|
+
- state is persisted under `~/.checkpointflow/` by default
|
|
31
|
+
- workflow files can live anywhere on disk
|
|
32
|
+
|
|
33
|
+
## What You Get
|
|
34
|
+
|
|
35
|
+
- A narrow, predictable runtime contract that is easy for agents to drive.
|
|
36
|
+
- Stable CLI commands for validate, run, resume, inspect, status, logs, cancel, and guide.
|
|
37
|
+
- Explicit pause/resume semantics instead of hidden conversational continuation.
|
|
38
|
+
- Deterministic control flow driven by persisted inputs and step outputs.
|
|
39
|
+
- A default user-scoped state store at `~/.checkpointflow/`.
|
|
40
|
+
- A workflow format that is simple enough to derive from human-agent conversation.
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
Install with `uv`:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
uv tool install checkpointflow
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Then verify the CLI is available:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
cpf --help
|
|
54
|
+
cpf guide
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
If you want to use it inside an existing Python project:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
uv add checkpointflow
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Quick Start
|
|
64
|
+
|
|
65
|
+
Create a workflow file anywhere you want, for example `workflow.yaml`:
|
|
66
|
+
|
|
67
|
+
```yaml
|
|
68
|
+
schema_version: checkpointflow/v1
|
|
69
|
+
workflow:
|
|
70
|
+
id: publish_confluence_change
|
|
71
|
+
name: Publish Confluence change
|
|
72
|
+
version: 1.0.0
|
|
73
|
+
inputs:
|
|
74
|
+
type: object
|
|
75
|
+
required: [page_id, source_file]
|
|
76
|
+
properties:
|
|
77
|
+
page_id:
|
|
78
|
+
type: string
|
|
79
|
+
source_file:
|
|
80
|
+
type: string
|
|
81
|
+
|
|
82
|
+
steps:
|
|
83
|
+
- id: plan
|
|
84
|
+
kind: cli
|
|
85
|
+
command: confpub plan --page-id ${inputs.page_id} --file ${inputs.source_file} --format json
|
|
86
|
+
outputs:
|
|
87
|
+
type: object
|
|
88
|
+
required: [plan_file, summary]
|
|
89
|
+
properties:
|
|
90
|
+
plan_file: { type: string }
|
|
91
|
+
summary: { type: string }
|
|
92
|
+
|
|
93
|
+
- id: approval
|
|
94
|
+
kind: await_event
|
|
95
|
+
audience: user
|
|
96
|
+
event_name: change_approval
|
|
97
|
+
prompt: Review the proposed page update and approve or reject it.
|
|
98
|
+
input_schema:
|
|
99
|
+
type: object
|
|
100
|
+
required: [decision]
|
|
101
|
+
properties:
|
|
102
|
+
decision:
|
|
103
|
+
type: string
|
|
104
|
+
enum: [approve, reject]
|
|
105
|
+
comment:
|
|
106
|
+
type: string
|
|
107
|
+
transitions:
|
|
108
|
+
- when: ${event.decision == "approve"}
|
|
109
|
+
next: apply
|
|
110
|
+
- when: ${event.decision == "reject"}
|
|
111
|
+
next: rejected
|
|
112
|
+
|
|
113
|
+
- id: apply
|
|
114
|
+
kind: cli
|
|
115
|
+
command: confpub apply --plan ${steps.plan.outputs.plan_file} --format json
|
|
116
|
+
outputs:
|
|
117
|
+
type: object
|
|
118
|
+
required: [page_url]
|
|
119
|
+
properties:
|
|
120
|
+
page_url: { type: string }
|
|
121
|
+
|
|
122
|
+
- id: rejected
|
|
123
|
+
kind: end
|
|
124
|
+
result:
|
|
125
|
+
status: rejected
|
|
126
|
+
|
|
127
|
+
outputs:
|
|
128
|
+
type: object
|
|
129
|
+
properties:
|
|
130
|
+
page_url:
|
|
131
|
+
from: steps.apply.outputs.page_url
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Create an input file:
|
|
135
|
+
|
|
136
|
+
```json
|
|
137
|
+
{
|
|
138
|
+
"page_id": "123456",
|
|
139
|
+
"source_file": "docs/architecture.md"
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Validate the workflow:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
cpf validate -f workflow.yaml
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Run it:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
cpf run -f workflow.yaml --input @input.json
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
If the workflow reaches a wait point, `checkpointflow` exits with code `40` and prints a waiting envelope on stdout:
|
|
156
|
+
|
|
157
|
+
```json
|
|
158
|
+
{
|
|
159
|
+
"schema_version": "checkpointflow-run/v1",
|
|
160
|
+
"ok": true,
|
|
161
|
+
"command": "run",
|
|
162
|
+
"status": "waiting",
|
|
163
|
+
"exit_code": 40,
|
|
164
|
+
"run_id": "01JQEXAMPLE00000000000002",
|
|
165
|
+
"current_step_id": "approval",
|
|
166
|
+
"wait": {
|
|
167
|
+
"audience": "user",
|
|
168
|
+
"event_name": "change_approval",
|
|
169
|
+
"prompt": "Review the proposed page update and approve or reject it.",
|
|
170
|
+
"input_schema": {
|
|
171
|
+
"type": "object",
|
|
172
|
+
"required": ["decision"],
|
|
173
|
+
"properties": {
|
|
174
|
+
"decision": {
|
|
175
|
+
"type": "string",
|
|
176
|
+
"enum": ["approve", "reject"]
|
|
177
|
+
},
|
|
178
|
+
"comment": {
|
|
179
|
+
"type": "string"
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
"resume": {
|
|
184
|
+
"command": "cpf resume --run-id 01JQEXAMPLE00000000000002 --event change_approval --input @response.json"
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Resume with explicit input:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
cpf resume --run-id 01JQEXAMPLE00000000000002 --event change_approval --input @response.json
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Core Ideas
|
|
197
|
+
|
|
198
|
+
### Explicit pause and resume
|
|
199
|
+
|
|
200
|
+
`checkpointflow` uses a single pause primitive:
|
|
201
|
+
|
|
202
|
+
- `await_event`
|
|
203
|
+
|
|
204
|
+
That same primitive covers:
|
|
205
|
+
|
|
206
|
+
- user approval
|
|
207
|
+
- agent decision
|
|
208
|
+
- callback from another system
|
|
209
|
+
- manual operator continuation
|
|
210
|
+
|
|
211
|
+
This keeps the runtime small and the resume contract uniform.
|
|
212
|
+
|
|
213
|
+
### Deterministic orchestration
|
|
214
|
+
|
|
215
|
+
Control flow is driven by persisted workflow inputs, validated event payloads, and step outputs. The orchestrator does not depend on hidden prompt state, wall-clock reads during evaluation, or direct network results inside expressions.
|
|
216
|
+
|
|
217
|
+
### Agent-agnostic execution
|
|
218
|
+
|
|
219
|
+
A workflow can be driven by:
|
|
220
|
+
|
|
221
|
+
- Codex
|
|
222
|
+
- Claude Code
|
|
223
|
+
- GitHub Copilot agents
|
|
224
|
+
- CI jobs
|
|
225
|
+
- shell scripts
|
|
226
|
+
- a human operator
|
|
227
|
+
|
|
228
|
+
The workflow remains the same because the runtime contract remains the same.
|
|
229
|
+
|
|
230
|
+
## Stable CLI Surface
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
cpf init
|
|
234
|
+
cpf guide
|
|
235
|
+
cpf validate -f workflow.yaml
|
|
236
|
+
cpf run -f workflow.yaml --input @input.json
|
|
237
|
+
cpf resume --run-id <run_id> --event <event_name> --input @event.json
|
|
238
|
+
cpf status --run-id <run_id>
|
|
239
|
+
cpf inspect --run-id <run_id>
|
|
240
|
+
cpf logs --run-id <run_id>
|
|
241
|
+
cpf cancel --run-id <run_id> --reason "..."
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Workflow Model
|
|
245
|
+
|
|
246
|
+
The runtime is intentionally opinionated.
|
|
247
|
+
|
|
248
|
+
For most production workflows, teams start with:
|
|
249
|
+
|
|
250
|
+
- `cli`
|
|
251
|
+
- `await_event`
|
|
252
|
+
- `end`
|
|
253
|
+
|
|
254
|
+
That narrow core covers a surprising amount of real work while keeping the execution model understandable and replay-safe.
|
|
255
|
+
|
|
256
|
+
## Storage Model
|
|
257
|
+
|
|
258
|
+
Workflow location and runtime state are separate concerns.
|
|
259
|
+
|
|
260
|
+
- Workflow files can live anywhere on disk.
|
|
261
|
+
- Runtime state defaults to `~/.checkpointflow/`.
|
|
262
|
+
- Run metadata lives in `~/.checkpointflow/runs.db`.
|
|
263
|
+
- Per-run artifacts live under `~/.checkpointflow/runs/<run_id>/`.
|
|
264
|
+
- The workflow source path is recorded as metadata, but it does not determine where state is stored.
|
|
265
|
+
|
|
266
|
+
This makes workflows easy to keep in repos, temp directories, shared folders, or generated output locations without coupling them to runtime storage.
|
|
267
|
+
|
|
268
|
+
## Exit Codes
|
|
269
|
+
|
|
270
|
+
```text
|
|
271
|
+
0 Success
|
|
272
|
+
10 Validation error
|
|
273
|
+
20 Runtime error
|
|
274
|
+
30 Step failed
|
|
275
|
+
40 Waiting for external event
|
|
276
|
+
50 Cancelled
|
|
277
|
+
60 Persistence error
|
|
278
|
+
70 Concurrency or lock error
|
|
279
|
+
80 Unsupported feature
|
|
280
|
+
90 Internal error
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
`40` is not a failure. It means the workflow is paused and ready to be resumed with an explicit event payload.
|
|
284
|
+
|
|
285
|
+
## Who It Is For
|
|
286
|
+
|
|
287
|
+
`checkpointflow` is a good fit if you want to:
|
|
288
|
+
|
|
289
|
+
- turn a conversation into a durable workflow file
|
|
290
|
+
- run mixed human-agent workflows without hidden continuation
|
|
291
|
+
- encode operational runbooks that can be resumed later
|
|
292
|
+
- keep the reliable parts of an agent workflow outside the prompt
|
|
293
|
+
- hand work from one agent or operator to another safely
|
|
294
|
+
|
|
295
|
+
It is not trying to be a general-purpose programming language or a heavyweight distributed orchestration platform.
|
|
296
|
+
|
|
297
|
+
## Documentation
|
|
298
|
+
|
|
299
|
+
- [Guide](./checkpointflow-guide.md)
|
|
300
|
+
- [Product and Technical Spec](./checkpointflow-prd.md)
|
|
301
|
+
- [Phase 1 Implementation Plan](./checkpointflow-phase1-implementation-plan.md)
|
|
302
|
+
- [Workflow Schema](./schemas/checkpointflow.schema.json)
|
|
303
|
+
- [Run Envelope Schema](./schemas/checkpointflow-run-envelope.schema.json)
|
|
304
|
+
- [Example Workflow](./examples/publish-confluence-change.yaml)
|
|
305
|
+
- [Contributing](./CONTRIBUTING.md)
|
|
306
|
+
|
|
307
|
+
## Development
|
|
308
|
+
|
|
309
|
+
This project is developed with strict TDD and uses `uv` as the package manager.
|
|
310
|
+
|
|
311
|
+
```bash
|
|
312
|
+
uv sync --group dev
|
|
313
|
+
uv run python scripts/install_git_hooks.py
|
|
314
|
+
uv run pytest
|
|
315
|
+
uv run ruff check .
|
|
316
|
+
uv run mypy
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
GitHub Actions enforces the same quality gate in CI:
|
|
320
|
+
|
|
321
|
+
- `uv lock --check`
|
|
322
|
+
- `ruff format --check`
|
|
323
|
+
- `ruff check`
|
|
324
|
+
- `mypy`
|
|
325
|
+
- cross-platform `pytest`
|
|
326
|
+
- `uv build` plus a built-wheel smoke test
|
|
327
|
+
|
|
328
|
+
## License
|
|
329
|
+
|
|
330
|
+
Private repository.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "checkpointflow"
|
|
3
|
+
version = "1.0.4"
|
|
4
|
+
description = "CLI toolchain for authoring and running deterministic, resumable agent workflows."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.13"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"jsonschema>=4.26.0",
|
|
9
|
+
"pydantic>=2.12.5",
|
|
10
|
+
"pyyaml>=6.0.3",
|
|
11
|
+
"rich>=14.3.3",
|
|
12
|
+
"typer>=0.24.1",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[project.scripts]
|
|
16
|
+
cpf = "checkpointflow.cli:main"
|
|
17
|
+
|
|
18
|
+
[build-system]
|
|
19
|
+
requires = ["uv_build>=0.11.2,<0.12"]
|
|
20
|
+
build-backend = "uv_build"
|
|
21
|
+
|
|
22
|
+
[dependency-groups]
|
|
23
|
+
dev = [
|
|
24
|
+
"mypy>=1.19.1",
|
|
25
|
+
"pytest>=9.0.2",
|
|
26
|
+
"pytest-cov>=7.1.0",
|
|
27
|
+
"ruff>=0.15.8",
|
|
28
|
+
"types-pyyaml>=6.0.12.20250915",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[tool.ruff]
|
|
32
|
+
target-version = "py313"
|
|
33
|
+
line-length = 100
|
|
34
|
+
src = ["src", "tests"]
|
|
35
|
+
|
|
36
|
+
[tool.ruff.lint]
|
|
37
|
+
select = ["B", "E", "F", "I", "PTH", "RET", "RUF", "SIM", "UP"]
|
|
38
|
+
|
|
39
|
+
[tool.ruff.format]
|
|
40
|
+
docstring-code-format = true
|
|
41
|
+
|
|
42
|
+
[tool.mypy]
|
|
43
|
+
python_version = "3.13"
|
|
44
|
+
strict = true
|
|
45
|
+
packages = ["checkpointflow"]
|
|
46
|
+
plugins = ["pydantic.mypy"]
|
|
47
|
+
|
|
48
|
+
[tool.pytest.ini_options]
|
|
49
|
+
addopts = "-ra --maxfail=1"
|
|
50
|
+
testpaths = ["tests"]
|