rfgen.execution_evidence

rfgen.execution_evidence is the strict, immutable record format that links a workstream’s implementation commit, acceptance evidence, scientific validation, and chapter gate. It verifies submitted evidence against the trusted plan and Git repository. It does not run the workflow that produced that evidence.

All models are frozen Pydantic v2 models with unknown fields forbidden. Serialized timestamps are canonical UTC RFC 3339 values, SHA-256 fields are lower-case 64-character hex strings, and Git commits are 40-character IDs.

Library-backed primitives

The record boundary composes established, non-RF libraries rather than reimplementing serialization or schema machinery:

  • Pydantic v2 models own typed decoding, frozen model construction, and forbidden-extra-field checks.

  • jsonschema.Draft202012Validator and FormatChecker validate each persisted JSON instance against the packaged Draft 2020-12 schema before typed decoding.

  • RFC 8785 supplies canonical JSON bytes for schema and self-digest identity; rfgen calls the rfc8785 package implementation rather than maintaining a canonicalizer.

  • PyYAML safe_load parses the trusted plan frontmatter without constructing arbitrary Python objects.

  • Git’s plumbing commands provide repository, tree, and ancestry observations used by the evidence records.

These dependency-backed checks establish typed, instance-schema, canonical serialization, safe-plan parsing, and repository-observation boundaries. The cross-record policy assembled by ExecutionEvidenceValidatorV1 is separate framework behavior; its contract appears below rather than being attributed to these libraries.

Canonical JSON means that equal JSON values have one deterministic byte representation, even if their source text differs in whitespace or object-key order. That stable representation makes a SHA-256 digest reproducible across processes and machines.

Each evidence record also carries a self-hash, its stored SHA-256 fingerprint over the record’s canonical payload fields rather than over the hash field itself. The validator recomputes the fingerprint before trusting a record; a mismatch means that its submitted contents changed after the fingerprint was created.

Module summary

from pathlib import Path

from rfgen.execution_evidence import ExecutionEvidenceValidatorV1, parse_evidence_record

record = parse_evidence_record(raw_json_object)
validator = ExecutionEvidenceValidatorV1(
    Path.cwd(),
    plan_path=Path(".agent-state/rfgen-build-plan/plan.md"),
    chapter_guide_path=Path(".agent-state/rfgen-build-plan/chapters.md"),
)
report = validator.validate_bundle((record,))
assert report.overall == "PASS"

Use CLI / evidence validate when records are JSON files. Use this module when a service has typed records and needs the same in-process semantic decision.

Schema identity and record parsing

EXECUTION_EVIDENCE_SCHEMA_VERSION is 1. EXECUTION_EVIDENCE_SCHEMA_RAW_SHA256 identifies packaged schema bytes and EXECUTION_EVIDENCE_SCHEMA_CANONICAL_SHA256 identifies its RFC 8785 canonical JSON value. These are public compatibility constants.

load_execution_evidence_schema()

def load_execution_evidence_schema() -> Mapping[str, object]

Loads the packaged Draft 2020-12 schema after verifying both public digests. It raises EvidenceSchemaError for invalid package bytes, JSON, or schema identity. Callers cannot replace this trust boundary with another schema.

parse_evidence_record(value)

def parse_evidence_record(value: Mapping[str, object]) -> EvidenceRecordV1

Parses exactly one object into the discriminated EvidenceRecordV1 union. The object must carry one of the six kind values, match the packaged schema, and use canonical UTC timestamps. It raises ValueError for an unknown kind or timestamp spelling, EvidenceSchemaError for schema-boundary failures, and Pydantic or JSON-schema errors for invalid instances.

EvidenceRecordV1

The union comprises ModuleAcceptanceGateV1, ModuleStatusV1, WorkstreamExecutionManifestV1, ChapterGateReportV1, ScientificValidationHandoffV1, and ScientificValidationResultV1. A bundle may be heterogeneous, but each member must contain its own kind.

Component and selector records

Model

Fields

Contract

ArtifactV1

ID, kind, URI, SHA-256, size, producer command

Immutable input, experiment, figure, report, gate, or regression artifact.

DependencyCommitV1

module name, commit

One direct plan dependency and accepted Git commit.

GateEvidenceV1

ID, URI, digest, size, media type, non-mock flag, boundary

Acceptance or chapter check evidence, with REAL_LIBRARY, PROCESS, FILESYSTEM, SERVICE, POLICY, HARDWARE, or TRUST_ROOT boundary.

WorkstreamRowV1

modules, repository, wave/base, branch/worktree/owner, scope, dependencies, tip/state/times/digest

One owned worktree scope. State is ACTIVE, MERGED, or ABORTED.

WaveCommitV1

wave, base, row digests, merge commit, time, digest

One sealed integration wave.

GateCheckV1

check ID, requirement, command/environment, times, exit/status/evidence/reason/digest

One chapter check. Status is PASS, FAIL, UNAVAILABLE, or SKIPPED.

SelectorGateRowV1

row ID, aggregate check, advertised/required/status/evidence IDs

Report-side selector availability projection.

GateCheckV1

One immutable chapter-gate observation. check_id identifies the gate, required states whether that gate is mandatory, and command plus environment_sha256 identify how it was evaluated. status is one of PASS, FAIL, UNAVAILABLE, or SKIPPED; evidence holds the immutable artifacts for the observation. reason_code is present when the result needs a machine-readable explanation, and check_sha256 is the canonical self-hash.

DSSE_ROUNDTRIP is a trust-root check ID recorded by this model. Its non-mock evidence demonstrates the real Sigstore DSSE verification boundary; see SigstoreDSSEAdapter.

SelectorProvenanceV1 and SelectorInventoryV1

A selector is a named capability that a chapter can make available, for example a plugin or configuration-defined backend. SelectorProvenanceV1(row_id, sources) records where that capability was discovered outside the submitted evidence bundle.

The non-empty, sorted, unique sources are entry-point, installed Python package registration metadata, and/or packaged-config, a configuration declaration shipped with a package. SelectorInventoryV1(chapter_id, selectors) is the caller-owned, sorted inventory built from those deployment inputs and supplied to the validator. Keeping it outside a chapter report prevents the report from declaring a capability for itself.

def sources_by_row_id(self) -> Mapping[str, frozenset[str]]

Returns the immutable lookup used during chapter validation. It has no I/O and does not perform selector discovery.

Scientific-validation component records

Model

Contract

RegressionRequirementV1, DeferredRequirementV1

Original finding or deferred item with its evidence digest.

LensResultV1

One of six scientific lenses, severity counts, findings, artifacts, and result digest.

ExperimentResultV1

Command, environment, input and output artifact digests, status, and result digest.

ReportAuthorResultV1, ValidationGateResultV1

Report output and render, audience, symmetry, claim-card, or banned-word gate evidence.

RegressionResultV1, DeferredArchitectureRowV1

Regression reproduction result or accepted deferral, including command/rationale and evidence.

Every listed model is immutable and has the fields named by its row. The semantic validator enforces canonical ordering, self-hashes, and non-empty artifacts where their contract requires them.

Top-level records

ModuleAcceptanceGateV1

Carries module_name, implementation commit/tree/scope digests, manifest and workstream-row digests, command/environment, evidence, status/exit code, times, and gate_sha256. A passing gate has exit code zero and non-mock evidence. It proves one module’s sealed implementation scope passed its engineering acceptance boundary.

ModuleStatusV1

Carries module/layer/chapter/evidence class, implementation commit, status, acceptance and engineering/scientific/release digests, optional frozen release report projection, predecessor link, time, and status_sha256. Status is planned, implemented, engineering-validated, optional scientifically-validated, or release-qualified. Scientific modules require a scientific result before their scientific or release-qualified status.

WorkstreamExecutionManifestV1

Carries repository/chapter identity, plan and chapter-guide digests, immutable base, integration branch/worktree/owner, concurrency, workstream rows, wave commits, active writers, timestamps, and manifest_sha256. Every acceptance and chapter gate binds to this immutable plan-and-Git projection.

ChapterGateReportV1

Carries chapter, plan/guide/manifest/integration digests, module status digests, checks, selector rows, times, outcome, failure IDs, and report_sha256. A passing report needs the exact required check set, validated module statuses and scientific results, plus a separately supplied inventory.

ScientificValidationHandoffV1 and ScientificValidationResultV1

The handoff binds planned module/component, implementation, plan/guide, acceptance artifacts and gate, regressions, deferrals, freeze time, and digest. The result binds the handoff to all lens results, experiments, report author, validation gates, regressions, deferrals, times, outcome, and digest. A result starts after the handoff and must match the exact planned scientific component.

Validation result models

EvidenceRecordKind is the stable integer ordering of the six kinds. EvidenceRecordDigestV1(kind, self_sha256) is a normalized record identity. EvidenceValidationFailureCode is INVOCATION, SCHEMA_BUNDLE, INSTANCE_SCHEMA, SEMANTIC, GIT, or INTERNAL; its corresponding failure model contains record index, code, path, and redacted detail ID.

ValidatedEvidenceV1 returns record kind, self digest, schema digest, and validation time. ExecutionEvidenceValidationReportV1 returns ordered record digests, bundle digest, seen/passed counts, failures, timestamps, outcome, and report digest after a bundle call.

ExecutionEvidenceValidatorV1

class ExecutionEvidenceValidatorV1:
    def __init__(
        self,
        repository: Path,
        *,
        plan_path: Path,
        chapter_guide_path: Path,
        selector_inventories: Sequence[SelectorInventoryV1] = (),
    ) -> None: ...

Custom logic: verify as follows

The cross-record linker is intentionally custom framework policy. No general-purpose library can decide whether this repository’s frozen plan, sealed integration waves, declared selector inventory, module-status history, and scientific handoff/result records satisfy rfgen’s release rules. The implementation is limited to binding those project-owned records and Git observations after the library-backed parsing and schema checks above; it does not replace a serializer, JSON-schema validator, canonicalizer, YAML parser, or Git implementation.

To verify a persisted bundle in the same boundary used by automation, run rfgen evidence validate --bundle evidence.json --selector-inventory selectors.json --repository checkout --json. A PASS report means the exact bundle passed its schema, digest, plan, Git, selector, status, and scientific link checks; exit 5 or a FAIL report means it did not. The full invocation and exit-code contract is in CLI / evidence validate. The repository acceptance coverage is tests/unit/test_layer11_public_evidence.py, which exercises both valid bundles and tampered bindings.

repository is the checkout for Git ancestry and scope checks. plan_path and chapter_guide_path are trusted planning artifacts. At most one explicit, external selector inventory is allowed per chapter. Construction verifies the packaged schema and safely parses plan frontmatter; it raises ValidationError for duplicate inventory or bad trusted plan metadata and EvidenceSchemaError for a schema identity failure.

Semantic methods

Method

Inputs and return

Validation boundary and failure

validate_acceptance_gate(gate, manifest)

Typed gate and manifest to ValidatedEvidenceV1

Sealed-wave ancestry, scoped tree, status/exit relationship, and primary non-mock evidence. Raises ValidationError on any broken binding.

validate_workstream_manifest(manifest)

Typed manifest to ValidatedEvidenceV1

Self-hash, repository/plan/guide, wave partition, ownership scope, direct dependencies, ancestry, and chronology. Raises ValidationError. Bundle-local caching never survives a call.

validate_status_history(rows, *, acceptance_gates, scientific_results, release_report)

One module’s status tuple to ValidatedEvidenceV1

Contiguous predecessor chain, gate/result/release binding. Empty, reordered, or mismatched histories raise ValidationError.

validate_chapter_gate(report, *, manifest, module_statuses, scientific_results, selector_inventory)

Typed chapter inputs to ValidatedEvidenceV1

Exact check order/evidence, status/science projection, and external inventory. A report that invents selectors raises ValidationError.

validate_scientific_pair(handoff, result, *, manifest)

Typed scientific pair to ValidatedEvidenceV1

Planned component mapping, acceptance gate, timing, six lenses, experiments, report gates, regressions, and deferrals. Raises ValidationError.

validate_bundle

def validate_bundle(
    self, records: tuple[EvidenceRecordV1, ...]
) -> ExecutionEvidenceValidationReportV1

This public API deliberately accepts exactly one argument. It validates self-hashes first and then all manifest, acceptance, science, chapter, and status links. Ordinary semantic failures are returned in the report rather than raised. It raises ValidationError only for unsupported nested bundle invocation. Chapter reports require the constructor-supplied external selector inventory; the validator never discovers selectors from evidence records.

Bundle-validation lifecycle

Callers parse persisted objects with parse_evidence_record(...) before this method, then pass the resulting tuple once. A correct implementation follows this ordered checklist:

  1. Reject a nested call, initialize an empty manifest-validation cache scoped to this call, and clear that cache in finally, including when validation fails. Git observations must not survive into a later bundle.

  2. Start the report clock, assign each input record its original tuple index, and validate every record’s self-digest. Append a SELF_HASH semantic failure at the original index for a bad digest, but continue so the report can collect independent failures.

  3. Partition the typed tuple into acceptance gates, module statuses, manifests, scientific handoffs, scientific results, and chapter reports. Build the manifest lookup by manifest_sha256 and preserve the original record-index lookup for failures.

  4. Validate each manifest against the frozen plan, chapter guide, repository, wave, scope, dependency, ancestry, and chronology rules. Convert a failed manifest binding into its record’s GIT/MANIFEST failure, then validate each gate against the manifest selected by its manifest digest. A missing selected manifest is a GATE_MANIFEST semantic failure.

  5. For every scientific result, find the handoff by handoff_sha256, derive its manifest from the handoff’s acceptance gate, and validate the pair. Missing handoff or manifest and a broken pair both yield the result record’s SCIENTIFIC_PAIR failure.

  6. For every chapter report, select its manifest and only those status rows whose chapter and digest appear in the report. Obtain the matching constructor-supplied SelectorInventoryV1; never derive it from the report. Validate the report with those statuses and all scientific results. A missing manifest, inventory, or invalid binding yields CHAPTER_GATE.

  7. Group statuses by module, sort each group by recorded_at, and validate its history against all gates, scientific results, and the release-report projection on the release-qualified row. Track every engineering gate consumed by a valid history. Then add global failures for duplicate gates, an orphan gate, or duplicate normalized record identities.

  8. Normalize identities in (kind, self_sha256) order, digest that ordered list into the bundle digest, sort failures by index, code, and path, and build the immutable report. records_passed is the input count minus the number of distinct failing indexes; overall is PASS only when the failure list is empty. Digest the completed report last.

See Also

  • CLI: JSON invocation, inventory input, and exit codes.

  • Errors: framework error hierarchy.

  • Build and CI: checks that produce evidence.