Direct answer: Give every candidate an immutable version ID or SHA-256 hash, bind each reviewer’s verdict to that exact value, and invalidate both verdicts whenever the content changes. Publication is eligible only when two independent reviewers approve the same current version and all automated gates pass.
Plain-language definitions
An immutable candidate version is a saved draft that is never edited in place; a change creates a new version. A stale approval is a verdict attached to an older version, even if the edit was only one line. The hash is a digital fingerprint: if the file changes, its fingerprint changes.
The handoff state machine
| State | Required artifacts | Allowed next step |
|---|---|---|
| Candidate ready | Batch ID, round, candidate path, candidate hash | Send the same version independently to reviewers A and B |
| Awaiting review | Zero, one, or two verdicts bound to current hash | Wait; do not publish |
| Revision required | At least one blocker or failed article | Create a new immutable round and clear both verdict paths |
| Stale review | Verdict batch, round, or hash does not match | Discard the mismatched verdict reference and request fresh review |
| Approved | A and B pass every article on the same hash; automated gates pass | Publish once, then verify and record receipt |
| Published | Post IDs, URLs, verification results, time | Prevent duplicate publication of the batch |
Required handoff record
- Candidate: batch ID, round, file path, SHA-256, and creation time.
- Reviewer verdict: reviewer identity, same batch and round, reviewed hash, pass/fail, article-level verdicts, blockers, and review time.
- Revision event: every blocker, its source A or B, the exact changed section, new round, and new hash.
- Invalidation event: which old approvals became stale and why.
- Publish eligibility: unanimous current-version approval plus deterministic gate results.
Synthetic timeline: REV-208
REV-208 and all events below are synthetic. They illustrate two rounds.
- 09:00 — editor saves round 1 as
REV-208-r1.json, hashaaa111…. - 09:12 — reviewer A passes
aaa111…. - 09:18 — reviewer B fails
aaa111…and requests a clearer example. State becomes revision required. - 09:30 — editor changes one sentence and saves round 2 as a new file, hash
bbb222…. Both round-1 verdicts are invalidated; A’s old pass cannot be copied forward. - 09:35 — a late message says reviewer A passed round 1. The controller rejects it because
aaa111…does not match currentbbb222…. - 09:44 — A passes
bbb222…; publication still waits. - 09:51 — B passes
bbb222…. Automated similarity and content gates run on that same file. Only after they pass does REV-208 become publish eligible.
Race conditions and failure handling
Write state through a temporary file and atomic rename so two processes cannot leave half-written JSON. Acquire a batch lock before changing state or publishing. Re-read the current hash immediately before any write to WordPress. On the first publishing error, stop; if this round created posts, return those newly created posts to draft. Never describe a partial batch as successful.
Acceptance checklist
- Both reports exist and identify different reviewer roles.
- Batch ID, round, and reviewed hash match the current candidate exactly.
- Both overall verdicts pass and every article-level verdict passes.
- All required automated gates pass against the immutable candidate.
- No prior publish receipt exists for this batch.
- Every live post is read back through authenticated REST and fetched publicly.
Adult and compliance notice
This editorial-control article is neutral operational guidance for adult-information publishing. It does not make product, price, stock, legal, health, safety, or availability claims.