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:
the checkpoint’s generated dated report and baseline together with an exact finding record;
a test-only RED regression that exposes the defect;
a code-only GREEN repair that leaves the regression unchanged; and
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.
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.
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.
3
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.
4
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.
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.
6
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.
7
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.
8
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.
9
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.
10
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.
11
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.
12
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.
13
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.
14
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.
15
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.
16
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.
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.
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.
18
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.
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.