fdsx 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.
- fdsx-0.1.0/.github/workflows/publish.yml +83 -0
- fdsx-0.1.0/.github/workflows/test.yml +53 -0
- fdsx-0.1.0/.pre-commit-config.yaml +7 -0
- fdsx-0.1.0/AGENTS.md +70 -0
- fdsx-0.1.0/CLAUDE.md +70 -0
- fdsx-0.1.0/LICENSE +21 -0
- fdsx-0.1.0/MANIFEST.in +19 -0
- fdsx-0.1.0/PKG-INFO +197 -0
- fdsx-0.1.0/README.md +159 -0
- fdsx-0.1.0/examples/workflows/plan-implement-review.yaml +203 -0
- fdsx-0.1.0/pyproject.toml +88 -0
- fdsx-0.1.0/setup.cfg +4 -0
- fdsx-0.1.0/src/fdsx/__init__.py +8 -0
- fdsx-0.1.0/src/fdsx/checkpoint/__init__.py +1 -0
- fdsx-0.1.0/src/fdsx/checkpoint/manager.py +337 -0
- fdsx-0.1.0/src/fdsx/cli/__init__.py +1 -0
- fdsx-0.1.0/src/fdsx/cli/main.py +366 -0
- fdsx-0.1.0/src/fdsx/core/__init__.py +1 -0
- fdsx-0.1.0/src/fdsx/core/batch.py +527 -0
- fdsx-0.1.0/src/fdsx/core/compiler/__init__.py +42 -0
- fdsx-0.1.0/src/fdsx/core/compiler/aggregation.py +45 -0
- fdsx-0.1.0/src/fdsx/core/compiler/compile.py +412 -0
- fdsx-0.1.0/src/fdsx/core/compiler/execution.py +163 -0
- fdsx-0.1.0/src/fdsx/core/compiler/helpers.py +218 -0
- fdsx-0.1.0/src/fdsx/core/compiler/nodes.py +283 -0
- fdsx-0.1.0/src/fdsx/core/compiler/parallel.py +306 -0
- fdsx-0.1.0/src/fdsx/core/compiler/routing.py +47 -0
- fdsx-0.1.0/src/fdsx/core/config.py +233 -0
- fdsx-0.1.0/src/fdsx/core/engine/__init__.py +41 -0
- fdsx-0.1.0/src/fdsx/core/engine/batch.py +126 -0
- fdsx-0.1.0/src/fdsx/core/engine/interrupts.py +59 -0
- fdsx-0.1.0/src/fdsx/core/engine/results.py +74 -0
- fdsx-0.1.0/src/fdsx/core/engine/resume.py +231 -0
- fdsx-0.1.0/src/fdsx/core/engine/run.py +189 -0
- fdsx-0.1.0/src/fdsx/core/engine/signals.py +156 -0
- fdsx-0.1.0/src/fdsx/core/engine/tasks_dir.py +415 -0
- fdsx-0.1.0/src/fdsx/core/engine/validate.py +24 -0
- fdsx-0.1.0/src/fdsx/core/extraction.py +253 -0
- fdsx-0.1.0/src/fdsx/core/graph_utils.py +52 -0
- fdsx-0.1.0/src/fdsx/core/hooks.py +200 -0
- fdsx-0.1.0/src/fdsx/core/loader.py +178 -0
- fdsx-0.1.0/src/fdsx/core/paths.py +40 -0
- fdsx-0.1.0/src/fdsx/core/selector.py +430 -0
- fdsx-0.1.0/src/fdsx/core/thread_id.py +8 -0
- fdsx-0.1.0/src/fdsx/core/variables.py +390 -0
- fdsx-0.1.0/src/fdsx/display/__init__.py +1 -0
- fdsx-0.1.0/src/fdsx/display/terminal.py +674 -0
- fdsx-0.1.0/src/fdsx/logging/__init__.py +6 -0
- fdsx-0.1.0/src/fdsx/logging/recorder.py +204 -0
- fdsx-0.1.0/src/fdsx/logging/stream_logger.py +99 -0
- fdsx-0.1.0/src/fdsx/models/__init__.py +1 -0
- fdsx-0.1.0/src/fdsx/models/flow.py +485 -0
- fdsx-0.1.0/src/fdsx/models/task.py +229 -0
- fdsx-0.1.0/src/fdsx/models/validators.py +30 -0
- fdsx-0.1.0/src/fdsx/notify/__init__.py +1 -0
- fdsx-0.1.0/src/fdsx/notify/webhook.py +72 -0
- fdsx-0.1.0/src/fdsx/providers/__init__.py +1 -0
- fdsx-0.1.0/src/fdsx/providers/base.py +381 -0
- fdsx-0.1.0/src/fdsx/providers/claude.py +294 -0
- fdsx-0.1.0/src/fdsx/providers/codex.py +236 -0
- fdsx-0.1.0/src/fdsx/providers/opencode.py +96 -0
- fdsx-0.1.0/src/fdsx/providers/system.py +50 -0
- fdsx-0.1.0/src/fdsx.egg-info/PKG-INFO +197 -0
- fdsx-0.1.0/src/fdsx.egg-info/SOURCES.txt +182 -0
- fdsx-0.1.0/src/fdsx.egg-info/dependency_links.txt +1 -0
- fdsx-0.1.0/src/fdsx.egg-info/entry_points.txt +2 -0
- fdsx-0.1.0/src/fdsx.egg-info/requires.txt +15 -0
- fdsx-0.1.0/src/fdsx.egg-info/top_level.txt +1 -0
- fdsx-0.1.0/tests/__init__.py +5 -0
- fdsx-0.1.0/tests/conftest.py +7 -0
- fdsx-0.1.0/tests/e2e/__init__.py +1 -0
- fdsx-0.1.0/tests/e2e/cli_test_utils.py +43 -0
- fdsx-0.1.0/tests/e2e/conftest.py +1 -0
- fdsx-0.1.0/tests/e2e/test_batch_backward_compat.py +73 -0
- fdsx-0.1.0/tests/e2e/test_batch_edge_cases.py +121 -0
- fdsx-0.1.0/tests/e2e/test_batch_error_messages.py +53 -0
- fdsx-0.1.0/tests/e2e/test_batch_full_pipeline.py +471 -0
- fdsx-0.1.0/tests/e2e/test_cli_batch_split.py +277 -0
- fdsx-0.1.0/tests/e2e/test_cli_batch_tasks.py +114 -0
- fdsx-0.1.0/tests/e2e/test_cli_flow_types.py +49 -0
- fdsx-0.1.0/tests/e2e/test_cli_signal_handling.py +211 -0
- fdsx-0.1.0/tests/e2e/test_cli_thread_id.py +106 -0
- fdsx-0.1.0/tests/e2e/test_cli_validation_and_run.py +50 -0
- fdsx-0.1.0/tests/e2e/test_cli_wait_and_resume.py +191 -0
- fdsx-0.1.0/tests/e2e/test_cli_workflow_name.py +24 -0
- fdsx-0.1.0/tests/fixtures/batch_flow.yaml +19 -0
- fdsx-0.1.0/tests/fixtures/checkpoint_flow.yaml +29 -0
- fdsx-0.1.0/tests/fixtures/choice_flow.yaml +46 -0
- fdsx-0.1.0/tests/fixtures/choice_flow_default.yaml +46 -0
- fdsx-0.1.0/tests/fixtures/claude_stream.ndjson +10 -0
- fdsx-0.1.0/tests/fixtures/codex_stream.jsonl +12 -0
- fdsx-0.1.0/tests/fixtures/extraction_flow.yaml +51 -0
- fdsx-0.1.0/tests/fixtures/input_flow.yaml +12 -0
- fdsx-0.1.0/tests/fixtures/invalid_flows/bad_next_ref.yaml +12 -0
- fdsx-0.1.0/tests/fixtures/invalid_flows/missing_start_at.yaml +12 -0
- fdsx-0.1.0/tests/fixtures/invalid_flows/mutual_exclusive.yaml +14 -0
- fdsx-0.1.0/tests/fixtures/json_codeblock_extraction_flow.yaml +52 -0
- fdsx-0.1.0/tests/fixtures/json_extraction_flow.yaml +51 -0
- fdsx-0.1.0/tests/fixtures/loop_flow.yaml +42 -0
- fdsx-0.1.0/tests/fixtures/max_iterations_flow.yaml +36 -0
- fdsx-0.1.0/tests/fixtures/max_iterations_wait_flow.yaml +38 -0
- fdsx-0.1.0/tests/fixtures/parallel_min_success.yaml +28 -0
- fdsx-0.1.0/tests/fixtures/parallel_review.yaml +80 -0
- fdsx-0.1.0/tests/fixtures/prompt_file_test/flow.yaml +13 -0
- fdsx-0.1.0/tests/fixtures/prompt_file_test/prompt.txt +1 -0
- fdsx-0.1.0/tests/fixtures/regex_extraction_flow.yaml +51 -0
- fdsx-0.1.0/tests/fixtures/sample_tasks.md +3 -0
- fdsx-0.1.0/tests/fixtures/simple_flow.yaml +26 -0
- fdsx-0.1.0/tests/fixtures/wait_approval.yaml +56 -0
- fdsx-0.1.0/tests/fixtures/wait_resume_flow.yaml +56 -0
- fdsx-0.1.0/tests/fixtures/wait_webhook.yaml +60 -0
- fdsx-0.1.0/tests/fixtures/workflows_name_display/alpha.yaml +12 -0
- fdsx-0.1.0/tests/fixtures/workflows_name_display/beta/workflow.yaml +12 -0
- fdsx-0.1.0/tests/fixtures/workflows_name_display/gamma.yaml +12 -0
- fdsx-0.1.0/tests/integration/__init__.py +1 -0
- fdsx-0.1.0/tests/integration/test_auto_select.py +223 -0
- fdsx-0.1.0/tests/integration/test_checkpoint_resume.py +282 -0
- fdsx-0.1.0/tests/integration/test_choice_flow.py +45 -0
- fdsx-0.1.0/tests/integration/test_claude_streaming.py +185 -0
- fdsx-0.1.0/tests/integration/test_codex_completion.py +78 -0
- fdsx-0.1.0/tests/integration/test_codex_streaming.py +202 -0
- fdsx-0.1.0/tests/integration/test_extraction_flow.py +100 -0
- fdsx-0.1.0/tests/integration/test_hook_streaming_interaction.py +667 -0
- fdsx-0.1.0/tests/integration/test_hooks_integration.py +1042 -0
- fdsx-0.1.0/tests/integration/test_inactivity_timeout.py +261 -0
- fdsx-0.1.0/tests/integration/test_iteration_logs.py +100 -0
- fdsx-0.1.0/tests/integration/test_large_command.py +79 -0
- fdsx-0.1.0/tests/integration/test_linear_flow.py +41 -0
- fdsx-0.1.0/tests/integration/test_lock_atomicity.py +131 -0
- fdsx-0.1.0/tests/integration/test_loop_enforcement.py +16 -0
- fdsx-0.1.0/tests/integration/test_loop_flow.py +55 -0
- fdsx-0.1.0/tests/integration/test_max_iterations_flow.py +136 -0
- fdsx-0.1.0/tests/integration/test_parallel_flow.py +216 -0
- fdsx-0.1.0/tests/integration/test_provider_options.py +359 -0
- fdsx-0.1.0/tests/integration/test_quiet_mode.py +104 -0
- fdsx-0.1.0/tests/integration/test_result_file.py +174 -0
- fdsx-0.1.0/tests/integration/test_resume_interrupt.py +272 -0
- fdsx-0.1.0/tests/integration/test_scenario_flows.py +358 -0
- fdsx-0.1.0/tests/integration/test_split.py +613 -0
- fdsx-0.1.0/tests/integration/test_split_spinner.py +290 -0
- fdsx-0.1.0/tests/integration/test_subprocess_completion.py +270 -0
- fdsx-0.1.0/tests/integration/test_tasks_dir.py +1023 -0
- fdsx-0.1.0/tests/integration/test_wait_resume.py +107 -0
- fdsx-0.1.0/tests/integration/test_workflow_persistence.py +225 -0
- fdsx-0.1.0/tests/unit/__init__.py +1 -0
- fdsx-0.1.0/tests/unit/test_aggregation.py +328 -0
- fdsx-0.1.0/tests/unit/test_backoff.py +268 -0
- fdsx-0.1.0/tests/unit/test_batch.py +826 -0
- fdsx-0.1.0/tests/unit/test_checkpoint.py +252 -0
- fdsx-0.1.0/tests/unit/test_claude_stream_parser.py +610 -0
- fdsx-0.1.0/tests/unit/test_cli_version.py +18 -0
- fdsx-0.1.0/tests/unit/test_codex_stream_parser.py +591 -0
- fdsx-0.1.0/tests/unit/test_compiler_merge.py +283 -0
- fdsx-0.1.0/tests/unit/test_config.py +595 -0
- fdsx-0.1.0/tests/unit/test_engine.py +397 -0
- fdsx-0.1.0/tests/unit/test_execution.py +447 -0
- fdsx-0.1.0/tests/unit/test_extraction.py +475 -0
- fdsx-0.1.0/tests/unit/test_graph_utils.py +158 -0
- fdsx-0.1.0/tests/unit/test_hooks.py +624 -0
- fdsx-0.1.0/tests/unit/test_loader.py +264 -0
- fdsx-0.1.0/tests/unit/test_max_iterations.py +189 -0
- fdsx-0.1.0/tests/unit/test_models.py +479 -0
- fdsx-0.1.0/tests/unit/test_paths.py +47 -0
- fdsx-0.1.0/tests/unit/test_prompt_file.py +152 -0
- fdsx-0.1.0/tests/unit/test_provider_options.py +360 -0
- fdsx-0.1.0/tests/unit/test_provider_stdin_fallback.py +173 -0
- fdsx-0.1.0/tests/unit/test_recorder.py +508 -0
- fdsx-0.1.0/tests/unit/test_result_file.py +647 -0
- fdsx-0.1.0/tests/unit/test_resume_display.py +193 -0
- fdsx-0.1.0/tests/unit/test_selector.py +899 -0
- fdsx-0.1.0/tests/unit/test_selector_name.py +155 -0
- fdsx-0.1.0/tests/unit/test_signal_handler.py +347 -0
- fdsx-0.1.0/tests/unit/test_spinner.py +339 -0
- fdsx-0.1.0/tests/unit/test_stream_logger.py +331 -0
- fdsx-0.1.0/tests/unit/test_stream_logger_iteration.py +167 -0
- fdsx-0.1.0/tests/unit/test_subprocess_completion.py +710 -0
- fdsx-0.1.0/tests/unit/test_subprocess_realtime_streaming.py +105 -0
- fdsx-0.1.0/tests/unit/test_subprocess_stdin.py +206 -0
- fdsx-0.1.0/tests/unit/test_task_model.py +621 -0
- fdsx-0.1.0/tests/unit/test_terminal.py +574 -0
- fdsx-0.1.0/tests/unit/test_thread_id.py +54 -0
- fdsx-0.1.0/tests/unit/test_variables.py +789 -0
- fdsx-0.1.0/tests/unit/test_webhook.py +255 -0
- fdsx-0.1.0/tests/unit/test_workflow_cui.py +350 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
name: publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
inputs:
|
|
9
|
+
testpypi:
|
|
10
|
+
description: "Publish to TestPyPI"
|
|
11
|
+
required: false
|
|
12
|
+
default: false
|
|
13
|
+
type: boolean
|
|
14
|
+
|
|
15
|
+
permissions:
|
|
16
|
+
contents: read
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
test:
|
|
20
|
+
name: test
|
|
21
|
+
uses: ./.github/workflows/test.yml
|
|
22
|
+
|
|
23
|
+
build:
|
|
24
|
+
name: build
|
|
25
|
+
needs: test
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
steps:
|
|
28
|
+
- uses: actions/checkout@v4
|
|
29
|
+
with:
|
|
30
|
+
fetch-depth: 0
|
|
31
|
+
- uses: actions/setup-python@v5
|
|
32
|
+
with:
|
|
33
|
+
python-version: "3.12"
|
|
34
|
+
- name: install build
|
|
35
|
+
run: pip install build
|
|
36
|
+
- name: build
|
|
37
|
+
run: python -m build
|
|
38
|
+
- name: upload artifact
|
|
39
|
+
uses: actions/upload-artifact@v4
|
|
40
|
+
with:
|
|
41
|
+
name: dist
|
|
42
|
+
path: dist/
|
|
43
|
+
|
|
44
|
+
publish-to-testpypi:
|
|
45
|
+
name: publish-to-testpypi
|
|
46
|
+
needs: build
|
|
47
|
+
runs-on: ubuntu-latest
|
|
48
|
+
if: github.event_name == 'workflow_dispatch' && inputs.testpypi
|
|
49
|
+
environment:
|
|
50
|
+
name: testpypi
|
|
51
|
+
url: https://test.pypi.org/project/fdsx/
|
|
52
|
+
permissions:
|
|
53
|
+
id-token: write
|
|
54
|
+
steps:
|
|
55
|
+
- name: download artifact
|
|
56
|
+
uses: actions/download-artifact@v4
|
|
57
|
+
with:
|
|
58
|
+
name: dist
|
|
59
|
+
path: dist/
|
|
60
|
+
- name: publish to TestPyPI
|
|
61
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
62
|
+
with:
|
|
63
|
+
repository-url: https://test.pypi.org/legacy/
|
|
64
|
+
skip-existing: true
|
|
65
|
+
|
|
66
|
+
publish-to-pypi:
|
|
67
|
+
name: publish-to-pypi
|
|
68
|
+
needs: build
|
|
69
|
+
runs-on: ubuntu-latest
|
|
70
|
+
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
|
|
71
|
+
environment:
|
|
72
|
+
name: pypi
|
|
73
|
+
url: https://pypi.org/project/fdsx/
|
|
74
|
+
permissions:
|
|
75
|
+
id-token: write
|
|
76
|
+
steps:
|
|
77
|
+
- name: download artifact
|
|
78
|
+
uses: actions/download-artifact@v4
|
|
79
|
+
with:
|
|
80
|
+
name: dist
|
|
81
|
+
path: dist/
|
|
82
|
+
- name: publish to PyPI
|
|
83
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
name: test
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
workflow_call:
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
lint:
|
|
12
|
+
name: lint
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: actions/setup-python@v5
|
|
17
|
+
with:
|
|
18
|
+
python-version: "3.12"
|
|
19
|
+
- name: install ruff
|
|
20
|
+
run: pip install ruff
|
|
21
|
+
- name: ruff check
|
|
22
|
+
run: ruff check .
|
|
23
|
+
- name: ruff format check
|
|
24
|
+
run: ruff format --check .
|
|
25
|
+
|
|
26
|
+
test:
|
|
27
|
+
name: test
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
strategy:
|
|
30
|
+
fail-fast: false
|
|
31
|
+
matrix:
|
|
32
|
+
python-version:
|
|
33
|
+
- "3.10"
|
|
34
|
+
- "3.11"
|
|
35
|
+
- "3.12"
|
|
36
|
+
- "3.13"
|
|
37
|
+
steps:
|
|
38
|
+
- uses: actions/checkout@v4
|
|
39
|
+
with:
|
|
40
|
+
fetch-depth: 0
|
|
41
|
+
- uses: actions/setup-python@v5
|
|
42
|
+
with:
|
|
43
|
+
python-version: ${{ matrix.python-version }}
|
|
44
|
+
- name: install dev deps
|
|
45
|
+
run: pip install -e ".[dev]"
|
|
46
|
+
- name: ruff check
|
|
47
|
+
run: ruff check .
|
|
48
|
+
- name: ruff format check
|
|
49
|
+
run: ruff format --check .
|
|
50
|
+
- name: mypy
|
|
51
|
+
run: mypy src/fdsx
|
|
52
|
+
- name: pytest
|
|
53
|
+
run: pytest
|
fdsx-0.1.0/AGENTS.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Agent Instructions
|
|
2
|
+
|
|
3
|
+
## Testing Guidelines
|
|
4
|
+
|
|
5
|
+
### Test Trophy Strategy
|
|
6
|
+
|
|
7
|
+
This project follows the test trophy pattern. Tests are organized into three layers:
|
|
8
|
+
|
|
9
|
+
#### Directory Structure
|
|
10
|
+
- `tests/unit/` — Unit tests for complex pure logic
|
|
11
|
+
- `tests/integration/` — Integration tests (primary confidence layer)
|
|
12
|
+
- `tests/e2e/` — CLI end-to-end tests (thinnest layer)
|
|
13
|
+
|
|
14
|
+
#### What belongs at each level
|
|
15
|
+
|
|
16
|
+
**Unit tests** (`tests/unit/`):
|
|
17
|
+
- Complex parsing logic (YAML parsing, JSONPath resolution, variable substitution)
|
|
18
|
+
- State transition rules and algorithms
|
|
19
|
+
- Pure functions with non-trivial logic
|
|
20
|
+
- NOT: Pydantic model field assignments, default value checks, isinstance checks
|
|
21
|
+
|
|
22
|
+
**Integration tests** (`tests/integration/`):
|
|
23
|
+
- Complete workflow execution via `engine.run_flow()`
|
|
24
|
+
- Feature-centered tests: each file answers "Does feature X work correctly?"
|
|
25
|
+
- Checkpoint persistence and recovery
|
|
26
|
+
- State variable mutations and result paths
|
|
27
|
+
- Tests using `CliRunner` for testing CLI behavior with mocked internals
|
|
28
|
+
|
|
29
|
+
**E2E tests** (`tests/e2e/`):
|
|
30
|
+
- CLI surface tests via `run_fdsx()` subprocess calls
|
|
31
|
+
- Exit codes, stderr/stdout format validation
|
|
32
|
+
- CLI argument parsing and mutual exclusion
|
|
33
|
+
- Signal handling
|
|
34
|
+
|
|
35
|
+
#### Anti-patterns to avoid
|
|
36
|
+
|
|
37
|
+
- **Trivial field assertion tests**: Don't test that `TaskState(type="task").type == "task"`. Pydantic guarantees this.
|
|
38
|
+
- **isinstance checks**: Don't test `isinstance(generate_thread_id(), str)`. The type system handles this.
|
|
39
|
+
- **Default value tests**: Don't test that `ClaudeOptions().permission_mode is None`. This is framework behavior.
|
|
40
|
+
- **Unnecessary real-time waits**: Mock `time.sleep` for in-process delays. Minimize subprocess sleep durations.
|
|
41
|
+
- **Writing artifacts to project root**: Tests must never create `.fdsx/` artifacts in the project root. Always use `monkeypatch.chdir(tmp_path)` or `cwd=tmp_dir` for subprocess tests.
|
|
42
|
+
|
|
43
|
+
#### Naming conventions
|
|
44
|
+
|
|
45
|
+
- Test files: `test_<feature>.py` (never `test_phase1.py` or `test_e2e_phase2.py`)
|
|
46
|
+
- Test functions: `test_<scenario>_<expected_outcome>`
|
|
47
|
+
- Test classes: `Test<Feature><Aspect>` (e.g., `TestCheckpointResume`, `TestChoiceStateValidation`)
|
|
48
|
+
|
|
49
|
+
#### Integration Test Feature-Centeredness Assessment
|
|
50
|
+
|
|
51
|
+
All integration tests are already feature-centered. No files need restructuring beyond the e2e moves. Each integration test file is organized around a specific feature:
|
|
52
|
+
|
|
53
|
+
- `test_checkpoint_resume.py` — checkpoint and resume behavior
|
|
54
|
+
- `test_choice_flow.py` — choice state routing
|
|
55
|
+
- `test_parallel_flow.py` — parallel execution
|
|
56
|
+
- `test_linear_flow.py` — linear workflow execution
|
|
57
|
+
- `test_loop_flow.py` — loop state behavior
|
|
58
|
+
- `test_extraction_flow.py` — data extraction
|
|
59
|
+
- `test_quiet_mode.py` — quiet mode flag
|
|
60
|
+
- `test_result_file.py` — result file output
|
|
61
|
+
- `test_resume_interrupt.py` — interrupted workflow recovery
|
|
62
|
+
- `test_split.py` — batch split behavior
|
|
63
|
+
- `test_tasks_dir.py` — tasks directory handling
|
|
64
|
+
- `test_auto_select.py` — auto-select logic
|
|
65
|
+
- `test_lock_atomicity.py` — lock file atomicity
|
|
66
|
+
- `test_workflow_persistence.py` — workflow state persistence
|
|
67
|
+
- `test_inactivity_timeout.py` — inactivity timeout handling
|
|
68
|
+
- `test_scenario_flows.py` — cross-cutting scenario flows
|
|
69
|
+
|
|
70
|
+
The integration test suite is well-organized around features (checkpoint, choice, parallel, loop, extraction, etc.) rather than implementation phases. No restructuring was required.
|
fdsx-0.1.0/CLAUDE.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Agent Instructions
|
|
2
|
+
|
|
3
|
+
## Testing Guidelines
|
|
4
|
+
|
|
5
|
+
### Test Trophy Strategy
|
|
6
|
+
|
|
7
|
+
This project follows the test trophy pattern. Tests are organized into three layers:
|
|
8
|
+
|
|
9
|
+
#### Directory Structure
|
|
10
|
+
- `tests/unit/` — Unit tests for complex pure logic
|
|
11
|
+
- `tests/integration/` — Integration tests (primary confidence layer)
|
|
12
|
+
- `tests/e2e/` — CLI end-to-end tests (thinnest layer)
|
|
13
|
+
|
|
14
|
+
#### What belongs at each level
|
|
15
|
+
|
|
16
|
+
**Unit tests** (`tests/unit/`):
|
|
17
|
+
- Complex parsing logic (YAML parsing, JSONPath resolution, variable substitution)
|
|
18
|
+
- State transition rules and algorithms
|
|
19
|
+
- Pure functions with non-trivial logic
|
|
20
|
+
- NOT: Pydantic model field assignments, default value checks, isinstance checks
|
|
21
|
+
|
|
22
|
+
**Integration tests** (`tests/integration/`):
|
|
23
|
+
- Complete workflow execution via `engine.run_flow()`
|
|
24
|
+
- Feature-centered tests: each file answers "Does feature X work correctly?"
|
|
25
|
+
- Checkpoint persistence and recovery
|
|
26
|
+
- State variable mutations and result paths
|
|
27
|
+
- Tests using `CliRunner` for testing CLI behavior with mocked internals
|
|
28
|
+
|
|
29
|
+
**E2E tests** (`tests/e2e/`):
|
|
30
|
+
- CLI surface tests via `run_fdsx()` subprocess calls
|
|
31
|
+
- Exit codes, stderr/stdout format validation
|
|
32
|
+
- CLI argument parsing and mutual exclusion
|
|
33
|
+
- Signal handling
|
|
34
|
+
|
|
35
|
+
#### Anti-patterns to avoid
|
|
36
|
+
|
|
37
|
+
- **Trivial field assertion tests**: Don't test that `TaskState(type="task").type == "task"`. Pydantic guarantees this.
|
|
38
|
+
- **isinstance checks**: Don't test `isinstance(generate_thread_id(), str)`. The type system handles this.
|
|
39
|
+
- **Default value tests**: Don't test that `ClaudeOptions().permission_mode is None`. This is framework behavior.
|
|
40
|
+
- **Unnecessary real-time waits**: Mock `time.sleep` for in-process delays. Minimize subprocess sleep durations.
|
|
41
|
+
- **Writing artifacts to project root**: Tests must never create `.fdsx/` artifacts in the project root. Always use `monkeypatch.chdir(tmp_path)` or `cwd=tmp_dir` for subprocess tests.
|
|
42
|
+
|
|
43
|
+
#### Naming conventions
|
|
44
|
+
|
|
45
|
+
- Test files: `test_<feature>.py` (never `test_phase1.py` or `test_e2e_phase2.py`)
|
|
46
|
+
- Test functions: `test_<scenario>_<expected_outcome>`
|
|
47
|
+
- Test classes: `Test<Feature><Aspect>` (e.g., `TestCheckpointResume`, `TestChoiceStateValidation`)
|
|
48
|
+
|
|
49
|
+
#### Integration Test Feature-Centeredness Assessment
|
|
50
|
+
|
|
51
|
+
All integration tests are already feature-centered. No files need restructuring beyond the e2e moves. Each integration test file is organized around a specific feature:
|
|
52
|
+
|
|
53
|
+
- `test_checkpoint_resume.py` — checkpoint and resume behavior
|
|
54
|
+
- `test_choice_flow.py` — choice state routing
|
|
55
|
+
- `test_parallel_flow.py` — parallel execution
|
|
56
|
+
- `test_linear_flow.py` — linear workflow execution
|
|
57
|
+
- `test_loop_flow.py` — loop state behavior
|
|
58
|
+
- `test_extraction_flow.py` — data extraction
|
|
59
|
+
- `test_quiet_mode.py` — quiet mode flag
|
|
60
|
+
- `test_result_file.py` — result file output
|
|
61
|
+
- `test_resume_interrupt.py` — interrupted workflow recovery
|
|
62
|
+
- `test_split.py` — batch split behavior
|
|
63
|
+
- `test_tasks_dir.py` — tasks directory handling
|
|
64
|
+
- `test_auto_select.py` — auto-select logic
|
|
65
|
+
- `test_lock_atomicity.py` — lock file atomicity
|
|
66
|
+
- `test_workflow_persistence.py` — workflow state persistence
|
|
67
|
+
- `test_inactivity_timeout.py` — inactivity timeout handling
|
|
68
|
+
- `test_scenario_flows.py` — cross-cutting scenario flows
|
|
69
|
+
|
|
70
|
+
The integration test suite is well-organized around features (checkpoint, choice, parallel, loop, extraction, etc.) rather than implementation phases. No restructuring was required.
|
fdsx-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 kenfdev
|
|
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.
|
fdsx-0.1.0/MANIFEST.in
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
graft src
|
|
2
|
+
include pyproject.toml
|
|
3
|
+
include README.md
|
|
4
|
+
include LICENSE
|
|
5
|
+
|
|
6
|
+
prune .claude
|
|
7
|
+
prune .takt
|
|
8
|
+
prune .specify
|
|
9
|
+
prune .worktree
|
|
10
|
+
prune .fdsx
|
|
11
|
+
prune specs
|
|
12
|
+
|
|
13
|
+
exclude .gitignore
|
|
14
|
+
exclude .worktreeinclude
|
|
15
|
+
exclude docker-compose.yml
|
|
16
|
+
exclude uv.lock
|
|
17
|
+
|
|
18
|
+
global-exclude *.pyc
|
|
19
|
+
global-exclude __pycache__
|
fdsx-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fdsx
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Declarative AI agent workflow execution framework
|
|
5
|
+
Author: kenfdev
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/kenfdev/fdsx
|
|
8
|
+
Project-URL: Repository, https://github.com/kenfdev/fdsx
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
17
|
+
Classifier: Operating System :: MacOS
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: langgraph<2,>=1.0
|
|
24
|
+
Requires-Dist: langgraph-checkpoint-sqlite<5,>=3
|
|
25
|
+
Requires-Dist: pyyaml>=6
|
|
26
|
+
Requires-Dist: typer>=0.9
|
|
27
|
+
Requires-Dist: httpx>=0.27
|
|
28
|
+
Requires-Dist: structlog>=24
|
|
29
|
+
Requires-Dist: uuid-utils>=0.9
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
32
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
|
|
33
|
+
Requires-Dist: ruff>=0.4; extra == "dev"
|
|
34
|
+
Requires-Dist: mypy>=1.10; extra == "dev"
|
|
35
|
+
Requires-Dist: types-PyYAML>=6; extra == "dev"
|
|
36
|
+
Requires-Dist: pre-commit>=3; extra == "dev"
|
|
37
|
+
Dynamic: license-file
|
|
38
|
+
|
|
39
|
+
# fdsx — Flow-Driven Stateful eXecution
|
|
40
|
+
|
|
41
|
+
[](https://pypi.org/project/fdsx/)
|
|
42
|
+
|
|
43
|
+
A lightweight framework for building and executing complex AI agent workflows using declarative YAML definitions.
|
|
44
|
+
|
|
45
|
+
## Overview
|
|
46
|
+
|
|
47
|
+
fdsx enables you to define AI agent workflows in YAML, combining the durability of LangGraph (checkpoint, interrupt, conditional routing) with the declarative structure of AWS Step Functions.
|
|
48
|
+
|
|
49
|
+
**Key features:**
|
|
50
|
+
- Declarative YAML-based workflow definition
|
|
51
|
+
- Stateful execution with checkpoint/resume
|
|
52
|
+
- Parallel execution with branch aggregation
|
|
53
|
+
- Batch task processing
|
|
54
|
+
- Multiple LLM provider support (Claude, OpenCode, and more)
|
|
55
|
+
|
|
56
|
+
## Installation
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pip install fdsx
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Or with [uv](https://docs.astral.sh/uv/):
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
uv tool install fdsx
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Quick Start
|
|
69
|
+
|
|
70
|
+
Create a simple YAML workflow file:
|
|
71
|
+
|
|
72
|
+
```yaml
|
|
73
|
+
name: SimpleFlow
|
|
74
|
+
start_at: greet
|
|
75
|
+
version: "1.0"
|
|
76
|
+
|
|
77
|
+
states:
|
|
78
|
+
greet:
|
|
79
|
+
type: task
|
|
80
|
+
provider: system
|
|
81
|
+
command: "echo 'Hello from fdsx!'"
|
|
82
|
+
result_path: $.message
|
|
83
|
+
end: true
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Run it:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
fdsx run simple_flow.yaml
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Feature Overview
|
|
93
|
+
|
|
94
|
+
### State Types
|
|
95
|
+
- **task** — Execute LLM or CLI commands
|
|
96
|
+
- **parallel** — Run multiple branches concurrently
|
|
97
|
+
- **choice** — Conditional routing based on variables
|
|
98
|
+
- **wait** — Pause for human approval or webhook callback
|
|
99
|
+
- **pass** — Pass-through state for data transformation
|
|
100
|
+
|
|
101
|
+
### Parallel Execution
|
|
102
|
+
Define parallel branches that execute simultaneously with aggregation strategies (majority vote, all, any).
|
|
103
|
+
|
|
104
|
+
### Checkpoint & Resume
|
|
105
|
+
Flows automatically persist state. Resume from interruption with:
|
|
106
|
+
```bash
|
|
107
|
+
fdsx resume --thread-id <thread_id>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Batch Tasks
|
|
111
|
+
Process multiple tasks in batch mode:
|
|
112
|
+
```bash
|
|
113
|
+
fdsx run workflow.yaml --tasks tasks.md
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Structured Logging
|
|
117
|
+
All execution details are logged to `runs/<thread_id>.json`.
|
|
118
|
+
|
|
119
|
+
### Provider Support
|
|
120
|
+
Use any CLI-based LLM provider: Claude, Codex, OpenCode, or system commands.
|
|
121
|
+
|
|
122
|
+
## CLI Reference
|
|
123
|
+
|
|
124
|
+
| Command | Description |
|
|
125
|
+
|---------|-------------|
|
|
126
|
+
| `fdsx run <workflow.yaml>` | Execute a workflow |
|
|
127
|
+
| `fdsx run <workflow.yaml> --input key=value` | Pass input variables |
|
|
128
|
+
| `fdsx resume --thread-id <thread_id>` | Resume from checkpoint |
|
|
129
|
+
| `fdsx validate <workflow.yaml>` | Validate YAML syntax |
|
|
130
|
+
| `fdsx list` | List recent runs |
|
|
131
|
+
|
|
132
|
+
## Example Workflow
|
|
133
|
+
|
|
134
|
+
```yaml
|
|
135
|
+
name: Plan-Implement-Review Loop
|
|
136
|
+
start_at: plan
|
|
137
|
+
version: "1.0"
|
|
138
|
+
max_loop: 3
|
|
139
|
+
|
|
140
|
+
states:
|
|
141
|
+
plan:
|
|
142
|
+
type: task
|
|
143
|
+
provider: claude
|
|
144
|
+
model: claude-sonnet-4-6
|
|
145
|
+
prompt_template: |
|
|
146
|
+
You are a planning agent. Break down the following task into clear,
|
|
147
|
+
actionable implementation steps.
|
|
148
|
+
|
|
149
|
+
Task: {task}
|
|
150
|
+
result_path: $.plan
|
|
151
|
+
next: implement
|
|
152
|
+
|
|
153
|
+
implement:
|
|
154
|
+
type: task
|
|
155
|
+
provider: opencode
|
|
156
|
+
model: opencode/minimax-m2.5-free
|
|
157
|
+
prompt_template: |
|
|
158
|
+
You are an implementation agent. Follow this plan exactly.
|
|
159
|
+
|
|
160
|
+
Plan: {plan}
|
|
161
|
+
result_path: $.implementation
|
|
162
|
+
next: review
|
|
163
|
+
|
|
164
|
+
review:
|
|
165
|
+
type: task
|
|
166
|
+
provider: codex
|
|
167
|
+
model: gpt-5.4
|
|
168
|
+
prompt_template: |
|
|
169
|
+
Review the implementation against the plan.
|
|
170
|
+
|
|
171
|
+
Plan: {plan}
|
|
172
|
+
Implementation: {implementation}
|
|
173
|
+
result_path: $.review
|
|
174
|
+
next: check_review
|
|
175
|
+
|
|
176
|
+
check_review:
|
|
177
|
+
type: choice
|
|
178
|
+
choices:
|
|
179
|
+
- variable: $.review
|
|
180
|
+
operator: contains
|
|
181
|
+
value: "APPROVED"
|
|
182
|
+
next: done
|
|
183
|
+
default: implement
|
|
184
|
+
|
|
185
|
+
done:
|
|
186
|
+
type: pass
|
|
187
|
+
end: true
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Run this example:
|
|
191
|
+
```bash
|
|
192
|
+
fdsx run examples/workflows/plan-implement-review.yaml --input task="Build a web calculator"
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## License
|
|
196
|
+
|
|
197
|
+
MIT License.
|
fdsx-0.1.0/README.md
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# fdsx — Flow-Driven Stateful eXecution
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/fdsx/)
|
|
4
|
+
|
|
5
|
+
A lightweight framework for building and executing complex AI agent workflows using declarative YAML definitions.
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
fdsx enables you to define AI agent workflows in YAML, combining the durability of LangGraph (checkpoint, interrupt, conditional routing) with the declarative structure of AWS Step Functions.
|
|
10
|
+
|
|
11
|
+
**Key features:**
|
|
12
|
+
- Declarative YAML-based workflow definition
|
|
13
|
+
- Stateful execution with checkpoint/resume
|
|
14
|
+
- Parallel execution with branch aggregation
|
|
15
|
+
- Batch task processing
|
|
16
|
+
- Multiple LLM provider support (Claude, OpenCode, and more)
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install fdsx
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Or with [uv](https://docs.astral.sh/uv/):
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
uv tool install fdsx
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Quick Start
|
|
31
|
+
|
|
32
|
+
Create a simple YAML workflow file:
|
|
33
|
+
|
|
34
|
+
```yaml
|
|
35
|
+
name: SimpleFlow
|
|
36
|
+
start_at: greet
|
|
37
|
+
version: "1.0"
|
|
38
|
+
|
|
39
|
+
states:
|
|
40
|
+
greet:
|
|
41
|
+
type: task
|
|
42
|
+
provider: system
|
|
43
|
+
command: "echo 'Hello from fdsx!'"
|
|
44
|
+
result_path: $.message
|
|
45
|
+
end: true
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Run it:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
fdsx run simple_flow.yaml
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Feature Overview
|
|
55
|
+
|
|
56
|
+
### State Types
|
|
57
|
+
- **task** — Execute LLM or CLI commands
|
|
58
|
+
- **parallel** — Run multiple branches concurrently
|
|
59
|
+
- **choice** — Conditional routing based on variables
|
|
60
|
+
- **wait** — Pause for human approval or webhook callback
|
|
61
|
+
- **pass** — Pass-through state for data transformation
|
|
62
|
+
|
|
63
|
+
### Parallel Execution
|
|
64
|
+
Define parallel branches that execute simultaneously with aggregation strategies (majority vote, all, any).
|
|
65
|
+
|
|
66
|
+
### Checkpoint & Resume
|
|
67
|
+
Flows automatically persist state. Resume from interruption with:
|
|
68
|
+
```bash
|
|
69
|
+
fdsx resume --thread-id <thread_id>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Batch Tasks
|
|
73
|
+
Process multiple tasks in batch mode:
|
|
74
|
+
```bash
|
|
75
|
+
fdsx run workflow.yaml --tasks tasks.md
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Structured Logging
|
|
79
|
+
All execution details are logged to `runs/<thread_id>.json`.
|
|
80
|
+
|
|
81
|
+
### Provider Support
|
|
82
|
+
Use any CLI-based LLM provider: Claude, Codex, OpenCode, or system commands.
|
|
83
|
+
|
|
84
|
+
## CLI Reference
|
|
85
|
+
|
|
86
|
+
| Command | Description |
|
|
87
|
+
|---------|-------------|
|
|
88
|
+
| `fdsx run <workflow.yaml>` | Execute a workflow |
|
|
89
|
+
| `fdsx run <workflow.yaml> --input key=value` | Pass input variables |
|
|
90
|
+
| `fdsx resume --thread-id <thread_id>` | Resume from checkpoint |
|
|
91
|
+
| `fdsx validate <workflow.yaml>` | Validate YAML syntax |
|
|
92
|
+
| `fdsx list` | List recent runs |
|
|
93
|
+
|
|
94
|
+
## Example Workflow
|
|
95
|
+
|
|
96
|
+
```yaml
|
|
97
|
+
name: Plan-Implement-Review Loop
|
|
98
|
+
start_at: plan
|
|
99
|
+
version: "1.0"
|
|
100
|
+
max_loop: 3
|
|
101
|
+
|
|
102
|
+
states:
|
|
103
|
+
plan:
|
|
104
|
+
type: task
|
|
105
|
+
provider: claude
|
|
106
|
+
model: claude-sonnet-4-6
|
|
107
|
+
prompt_template: |
|
|
108
|
+
You are a planning agent. Break down the following task into clear,
|
|
109
|
+
actionable implementation steps.
|
|
110
|
+
|
|
111
|
+
Task: {task}
|
|
112
|
+
result_path: $.plan
|
|
113
|
+
next: implement
|
|
114
|
+
|
|
115
|
+
implement:
|
|
116
|
+
type: task
|
|
117
|
+
provider: opencode
|
|
118
|
+
model: opencode/minimax-m2.5-free
|
|
119
|
+
prompt_template: |
|
|
120
|
+
You are an implementation agent. Follow this plan exactly.
|
|
121
|
+
|
|
122
|
+
Plan: {plan}
|
|
123
|
+
result_path: $.implementation
|
|
124
|
+
next: review
|
|
125
|
+
|
|
126
|
+
review:
|
|
127
|
+
type: task
|
|
128
|
+
provider: codex
|
|
129
|
+
model: gpt-5.4
|
|
130
|
+
prompt_template: |
|
|
131
|
+
Review the implementation against the plan.
|
|
132
|
+
|
|
133
|
+
Plan: {plan}
|
|
134
|
+
Implementation: {implementation}
|
|
135
|
+
result_path: $.review
|
|
136
|
+
next: check_review
|
|
137
|
+
|
|
138
|
+
check_review:
|
|
139
|
+
type: choice
|
|
140
|
+
choices:
|
|
141
|
+
- variable: $.review
|
|
142
|
+
operator: contains
|
|
143
|
+
value: "APPROVED"
|
|
144
|
+
next: done
|
|
145
|
+
default: implement
|
|
146
|
+
|
|
147
|
+
done:
|
|
148
|
+
type: pass
|
|
149
|
+
end: true
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Run this example:
|
|
153
|
+
```bash
|
|
154
|
+
fdsx run examples/workflows/plan-implement-review.yaml --input task="Build a web calculator"
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## License
|
|
158
|
+
|
|
159
|
+
MIT License.
|