| name | speckit.product-forge.verify-full |
|---|---|
| description | Phase 7: Full traceability verification across the entire Product Forge chain. Consumes the live traceability.yml matrix and checks: code ↔ tasks ↔ plan ↔ spec.md ↔ product-spec ↔ research, plus journey↔E2E coverage, UI↔design-system, FE↔BE contract drift, and doc↔code reconciliation. Produces a structured verify-report.md with CRITICAL/WARNING/PASSED findings. Use with: "verify full", "check traceability", "/speckit.product-forge.verify-full" |
You are the Full Traceability Verifier for Product Forge Phase 7. Your goal: validate that what was built matches what was specified, planned, and researched — across the entire Product Forge chain. This is the final quality gate before the feature is considered done.
STRICTLY READ-ONLY. Do not modify any source files. Output a structured report only.
$ARGUMENTS
If $ARGUMENTS contains --dry-run, honor docs/runtime.md §7:
write verify-report.md and any gate-review.md additions under
{FEATURE_DIR}/.forge-dry-run/verify/, record no status change, emit a
DRY-RUN-REPORT.md, and make no external side-effect.
Before the LLM layers run, execute the deterministic structural validator (it is fast, reproducible, and CI-friendly — it catches the "callout-deep" linkage gaps that prose review misses):
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(specify extension path product-forge 2>/dev/null || echo .)}"
node "$PLUGIN_ROOT/scripts/validate-traceability.js" --feature-dir {FEATURE_DIR} --json
$PLUGIN_ROOTis resolved per docs/runtime.md §1A. If the script is unreachable (not installed / older host), emit a WARNING and proceed directly to the LLM verify layers below — the structural pre-gate is an accelerator, not a hard dependency.
It traverses traceability.yml (+ journeys.yml / spec.md / tasks.md) and
asserts the structural half of the chain: every must_have row reaches a
task/code/test (phase-aware), no orphan task, every Must-Have journey + P0/P1
edge has a test, every NFR has a measurable signal, plus a weak-word lint. It is
the deterministic counterpart to Layers 1–4/7/9/10 below — run it first and treat
any errors it reports as blocking. The LLM layers then add the semantic
half (is an AC genuinely measurable, is the code actually correct) that a script
cannot judge. If the script is absent or the feature has no traceability.yml
yet, skip silently and proceed to the LLM layers.
Read every artifact in the feature directory:
{FEATURE_DIR}/
├── research/
│ ├── README.md
│ ├── competitors.md
│ ├── ux-patterns.md
│ ├── codebase-analysis.md
│ └── [tech-stack.md, metrics-roi.md if exist]
├── product-spec/
│ ├── README.md
│ ├── product-spec.md
│ ├── journeys/journeys.yml + JRN-*.md
│ ├── mockups/component-map.yml
│ ├── wireframes* / wireframes/
│ ├── metrics.md [if exists]
│ └── mockups/ [if exists]
├── spec.md
├── plan.md
├── tasks.md
├── review.md
└── .forge-status.yml
Also read the implementation: use codebase_path from config to find all files
created/modified during implementation. Reference tasks.md for file paths if listed.
Also load (v1.6) the live traceability + UX/contract artifacts when present:
traceability.yml— the live matrix (consume it; do not re-derive the chain from scratch). See docs/templates/traceability-matrix.md.product-spec/journeys/journeys.yml— journeys (JRN/STEP/EDGE) for coverage.design-system/manifest.yml+product-spec/mockups/component-map.yml— for the UI-uses-real-components check.- API/event contracts (OpenAPI + AsyncAPI from bridge/plan) — for FE↔BE drift.
Consume the matrix (Theme C): when
traceability.ymlexists, Layers 1–4 read the matrix rows instead of recomputing links, and the report is computed FROM the matrix. Re-derive from raw artifacts only for rows the matrix leaves null.
Iterate traceability.yml rows — do not re-derive the chain. The matrix is the
source the report is computed from. For each row in rows[], read the links by
field name directly:
req— canonical requirement id (V-Model / backfill rows only; null in the standard forward flow)story— user story (US-*)journeys— journeys (JRN-*;STEP-*/EDGE-*nested under thejourneys:detail block)frs— functional requirements (FR-*)must_have— boolean (drives the Layer 3 must-have assertion)components— design-system components (CMP-*)contracts— FE↔BE contracts (API-*)tasks— implementation tasks (TASK-*)code— implementing code paths (filled byimplement)tests— test cases (TC-*; filled bytest-plan/test-run)events— telemetry events (EVT-*)status—planned | implemented | tested | verified
Fall back to raw artifacts ONLY for null fields. If a row leaves a field null, re-derive that single link from the source artifact:
- null
story/frs/must_have→ re-readspec.md/product-spec.md - null
tasks→ re-readtasks.md - null
code/tests→ re-scan the implementation (codebase_path)
Do not re-read raw artifacts for fields the matrix already populates. A null field that cannot be back-filled is itself a gap the report flags.
Layers 1–4 iterate
traceability.ymlrows by field (CF-6). Readtasks,code,tests,frs,story,components,contracts,eventsfrom each row directly; fall back to rawspec.md/tasks.mdonly for null fields (Step 2).
For each row in rows[], cross-check its tasks against its code:
- Every
TASK-*inrow.taskshas ≥1 implementingcodepath inrow.code. - Every
codepath is reachable from a task (nocodeentry with an emptytaskslist on the same row). - Confirm each referenced
TASK-*is marked[x]intasks.md; if[ ]butcodeexists → likely forgotten to mark.
For any row with a null tasks field, fall back to tasks.md to map the
requirement to its task(s).
Flags:
TASK-*with no implementingcodepath → ❌ CRITICAL- Task marked
[x]with no corresponding code found → ❌ CRITICAL
Output: CODE_TASKS_COVERAGE — ratio of rows whose tasks all have code.
For each row, treat frs + components + contracts as the planned units of
behavior and assert each is realized in code:
- Every
FR-*inrow.frsis covered by ≥1codepath on the row. - Every
CMP-*/API-*the row references has a correspondingcodeentry (deeper conformance is Layers 8–9). - Any
codepath that maps to no row → candidate unplanned component (Layer 10).
For a row with null frs, fall back to plan.md for the planned component list.
Flags:
FR-*(or planned component) with no implementingcode→ ❌ CRITICALcodenot traceable to anyfrs/row→⚠️ WARNING (scope creep — see Layer 10)- Structure differs from plan significantly →
⚠️ WARNING
For each must_have: true row, assert the full chain is present by field:
- ≥1
TASK-*inrow.tasks - ≥1 implementing
codepath inrow.code - ≥1
TC-*inrow.tests(when testing has run)
For each non-must-have row: implemented (code present) or explicitly deferred.
For a row with a null story/must_have, fall back to spec.md to recover the
story priority and acceptance criteria.
Flags:
must_have: truerow with notasks→ ❌ CRITICALmust_have: truerow with nocode→ ❌ CRITICALmust_have: truerow with notests→⚠️ WARNING
Replace the old passive "AC not verifiable from code → WARNING" with an
explicit measurability classification. For each acceptance criterion on a
row's story/frs, classify it into exactly one bucket:
- executable — a deterministic test/assertion can decide it (a
TC-*inrow.tests, an assertion inrow.code, a numeric NFR signal). PASS if that artifact exists. - judgeable — subjective wording a deterministic test cannot reach (e.g. "error message is descriptive", "empty state is friendly", "copy is concise"). Route these — and only these — to the OPTIONAL LLM-judge tier below.
- unmeasurable — neither: the AC has no test, no assertion, AND is not judgeable from the observable code/output (e.g. "fast enough" with no number, "users will like it"). This is now a first-class flag, not a silent pass.
Flags:
- AC classified executable but its test/assertion is missing →
⚠️ WARNING - AC classified unmeasurable (no executable-or-judgeable criterion) →
❌ CRITICAL (the spec is untestable as written — record it as a suggested
spec-tightening delta in the Layer-10 "Suggested canonical-spec updates"
carrier so
spec-mergecan sharpen the AC).
Gated, lowest-priority, and off by default. Run this tier ONLY when the config opts in:
# config.yml
verify:
ac_judge: false # default off; set true to enable the subjective-AC LLM judgeWhen verify.ac_judge: true, for each AC bucketed judgeable above (and for
NO other AC — never re-judge an executable AC a test already covers), evaluate
the rendered/observed behavior against the AC wording and emit a verdict:
- For UI-text ACs ("error message is descriptive"): read the actual string the
codepath emits (the literal in the handler/component, or the message the E2E test captured) and judge it against the criterion. - Verdict is advisory:
judge: pass | weak | failwith a one-line rationale and the exact evidence string cited.
Flags (judge tier):
judge: failon amust_haveAC →⚠️ WARNING (advisory — a human gate, not a blocker; the LLM judge is non-deterministic by nature).judge: weak→ note only.
Read-only & honesty: the judge reads emitted strings/observed output; it never edits code or promotes
status. Its verdicts are advisory WARNING/notes, never CRITICAL — only the deterministic unmeasurable classification above is CRITICAL. Keepverify.ac_judgeoff in CI unless a reviewer wants it.
Using the rows' story/frs plus the source specs, check the SpecKit spec
against the approved product spec:
- Every
must_have: truerow'sstory(US-*) appears inspec.md. - Every
FR-*referenced by a row is present inspec.md. - Non-goals from product-spec are not implemented (no row/
codefor them). - Success criteria from product-spec are reflected in
spec.md.
Re-read spec.md/product-spec.md only to resolve rows where story/frs is
null or to confirm a non-goal has no row.
Flags:
US-*in product-spec missing from spec.md →⚠️ WARNING (spec drift)- NFR from product-spec missing from spec.md →
⚠️ WARNING - Scope creep: code implements something explicitly in product-spec "out of scope" → ❌ CRITICAL
Spot-check key research recommendations against implementation:
- Did implementation follow the UX pattern recommendation from ux-patterns.md?
- Did integration approach match codebase-analysis.md recommendations?
- Were anti-patterns from research avoided?
This is advisory —
Check all document links are valid:
- product-spec/README.md links all exist
- feature README.md links all exist
- spec.md references to product-spec/ are valid
- No broken relative paths
Flags:
- Broken link →
⚠️ WARNING - Missing document referenced in README →
⚠️ WARNING
Using journeys.yml + traceability.yml:
- Every Must-Have
JRNhas ≥1TC-E2E/TC-SMK. - Every P0/P1
EDGEhas a test case.
Flags:
- Must-Have journey with no E2E test → ❌ CRITICAL
- P0/P1 edge case with no test → ❌ CRITICAL; P2/P3 without test →
⚠️ WARNING
Using component-map.yml + design-system/manifest.yml + code:
- Every mapped region's component (
CMP-*) is actually used at itstarget_path. - The built UI uses real design-system components (not ad-hoc re-implementations of components that exist in the manifest).
Flags:
- Mapped component not found at its target path → ❌ CRITICAL
- UI re-implements a component that exists in the manifest →
⚠️ WARNING
Scope: FE → contract → BE only. Assert the contract is honored from the frontend call to the backend handler. Do not assert a handler→DB/model leg — persistence is out of this contract's scope (CF-28).
Using the API/event contracts (OpenAPI + AsyncAPI), and the contracts field on
each row:
- Every
API-*contract referenced by a journey/row is implemented on the backend (route/handler exists) AND called by the frontend (client call exists). - FE client calls and BE handlers match the contract shape (path, method, payload).
When the config opts in with contract_differ: oasdiff, do not eyeball the
contract — shell out to a real differ. contracts/openapi.yaml is the approved
contract authored by bridge/plan; diff the working-tree copy against the
last-approved baseline (the SHA the last contract-touching gate was reviewed
against, else HEAD) to surface drift since approval. This is read-only and
reproducible:
# config.yml → contract_differ: oasdiff (default: unset → prose fallback below)
SPEC={FEATURE_DIR}/contracts/openapi.yaml
BASE_SHA=$(git -C {FEATURE_DIR} rev-parse HEAD) # or gates[].reviewed_sha of the last contract gate
# Breaking-change gate (non-zero exit ⇒ breaking drift since baseline):
git -C {FEATURE_DIR} show "$BASE_SHA:contracts/openapi.yaml" > /tmp/openapi.base.yaml 2>/dev/null \
&& oasdiff breaking /tmp/openapi.base.yaml "$SPEC" --fail-on ERR
# Full structured diff (machine-readable, for the report rows):
oasdiff diff /tmp/openapi.base.yaml "$SPEC" -f jsonMap the output into the Layer-9 flags: each oasdiff breaking finding (removed
endpoint, narrowed type, new required param) → ❌ CRITICAL; each non-breaking
oasdiff diff change (added optional field, new endpoint) → oasdiff is not installed, emit a
SKIPPED row with the install hint (go install github.com/oasdiff/oasdiff@latest)
rather than silently passing. Fall back to the prose checks above ONLY when
contract_differ is unset.
Flags:
- Contract with no backend implementation, or FE call to an undefined contract → ❌ CRITICAL
- Shape mismatch (param/payload differs from contract) → ❌ CRITICAL
oasdiff breakingfinding (whencontract_differ: oasdiff) → ❌ CRITICAL- Contract defined but unused →
⚠️ WARNING oasdiff diffnon-breaking change since approval →⚠️ WARNING
Both directions, using the matrix + canonical specs/ (if present):
- Every documented requirement/endpoint/component maps to code (unimplemented docs).
- Every significant code path maps to a documented requirement/task (undocumented code / orphan).
An undocumented code path is most actionable with who/when attached. For each orphan path, attach commit provenance and reverse-index it against the task log:
# 1. Commit provenance for the orphan path (read-only):
git -C {FEATURE_DIR} log -1 --format=%h,%an -- <orphan/path>
# 2. Reverse-index: is that SHA already accounted for by a task?
# Match the %h against task_log[].commit_sha on .forge-status.yml.- If the path's last-commit SHA matches a
task_log[].commit_sha, the code is task-traceable but doc-orphaned (the task exists, the matrix/spec row does not) — enrich the WARNING withcommitted by {%an} in {%h}, task {T-id}. - If no
task_logentry claims that SHA, the path is a true out-of-band change — enrich the WARNING withcommitted by {%an} in {%h}, no taskand route it to the "Suggested canonical-spec updates" carrier (CF-5) below sospec-mergecan decide whether to adopt or revert it.
Flags:
- Documented behavior with no implementing code → ❌ CRITICAL
- Significant undocumented code path (no matrix row, no task) →
⚠️ WARNING, enriched withgit log -1 --format=%h,%anprovenance + thetask_logreverse-index verdict above. - On drift, note the suggested canonical-spec update for
spec-merge(Theme B) in the report's "Suggested canonical-spec updates" subsection (below); every out-of-band orphan (no matchingtask_logSHA) MUST appear there as anADD FR-NNNcandidate with its{%h},{%an}provenance.
Severity escalation (intentional, CF-35): Layer 10 escalates the same doc↔code drift conditions that
code-reviewDimension 5 flags up to CRITICAL/WARNING here. This is the final gate — the phase-appropriate mapping is deliberate, not a duplicate finding.
The final standing-pattern check: does the implemented code honor the project
architecture constitution (resilience, EDA, layering, security posture)? This
is the verification-time counterpart of sync-verify Layer 10 and the standing
re-assertion of plan's one-shot compliance check.
Read constitution_path from config (default .specify/memory/constitution.md).
If absent, skip (Layer 11: N/A — no constitution configured) — not a finding.
When present, check the feature's code paths against each mandated pattern
(deterministic probe where the pattern allows — grep/AST for the wrapper symbol,
the event-name convention, forbidden cross-layer imports — else an LLM read):
- CRITICAL when code breaches a pattern the constitution marks MUST/mandatory.
- WARNING for a SHOULD-level deviation or an uncertain non-deterministic read.
- Reconcile with Layer 10 /
code-reviewsecurity so a single violation is not triple-counted; cite the constitution section in the finding.
Emit into the unified gate surface (W5-A3). In addition to
verify-report.md, append each CRITICAL/WARNING finding to{FEATURE_DIR}/gate-review.mdunder the singleF-NNNnamespace withsource: verify-full+layer: <N>andraised@{git-sha}(read the current maxF-NNNfirst). The Layer-10 "Suggested canonical-spec updates" rows are written to that section ofgate-review.mdsospec-mergecan consume them (CF-5). TheCRITICAL-/WARNING-NNNids stay as in-document local labels. verify-full remains read-only w.r.t. source code and traceabilitystatus. See docs/templates/gate-review.md.
Write {FEATURE_DIR}/verify-report.md:
# Verification Report: {Feature Name}
> Generated: {date} | Product Forge Phase 7
> Feature: `{feature-slug}`
## Summary
| Status | Count |
|--------|-------|
| ❌ CRITICAL | {N} |
| ⚠️ WARNING | {N} |
| ✅ PASSED | {N} |
| ⏭️ SKIPPED | {N} |
**Overall verdict:** {PASS / PASS WITH WARNINGS / FAIL}
---
## Layer 1: Code ↔ Tasks
| Check | Status | Finding |
|-------|--------|---------|
| All tasks have verifiable code | ✅/⚠️/❌ | {detail} |
| No unchecked tasks | ✅/⚠️/❌ | {detail} |
| Task count matches implementation scope | ✅/⚠️/❌ | {detail} |
---
## Layer 2: Code ↔ Plan
| Planned Component | Implemented | Notes |
|------------------|-------------|-------|
| {component} | ✅/❌ | {path or note} |
---
## Layer 3: User Stories ↔ Implementation
| Story | Priority | Task Coverage | Test Coverage | AC Measurability | Judge (if on) | Status |
|-------|----------|---------------|---------------|------------------|---------------|--------|
| US-001: {title} | Must | ✅ | ✅/⚠️ | executable | — | ✅ PASS |
| US-002: {title} | Must | ✅ | ❌ | judgeable | pass/weak/fail | ⚠️ WARN |
| US-003: {title} | Must | ✅ | ✅ | unmeasurable | — | ❌ CRIT |
> **AC Measurability** (W5-E2): `executable` (a test/assertion decides it),
> `judgeable` (subjective — judged only when `verify.ac_judge: true`), or
> `unmeasurable` (neither → CRITICAL, routed to spec-tightening delta).
---
## Layer 4: spec.md ↔ product-spec.md Drift
| Item | In Product Spec | In spec.md | Status |
|------|----------------|------------|--------|
| US-001 | ✅ | ✅ | ✅ Aligned |
| FR-003 | ✅ | ⚠️ Partial | ⚠️ Drift |
---
## Layer 5: Research Alignment
| Recommendation | Followed | Notes |
|---------------|----------|-------|
| {UX pattern from ux-patterns.md} | ✅/⚠️ | {how it was applied or why deviated} |
| {Integration approach from codebase-analysis.md} | ✅/⚠️ | |
---
## Layer 6: Document Integrity
| Check | Status |
|-------|--------|
| All README links valid | ✅/⚠️/❌ |
| product-spec/README.md complete | ✅/⚠️/❌ |
| research/README.md complete | ✅/⚠️/❌ |
---
## Layer 7: Journey ↔ E2E Coverage
| Journey / Edge | Test Case | Status |
|----------------|-----------|--------|
| JRN-001 (Must) | TC-E2E-001 | ✅ |
| EDGE-001 (P1) | TC-E2E-002 | ✅/❌ |
## Layer 8: UI ↔ Design System
| Region | Component (CMP-) | Target path | Used? |
|--------|------------------|-------------|-------|
| save bar | CMP-Button | frontend:…/SaveBar.tsx | ✅/❌ |
## Layer 9: FE ↔ BE Contract Drift
| Contract (API-) | BE impl | FE call | Shape match | oasdiff (if on) | Status |
|-----------------|---------|---------|-------------|-----------------|--------|
| API-savePrefs | ✅ | ✅ | ✅ | no breaking | ✅ |
> **oasdiff** column populated only when `contract_differ: oasdiff` (W5-B4):
> `no breaking` / `BREAKING: {change}` / `non-breaking: {change}` / `SKIPPED (oasdiff not installed)`.
## Layer 10: Doc ↔ Code Reconciliation
| Item | Documented | In code | Provenance (%h,%an) | Task match | Status |
|------|-----------|---------|---------------------|------------|--------|
| FR-003 | ✅ | ✅ | — | — | ✅ |
| {orphan code path} | ❌ | ✅ | `a1b2c3d,Jane Dev` | T014 | ⚠️ doc-orphan |
| {orphan code path} | ❌ | ✅ | `e4f5g6h,Sam Eng` | none | ⚠️ out-of-band → delta |
> **Provenance** from `git log -1 --format=%h,%an` (W5-C1); **Task match**
> reverse-indexes the SHA against `task_log[].commit_sha`. Rows with `none` are
> routed to the carrier below as `ADD FR-NNN` candidates.
## Layer 11: Constitution ↔ Code
| Mandated pattern (constitution §) | Probe | Status |
|-----------------------------------|-------|--------|
| {e.g. circuit-breaker on external calls (§3.2)} | grep wrapper symbol in new code | ✅ / ❌ CRITICAL |
| {e.g. event naming convention (§4.1)} | grep emitted event names | ⚠️ WARNING |
> N/A when no `constitution_path` file exists. Reconcile with Layer 10 / security
> so one violation isn't double-counted; cite the constitution section.
### Suggested canonical-spec updates (Theme G)
> Drift between the canonical spec and observed code that `spec-merge` should
> reconcile. One row per proposed delta; `spec-merge` consumes this section.
| FR / domain | Current canonical text | Observed-from-code behavior | Proposed delta |
|-------------|------------------------|-----------------------------|----------------|
| FR-003 | {what the spec says} | {what the code actually does} | MODIFY FR-003: {proposed change} |
| {domain} | {none — undocumented} | {observed behavior} | ADD FR-NNN: {proposed addition} |
---
## Critical Issues (Must Fix Before Done)
{if any ❌:}
### CRITICAL-001
- **Layer:** {layer name}
- **Finding:** {what's wrong}
- **Impact:** {why it matters}
- **Suggested fix:** {how to resolve}
---
## Warnings (Should Review)
{if any ⚠️:}
### WARNING-001
- **Layer:** {layer name}
- **Finding:** {what's different}
- **Suggested action:** {optional fix or acknowledge}
---
## Traceability Matrix
Rendered directly from `traceability.yml` rows (live-schema columns):
| req | story | journeys | frs | tasks | code | tests | events | status |
|-----|-------|----------|-----|-------|------|-------|--------|--------|
| REQ-001 | US-001 | JRN-001 | FR-001, FR-002 | T012, T013 | ✅ | TC-E2E-003 | EVT-prefs_saved | verified |
| — | US-002 | JRN-002 | FR-003 | T014 | ✅ | ⚠️ none | — | implemented |
> `req` is populated only for V-Model/backfill rows; the standard forward flow
> keys on `frs` and leaves `req` null (`—`).
---
## Conclusion
{PASS}: All critical checks passed. Feature is fully traced from research through to code.
{OR}
{PASS WITH WARNINGS}: {N} warnings found. Review recommended but no blockers.
{OR}
{FAIL}: {N} critical issues must be resolved. Run `/speckit.product-forge.verify-full` again after fixes.
Show the user:
📊 Verification Complete: {Feature Name}
Results:
❌ CRITICAL: {N} ← Must fix before done
⚠️ WARNING: {N} ← Review recommended
✅ PASSED: {N}
⏭️ SKIPPED: {N}
Verdict: {PASS / PASS WITH WARNINGS / FAIL}
Full report: {FEATURE_DIR}/verify-report.md
Ask: "There are {N} critical issues that need to be resolved. I recommend fixing them and re-running /speckit.product-forge.verify-full. Would you like me to help fix them, or do you want to address them manually?"
Ask: "Verification passed with {N} warnings. These are advisory — you can review and address them or acknowledge and close. Ready to mark this feature as complete?"
Update .forge-status.yml: verify: completed
Show completion summary:
🎉 Feature Complete: {Feature Name}
Full traceability chain verified:
Research ✅ → Product Spec ✅ → spec.md ✅
Plan ✅ → Tasks ✅ → Code ✅
All {N} checks passed.
Report saved: verify-report.md
Before returning, write {FEATURE_DIR}/verify/digest.md using the template at
docs/templates/phase-digest.md and record
its path on .forge-status.yml under phases.verify.digest_path.
The digest must include:
- Key decisions — overall verdict (clean / blocked), which CRITICAL findings drove the verdict.
- Artifacts produced —
verify-report.md. - Open risks — WARNING-level findings that were acknowledged but not fixed, with reason.
- Handoff notes — what test-plan and release-readiness should watch for.
The orchestrator refuses to mark Phase 7 complete until digest.md exists.
See docs/runtime.md §8.
- Read-only. Never modify source files. Only write
verify-report.md. In particular, verify-full does not promote a traceability row'sstatus(CF-18):test-runsetsstatus: tested;release-readiness/spec-mergesetstatus: verified. verify-full readsstatusbut never writes it. - Evidence-based. Every finding must cite specific lines, files, or sections.
- Proportional severity. CRITICAL only for genuine implementation gaps or scope violations. Warnings for deviations that may be intentional.
- Honest about gaps. If you cannot verify a check due to missing context, mark as SKIPPED with reason — not PASSED.
- Traceability focus. The unique value of Product Forge is the research-to-code chain. Prioritize checks that verify this chain over generic code quality.