Port the sssf skill from ~/.agents/skills/sssf into this repo so it can be distributed and installed with the skills CLI (skills add INDigitalStudio/skills --skill sssf). - Copy the skill (SKILL.md, cookbooks, references, scripts, templates, and the visualizer app source) into sssf/. - Gitignore build/runtime artifacts: the visualizer's node_modules/ and dist/, Python bytecode, and the machine-specific repos.json. - Make the skill location-independent: install.py now stamps the skill's real path into the stamped justfile's skill_dir (replacing the hardcoded ~/.agents/skills/sssf), so 'just obs' finds the visualizer wherever the CLI installed the skill. - Update cookbooks to use <skill>/scripts/... instead of the hardcoded path, and document the skills CLI install command. - Update the repo README with install instructions.
79 lines
3.6 KiB
Python
79 lines
3.6 KiB
Python
#!/usr/bin/env -S uv run
|
|
# /// script
|
|
# dependencies = ["pydantic", "python-dotenv", "pyyaml", "rich"]
|
|
# ///
|
|
"""ADW Build Test — implement, then verify; failures flow back into the builder.
|
|
|
|
Usage:
|
|
uv run adws/adw_build_test.py "<prompt or path/to/prompt.md>" [--config adws/adw_sssf_config/sssf.config.yaml] [--adw-id a1b2c3d4]
|
|
|
|
Phases: engineer(request) -> builder -> code(test) [-> builder(fix) -> code(test) ... bounded]
|
|
|
|
Testing is CODE. The suite's command is written down in adw_modules/quality.py,
|
|
so running it needs no judgement — only repairing it does. Failures reach the
|
|
builder as an envelope through `quality.as_envelope`, which is the same door an
|
|
agent's report came through, so the repair loop is unchanged.
|
|
|
|
A failing suite does NOT fail its phase: the runner did its job, the code is
|
|
what failed. It fails the run, checked at the end, after the bounded fix loop
|
|
has had its chances.
|
|
"""
|
|
|
|
import argparse
|
|
import sys
|
|
|
|
from adw_modules import agents, gates, quality, session, utils
|
|
from adw_modules.data_types import AgentCall, BuildOutput, PhaseParams
|
|
|
|
REQUIRED_AGENTS = ["builder"]
|
|
MAX_FIX_LOOPS = 3
|
|
|
|
|
|
def main(prompt: str, config: str = "adws/adw_sssf_config/sssf.config.yaml", adw_id: str | None = None) -> int:
|
|
cfg = agents.load_config(config)
|
|
agents.validate(cfg, REQUIRED_AGENTS)
|
|
run = session.ensure(cfg, adw_id)
|
|
|
|
def record(ph, result) -> None:
|
|
passed = sum(1 for check in result.checks if check.passed)
|
|
ph.log(passed=result.passed, checks=f"{passed}/{len(result.checks)}",
|
|
artifacts=", ".join(result.artifacts))
|
|
|
|
with run.phase(PhaseParams(name="request", kind="engineer", owner=run.engineer,
|
|
description="Capture the incoming ask")) as ph:
|
|
ph.log(input=prompt)
|
|
|
|
with run.phase(PhaseParams(name="build", kind="agent", owner="builder",
|
|
description="Implement the request")) as ph:
|
|
previous = ph.call(AgentCall(output_type=BuildOutput, prompt=prompt,
|
|
gates=[gates.diff_matches_claims]))
|
|
|
|
test = None
|
|
for i in range(1, MAX_FIX_LOOPS + 1):
|
|
with run.phase(PhaseParams(name=f"test_{i}", kind="code", owner="quality",
|
|
description="Run the suite — a known command, so code runs "
|
|
"it and no agent has to rediscover it")) as ph:
|
|
test = quality.run_tests(run)
|
|
record(ph, test)
|
|
|
|
if test.passed:
|
|
break
|
|
|
|
with run.phase(PhaseParams(name=f"fix_{i}", kind="agent", owner="builder", retries=1,
|
|
description="Repair what the suite reported, from its "
|
|
"verbatim output")) as ph:
|
|
previous = ph.call(AgentCall(output_type=BuildOutput, prompt=prompt,
|
|
previous=quality.as_envelope(test, "tests"),
|
|
gates=[gates.diff_matches_claims]))
|
|
|
|
return run.finish(accepted=test is not None and test.passed,
|
|
reason=f"the suite still failed after {MAX_FIX_LOOPS} fix attempt(s)")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("prompt", help="inline text or a path to a prompt file")
|
|
parser.add_argument("--config", default="adws/adw_sssf_config/sssf.config.yaml")
|
|
parser.add_argument("--adw-id", default=None, help="join or pin an existing session")
|
|
args = parser.parse_args()
|
|
sys.exit(main(utils.resolve_prompt(args.prompt), args.config, args.adw_id))
|