feat: check in workflow contract manifest
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
# Task 4 Report
|
||||
|
||||
## Scope
|
||||
|
||||
Implemented the workflow contract manifest module CLI and checked in the manifest artifact required by Task 4.
|
||||
|
||||
Created:
|
||||
|
||||
- `src/wf_contract_manifest/__main__.py`
|
||||
- `tests/wf_contract_manifest/test_cli.py`
|
||||
- `contracts/workflow-api.manifest.json`, generated by `python -m wf_contract_manifest write`
|
||||
|
||||
The CLI supports only the module commands `write` and `check`; no project script was added.
|
||||
|
||||
## TDD Evidence
|
||||
|
||||
The CLI tests were written before the production module. The required RED run failed during collection with:
|
||||
|
||||
`ModuleNotFoundError: No module named 'wf_contract_manifest.__main__'`
|
||||
|
||||
After implementing the module CLI, the CLI test file passed with `3 passed`.
|
||||
|
||||
## Verification
|
||||
|
||||
- CLI tests: `3 passed`
|
||||
- All manifest tests: `51 passed`
|
||||
- Ruff: `All checks passed!`
|
||||
- basedpyright: `0 errors, 0 warnings, 0 notes`
|
||||
- `git diff --check`: passed
|
||||
- Module generation: wrote `contracts/workflow-api.manifest.json`
|
||||
- Module drift check: checked the generated manifest successfully
|
||||
- Independent bounded JSON assertions:
|
||||
- operations: `70`
|
||||
- component schemas: `126`
|
||||
- component errors: `1`
|
||||
- first method: `workflow.admin.auth.delete`
|
||||
- last method: `workflow.sources.list`
|
||||
|
||||
## Review And Concerns
|
||||
|
||||
The CLI follows the exact Task 4 interface and catches the manifest, drift, and value errors specified by the brief. The generated JSON was not hand-edited. No Task 4 concerns remain.
|
||||
|
||||
The worktree contained a pre-existing staged deletion of `.superpowers/sdd/task-2-report.md`; it was preserved and excluded from the Task 4 staging set.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,40 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
from collections.abc import Sequence
|
||||
|
||||
from .generate import generate_manifest
|
||||
from .io import (
|
||||
DEFAULT_MANIFEST_PATH,
|
||||
ManifestDriftError,
|
||||
check_manifest,
|
||||
write_manifest,
|
||||
)
|
||||
from .model import ManifestError
|
||||
|
||||
|
||||
def _parser() -> argparse.ArgumentParser:
|
||||
parser = argparse.ArgumentParser(description="Manage the checked workflow API contract manifest.")
|
||||
parser.add_argument("command", choices=("write", "check"))
|
||||
return parser
|
||||
|
||||
|
||||
def main(argv: Sequence[str] | None = None) -> int:
|
||||
args = _parser().parse_args(argv)
|
||||
try:
|
||||
manifest = generate_manifest()
|
||||
if args.command == "write":
|
||||
path = write_manifest(manifest, DEFAULT_MANIFEST_PATH)
|
||||
print(f"wrote {path}")
|
||||
else:
|
||||
check_manifest(manifest, DEFAULT_MANIFEST_PATH)
|
||||
print(f"checked {DEFAULT_MANIFEST_PATH}")
|
||||
except (ManifestError, ManifestDriftError, ValueError) as error:
|
||||
print(str(error), file=sys.stderr)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,51 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from wf_contract_manifest import (
|
||||
ContractManifest,
|
||||
ManifestDriftError,
|
||||
manifest_from_openrpc,
|
||||
)
|
||||
from wf_contract_manifest.__main__ import main
|
||||
|
||||
from .fixtures import synthetic_openrpc_document
|
||||
|
||||
|
||||
def _manifest() -> ContractManifest:
|
||||
return manifest_from_openrpc(synthetic_openrpc_document())
|
||||
|
||||
|
||||
def test_write_generates_once_and_writes_requested_contract(monkeypatch, tmp_path: Path) -> None:
|
||||
manifest = _manifest()
|
||||
calls: list[tuple[object, Path]] = []
|
||||
monkeypatch.setattr("wf_contract_manifest.__main__.generate_manifest", lambda: manifest)
|
||||
monkeypatch.setattr(
|
||||
"wf_contract_manifest.__main__.write_manifest",
|
||||
lambda value, path: calls.append((value, path)) or path,
|
||||
)
|
||||
monkeypatch.setattr("wf_contract_manifest.__main__.DEFAULT_MANIFEST_PATH", tmp_path / "manifest.json")
|
||||
|
||||
assert main(["write"]) == 0
|
||||
assert calls == [(manifest, tmp_path / "manifest.json")]
|
||||
|
||||
|
||||
def test_check_returns_nonzero_and_prints_drift_guidance(monkeypatch, capsys) -> None:
|
||||
monkeypatch.setattr("wf_contract_manifest.__main__.generate_manifest", _manifest)
|
||||
|
||||
def fail_check(_manifest, _path) -> None:
|
||||
raise ManifestDriftError("stale; run `python -m wf_contract_manifest write`")
|
||||
|
||||
monkeypatch.setattr("wf_contract_manifest.__main__.check_manifest", fail_check)
|
||||
|
||||
assert main(["check"]) == 1
|
||||
assert "python -m wf_contract_manifest write" in capsys.readouterr().err
|
||||
|
||||
|
||||
def test_rejects_unknown_command() -> None:
|
||||
with pytest.raises(SystemExit) as exc_info:
|
||||
main(["unknown"])
|
||||
|
||||
assert exc_info.value.code == 2
|
||||
Reference in New Issue
Block a user