Agentic systems / Pattern 03 / Build the working path

Implement a plan contract and a separate verifier

Inspect actual plan validation, claim generation and contradictory evidence tests before connecting a real planning model.

By Sandeep Belgavi · 23 October 2026 · Architecture and design patterns

Run the plan fixture locally

Download pattern_03.py and test_pattern_03.py together. On macOS use python3 -B pattern_03.py; on Windows use py -3 -B pattern_03.py. The expected line is steps=1 verified=true remediation=none. Execute -B -m unittest -v test_pattern_03.py with the same interpreter. Windows Python 3.13 was used for the tests; macOS commands are guidance.

The planner is a callable returning one Step, and both evidence inputs are synthetic dictionaries. No model, cloud client or remediation endpoint exists in this module. The purpose is to make the plan and verification boundaries executable. A real planner adapter would produce the same Step objects only after parsing and validating its response, with separate request timeout and usage accounting around the model call.

Architecture and request sequence

Component diagram for Implement a plan contract and a separate verifier
Original reference architecture by Sandeep Belgavi. Arrows show the responsibilities and transitions described below.
Read the architecture as text
  • Planner callable: Returns Step objects
  • Whole-plan validation: Size, type and unique-key checks
  • Report generation: Degraded or unknown per reference
  • Independent mapping: Separate supplied evidence input
  • Narrow verdict: Value agreement only
  • Planner callable → Whole-plan validation: validate every step
  • Whole-plan validation → Report generation: read approved references
  • Report generation → Independent mapping: compare report values
  • Independent mapping → Narrow verdict: return supported or unknown
Request sequence for Implement a plan contract and a separate verifier
Illustrative sequence for this design; failure and recovery variants are explained in the article.
Read the sequence as text
  1. Caller → Executor: Supply planner callable
  2. Executor → Planner: Request Step list
  3. Planner → Executor: Return one typed step
  4. Executor → Verifier: Provide degraded fixture report
  5. Verifier → Caller: Return true without remediation

Validate the entire plan before reading

execute first validates the maximum step count, which must be an integer from one to ten. It calls the planner and requires a nonempty list or tuple no larger than that count. Every element must be a Step with nonempty key and evidence_ref, and step keys must be unique. Any violation raises ValueError before the evidence loop starts. That ordering means a malformed final step cannot leave earlier reads represented as a completed plan.

The allowed plan is deliberately simple. It has no branches, nested workers, dependencies or arbitrary Python expressions. Adding those features would require additional validation and scheduling logic; the current function should not be described as a general DAG executor. Planner exceptions propagate to the caller. A production wrapper must record them and decide whether retry is appropriate rather than treating a failed planning request as an empty successful investigation.

Follow execution and verification separately

For each accepted step, the executor reads its evidence reference. A value equal to high creates a degraded claim; every other value creates unknown. The report retains the step key and reference beside that claim. This is a deterministic interpretation rule, not a diagnosis model. It intentionally avoids inventing a normal result from missing data. A plan can execute completely yet produce only unknown claims, which the verifier will not accept.

verify receives the report and an independent_records mapping. Empty reports fail. Each item must be a dictionary whose claim is degraded and whose reference resolves to high in that second mapping. The verifier checks this narrow value agreement only. It does not validate a complete report schema, authenticate records, confirm freshness or detect every duplicated claim. Those limitations are deliberate and should remain visible when extending the example.

Persist the disagreement, not just the boolean

A proposed persistent record contains the accepted plan, executor input revision, report, verifier input revision and verification result. Store evidence locators rather than copying unlimited telemetry into each row. When verification disagrees, retain the mismatch and its retrieval times. A later operator needs to know whether execution and verification observed different windows or whether the executor made a wrong inference from the same data. A bare false value discards that useful distinction.

Do not let an executor update the verifier's evidence store as part of reporting its result. Use a separate read path or permission boundary appropriate to the actual threat model. The teaching program merely passes two mappings and offers no access isolation. For a persistent service, give records immutable revision identifiers and compare expected versions when saving a result. Reprocessing a plan under new evidence should create a new assessment rather than silently change a historical verdict.

Prepare a container experiment with honest limits

A reference image can package the two source files under a non-root Python user and run the unittest command as a build-stage check. The final command runs the synthetic demonstration and exits. Use an immutable base-image digest and a read-only root filesystem where the deployment environment supports it. This is a reference packaging plan; no image build or cloud deployment was performed as part of the local tests.

For a service adaptation, separate configuration for planning, evidence reading and verification. If planning and verification use providers, each adapter needs its own deadline and resource budget, while the parent run retains an overall ceiling. Do not grant a planning credential permission to write verifier inputs. Test the packaged artifact with contradictory evidence and confirm it reports unsupported work without trying a repair. Containers are process boundaries, not substitutes for role-specific authorization or independent evidence acquisition.

Behavioral evidence and extension checklist

The linked eight tests cover a supported result, empty and oversized plans, duplicate keys, malformed steps, missing records, conflicting evidence and empty-report rejection. The primary reference is the original source. They do not test a language model or durable store. Add freshness, schema validation, provenance and deadline tests when those capabilities exist; avoid claiming them from the current boolean verifier.