Parallel Verification (PV)
What this is, and why it matters
Unit tests and acceptance tests run inside the system's own worldview. They
trust the code's mocks, its in-memory state, and its own assertions about what
happened. That trust is exactly the problem: a provider can return a state file
saying a certificate exists, an API can return 201 Created, and the code can
pass every test — while nothing real ever landed in the backend.
Parallel Verification is the practice of confirming an effect from the
outside. You operate the system through its real public interface as a black
box, capture what the system claims it did, and then go to the actual system
of record through a separate, independent path and check whether the effect
is really there. The verification path must not share code, libraries, or trust
with the thing under test — that independence is the entire point. The code
author (human or agent) can make the code lie; they cannot make psql,
RACDCERT, the AWS CLI, or the Vault CLI lie about what is actually stored.
It's called Parallel for two reasons:
- It runs alongside and outside the normal test pyramid — a parallel track
to unit/acceptance tests, doing full integration verification that those
can't.
- The deliverable is a standalone harness that executes independently of
the context that wrote the code. A human or CI job can re-run it cold. Because
it talks to real systems through real CLIs, it cannot be talked out of the
truth. The harness is the firewall against self-deception.
The two-observer principle is the heart of it: the system's self-report
(its API response, its state file, its return value) and the backend's
ground truth (what an independent CLI sees in the real system of record)
must agree. PV is the diff between those two observers. Any gap is a finding.
What you deliver
Two artifacts, always:
- A reusable harness — executable scripts (typically bash) that generate
inputs, drive the system, perform last-mile verification, and exit non-zero
on any mismatch. It lives in its own self-contained directory (see "Where the
harness lives" below) so anyone can re-run it in isolation. It is not a
throwaway; it is the proof, kept.
- A findings report (
FINDINGS.md) — the human-readable verdict: what was
verified, the evidence, and any discoveries that go beyond pass/fail
(severity, root cause, remediation). See the structure below.
Where the harness lives — ask first, isolate always
Before writing anything, decide where the harness goes — and don't assume.
Ask the user where to create the PV harness, defaulting to
./scratchpads/. Repos differ — some have a verify/ dir, a test/e2e/
tree, or a dedicated scratch area — and a wrong guess scatters artifacts where
they don't belong. Propose ./scratchpads/<feature>-pv/ and let them confirm
or redirect before you create files.
Each PV test is atomic and self-contained. Give every test its own
directory under the chosen location, holding everything it needs: its own
input files (main.tf, request payloads), its own runner script, its own
log, its own state. A test must be runnable in isolation, in any order,
without depending on another test's setup or leftovers — and its cleanup trap
must remove only what it created. This keeps failures localized and lets you
re-run one scenario without disturbing the others.
Don't assume scaffolding that isn't there. Do not presume an existing
harness to model from, a particular SDK or worktree layout, or a sibling repo
path. Discover the real structure first — search the repo, read what's
actually present — and when it isn't evident, ask rather than inventing a
path. A confident wrong assumption about where code or a backing service
lives quietly invalidates the entire run; verifying against the wrong thing
is the one failure mode PV exists to prevent.
The core PV loop
Apply this loop regardless of backend. The backend only changes step 5's
mechanics; the shape is universal.
Identify the contract. What does the system claim to do, and where does
the truth ultimately live? Name the real system of record (the Postgres row,
the RACF profile, the Vault path, the Okta user, the S3 object). If the
effect doesn't land somewhere external and inspectable, PV may not apply —
say so.
Generate realistic inputs. Produce payloads/configs that exercise the
real surface area — not just the happy path, but the fields and combinations
that are easy to get wrong (types, owners, defaults, optional attributes).
In the z/OSMF examples this was a main.tf; for a REST API it's a set of
request bodies.
Operate the system as a black box. Drive it through its real public
interface — terraform apply, curl, the CLI, the SDK's public API — never
by reaching into its internals. If you're testing a Terraform provider, point
at the locally built provider via dev overrides and run the actual
terraform binary. Black-box discipline is what makes the result credible.
Capture the system's self-report ("expected"). Record what the system
says it did: the API response body, terraform.tfstate, the CLI's output,
the returned IDs/serials. Parse it into structured values (jq is your
friend) — these become the expectations you'll check against reality.
Last-mile verify against ground truth ("actual"). Go to the real system
of record through an independent path and read what's actually there.
- REST API → Postgres:
psql and SELECT the rows the API claims to have written.
- Terraform provider for AWS: the
aws CLI to describe the real resource.
- Terraform provider for IBM z/OS RACF: SSH +
tsocmd "RACDCERT ... LIST".
- Vault engine: the
vault CLI to read/list the secret path.
- Okta engine: Okta's REST API or CLI to fetch the user/group/app.
The last-mile method is not always obvious. When it isn't, ask the user
rather than guessing — they know their environment, credentials, and what
"the truth" means here. See references/last-mile-catalog.md for the
decision framework and a backend→method catalog.
Compare field by field. Diff expected against actual for every attribute
that matters. Emit PASS / FAIL / WARN per check (WARN for things like a
format mismatch that may be benign). A single comparison is worth a hundred
assertions the code makes about itself.
Probe beyond create. The interesting bugs live in the rest of the
lifecycle. Exercise and verify:
- Idempotency — re-run with no input change; there must be no drift.
For Terraform,
plan -detailed-exitcode returning 2 means drift (a bug);
0 means clean.
- Update / renewal — change an input; confirm the backend changed to match.
- Teardown — destroy/delete; confirm the backend is actually clean, with
no orphaned or ghost entries left behind.
- Failure & rollback — where feasible, force a failure mid-operation and
confirm the system doesn't leave partial garbage.
Report. Produce the machine summary (counts, exit code) and the
FINDINGS.md writeup. Surface not just pass/fail but anything you learned —
a missing permission, a non-standard output format, a confusing-but-correct
behavior. Those discoveries are often the most valuable output.
Choosing the last-mile method
This is the judgment-heavy step, so give it real thought. A good last-mile path
is independent (doesn't reuse the system-under-test's own code to read
back), authoritative (reads the actual system of record, not a cache or a
mirror), and inspectable at the field level (you can extract the specific
values to compare).
When the path is genuinely unclear — an unusual backend, an internal system,
ambiguous "truth", or missing credentials — ask the user. Offer the options
you can see and let them pick or correct you. Guessing a last-mile method and
verifying against the wrong source of truth is worse than asking: it produces a
confident green result that means nothing.
references/last-mile-catalog.md holds the framework plus a catalog of known
backend → verification-path mappings.
Harness conventions
Don't reinvent the scaffolding each time. references/harness-conventions.md
distills the reusable bash patterns — colored PASS/FAIL/WARN logging with
counters, argument parsing, credential resolution order (flags > creds file >
env), prerequisite and connectivity checks, jq-based state parsing, phased
runners (--phase N), cross-phase fact capture, cleanup traps that always tear
down, and meaningful exit codes. Read it before writing a harness so your output
matches the proven shape and a reviewer recognizes it instantly.
Key invariants worth stating up front:
- Exit non-zero on any FAIL. The harness's exit code is its verdict; CI
depends on it.
- Always clean up. Use a trap so a mid-run failure still tears down created
resources — orphans poison the next run and the real environment.
- Capture evidence. Log the actual backend output you compared against, so a
reader can see why a check passed or failed, not just that it did.
The findings report (FINDINGS.md)
Structure it so a reviewer gets the verdict in five seconds and the depth on
demand. Model it on this shape (drawn from the z/OSMF CSR verification):
# <Feature> Verification — Findings (<env/host>)
**Result: PASS|FAIL.** <One-paragraph verdict: what works, what doesn't, the
single most important takeaway.>
## What was verified
| Check | Result | Evidence |
|---|---|---|
| <claim checked> | PASS | <the actual backend value / serial / row that proves it> |
## State trajectory (if lifecycle was exercised)
<table tracking key values across apply → plan → update → destroy phases>
## New findings discovered during testing
### FINDING N — <title> (<SEVERITY>, <merge impact>)
<what you observed, root cause, and concrete remediation / action items>
## Recommendation
<ship / ship-after-fix / block, with the reasoning>
## Reproducing
<exact commands + prerequisites to re-run the harness>
The Evidence column matters most: cite the real serial, row, or CLI output you
saw. Evidence is what makes the report trustworthy rather than another assertion.
Backend-specific recipes
Read the one that matches the system under test — each has the concrete
generate→operate→last-mile recipe and the gotchas for that backend:
references/terraform-providers.md — any Terraform provider: dev overrides /
local provider, apply, parsing terraform.tfstate, plan -detailed-exitcode
for idempotency, the apply/plan/renew/destroy phase pattern, and how last-mile
branches by what the provider targets (AWS, IBM z/OS, Vault, Okta…).
references/rest-api-database.md — REST/gRPC APIs whose effects land in a
database: payload generation, black-box driving with curl, capturing
responses, and last-mile SELECTs via psql/mysql/mongosh, including the
reverse check (delete via API → confirm gone in DB).
references/vault-and-secrets.md — Vault engines and secret stores: operating
via the vault CLI or the Terraform Vault provider, then reading back the path.
references/okta-and-saas-apis.md — Okta and SaaS-API backends: driving via
API/CLI and last-mile via the provider's own REST API or CLI.
references/ibm-zos-racf.md — IBM z/OS RACF over SSH + TSO (tsocmd,
RACDCERT), including the message codes that signal real failures.
If the backend isn't covered, apply the core loop, use
references/last-mile-catalog.md to choose a path (asking the user when
unsure), and follow references/harness-conventions.md for the scaffolding.
1---2name: parallel-verification3description: Build a Parallel Verification (PV) harness — an out-of-band, last-mile integration check that proves a system actually does what it claims by driving its real public interface and then confirming the effects against the real backend through an INDEPENDENT path the code author cannot fake. Use this whenever you are building or reviewing something whose correctness ultimately lands in an external system of record: a REST API that writes to a database, a Terraform provider, a Vault secrets engine, an Okta integration, an IBM z/OS RACF resource, a message queue, an object store, an IAM policy, etc. Trigger on phrases like "verify this end-to-end", "prove it really works", "last-mile verification", "out-of-band check", "did it actually write to the database / state / backend", "black-box integration test", "build a verification harness", or any time unit tests and acceptance tests pass but you still don't trust that the real-world effect happened. Reach for PV precisely when you want a check that does not trust th4---56# Parallel Verification (PV)78## What this is, and why it matters910Unit tests and acceptance tests run *inside* the system's own worldview. They11trust the code's mocks, its in-memory state, and its own assertions about what12happened. That trust is exactly the problem: a provider can return a state file13saying a certificate exists, an API can return `201 Created`, and the code can14pass every test — while nothing real ever landed in the backend.1516**Parallel Verification is the practice of confirming an effect from the17outside.** You operate the system through its real public interface as a black18box, capture what the system *claims* it did, and then go to the actual system19of record through a **separate, independent path** and check whether the effect20is really there. The verification path must not share code, libraries, or trust21with the thing under test — that independence is the entire point. The code22author (human or agent) can make the code lie; they cannot make `psql`,23`RACDCERT`, the AWS CLI, or the Vault CLI lie about what is actually stored.2425It's called **Parallel** for two reasons:26271. It runs *alongside and outside* the normal test pyramid — a parallel track28 to unit/acceptance tests, doing full integration verification that those29 can't.302. The deliverable is a **standalone harness** that executes independently of31 the context that wrote the code. A human or CI job can re-run it cold. Because32 it talks to real systems through real CLIs, it cannot be talked out of the33 truth. The harness *is* the firewall against self-deception.3435The two-observer principle is the heart of it: the system's **self-report**36(its API response, its state file, its return value) and the backend's37**ground truth** (what an independent CLI sees in the real system of record)38must agree. PV is the diff between those two observers. Any gap is a finding.3940## What you deliver4142Two artifacts, always:43441. **A reusable harness** — executable scripts (typically bash) that generate45 inputs, drive the system, perform last-mile verification, and exit non-zero46 on any mismatch. It lives in its own self-contained directory (see "Where the47 harness lives" below) so anyone can re-run it in isolation. It is not a48 throwaway; it is the proof, kept.492. **A findings report** (`FINDINGS.md`) — the human-readable verdict: what was50 verified, the evidence, and any discoveries that go beyond pass/fail51 (severity, root cause, remediation). See the structure below.5253## Where the harness lives — ask first, isolate always5455Before writing anything, decide *where* the harness goes — and don't assume.5657- **Ask the user where to create the PV harness**, defaulting to58 `./scratchpads/`. Repos differ — some have a `verify/` dir, a `test/e2e/`59 tree, or a dedicated scratch area — and a wrong guess scatters artifacts where60 they don't belong. Propose `./scratchpads/<feature>-pv/` and let them confirm61 or redirect before you create files.6263- **Each PV test is atomic and self-contained.** Give every test its own64 directory under the chosen location, holding everything it needs: its own65 input files (`main.tf`, request payloads), its own runner script, its own66 log, its own state. A test must be runnable in isolation, in any order,67 without depending on another test's setup or leftovers — and its cleanup trap68 must remove only what it created. This keeps failures localized and lets you69 re-run one scenario without disturbing the others.7071- **Don't assume scaffolding that isn't there.** Do not presume an existing72 harness to model from, a particular SDK or worktree layout, or a sibling repo73 path. Discover the real structure first — search the repo, read what's74 actually present — and when it isn't evident, ask rather than inventing a75 path. A confident wrong assumption about where code or a backing service76 lives quietly invalidates the entire run; verifying against the wrong thing77 is the one failure mode PV exists to prevent.7879## The core PV loop8081Apply this loop regardless of backend. The backend only changes step 5's82mechanics; the shape is universal.83841. **Identify the contract.** What does the system claim to do, and where does85 the truth ultimately live? Name the real system of record (the Postgres row,86 the RACF profile, the Vault path, the Okta user, the S3 object). If the87 effect doesn't land *somewhere external and inspectable*, PV may not apply —88 say so.89902. **Generate realistic inputs.** Produce payloads/configs that exercise the91 real surface area — not just the happy path, but the fields and combinations92 that are easy to get wrong (types, owners, defaults, optional attributes).93 In the z/OSMF examples this was a `main.tf`; for a REST API it's a set of94 request bodies.95963. **Operate the system as a black box.** Drive it through its real public97 interface — `terraform apply`, `curl`, the CLI, the SDK's public API — never98 by reaching into its internals. If you're testing a Terraform provider, point99 at the locally built provider via dev overrides and run the actual100 `terraform` binary. Black-box discipline is what makes the result credible.1011024. **Capture the system's self-report ("expected").** Record what the system103 says it did: the API response body, `terraform.tfstate`, the CLI's output,104 the returned IDs/serials. Parse it into structured values (jq is your105 friend) — these become the expectations you'll check against reality.1061075. **Last-mile verify against ground truth ("actual").** Go to the real system108 of record through an independent path and read what's actually there.109 - REST API → Postgres: `psql` and `SELECT` the rows the API claims to have written.110 - Terraform provider for AWS: the `aws` CLI to describe the real resource.111 - Terraform provider for IBM z/OS RACF: SSH + `tsocmd "RACDCERT ... LIST"`.112 - Vault engine: the `vault` CLI to read/list the secret path.113 - Okta engine: Okta's REST API or CLI to fetch the user/group/app.114115 **The last-mile method is not always obvious. When it isn't, ask the user**116 rather than guessing — they know their environment, credentials, and what117 "the truth" means here. See `references/last-mile-catalog.md` for the118 decision framework and a backend→method catalog.1191206. **Compare field by field.** Diff expected against actual for every attribute121 that matters. Emit `PASS` / `FAIL` / `WARN` per check (WARN for things like a122 format mismatch that may be benign). A single comparison is worth a hundred123 assertions the code makes about itself.1241257. **Probe beyond create.** The interesting bugs live in the rest of the126 lifecycle. Exercise and verify:127 - **Idempotency** — re-run with no input change; there must be no drift.128 For Terraform, `plan -detailed-exitcode` returning `2` means drift (a bug);129 `0` means clean.130 - **Update / renewal** — change an input; confirm the backend changed to match.131 - **Teardown** — destroy/delete; confirm the backend is actually clean, with132 no orphaned or ghost entries left behind.133 - **Failure & rollback** — where feasible, force a failure mid-operation and134 confirm the system doesn't leave partial garbage.1351368. **Report.** Produce the machine summary (counts, exit code) and the137 `FINDINGS.md` writeup. Surface not just pass/fail but anything you learned —138 a missing permission, a non-standard output format, a confusing-but-correct139 behavior. Those discoveries are often the most valuable output.140141## Choosing the last-mile method142143This is the judgment-heavy step, so give it real thought. A good last-mile path144is **independent** (doesn't reuse the system-under-test's own code to read145back), **authoritative** (reads the actual system of record, not a cache or a146mirror), and **inspectable at the field level** (you can extract the specific147values to compare).148149When the path is genuinely unclear — an unusual backend, an internal system,150ambiguous "truth", or missing credentials — **ask the user**. Offer the options151you can see and let them pick or correct you. Guessing a last-mile method and152verifying against the wrong source of truth is worse than asking: it produces a153confident green result that means nothing.154155`references/last-mile-catalog.md` holds the framework plus a catalog of known156backend → verification-path mappings.157158## Harness conventions159160Don't reinvent the scaffolding each time. `references/harness-conventions.md`161distills the reusable bash patterns — colored `PASS/FAIL/WARN` logging with162counters, argument parsing, credential resolution order (flags > creds file >163env), prerequisite and connectivity checks, jq-based state parsing, phased164runners (`--phase N`), cross-phase fact capture, cleanup traps that always tear165down, and meaningful exit codes. Read it before writing a harness so your output166matches the proven shape and a reviewer recognizes it instantly.167168Key invariants worth stating up front:169- **Exit non-zero on any FAIL.** The harness's exit code is its verdict; CI170 depends on it.171- **Always clean up.** Use a trap so a mid-run failure still tears down created172 resources — orphans poison the next run and the real environment.173- **Capture evidence.** Log the actual backend output you compared against, so a174 reader can see *why* a check passed or failed, not just that it did.175176## The findings report (`FINDINGS.md`)177178Structure it so a reviewer gets the verdict in five seconds and the depth on179demand. Model it on this shape (drawn from the z/OSMF CSR verification):180181```markdown182# <Feature> Verification — Findings (<env/host>)183184**Result: PASS|FAIL.** <One-paragraph verdict: what works, what doesn't, the185single most important takeaway.>186187## What was verified188| Check | Result | Evidence |189|---|---|---|190| <claim checked> | PASS | <the actual backend value / serial / row that proves it> |191192## State trajectory (if lifecycle was exercised)193<table tracking key values across apply → plan → update → destroy phases>194195## New findings discovered during testing196### FINDING N — <title> (<SEVERITY>, <merge impact>)197<what you observed, root cause, and concrete remediation / action items>198199## Recommendation200<ship / ship-after-fix / block, with the reasoning>201202## Reproducing203<exact commands + prerequisites to re-run the harness>204```205206The `Evidence` column matters most: cite the real serial, row, or CLI output you207saw. Evidence is what makes the report trustworthy rather than another assertion.208209## Backend-specific recipes210211Read the one that matches the system under test — each has the concrete212generate→operate→last-mile recipe and the gotchas for that backend:213214- `references/terraform-providers.md` — any Terraform provider: dev overrides /215 local provider, `apply`, parsing `terraform.tfstate`, `plan -detailed-exitcode`216 for idempotency, the apply/plan/renew/destroy phase pattern, and how last-mile217 branches by what the provider targets (AWS, IBM z/OS, Vault, Okta…).218- `references/rest-api-database.md` — REST/gRPC APIs whose effects land in a219 database: payload generation, black-box driving with `curl`, capturing220 responses, and last-mile `SELECT`s via `psql`/`mysql`/`mongosh`, including the221 reverse check (delete via API → confirm gone in DB).222- `references/vault-and-secrets.md` — Vault engines and secret stores: operating223 via the `vault` CLI or the Terraform Vault provider, then reading back the path.224- `references/okta-and-saas-apis.md` — Okta and SaaS-API backends: driving via225 API/CLI and last-mile via the provider's own REST API or CLI.226- `references/ibm-zos-racf.md` — IBM z/OS RACF over SSH + TSO (`tsocmd`,227 `RACDCERT`), including the message codes that signal real failures.228229If the backend isn't covered, apply the core loop, use230`references/last-mile-catalog.md` to choose a path (asking the user when231unsure), and follow `references/harness-conventions.md` for the scaffolding.