Architecture
Data Flow
Section titled “Data Flow”The same flow as text (terminal-friendly fallback for screen readers and CLI viewers):
Source repo workflow → Builds structured submission (JSON) → Emits via repository_dispatch to testing-os
testing-os ingestion pipeline → Schema validation (AJV) → Provenance check (GitHub API) → Policy evaluation (enforcement, scenarios, freshness) → Verdict computation (source proposes; verifier confirms or downgrades)
→ Accepted: records/<org>/<repo>/YYYY/MM/DD/<run-id>.json → Rejected: records/_rejected/<org>/<repo>/YYYY/MM/DD/...
→ Index rebuild: indexes/latest-by-repo.json indexes/failing.json indexes/stale.jsonKey Design Decisions
Section titled “Key Design Decisions”Central Ingestion
Section titled “Central Ingestion”Source repos never write records directly. They emit structured payloads via repository_dispatch, and only the testing-os bot writes to the records directory. This prevents source repos from fabricating evidence.
Verdict Ownership
Section titled “Verdict Ownership”The source repo proposes a verdict (overall_verdict in the submission). The verifier can confirm or downgrade — never upgrade. A source claiming “pass” that fails schema or policy validation becomes “fail.”
Sharded Persistence
Section titled “Sharded Persistence”Records are stored at records/<org>/<repo>/YYYY/MM/DD/<run-id>.json. This provides natural time-sharding, easy browsing, and clean git history without merge conflicts.
Generated Indexes
Section titled “Generated Indexes”latest-by-repo.json is rebuilt from accepted records after every ingestion. Consumers read indexes, not the raw record tree. This keeps reads fast without scanning git history.
Components
Section titled “Components”| Component | Location | Purpose |
|---|---|---|
| Verifier | packages/verify/ | Schema, provenance, policy, verdict validation |
| Ingestion | packages/ingest/ | Pipeline orchestration, atomic persistence, index rebuild |
| Submission builder | packages/report/ | Canonical submission assembly for source repos |
| Portfolio | packages/portfolio/ | Org-level summary generation |
Verifier Pipeline (7 steps)
Section titled “Verifier Pipeline (7 steps)”The verifier (packages/verify/index.js) processes each submission through seven stages in order:
- Schema validation — validates the submission against
dogfood-record-submission.schema.jsonusing AJV. - Verifier-owned field guard — rejects submissions that include fields only the verifier may set (
policy_version,verification, oroverall_verdictas an object). - Provenance check — confirms the source workflow run actually exists via the GitHub Actions API (or a stub adapter in tests).
- Step results validation — checks that each scenario’s required steps have matching results and that verdicts are internally consistent.
- Policy evaluation — evaluates enforcement tier, required scenarios, freshness, and execution-mode constraints from the repo or global policy.
- Verdict computation — computes the final verdict. The source proposes a verdict string; the verifier may confirm or downgrade, never upgrade. Verdict severity from highest to lowest: fail, blocked, partial, pass.
- Record assembly — builds the persisted record with verifier-owned fields (
verification.status,verification.verified_at,overall_verdict.verified,overall_verdict.downgraded).
Generated Indexes
Section titled “Generated Indexes”The index generator (packages/ingest/rebuild-indexes.js) produces three files after every ingestion:
| Index | Content |
|---|---|
indexes/latest-by-repo.json | Latest accepted record per repo and surface — the primary read model for consumers |
indexes/failing.json | Records where the verified verdict is not pass |
indexes/stale.json | Repo/surface pairs with no accepted record within the staleness threshold (default 30 days) |
Atomic Persistence
Section titled “Atomic Persistence”Records are written atomically: the persist layer writes to a temporary file, then renames it to the final path. Duplicate detection by run_id prevents double-writes (collisions surface as DUPLICATE_RUN_ID — see Error Code Reference). Accepted records go to records/<org>/<repo>/YYYY/MM/DD/, rejected records to records/_rejected/<org>/<repo>/YYYY/MM/DD/.
Rebuild Outcomes
Section titled “Rebuild Outcomes”packages/ingest/rebuild-indexes.js returns four arrays per call: accepted, rejected, corrupted, skipped.
accepted/rejected— record loaded cleanly; routed byverification.status.corrupted—JSON.parsefailed on the file. The rebuild logs[rebuild-indexes] corrupted record skipped: <path> — <error>to stderr and continues; the record is excluded from all indexes.skipped— record loaded but missingrun_id.
Corruption does not fail the rebuild — the index is silently incomplete until repaired. See Operating Guide → Corrupted Record Recovery for the procedure.
Enforcement Tiers
Section titled “Enforcement Tiers”| Mode | Behavior | Default |
|---|---|---|
required | Blocks on violation | Yes — all repos start here |
warn-only | Warns but doesn’t block | Must have documented reason + review date |
exempt | Skips evaluation entirely | Must have documented reason + review date |
Missing policy defaults to required — the safe default.