Skip to content

Remediate a checkpoint finding

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 !.

  1. YouBrowser

    Confirm the safe practice space

    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 this matters: A known fork and clone keep the remediation attempt separate from the course source and give Academy a repository it can reset safely.

    Expected result

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

    Evidence

    This is a prerequisite check by you. Academy Check does not authenticate how you created the fork or 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.

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.

  1. You

    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 this matters: Prepare creates the verifier-owned attempt branch and stages the real blocked-ticket summary defect before any learner work begins.

    Windows

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

    macOS

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

    Linux

    academy="${XDG_DATA_HOME:-$HOME/.local/share}/arbiter-academy/preview-0.32/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.

    Evidence

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

    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.

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.

  1. You

    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 this matters: The four required commits count only when they descend from the prepared attempt, not from a look-alike local branch.

    Windows

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

    macOS

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

    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.

    Evidence

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

    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.

  1. Your host

    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 this matters: The learner and agent need the domain, persistence, reporting, decision, and scenario boundaries before deciding what the finding proves.

    Any OS · Claude Code

    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.

    Any OS · Codex

    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.

    Any OS · Pi

    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.

    Evidence

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

    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.

  1. Your host

    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 this matters: The checkpoint lane frames the observed product defect before the repair lane begins.

    Any OS · Claude Code

    /ca:checkpoint

    Any OS · Codex

    $ca-checkpoint

    Any OS · Pi

    /ca-checkpoint

    Any OS · Pi

    /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.

    Evidence

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

    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.

  1. YouActive harness

    Inspect the real defect before recording it

    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 this matters: 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.

    Evidence

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

    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.

  1. Your host

    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 this matters: The verifier requires an exact, standalone finding commit before the RED regression and GREEN repair.

    Any OS · Claude Code

    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.

    Any OS · Codex

    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.

    Any OS · Pi

    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.

    Evidence

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

    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.

  1. You

    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 this matters: A clean three-artifact commit gives the later history an unambiguous checkpoint-and-finding boundary.

    Windows

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

    macOS

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

    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.

    Evidence

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

    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.

  1. Your host

    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 this matters: The lesson proves that the prepared report is wrong with a real persisted blocked ticket rather than a synthetic JSON assertion.

    Any OS · Claude Code

    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.

    Any OS · Codex

    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.

    Any OS · Pi

    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.

    Evidence

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

    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.

  1. YouActive harness

    Recognize the meaningful RED result

    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 this matters: 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.

    Evidence

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

    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.

  1. Your host

    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 this matters: The test-only commit preserves the regression before the production repair can make it pass.

    Any OS · Claude Code

    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.

    Any OS · Codex

    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.

    Any OS · Pi

    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.

    Evidence

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

    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.

  1. Your host

    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 this matters: The bounded repair changes the definition of unresolved from open plus claimed to every ticket that is not completed.

    Any OS · Claude Code

    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.

    Any OS · Codex

    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.

    Any OS · Pi

    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.

    Evidence

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

    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.

  1. Your host

    Ask the agent to commit only the GREEN repair

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

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

    Any OS · Claude Code

    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.

    Any OS · Codex

    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.

    Any OS · Pi

    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.

    Evidence

    The receipt records this exact commit as remediation_commit.

    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.

  1. You

    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 this matters: 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.

    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 -

    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

    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.

    Evidence

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

    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.

  1. Your host

    Ask the agent to commit the final receipt

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

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

    Any OS · Claude Code

    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.

    Any OS · Codex

    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.

    Any OS · Pi

    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.

    Evidence

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

    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.

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.

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.

  1. You

    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 this matters: P05 Check rejects uncommitted or unrelated state so the committed four-step history is the whole claim.

    Windows

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

    macOS

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

    Linux

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

    Expected result

    Git prints no changed or untracked paths.

    Evidence

    A clean worktree is a direct Check precondition.

    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.

  1. You

    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 this matters: Check independently reconstructs the prepared fixture, commit ancestry, exact path roles, regression, repair, receipt, and live worktree.

    Windows

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

    macOS

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

    Linux

    academy="${XDG_DATA_HOME:-$HOME/.local/share}/arbiter-academy/preview-0.32/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.

    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.

    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.

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.

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.

  1. You

    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 this matters: Reset preserves the failed or incomplete attempt for inspection before creating the next numbered baseline.

    Windows

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

    macOS

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

    Linux

    academy="${XDG_DATA_HOME:-$HOME/.local/share}/arbiter-academy/preview-0.32/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.

    Evidence

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

    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.

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.

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.

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.

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.