Skip to main content

Trace & Validation

Two deterministic, offline checks keep your specs honest:

  • trace(product, technical, diff=None) — does every risky product requirement have a technical control behind it? Did anything drift? Does a code change touch (or violate) a requirement?
  • validate(spec) — structural best-practice lint on a single spec.

Both are pure functions (no LLM), so they are free and CI-safe.

trace() — keep the two specs in sync

from mycontext.rac import product, technical, trace, format_report

prod = product(INTENT)
tech = technical(product=prod)

report = trace(prod, tech)
print(format_report(report))

Signature

def trace(product: dict, technical: dict, diff: str | None = None) -> dict
ParameterTypeMeaning
productdictA product-requirements spec.
technicaldictA technical-requirements spec.
diffstr | NoneOptional unified diff (e.g. git diff) to analyze change impact.

What it answers

trace() answers three questions:

  1. Coverage — every risk-bearing product requirement (tasks, safety, approval/forbidden actions, gates) should have at least one technical control whose serves: references it.
  2. Orphans — does any technical serves: reference an ID that doesn't exist in the product spec (a typo or a stale link)?
  3. Diff impact — if a diff is supplied, which requirement IDs does the change touch, and does it violate anything (e.g. introduce a forbidden tool)?

The report shape

{
"status": "in_sync" | "review" | "drift_detected",
"coverage": {
"covered": ["T1", "P1", "P4", "A-3", "A-X", "G-1", ...],
"uncovered": [{"id": "P2", "kind": "safety", "label": "..."}],
"ratio": 1.0,
},
"orphans": [{"served_id": "T9", "location": "guardrails.output"}],
"diff_impact": [{"file": "handlers/reply.py", "touches": ["A-3"], "forbidden_used": []}],
"findings": ["[OK] Product and technical requirements are in sync."],
}
statusMeaningExit code (CLI)
in_syncFull coverage, no findings.0
reviewWarnings only (e.g. orphan references).0
drift_detectedAt least one [ERROR] — an uncovered requirement or a forbidden-tool violation.1

Which product requirements require coverage:

RequirementRequires coverage?
Tasks (T*)Yes
Safety (P*)Yes
Gates (G*)Yes
Actions with policy: approve or forbiddenYes
Actions with policy: autoNo
Rubrics (R-*)No (referenced, but not coverage-required)

Catching drift in a code change

Pass a diff to see which requirements a change touches — and to fail when it introduces a forbidden action:

diff = """diff --git a/handlers/reply.py b/handlers/reply.py
--- a/handlers/reply.py
+++ b/handlers/reply.py
@@ -1 +1,2 @@
+ delete_record(order_id)
"""

report = trace(prod, tech, diff=diff)
print(report["status"]) # 'drift_detected'
# findings include: "[ERROR] handlers/reply.py: introduces forbidden tool/action 'delete_record' ..."

trace() keyword-matches added/removed lines against the product spec's tool names and safety vocabulary. A forbidden tool appearing in the added lines is an error; touching a known requirement is reported as impact.

format_report() — readable markdown

from mycontext.rac import format_report

print(format_report(report))
# Requirements trace — IN_SYNC

Coverage: 12/12 risk-bearing requirements served (100%).

## Findings

- [OK] Product and technical requirements are in sync.

On the command line

mycontext rac trace --product product.yaml --technical technical.yaml
mycontext rac trace --product product.yaml --technical technical.yaml --diff change.diff
mycontext rac trace --product product.yaml --technical technical.yaml --out trace-report.md

The command exits 1 when status == "drift_detected", so it works directly as a CI gate.

validate() — lint a single spec

from mycontext.rac import product, validate

issues = validate(product(INTENT)) # list of "[ERROR] ..." / "[WARN] ..."

validate() dispatches on meta.spec_type, so it lints both product and technical specs through one entry point. Empty list = clean.

Validation rules

Product specs

RuleSeverity
At least one task is definedERROR
Every task references a rubric that existsERROR
There is an out-of-scope / refusal (no_draft) taskWARN
Irreversible / money actions are never policy: autoERROR
Every safety entry is a hard_gateERROR
Every safety entry has a machine-checkable assertWARN
Safety dataset slices have no dev split (contamination)ERROR
There is an absolute safety release gateWARN
Blocking open questions are unresolvedERROR
Non-blocking open questions remainWARN
meta.status is ready while TODO(OQ-n) markers remainERROR

Technical specs

RuleSeverity
Required sections present (architecture, guardrails, tools, cost, security)ERROR
Each guardrail layer (input/processing/output) has a controlWARN
Forbidden tools are declaredWARN
Blocking / non-blocking open questionsERROR / WARN

On the command line

mycontext rac validate product.yaml
# OK — no issues found. (exit 0)
# or prints issues; exit 1 if any [ERROR]

A complete CI gate

# Fail the build if the spec is unsound or the implementation drifted.
mycontext rac validate product.yaml || exit 1
mycontext rac validate technical.yaml || exit 1
mycontext rac trace --product product.yaml --technical technical.yaml || exit 1

Next