De-slop: remove every em-dash + banned words across all modules + capstone (#94)
Sync course wiki / sync-wiki (push) Successful in 4s

Co-authored-by: claude <claude@jpaul.io>
Co-committed-by: claude <claude@jpaul.io>
This commit was merged in pull request #94.
This commit is contained in:
2026-06-22 23:21:22 -04:00
committed by Claude (agent)
parent 513d7e7ac8
commit c098933f25
99 changed files with 1324 additions and 1315 deletions
@@ -1,19 +1,19 @@
"""Module 25 lab an autonomous-but-supervised agent orchestrator.
"""Module 25 lab: an autonomous-but-supervised agent orchestrator.
This is the smallest honest version of the two patterns in the module:
* issue-to-pr read an issue, let an agent implement it, run the gate, produce a PR PROPOSAL.
* self-heal run the gate; on failure, feed the failure back to the agent for a fix,
* issue-to-pr : read an issue, let an agent implement it, run the gate, produce a PR PROPOSAL.
* self-heal : run the gate; on failure, feed the failure back to the agent for a fix,
bounded by a retry cap; produce a PR PROPOSAL.
The load-bearing idea is in one place and you should be able to point at it: the agent NEVER merges.
Every path ends at `propose_pr()` a branch, a commit, and the command *you* would run to open the
Every path ends at `propose_pr()`: a branch, a commit, and the command *you* would run to open the
PR. The CI/review/security gates (Modules 14/15/10) and recovery (Module 12) are what supervise it,
not a human watching it type.
Run it two ways:
1. Simulated (no agent needed, fully deterministic) see the machinery and the gates:
1. Simulated (no agent needed, fully deterministic); see the machinery and the gates:
python agent_runner.py issue-to-pr issue-delete-command.md --simulate good
python agent_runner.py issue-to-pr issue-delete-command.md --simulate bad
python agent_runner.py self-heal --simulate bad
@@ -21,9 +21,9 @@ Run it two ways:
Simulation works on a SELF-CONTAINED demo target (agent_demo.py + test_agent_demo.py) so it is
deterministic and never corrupts your real tasks-app files. The gate it runs (ruff + pytest) is
the real one the same checks Module 14's CI runs.
the real one, the same checks Module 14's CI runs.
2. Real agent drives your own agentic tool against the actual issue. Point AGENT_CMD at your
2. Real agent: drives your own agentic tool against the actual issue. Point AGENT_CMD at your
tool's non-interactive / one-shot mode, then drop --simulate:
export AGENT_CMD='your-agent-cli --print --prompt-file {prompt_file}'
python agent_runner.py issue-to-pr issue-delete-command.md
@@ -52,7 +52,7 @@ CONFIG_CANDIDATES = ["AGENTS.md", ".agent/instructions.md", "agent-config.md"]
# --------------------------------------------------------------------------------------------------
# The gate the same lint + test checks Module 14 runs in CI, run locally so they're reproducible.
# The gate: the same lint + test checks Module 14 runs in CI, run locally so they're reproducible.
# This is the structural supervision. It does not care whether a human or an agent wrote the change.
# --------------------------------------------------------------------------------------------------
def run_gate() -> tuple[bool, str]:
@@ -65,7 +65,7 @@ def run_gate() -> tuple[bool, str]:
try:
proc = subprocess.run(cmd, capture_output=True, text=True)
except FileNotFoundError:
out.append(f" ! {cmd[0]} not installed `pip install pytest ruff`. Treating as a gate FAIL.")
out.append(f" ! {cmd[0]} not installed; run `pip install pytest ruff`. Treating as a gate FAIL.")
ok = False
continue
out.append(proc.stdout.rstrip())
@@ -78,7 +78,7 @@ def run_gate() -> tuple[bool, str]:
# --------------------------------------------------------------------------------------------------
# The agent real (your tool) or simulated (deterministic, for the lab).
# The agent: real (your tool) or simulated (deterministic, for the lab).
# --------------------------------------------------------------------------------------------------
def find_config() -> Path | None:
env = os.environ.get("AGENT_CONFIG")
@@ -93,14 +93,14 @@ def find_config() -> Path | None:
def build_prompt(task: str, *, issue_path: Path | None = None, failure: str | None = None) -> str:
"""Assemble the agent's brief: standing config (Module 5) + the specific task (issue or failure)."""
parts = ["You are working in a Git repository on the current branch. Make the change directly in",
"the files. Do not commit, push, or merge just edit. Follow the project's conventions."]
"the files. Do not commit, push, or merge; just edit. Follow the project's conventions."]
config = find_config()
if config:
parts += ["", f"# Project conventions (from {config})", config.read_text()]
if issue_path:
parts += ["", "# Task (issue to implement)", issue_path.read_text()]
if failure:
parts += ["", "# A CI check just failed. Fix the CODE so it passes do not weaken or delete",
parts += ["", "# A CI check just failed. Fix the CODE so it passes; do not weaken or delete",
"# the test to make it pass. Here is the failing output:", "```", failure, "```"]
return "\n".join(parts)
@@ -134,21 +134,21 @@ def simulate_implement(variant: str) -> None:
)
if variant == "good":
DEMO_SRC.write_text("def discount(price, pct):\n return price - price * pct / 100\n")
else: # 'bad' plausible but wrong: treats the percent as a flat amount.
else: # 'bad': plausible but wrong, treats the percent as a flat amount.
DEMO_SRC.write_text("def discount(price, pct):\n return price - pct\n")
def simulate_fix(variant: str, attempt: int) -> None:
if variant == "stuck":
# The "agent" keeps producing plausible, still-wrong fixes the loop must give up, not run forever.
# The "agent" keeps producing plausible, still-wrong fixes, so the loop must give up, not run forever.
DEMO_SRC.write_text(f"def discount(price, pct):\n return price - pct - {attempt}\n")
else: # 'bad' converges on the second attempt with the correct formula.
else: # 'bad': converges on the second attempt with the correct formula.
DEMO_SRC.write_text("def discount(price, pct):\n return price - price * pct / 100\n")
def simulate_cleanup() -> None:
"""Discard the simulator's demo artifacts. These are UNTRACKED new files, so `git restore`
(which only touches tracked files) can't remove them the simulator cleans up after itself."""
(which only touches tracked files) can't remove them, so the simulator cleans up after itself."""
for path in (DEMO_SRC, DEMO_TEST):
path.unlink(missing_ok=True)
@@ -163,7 +163,7 @@ def in_git_repo() -> bool:
def ensure_branch(name: str) -> None:
"""Create and switch to the agent's working branch. The orchestrator owns this git step the same
way agent-job.yml's runner does (`git switch -c`) you direct the automation and then verify the
way agent-job.yml's runner does (`git switch -c`): you direct the automation and then verify the
branch (`git branch`), instead of typing `git checkout` by hand. No-op outside a Git repo."""
if not in_git_repo():
return
@@ -175,7 +175,7 @@ def ensure_branch(name: str) -> None:
def propose_pr(message: str) -> None:
print("\n" + "=" * 80)
print("GATE PASSED. Proposing a PR NOT merging. A human reviews the diff (Module 10).")
print("GATE PASSED. Proposing a PR, NOT merging. A human reviews the diff (Module 10).")
print("=" * 80)
if in_git_repo():
subprocess.run(["git", "add", "-A"])
@@ -188,7 +188,7 @@ def propose_pr(message: str) -> None:
print(f" git push -u origin {branch}")
print(" # ...and open a pull request on your forge. CI + security gates run there.")
else:
print("\n(Not a Git repo skipping commit. In your tasks-app this would commit to the branch.)")
print("\n(Not a Git repo, so skipping commit. In your tasks-app this would commit to the branch.)")
print("\nThe agent stops here. It cannot merge. That is the whole safety model.")
@@ -249,14 +249,14 @@ def cmd_self_heal(simulate: str | None) -> int:
print(gate_output)
if attempt > RETRY_CAP - 1:
break
print(f"\n[self-heal] gate red attempt {attempt}/{RETRY_CAP - 1}: asking the agent for a fix.")
print(f"\n[self-heal] gate red, attempt {attempt}/{RETRY_CAP - 1}: asking the agent for a fix.")
if simulate:
simulate_fix(simulate, attempt)
else:
run_real_agent(build_prompt("fix", failure=gate_output))
print("\n" + "=" * 80)
print(f"SELF-HEAL GAVE UP after {RETRY_CAP - 1} attempts. Handing off to a human NOT looping forever.")
print(f"SELF-HEAL GAVE UP after {RETRY_CAP - 1} attempts. Handing off to a human, NOT looping forever.")
print("This cap is what stops an agent burning a runner bill chasing a flaky or impossible fix.")
print("=" * 80)
return 2