Preview 0.30

P05 - Remediate a checkpoint finding

Know before you begin

P05 is a guided, runnable Academy lesson in this preview. Its shared action cards are the public course route.

P05 is the first Practitioner exercise that asks you to preserve a precise remediation history. It uses the Workshop Queue practice project that Academy prepares in your learner clone; it does not ask you to experiment in your own production repository. Complete P04 first. If you need to learn how to make a GitHub fork, clone it locally, install Academy, or distinguish your terminal from your harness, use the prerequisite action below and return after F01.

Keep two places open: a native terminal at the learner clone for Academy and Git commands, and one CodeArbiter harness at the same clone for agent and CodeArbiter actions. A native-terminal command is entered directly and never starts with !. A shell command sent through a harness starts with exactly one !; this lesson does not ask you to type one. CodeArbiter commands and agent messages belong in the harness and never begin with !.

Confirm the safe practice space

You · Browser · All operating systems

P05 is a Practitioner lab. Complete P04 in your own fork and Academy clone first. If you have not yet forked, cloned, installed Academy, and practiced the terminal boundary, return to F01 before continuing. P05 changes only the prepared Workshop Queue exercise, never the Academy source clone.

Why

A known fork and clone keep the remediation attempt separate from the course source and give Academy a repository it can reset safely.

Reviewed resources for Confirm the safe practice space

Expected result

You have a clean Academy learner clone, an installed Academy CLI from F01, and a CodeArbiter harness pointed at that clone.

If that does not happen

If you are unsure which repository is your fork or where Academy is installed, stop and repeat F01. Do not run P05 in an unfamiliar checkout.

Evidence

This is a prerequisite check by you. Academy Check does not authenticate how you created the fork or clone.

What you will prove

The prepared exercise can persist a terminal blocked ticket through its normal model and store, but its JSON report wrongly excludes that ticket from the unresolved count. You will preserve four linear commits after Academy Prepare:

  1. the checkpoint's generated dated report and baseline together with an exact finding record;
  2. a test-only RED regression that exposes the defect;
  3. a code-only GREEN repair that leaves the regression unchanged; and
  4. a canonical receipt that names the first three real commit IDs.

The repair is deliberately small: unresolved means every ticket that is not completed. P05 Check can reconstruct the fixture, commits, path roles, regression, repair, receipt, and clean worktree. It cannot prove who reviewed a result, whether a checkpoint command was run, or the chronology of commands that led to the final commits.

Prepare safely

Prepare the numbered remediation attempt

From the clean main branch at your Academy learner clone root, run Prepare in a native terminal. This command is entered directly in the terminal, so it never begins with !.

Why

Prepare creates the verifier-owned attempt branch and stages the real blocked-ticket summary defect before any learner work begins.

You · Native terminal · Windows

$academy = "$env:LOCALAPPDATA\ArbiterAcademy\preview-0.30\Scripts\arbiter-academy.exe"
& $academy --repository (Get-Location).Path prepare P05-checkpoint-remediation

You · Native terminal · macOS

academy="${XDG_DATA_HOME:-$HOME/.local/share}/arbiter-academy/preview-0.30/bin/arbiter-academy"
"$academy" --repository "$PWD" prepare P05-checkpoint-remediation

You · Native terminal · Linux

academy="${XDG_DATA_HOME:-$HOME/.local/share}/arbiter-academy/preview-0.30/bin/arbiter-academy"
"$academy" --repository "$PWD" prepare P05-checkpoint-remediation

Expected result

Academy prints academy/P05-checkpoint-remediation/ATTEMPT_NUMBER and prepares a blocked ticket whose summary count is intentionally wrong.

If that does not happen

Preserve the exact error. Resolve only the named clean-worktree or branch condition; do not force-reset main or delete an earlier attempt.

Evidence

The printed attempt branch and prepared commit are the baseline that Check later reconstructs.

ATTEMPT_NUMBER is a value Academy prints, such as 1; it is not text to type literally. Stay on that numbered branch. The prepared commit also adds ADR-0005 and its decision-log entry. Do not edit those decision records, and do not repair the deliberately stale CONTEXT.md reference: that is the starting condition for P06.

Verify the prepared branch before changing anything

In the same native terminal, inspect the prepared branch, commit, and worktree. Compare the branch with the value Prepare printed. These are terminal commands, not harness commands, so do not add !.

Why

The four required commits count only when they descend from the prepared attempt, not from a look-alike local branch.

You · Native terminal · Windows

git branch --show-current
git rev-parse HEAD
git status --short

You · Native terminal · macOS

git branch --show-current
git rev-parse HEAD
git status --short

You · Native terminal · Linux

git branch --show-current
git rev-parse HEAD
git status --short

Expected result

The branch begins academy/P05-checkpoint-remediation/, HEAD is the prepared commit, and status prints no changed paths.

If that does not happen

If any value differs, preserve the output and use Reset. Do not rename a branch, rewrite history, or manually copy prepared files.

Evidence

You confirmed the attempt identity before creating the finding, RED, GREEN, and receipt commits.

Ask the agent to read the prepared boundary

In your active CodeArbiter harness, send the copied message. This is a request to the agent, not a shell command, so it never begins with !. The agent reads; it must not edit or commit in this step.

Why

The learner and agent need the domain, persistence, reporting, decision, and scenario boundaries before deciding what the finding proves.

Your agent · Claude Code harness · All operating systems

Open .codearbiter/decisions/0005-terminal-blocked-ticket-lifecycle.md, .codearbiter/decisions/decision-log.md, training_scenarios/P05-checkpoint-remediation.json, tests/test_cli.py, workshop_queue/model.py, workshop_queue/service.py, and workshop_queue/cli.py. Show the prepared blocked-ticket behavior and the summary expression that omits it. Do not edit, stage, run a host command, or commit.

Your agent · Codex harness · All operating systems

Open .codearbiter/decisions/0005-terminal-blocked-ticket-lifecycle.md, .codearbiter/decisions/decision-log.md, training_scenarios/P05-checkpoint-remediation.json, tests/test_cli.py, workshop_queue/model.py, workshop_queue/service.py, and workshop_queue/cli.py. Show the prepared blocked-ticket behavior and the summary expression that omits it. Do not edit, stage, run a host command, or commit.

Your agent · Pi harness · All operating systems

Open .codearbiter/decisions/0005-terminal-blocked-ticket-lifecycle.md, .codearbiter/decisions/decision-log.md, training_scenarios/P05-checkpoint-remediation.json, tests/test_cli.py, workshop_queue/model.py, workshop_queue/service.py, and workshop_queue/cli.py. Show the prepared blocked-ticket behavior and the summary expression that omits it. Do not edit, stage, run a host command, or commit.

Expected result

The agent can identify that blocked tickets persist through the normal model and store, while the report's unresolved total counts only open and claimed tickets.

If that does not happen

If a required file is missing or the branch is not the prepared attempt, stop. Preserve the attempt and Reset instead of substituting a similar source file.

Evidence

This read establishes the repository boundary for your own inspection; it is not a verifier-authenticated review.

Practice

Ask CodeArbiter to surface the checkpoint finding

In the selected harness, send the host-native checkpoint command. This is a CodeArbiter command, not a shell command, so it never begins with !.

Why

The checkpoint lane frames the observed product defect before the repair lane begins.

Your agent · Claude Code harness · All operating systems

/ca:checkpoint

Your agent · Codex harness · All operating systems

$ca-checkpoint

Your agent · Pi harness · All operating systems

/ca-checkpoint

Your agent · Pi harness · All operating systems

/skill:ca-checkpoint

Expected result

The agent reports that the blocked Workshop Queue ticket is omitted from the unresolved summary and names the report/test boundary.

If that does not happen

If the checkpoint reports another problem or cannot run, preserve its output and correct the named prerequisite or use Reset. Do not invent a checkpoint transcript.

Evidence

The finding commit below, not a chat transcript, is the repository artifact Check can inspect.

Inspect the real defect before recording it

You · Active CodeArbiter harness · All operating systems

Read the checkpoint result next to the prepared summary boundary. Confirm the claim is narrow: RQ-105 is blocked, its reason is venue-access clearance, and the unresolved count excludes it. Do not ask the agent to repair the code yet.

Why

A precise finding prevents a generic report or unrelated product change from masquerading as remediation.

Expected result

You can state the observed defect in one sentence and identify tests/test_cli.py plus workshop_queue/cli.py as the later test and code boundaries.

If that does not happen

If the result is not the prepared blocked-ticket defect, stop and Reset. Do not broaden the exercise to make a different finding fit.

Evidence

This is your inspection. Check does not authenticate a checkpoint run or a person's review.

Ask the agent to record only the finding

After the selected checkpoint command writes its generated checkpoint report and .codearbiter/last-checkpoint, send this message in the selected harness. It asks the agent to make the first of four commits, carrying those real generated artifacts with the finding, and forbids the test or production repair in that commit.

Why

The verifier requires an exact, standalone finding commit before the RED regression and GREEN repair.

Your agent · Claude Code harness · All operating systems

Create .codearbiter/reports/academy/P05-finding.md with exactly these UTF-8 bytes, including the final newline:
# P05 Finding: blocked tickets omitted from unresolved summary

Ticket `RQ-105` is blocked: `Venue access is awaiting facilities clearance`.
Affected paths: `tests/test_cli.py`, `workshop_queue/cli.py`.

Then identify the one dated .codearbiter/checkpoints/YYYY-MM-DD.md generated by the just-run checkpoint and confirm .codearbiter/last-checkpoint exists. Stage exactly that generated report, .codearbiter/last-checkpoint, and .codearbiter/reports/academy/P05-finding.md; show the staged path list and diff, then invoke the CodeArbiter commit gate. Do not edit tests or production code, create the receipt, amend, or rebase.

Your agent · Codex harness · All operating systems

Create .codearbiter/reports/academy/P05-finding.md with exactly these UTF-8 bytes, including the final newline:
# P05 Finding: blocked tickets omitted from unresolved summary

Ticket `RQ-105` is blocked: `Venue access is awaiting facilities clearance`.
Affected paths: `tests/test_cli.py`, `workshop_queue/cli.py`.

Then identify the one dated .codearbiter/checkpoints/YYYY-MM-DD.md generated by the just-run checkpoint and confirm .codearbiter/last-checkpoint exists. Stage exactly that generated report, .codearbiter/last-checkpoint, and .codearbiter/reports/academy/P05-finding.md; show the staged path list and diff, then invoke the CodeArbiter commit gate. Do not edit tests or production code, create the receipt, amend, or rebase.

Your agent · Pi harness · All operating systems

Create .codearbiter/reports/academy/P05-finding.md with exactly these UTF-8 bytes, including the final newline:
# P05 Finding: blocked tickets omitted from unresolved summary

Ticket `RQ-105` is blocked: `Venue access is awaiting facilities clearance`.
Affected paths: `tests/test_cli.py`, `workshop_queue/cli.py`.

Then identify the one dated .codearbiter/checkpoints/YYYY-MM-DD.md generated by the just-run checkpoint and confirm .codearbiter/last-checkpoint exists. Stage exactly that generated report, .codearbiter/last-checkpoint, and .codearbiter/reports/academy/P05-finding.md; show the staged path list and diff, then invoke the CodeArbiter commit gate. Do not edit tests or production code, create the receipt, amend, or rebase.

Expected result

One new descendant commit changes the generated dated checkpoint report, .codearbiter/last-checkpoint, and .codearbiter/reports/academy/P05-finding.md; it changes no test or production path.

If that does not happen

If a generated checkpoint artifact is absent, another path is staged, or the finding content differs, stop before committing. Preserve the attempt and Reset rather than amending a malformed evidence history.

Evidence

The first descendant commit preserves the real checkpoint side effects and proves the finding was recorded before the regression and repair.

Verify the finding commit boundary

Use a native terminal to inspect only the latest commit path list. These are direct terminal commands and never begin with !.

Why

A clean three-artifact commit gives the later history an unambiguous checkpoint-and-finding boundary.

You · Native terminal · Windows

git log -1 --format=%H
git diff-tree --no-commit-id --name-only -r HEAD

You · Native terminal · macOS

git log -1 --format=%H
git diff-tree --no-commit-id --name-only -r HEAD

You · Native terminal · Linux

git log -1 --format=%H
git diff-tree --no-commit-id --name-only -r HEAD

Expected result

The path list contains the generated dated checkpoint report, .codearbiter/last-checkpoint, and .codearbiter/reports/academy/P05-finding.md only.

If that does not happen

If the commit omits either checkpoint artifact or contains another path, preserve the attempt and Reset. Do not rewrite the commit with amend or rebase.

Evidence

The commit ID is later written into the final receipt as finding_commit.

Ask the agent to add the test-only RED regression

Send this message in the selected harness. The agent must change only the test, run it, and stop at the meaningful failing assertion before any production edit.

Why

The lesson proves that the prepared report is wrong with a real persisted blocked ticket rather than a synthetic JSON assertion.

Your agent · Claude Code harness · All operating systems

Change only tests/test_cli.py. The verifier does not accept an equivalent test. Insert this exact method in WorkshopQueueCliTests:
    def test_report_json_counts_blocked_ticket_as_unresolved(self) -> None:
        tickets = json.loads(self.fixture.read_text(encoding="utf-8"))
        tickets[0]["id"] = "RQ-105"
        self.fixture.write_text(json.dumps(tickets), encoding="utf-8")
        claim_result = self.run_cli("claim", "RQ-105", "--volunteer", "Sam")
        block_result = self.run_cli("block", "RQ-105", "--reason", "Venue access is awaiting facilities clearance")
        report = self.run_cli("report", "--format", "json")
        self.assertEqual(claim_result.returncode, 0, claim_result.stderr)
        self.assertEqual(block_result.returncode, 0, block_result.stderr)
        self.assertEqual(report.returncode, 0, report.stderr)
        parsed = json.loads(report.stdout)
        self.assertEqual(parsed["blocked"], 1)
        self.assertEqual(parsed["unresolved"], 1)
Run only the exact focused test with python -m unittest tests.test_cli.WorkshopQueueCliTests.test_report_json_counts_blocked_ticket_as_unresolved -v and stop when it fails because unresolved is 0 instead of 1. Do not edit workshop_queue, stage, or commit yet.

Your agent · Codex harness · All operating systems

Change only tests/test_cli.py. The verifier does not accept an equivalent test. Insert this exact method in WorkshopQueueCliTests:
    def test_report_json_counts_blocked_ticket_as_unresolved(self) -> None:
        tickets = json.loads(self.fixture.read_text(encoding="utf-8"))
        tickets[0]["id"] = "RQ-105"
        self.fixture.write_text(json.dumps(tickets), encoding="utf-8")
        claim_result = self.run_cli("claim", "RQ-105", "--volunteer", "Sam")
        block_result = self.run_cli("block", "RQ-105", "--reason", "Venue access is awaiting facilities clearance")
        report = self.run_cli("report", "--format", "json")
        self.assertEqual(claim_result.returncode, 0, claim_result.stderr)
        self.assertEqual(block_result.returncode, 0, block_result.stderr)
        self.assertEqual(report.returncode, 0, report.stderr)
        parsed = json.loads(report.stdout)
        self.assertEqual(parsed["blocked"], 1)
        self.assertEqual(parsed["unresolved"], 1)
Run only the exact focused test with python -m unittest tests.test_cli.WorkshopQueueCliTests.test_report_json_counts_blocked_ticket_as_unresolved -v and stop when it fails because unresolved is 0 instead of 1. Do not edit workshop_queue, stage, or commit yet.

Your agent · Pi harness · All operating systems

Change only tests/test_cli.py. The verifier does not accept an equivalent test. Insert this exact method in WorkshopQueueCliTests:
    def test_report_json_counts_blocked_ticket_as_unresolved(self) -> None:
        tickets = json.loads(self.fixture.read_text(encoding="utf-8"))
        tickets[0]["id"] = "RQ-105"
        self.fixture.write_text(json.dumps(tickets), encoding="utf-8")
        claim_result = self.run_cli("claim", "RQ-105", "--volunteer", "Sam")
        block_result = self.run_cli("block", "RQ-105", "--reason", "Venue access is awaiting facilities clearance")
        report = self.run_cli("report", "--format", "json")
        self.assertEqual(claim_result.returncode, 0, claim_result.stderr)
        self.assertEqual(block_result.returncode, 0, block_result.stderr)
        self.assertEqual(report.returncode, 0, report.stderr)
        parsed = json.loads(report.stdout)
        self.assertEqual(parsed["blocked"], 1)
        self.assertEqual(parsed["unresolved"], 1)
Run only the exact focused test with python -m unittest tests.test_cli.WorkshopQueueCliTests.test_report_json_counts_blocked_ticket_as_unresolved -v and stop when it fails because unresolved is 0 instead of 1. Do not edit workshop_queue, stage, or commit yet.

Expected result

Only tests/test_cli.py changes, and the focused test fails because the report says unresolved is 0 when the persisted blocked ticket makes it 1.

If that does not happen

If the test fails for an import, file, fixture, or command error, fix neither production nor unrelated setup. Preserve the output and Reset for a new attempt.

Evidence

The later RED commit contains only the unchanged regression method whose taught AST final Check requires exactly.

Recognize the meaningful RED result

You · Active CodeArbiter harness · All operating systems

Read the focused test result. The important failure is the unresolved assertion after a successful claim, block, and report, not a syntax, import, fixture, or command failure.

Why

A failing test demonstrates the product defect before the repair changes the report expression.

Expected result

The focused test reaches the JSON report and shows that blocked is 1 while unresolved is still counted incorrectly.

If that does not happen

If the test did not reach the unresolved assertion, do not continue to GREEN. Preserve the attempt and Reset instead of repairing a different problem.

Evidence

You observe this runtime result during the lesson. Final Check validates the committed test and repair, not command chronology.

Ask the agent to commit only the RED test

Send this message in the selected harness after you have observed the meaningful RED result.

Why

The test-only commit preserves the regression before the production repair can make it pass.

Your agent · Claude Code harness · All operating systems

Stage only tests/test_cli.py. Show the staged path list and diff, confirm the file contains the unchanged failing blocked-ticket regression, then invoke the CodeArbiter commit gate. Do not edit workshop_queue, create the receipt, amend, or rebase.

Your agent · Codex harness · All operating systems

Stage only tests/test_cli.py. Show the staged path list and diff, confirm the file contains the unchanged failing blocked-ticket regression, then invoke the CodeArbiter commit gate. Do not edit workshop_queue, create the receipt, amend, or rebase.

Your agent · Pi harness · All operating systems

Stage only tests/test_cli.py. Show the staged path list and diff, confirm the file contains the unchanged failing blocked-ticket regression, then invoke the CodeArbiter commit gate. Do not edit workshop_queue, create the receipt, amend, or rebase.

Expected result

A second descendant commit changes exactly tests/test_cli.py and retains the meaningful failing regression.

If that does not happen

If another file is staged or the test was changed after the observed RED result, preserve the attempt and Reset. Do not combine RED and GREEN into one commit.

Evidence

The final verifier requires this commit immediately after the finding and before the code-only repair.

Ask the agent to make the code-only GREEN repair

Send this message in the selected harness. It permits the smallest report repair only after the committed RED test exists.

Why

The bounded repair changes the definition of unresolved from open plus claimed to every ticket that is not completed.

Your agent · Claude Code harness · All operating systems

Change only workshop_queue/cli.py so the unresolved total is sum(ticket.status is not TicketStatus.COMPLETED for ticket in tickets). Do not change tests. Run the exact focused blocked-ticket test and stop after it passes. Do not stage or commit yet.

Your agent · Codex harness · All operating systems

Change only workshop_queue/cli.py so the unresolved total is sum(ticket.status is not TicketStatus.COMPLETED for ticket in tickets). Do not change tests. Run the exact focused blocked-ticket test and stop after it passes. Do not stage or commit yet.

Your agent · Pi harness · All operating systems

Change only workshop_queue/cli.py so the unresolved total is sum(ticket.status is not TicketStatus.COMPLETED for ticket in tickets). Do not change tests. Run the exact focused blocked-ticket test and stop after it passes. Do not stage or commit yet.

Expected result

The focused regression now passes, tests/test_cli.py remains byte-for-byte unchanged from RED, and only workshop_queue/cli.py is modified.

If that does not happen

If the focused test remains red or another production path changes, stop. Preserve the result and Reset rather than broadening the repair.

Evidence

The later GREEN commit must be a one-path descendant whose inverse recreates the prepared defect.

Ask the agent to commit only the GREEN repair

Send this message in the selected harness after the focused test passes.

Why

A code-only GREEN commit lets Check prove that the repair followed and did not modify the regression.

Your agent · Claude Code harness · All operating systems

Stage only workshop_queue/cli.py. Show the staged path list and diff, confirm tests/test_cli.py is unchanged from the RED commit, then invoke the CodeArbiter commit gate. Do not create the receipt, amend, or rebase.

Your agent · Codex harness · All operating systems

Stage only workshop_queue/cli.py. Show the staged path list and diff, confirm tests/test_cli.py is unchanged from the RED commit, then invoke the CodeArbiter commit gate. Do not create the receipt, amend, or rebase.

Your agent · Pi harness · All operating systems

Stage only workshop_queue/cli.py. Show the staged path list and diff, confirm tests/test_cli.py is unchanged from the RED commit, then invoke the CodeArbiter commit gate. Do not create the receipt, amend, or rebase.

Expected result

A third descendant commit changes exactly workshop_queue/cli.py and the focused test remains green.

If that does not happen

If the staged boundary includes the test or another path, stop and Reset. Do not make the history look right by amending or rebasing it.

Evidence

The receipt records this exact commit as remediation_commit.

Generate the exact receipt bytes last

Run the complete command for your operating system in the native terminal only after the finding, RED, and GREEN commits exist. It reads the three real commit IDs from Git, writes deterministic verifier-owned bytes, stages only the receipt, and shows the staged diff. Do not hand-edit the JSON.

Why

The fixed generator sorts keys, uses compact separators and ASCII escaping, and appends exactly one LF so the final receipt matches Check byte for byte.

You · Native terminal · Windows

@'
import json
import pathlib
import subprocess

def git(*arguments):
    return subprocess.run(["git", *arguments], check=True, stdout=subprocess.PIPE, text=True).stdout

if git("status", "--porcelain", "--untracked-files=all"):
    raise SystemExit("P05 receipt requires a clean worktree after GREEN")
reference_oid = git("rev-parse", "HEAD").strip()
commits = git("rev-list", "--reverse", "HEAD~3..HEAD").splitlines()
hex_digits = "0123456789abcdef"
if (
    not reference_oid
    or any(character not in hex_digits for character in reference_oid)
    or len(commits) != 3
    or any(
        len(commit) != len(reference_oid)
        or any(character not in hex_digits for character in commit)
        for commit in commits
    )
):
    raise SystemExit("P05 receipt requires exactly three commit IDs after Prepare")
payload = {
    "affected_paths": ["tests/test_cli.py", "workshop_queue/cli.py"],
    "finding_commit": commits[0],
    "finding_id": "ACADEMY-P05-BLOCKED-UNRESOLVED",
    "red_commit": commits[1],
    "remediation_commit": commits[2],
    "schema_version": 2,
    "status": "remediated",
}
receipt = pathlib.Path(".codearbiter/checkpoints/P05-academy.json")
receipt.parent.mkdir(parents=True, exist_ok=True)
receipt.write_bytes(json.dumps(payload, sort_keys=True, separators=(",", ":"), ensure_ascii=True).encode("utf-8") + b"\n")
subprocess.run(["git", "add", "--", receipt.as_posix()], check=True)
subprocess.run(["git", "diff", "--cached", "--", receipt.as_posix()], check=True)
'@ | python -

You · Native terminal · macOS

python3 - <<'PY'
import json
import pathlib
import subprocess

def git(*arguments):
    return subprocess.run(["git", *arguments], check=True, stdout=subprocess.PIPE, text=True).stdout

if git("status", "--porcelain", "--untracked-files=all"):
    raise SystemExit("P05 receipt requires a clean worktree after GREEN")
reference_oid = git("rev-parse", "HEAD").strip()
commits = git("rev-list", "--reverse", "HEAD~3..HEAD").splitlines()
hex_digits = "0123456789abcdef"
if (
    not reference_oid
    or any(character not in hex_digits for character in reference_oid)
    or len(commits) != 3
    or any(
        len(commit) != len(reference_oid)
        or any(character not in hex_digits for character in commit)
        for commit in commits
    )
):
    raise SystemExit("P05 receipt requires exactly three commit IDs after Prepare")
payload = {
    "affected_paths": ["tests/test_cli.py", "workshop_queue/cli.py"],
    "finding_commit": commits[0],
    "finding_id": "ACADEMY-P05-BLOCKED-UNRESOLVED",
    "red_commit": commits[1],
    "remediation_commit": commits[2],
    "schema_version": 2,
    "status": "remediated",
}
receipt = pathlib.Path(".codearbiter/checkpoints/P05-academy.json")
receipt.parent.mkdir(parents=True, exist_ok=True)
receipt.write_bytes(json.dumps(payload, sort_keys=True, separators=(",", ":"), ensure_ascii=True).encode("utf-8") + b"\n")
subprocess.run(["git", "add", "--", receipt.as_posix()], check=True)
subprocess.run(["git", "diff", "--cached", "--", receipt.as_posix()], check=True)
PY

You · Native terminal · Linux

python3 - <<'PY'
import json
import pathlib
import subprocess

def git(*arguments):
    return subprocess.run(["git", *arguments], check=True, stdout=subprocess.PIPE, text=True).stdout

if git("status", "--porcelain", "--untracked-files=all"):
    raise SystemExit("P05 receipt requires a clean worktree after GREEN")
reference_oid = git("rev-parse", "HEAD").strip()
commits = git("rev-list", "--reverse", "HEAD~3..HEAD").splitlines()
hex_digits = "0123456789abcdef"
if (
    not reference_oid
    or any(character not in hex_digits for character in reference_oid)
    or len(commits) != 3
    or any(
        len(commit) != len(reference_oid)
        or any(character not in hex_digits for character in commit)
        for commit in commits
    )
):
    raise SystemExit("P05 receipt requires exactly three commit IDs after Prepare")
payload = {
    "affected_paths": ["tests/test_cli.py", "workshop_queue/cli.py"],
    "finding_commit": commits[0],
    "finding_id": "ACADEMY-P05-BLOCKED-UNRESOLVED",
    "red_commit": commits[1],
    "remediation_commit": commits[2],
    "schema_version": 2,
    "status": "remediated",
}
receipt = pathlib.Path(".codearbiter/checkpoints/P05-academy.json")
receipt.parent.mkdir(parents=True, exist_ok=True)
receipt.write_bytes(json.dumps(payload, sort_keys=True, separators=(",", ":"), ensure_ascii=True).encode("utf-8") + b"\n")
subprocess.run(["git", "add", "--", receipt.as_posix()], check=True)
subprocess.run(["git", "diff", "--cached", "--", receipt.as_posix()], check=True)
PY

Expected result

The command shows only .codearbiter/checkpoints/P05-academy.json staged; its compact sorted-key JSON names the real finding, RED, and GREEN commit IDs and ends in exactly one LF.

If that does not happen

If generation stops, an ID is missing, a path is extra, or GREEN is not clean, do not hand-write JSON or rewrite history. Preserve the attempt and Reset.

Evidence

Check accepts only a canonical receipt in the fourth and final descendant commit.

Ask the agent to commit the final receipt

Send this message in the selected harness after reviewing the staged receipt.

Why

The receipt must be last so it can bind the already-committed finding, RED, and GREEN evidence.

Your agent · Claude Code harness · All operating systems

Confirm the staged path list is exactly .codearbiter/checkpoints/P05-academy.json, then invoke the CodeArbiter commit gate. Do not add files, amend, rebase, or push.

Your agent · Codex harness · All operating systems

Confirm the staged path list is exactly .codearbiter/checkpoints/P05-academy.json, then invoke the CodeArbiter commit gate. Do not add files, amend, rebase, or push.

Your agent · Pi harness · All operating systems

Confirm the staged path list is exactly .codearbiter/checkpoints/P05-academy.json, then invoke the CodeArbiter commit gate. Do not add files, amend, rebase, or push.

Expected result

The fourth and final descendant commit changes exactly .codearbiter/checkpoints/P05-academy.json.

If that does not happen

If the commit gate reports a boundary problem, preserve the attempt and use Reset. Never force a receipt into an earlier commit.

Evidence

The committed receipt is the only P05 artifact Academy Check reads as the final evidence binding.

Do not add a fifth evidence commit. Do not amend, rebase, force-reset, or use copied terminal output as a substitute for the required repository evidence. The point is not a plausible story about a repair; it is a reviewable history that makes the defect, regression, repair, and final receipt separable.

Recognize success

The completed attempt has exactly four descendant commits after Prepare and no pending worktree changes. The first changes the generated dated .codearbiter/checkpoints/YYYY-MM-DD.md, .codearbiter/last-checkpoint, and .codearbiter/reports/academy/P05-finding.md; the second changes only tests/test_cli.py; the third changes only workshop_queue/cli.py; and the fourth changes only .codearbiter/checkpoints/P05-academy.json.

The finding says that blocked RQ-105 is omitted from the unresolved summary. The RED action gives the agent the verifier's exact taught method; an equivalent replacement is not accepted. That test persists the fixture, reaches the real report, and fails because unresolved is wrong, not because the setup broke. The GREEN repair changes the unresolved predicate to every ticket that is not completed. Run the native-terminal action to generate the receipt last; its deterministic generator writes sorted keys, compact separators, ASCII escaping, and exactly one LF. The receipt contains schema_version, finding_id, finding_commit, red_commit, remediation_commit, and status. affected_paths is exactly, in order, tests/test_cli.py then workshop_queue/cli.py. A copied host command is guidance, not evidence that either command was invoked.

Confirm the completed attempt is clean

Before Check, inspect the worktree in a native terminal. This is a direct terminal command and never begins with !.

Why

P05 Check rejects uncommitted or unrelated state so the committed four-step history is the whole claim.

You · Native terminal · Windows

git status --short --untracked-files=all

You · Native terminal · macOS

git status --short --untracked-files=all

You · Native terminal · Linux

git status --short --untracked-files=all

Expected result

Git prints no changed or untracked paths.

If that does not happen

If status is not empty, do not delete evidence or add unrelated files to a P05 commit. Preserve the attempt and Reset.

Evidence

A clean worktree is a direct Check precondition.

Check

Run the external Academy Check

Run Check from the Academy learner clone root in a native terminal. This command is entered directly and never begins with !.

Why

Check independently reconstructs the prepared fixture, commit ancestry, exact path roles, regression, repair, receipt, and live worktree.

You · Native terminal · Windows

$academy = "$env:LOCALAPPDATA\ArbiterAcademy\preview-0.30\Scripts\arbiter-academy.exe"
& $academy --repository (Get-Location).Path check P05-checkpoint-remediation

You · Native terminal · macOS

academy="${XDG_DATA_HOME:-$HOME/.local/share}/arbiter-academy/preview-0.30/bin/arbiter-academy"
"$academy" --repository "$PWD" check P05-checkpoint-remediation

You · Native terminal · Linux

academy="${XDG_DATA_HOME:-$HOME/.local/share}/arbiter-academy/preview-0.30/bin/arbiter-academy"
"$academy" --repository "$PWD" check P05-checkpoint-remediation

Expected result

Check prints checkpoint P05-checkpoint-remediation: passed and records progress in .academy/progress.json.

If that does not happen

If Check fails, preserve the committed attempt and read the failed predicate. Start a new numbered attempt with Reset instead of rewriting committed evidence.

Evidence

Check validates Git artifacts and the final worktree. It does not authenticate a checkpoint run or a human review, and it does not prove command chronology.

A pass contains checkpoint P05-checkpoint-remediation: passed and records progress in .academy/progress.json. Check independently verifies the prepared ADR, decision log, blocked ticket lifecycle, staged defect, exact four-commit topology, path roles, RED regression, GREEN repair, canonical receipt, and clean worktree. Check does not authenticate a checkpoint run or a human review, and it does not prove command chronology. Treat the commands and inspection steps as the practice you observe, not as claims that a final-state verifier can honestly make.

Recover or continue

If the branch identity, finding boundary, RED result, code-only GREEN boundary, receipt shape, or Check result is wrong, preserve the attempt. Read the named failure and create a fresh numbered attempt rather than trying to make past commits look correct. A preserved failed attempt is useful evidence; a rewritten one teaches the wrong habit.

Create a preserved numbered retry

Use Reset only when P05 needs another attempt. Run it at the Academy learner clone root in a native terminal; the command never begins with !.

Why

Reset preserves the failed or incomplete attempt for inspection before creating the next numbered baseline.

You · Native terminal · Windows

$academy = "$env:LOCALAPPDATA\ArbiterAcademy\preview-0.30\Scripts\arbiter-academy.exe"
& $academy --repository (Get-Location).Path reset P05-checkpoint-remediation

You · Native terminal · macOS

academy="${XDG_DATA_HOME:-$HOME/.local/share}/arbiter-academy/preview-0.30/bin/arbiter-academy"
"$academy" --repository "$PWD" reset P05-checkpoint-remediation

You · Native terminal · Linux

academy="${XDG_DATA_HOME:-$HOME/.local/share}/arbiter-academy/preview-0.30/bin/arbiter-academy"
"$academy" --repository "$PWD" reset P05-checkpoint-remediation

Expected result

Academy preserves the earlier attempt and prints the next academy/P05-checkpoint-remediation/ATTEMPT_NUMBER branch.

If that does not happen

If Reset stops, preserve its message and resolve only the named repository condition. Never force-reset or delete a prior attempt.

Evidence

A new attempt begins from the clean fixture while the previous history remains inspectable.

Hint 1

When a path boundary is unclear, read the latest commit's path list before doing more work. The finding contains the dated checkpoint report, .codearbiter/last-checkpoint, and .codearbiter/reports/academy/P05-finding.md; RED is one test path; GREEN is one production path; and the receipt is one checkpoint path.

Hint 2

The prepared defect is already in workshop_queue/cli.py. The RED test must create the blocked ticket through the existing CLI boundary and show that blocked is 1 while unresolved is wrong. Do not create a second fixture or a second production change to make that assertion easier.

Hint 3

The receipt is not a progress note. It is the last commit and its three IDs must be the real finding, RED, and GREEN descendants in that order. If you cannot name those four one-path commits, use Reset rather than trying to repair the history.

P06 is the next public guided Academy lesson. Leave the completed P05 branch intact: the later recovery case is separate work, not a repair to fold into this remediation attempt.

Understand the mechanism

Checkpoint remediation is a chain of independently inspectable facts. The prepared fixture creates a real domain state; the finding narrows the observed defect; a direct test records the caller expectation before code changes; the production change is isolated; and the receipt binds those commits into one checkable statement. This makes a later reviewer able to distinguish a real remediation from an invented report, a generic event, a broad patch, or a history assembled after the fact.

Next step

Continue with P06-context-drift-recovery.

Recovery guidance