One useful idea
A valid number is not an authorized review. FindingCreate permits a declared criterion, integer rating 1..5 and bounded nonblank authored evidence. The endpoint assigns its own finding/evaluation/author identity, state=draft, revision=1 and no reviewer. Unknown body author/reviewer/state fields fail schema validation. Review permission comes from the configured actor and current state, not a checkbox or a supplied author name.
Declare the permitted state edges: draft→submitted via submit; submitted→approved via approve; submitted→rejected via reject. Only the draft’s author with author role submits. Only a reviewer-role actor whose ID differs from author_id approves/rejects a submitted finding. Approved/rejected states are terminal here. Preserve finding ID, parent, author, criterion, rating and original evidence; create a fresh value with next state, revision+1 and the correct reviewer ID. Do not infer a creative-quality winner or dataset eligibility from approved.
The client supplies expected_revision, an exact positive integer, not bool or a string. A prior GET is only a snapshot: another operation can change state before the next POST. Check the revision against the actual stored record inside the repository transaction; UPDATE must also compare the stored revision. The pure transition policy and database compare-and-update have different roles. 403 means this actor cannot use the permitted edge; 409 means stale revision or unavailable state edge. Read current state before deciding what to do next, rather than blindly retrying.
The four request flow is evaluation creation, finding draft, author submit and distinct reviewer approve/reject. A rejected finding retains its original evidence/rating and reviewer, rather than erasing a negative result. Record and controlled audit event commit together; a failure does not increment the revision or claim a completed review. The creation snapshot remains review_status_at_creation=unreviewed even after a finding becomes approved; query the current finding/audit instead.
The worked example uses real in-process routes and SQLite, with one denied self-review and a stale retry. An actual server/restart needs the separate setup commands and a second terminal. Finish every practice boundary before using --target practice serve. A used port should fail visibly; choose another, never stop someone else’s process. Stop only your own server with Ctrl+C.
The fixture author/reviewer credentials are PUBLIC invented classroom mappings, not DVP tutor credentials or proof of distinct real humans. All fixture actors can read the classroom data; no tenant/privacy model is supplied. This workflow does not certify rights, authenticity, creative correctness or Portfolio III independence. Retain actual changed-input artifacts, helper disclosure and an observed defense for the later capstone, rather than treating this module quiz as certification.
Refresh first: Actor dependency and strict schemas, 403 versus 409 recovery, Atomic finding/audit revision updates, Machine signals versus authored findings.
Trace a finished example
from pathlib import Path
from tempfile import TemporaryDirectory
from fastapi.testclient import TestClient
from evaluation_service.app import create_app, FIXTURE_CREDENTIALS
from evaluation_service.storage import Repository
author = {"Authorization": "Bearer fixture-author-a"}
reviewer = {"Authorization": "Bearer fixture-reviewer-a"}
with TemporaryDirectory(prefix="dvp-owned-review-") as folder:
store = Repository(Path(folder) / "example.db")
store.initialize(new=True)
with TestClient(create_app(store, credentials=FIXTURE_CREDENTIALS)) as client:
response = client.post("/evaluations", headers=author, json={
"asset_id": "Fresh_review", "width": 1920, "height": 1080, "frames": 240, "fps": 24})
evaluation_id = response.json()["id"]
path = "/evaluations/" + evaluation_id + "/findings"
draft = client.post(path, headers=author, json={
"criterion": "motion", "rating": 3, "evidence": "Invented frame 19"}).json()
transition = path + "/" + draft["id"] + "/transitions"
print(draft["state"], draft["revision"])
submitted = client.post(transition, headers=author,
json={"action": "submit", "expected_revision": 1}).json()
print(submitted["state"], submitted["revision"])
denied = client.post(transition, headers=author,
json={"action": "approve", "expected_revision": 2})
print(denied.status_code, denied.json()["error"]["code"])
approved = client.post(transition, headers=reviewer,
json={"action": "approve", "expected_revision": 2}).json()
print(approved["state"], approved["revision"], approved["reviewer_id"])
stale = client.post(transition, headers=reviewer,
json={"action": "approve", "expected_revision": 2})
print(stale.status_code, stale.json()["error"]["code"])
reopened = Repository(store.path)
reopened.initialize()
current = reopened.get_finding(evaluation_id, draft["id"])
print(current.evidence == draft["evidence"], current.revision)
print([event["event"] for event in reopened.audit(evaluation_id)])Expected output
draft 1
submitted 2
403 forbidden
approved 3 reviewer_A
409 conflict
True 3
['created', 'drafted', 'submit', 'approve']Actual API requests draft and submit a finding. Its own author cannot approve it, so the 403 adds no event. A distinct configured reviewer approves at revision 2, producing revision 3. A stale retry gets 409. Reopening reads the unchanged evidence/current revision and only four committed events; fixture identities still do not authenticate real humans.
The finished implementation is in evaluation_service/core.py and evaluation_service/cli.py. Reading it is guided practice, not independent evidence.
Predict the edge
Can a reviewer approve a draft at its current revision?
Compare your answer · self-reviewed
No: the available review edge requires submitted state. This is a 409 state conflict even with reviewer role; first the finding’s own author must submit it.
Find the race
A GET showed revision 2, but the stored finding is now revision 3. Should expected_revision=2 succeed?
Compare your answer · self-reviewed
No. Compare the actual stored revision inside the transaction and the UPDATE condition. Return 409 with no changed record/event; read the current state before deciding the next action.
Recall the evidence layers
Does approved/reviewer_A or a perfect quiz score certify real human judgment and Portfolio III completion?
Compare your answer · self-reviewed
No. The identities are public fixture mappings, and the quiz measures knowledge. Independent code/artifacts, limitations, helper disclosure and observed transfer/defense remain separate.
Change it, then build your own
One controlled change
Try approval while draft, another author’s submit, self-review after submission, valid rejection by the distinct reviewer and a stale retry. Predict 403 versus 409 and the unchanged/next revision before each actual request. Use a fresh owned fixture database, never reset old work.
Your independent task
Implement transition_finding in practice.py: revalidate finding/actor, refuse nonexact/nonpositive revisions, enforce current revision and declared state edges, require the author or distinct reviewer role/ID, preserve immutable evidence and return a fresh incremented finding. Complete the earlier Repository/transform/error boundaries, then run all practice tests and the actual practice CLI/server. Use the second-terminal requests in README/CONTRACT; no reference fallback is completion.
What success looks like
Allowed submit/approve/reject preserve evidence and advance exactly one revision. Forbidden/stale/terminal requests refuse without writes/events. The completed practice service persists the current finding and ordered audit across an actual stopped/restarted process. Reference demos, in-process checks and quiz scores remain distinct from independently produced artifacts.
Hint 1 · a question
Write a three-edge state table and the actor required by each edge. Which fields are immutable, and what changes?
Hint 2 · a concept cue
Validate the expected revision before state transition. Separate unavailable edge/conflict from disallowed actor/forbidden; do not derive identity from request body fields.
Hint 3 · a localized example
A new submitted finding has revision+1 and reviewer_id=None. Approval/rejection need an actor with reviewer role and ID different from author_id, preserving original evidence/rating.
Need the complete worked solution?
Open evaluation_service/core.py and evaluation_service/cli.py from the kit. Trace it, close it, then try fresh inputs in your own files. Treat the attempt as guided; seeing the solution does not award a practical pass.
Course help is guidance, not independent evidence. With JavaScript, opening help records guidance locally; otherwise note it in your README. Reset does not erase that history.
Repair a failed check
If self-approval succeeds, compare reviewer ID with original author_id and require reviewer role. If terminal states change, enumerate permitted edges rather than assigning any requested action. If stale writes overwrite, fix both pure revision policy and actual database compare-and-update. If the practice server silently uses references, check selected target imports; unfinished code must visibly refuse.
NotImplementedError means a practice stub is still unfinished. Read the failing test name and the last error line. Change one behavior, rerun that build, then rerun all implemented builds.
Show it works on new inputs
Use a fresh invented finding and changed criterion/rating/evidence. Demonstrate allowed submit/reject, refused self-review/stale/terminal requests, an actual server restart and matching persisted current finding/audit. Retain your own code/tests/commands/boundary map and helper disclosure; explain one new requirement and limitation. This prepares the capstone but does not replace independent Portfolio III work and observed defense.
Self-review: name the input, result, refused case and reason. Your local test output and explanation are separate from a quiz score; this page does not certify a pass.
Keep the idea
A workflow is explicit state, authority and concurrency policy. Honest current-state reads and atomic revision/audit writes matter more than a successful-looking checkbox.
Module 10 checkpoint
Five questions, followed by the separate practical task above. JavaScript loads the scored questions; the build, files and hints remain available without it.