Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,8 +189,11 @@ gate PASSED — receipt chain validates (9 receipts).

Mock mode swaps only the *model content* for a bundled sample task — pytest,
ultracode, the panel, and the gate all really execute, which is what makes
the no-key demo honest. Commands: `bob run <task>` · `bob gate`
(pre-commit chokepoint; `--complete-on-pass` seals the run) · `bob status`.
the no-key demo honest. bob's one runtime dependency is pytest
(`pip install pytest`, or `agent-ultra-kit[bob]`); the rest of the kit needs
nothing. Commands: `bob run <task>` · `bob gate --mark-pass` (pre-commit:
validates, keeps the run active) · `bob seal` (post-commit: releases it) ·
`bob hook-install` (writes all three hooks) · `bob status`.
Full guide: [docs/bob-the-builder.md](docs/bob-the-builder.md).

## What's in the box
Expand Down
1 change: 1 addition & 0 deletions docs/bob-the-builder.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ cross-check (ultracode receipts, the panel execution receipt).
## Quick start (no API key)

```bash
pip install pytest # bob's only runtime dependency (RED/GREEN run it)
agent-ultra bob run "add a slugify helper" --mock
```

Expand Down
4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ dependencies = []

[project.optional-dependencies]
dev = ["pytest>=7"]
# bob's RED/GREEN steps execute pytest at runtime; the core kit stays
# dependency-free, so install this extra (or pytest itself) to use bob:
# pip install agent-ultra-kit[bob]
bob = ["pytest>=7"]
# Optional heavy worker: LangChain Deep Agents as a multi-file builder/fixer.
# The core install stays zero-dependency; install this extra only if you want
# the Deep Agents worker: pip install agent-ultra-kit[deepagents]
Expand Down
45 changes: 34 additions & 11 deletions src/agent_ultra/adapters/doctor.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,17 +256,28 @@ def run_check(cmd, cwd):
def bob_demo(out_dir: Path):
"""Offline: the full 10-step pipeline passes its gate, then three faked
chains block — a skipped step, a fabricated fan-out claim, and a doctored
panel receipt. All state is isolated under *out_dir*."""
panel receipt. All state is isolated under *out_dir*.

Returns (ok, info); ok is None when pytest is absent (the kit core is
dependency-free, so a fresh install may not have it) — callers report
that as a WARN with the install hint, not a failure."""
import io
from ..bob import run_bob, gate_check
from ..bob.pipeline import BobRun
from ..bob.pipeline import BobRun, pytest_available, PYTEST_MISSING_MSG

if not pytest_available():
return None, {"skipped": PYTEST_MISSING_MSG}

ws = out_dir / "bob-demo"
key = out_dir / "bob-demo.key"
uc_home = out_dir / "bob-demo-ultracode"
sink = io.StringIO()
outcome = run_bob("add a slugify helper", ws, mock=True, key_path=key,
ultracode_home=uc_home, interactive=False, out=sink)
if not outcome.passed:
return False, {"pipeline_passed": False,
"blocked_at": outcome.blocked_at,
"run_id": outcome.run_id}

run = BobRun(workspace=ws, run_dir=ws / ".agent-ultra" / "bob" / "runs"
/ outcome.run_id, key=key.read_bytes())
Expand Down Expand Up @@ -336,11 +347,17 @@ def run_demo(args) -> int:
f"-> {uc['final_state']} -> receipt valid={uc['receipt_valid']} "
f"-> resume replays {uc['resume_cached']} cached ({uc['resume_calls']} calls)")
ok7, bb = bob_demo(out)
print(f"[{'ok' if ok7 else 'FAIL'}] bob: 10-step pipeline gate "
f"passed={bb['pipeline_passed']}; skipped step blocks="
f"{bb['skipped_step_blocks']}, fake fan-out blocks="
f"{bb['fake_fanout_blocks']}, doctored panel blocks="
f"{bb['fake_panel_blocks']}")
if ok7 is None:
print(f"[--] bob: skipped — {bb['skipped']}")
ok7 = True # absence of the optional test runner is not a failure
elif ok7:
print(f"[ok] bob: 10-step pipeline gate "
f"passed={bb['pipeline_passed']}; skipped step blocks="
f"{bb['skipped_step_blocks']}, fake fan-out blocks="
f"{bb['fake_fanout_blocks']}, doctored panel blocks="
f"{bb['fake_panel_blocks']}")
else:
print(f"[FAIL] bob: {json.dumps(bb)[:160]}")
print(f"\nartifacts: {out}")
all_ok = ok1 and ok2 and ok3 and ok4 and ok5 and ok6 and ok7
print("\nDEMO " + ("PASSED — the full loop works on this machine."
Expand Down Expand Up @@ -445,12 +462,18 @@ def add(status, name, detail=""):
except Exception as e:
add(FAIL, "ultracode", str(e)[:160])

# 7a3. bob: the 10-step pipeline passes its gate; faked chains block
# 7a3. bob: the 10-step pipeline passes its gate; faked chains block.
# pytest is bob's only runtime dependency and is optional for the rest
# of the kit — absent pytest is a WARN with the install hint, not a FAIL.
try:
ok, detail = bob_demo(scratch)
add(PASS if ok else FAIL,
"bob: 10-step pipeline gate passes; skipped/faked steps block",
json.dumps(detail)[:140])
if ok is None:
add(WARN, "bob: skipped — pytest not installed",
"RED/GREEN need it: pip install pytest")
else:
add(PASS if ok else FAIL,
"bob: 10-step pipeline gate passes; skipped/faked steps block",
json.dumps(detail)[:140])
except Exception as e:
add(FAIL, "bob pipeline", str(e)[:160])

Expand Down
21 changes: 20 additions & 1 deletion src/agent_ultra/bob/pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,19 @@ def complete(self, gate_result: dict) -> None:
# system pytest runner (steps 2 and 3)
# --------------------------------------------------------------------------

def pytest_available() -> bool:
"""The RED/GREEN steps execute pytest; the kit core is dependency-free,
so pytest may be absent on a fresh install. Callers check this to give a
clear 'pip install pytest' message instead of a cryptic empty RED."""
import importlib.util
return importlib.util.find_spec("pytest") is not None


PYTEST_MISSING_MSG = ("pytest is not installed in this environment — the "
"RED/GREEN steps need it: pip install pytest "
"(or: pip install 'agent-ultra-kit[dev]')")


# -v node lines: "tests/test_x.py::test_a PASSED [ 50%]" and parametrized ids
# that contain spaces: "test_x.py::test_a[hello world] PASSED [ 50%]". The id
# is everything up to the status word, which pytest right-pads before the
Expand Down Expand Up @@ -321,6 +334,10 @@ def run_pytest_step(run: BobRun, step: str, test_file: str,
if step not in ("red", "green"):
raise ValueError("step must be 'red' or 'green'")
step_name = "step02_red" if step == "red" else "step03_green"
if not pytest_available():
# write nothing: an honest "cannot run" beats a receipt recording a
# spawn failure as if the tests were exercised
return False, {}, PYTEST_MISSING_MSG

from ..broker.broker import classify
shown_cmd = f"{Path(sys.executable).name} -m pytest {test_file} -v"
Expand Down Expand Up @@ -609,7 +626,9 @@ def _counts(ev) -> dict:
checked["step03_green"] = f"{len(green_ids)} tests passing"

# ---- live re-run (re-derived, never trusted) ---------------------------
if rerun_tests and green is not None and test_file:
if rerun_tests and green is not None and test_file and not pytest_available():
errors.append(f"live re-run: {PYTEST_MISSING_MSG}")
elif rerun_tests and green is not None and test_file:
exit_code, output = _run_pytest(run.workspace, test_file, timeout=600)
live_ids = set(parse_pytest_nodes(output))
if exit_code != 0:
Expand Down
Loading