traceability, issue #282) answers one question per requirement:
is this actually proven, or just claimed? It renders a matrix of every requirement in scope
against five evidence kinds — implementation, test, security, compliance, approval — sourced
entirely from a run’s own on-disk artifacts (evidence.jsonl, validation.json,
approvals.json). Nothing here is inferred from the browser; the page’s own header comment states
the rule directly: it “never derives a pass from missing browser data and never recalculates
readiness.” Below the matrix sits the Traceability Drift panel (issue #74), an on-demand scan
of the same run for requirements nobody picked up, completed work missing its contracts, and stale
file references.
How it works
The evidence projection (server-owned)
buildEvidenceProjection in src/observability/dashboard/state/evidence.js builds the whole
model server-side on every poll; the client only renders it. For each run in scope, every
requirement (run.requirements) becomes one row, and each of the five EVIDENCE_KINDS
(implementation, test, security, compliance, approval) becomes one cell in that row:
- implementation / security / compliance cells read
evidence.jsonlentries whose kind (via a keyword match onkind/task_id/stage_id) matches the column and whose content references the requirement (referencesRequirement— an explicitrequirement_idfield, or a text-includes fallback). - test cells add validator check results (
task.validation.checks) on top of the ledger signals. - approval cells read
run.approvalsrecords, cross-checked withapprovalHistoryIssues/validateApprovalRecordfrom the harness’s own approval-audit module — a record that fails that audit renders asINVALID, not a false pass.
unknown is a first-class state, never silently coerced into a pass.
The top-level model also carries:
This same
evidenceCenter object also feeds the Release Readiness verdict
(state/readiness.js) as one check among several — so a requirement with failed or unknown
evidence shows up as a release blocker, not just a quiet cell in this page.
A separate, older per-run summary (
buildTraceMap in state/traceability.js, sent to the
client as state.traceMap) is still computed on every poll, but no UI page currently renders
it — the Evidence Center you see today is built entirely on evidenceCenter. Treat traceMap
as legacy plumbing, not the live model.Three views over one model
The page (src/observability/dashboard/ui/pages/traceability.js) never re-fetches — EVIDENCE_VIEW
just re-renders the same model three ways:
- Summary — the KPI strip (coverage / verified / failed / unknown) plus a release evidence verdict, followed by the same matrix as below.
- Matrix — one row per requirement, one column per evidence kind, filterable by cell status
(
EVIDENCE_STATUS: all / verified / failed / unknown) viasetEvidenceStatus. - Readiness rationale — the flat
rationalelist, one card per requirement×kind pair, each showing its status pill and up to two clickable source references.
evidenceSourceButton) only when the
source is linkable — i.e. its path is scoped to .rstack/ with no traversal. Clicking it calls
viewArtifact() (ui/drawer.js), which fetches /api/artifact?run=...&path=... and opens the
file in the drawer (rendered by artifact-render.js, or a raw <pre> fallback). This is the same
mechanism the Run Report and other pages use to open a real deliverable in place — Evidence Center
never links out to a filesystem path directly, only through that sandboxed endpoint.
A toolbar button, “Export same projection,” downloads the exact in-memory model object as
rstack-evidence-projection.json — what you see is what you get, byte for byte.
Traceability Drift panel
Below the matrix,fillDriftCard fetches /api/drift?run=<runId> for the first run in scope
((s.runs || [])[0]) and renders its findings. The server route (handleDrift in
server.js) calls the shared harness scanner, scanRunDrift (src/core/harness/drift.js) — the
identical function the rstack-agents drift CLI verb runs. The scanner reads the run’s real
artifacts and reports:
- Requirements without a task — a stage-02 requirement no task’s id/title/description/ acceptance criteria mentions.
- Task completeness — a task missing status, owner/agent, or a traceable stage/artifact.
- Missing builder/validator contracts on a task marked
PASS/DONE_WITH_CONCERNS(a waived task, per an on-record approval, gets a warning instead of an error). - Validator PASS with zero passing checks recorded — a verdict with no evidence behind it.
- Stale file references — files a builder claims to have modified, or evidence points at, that no longer exist on disk.
- Evidence pointing at an unknown task, or an approved artifact that no longer exists
(virtual gate names like
guardrail-override:004-implare excluded — they name a gate, not a file). - Readiness contradiction —
readiness.jsonreportsREADYwhile tasks are stillFAIL/BLOCKED.
severity (error = a broken evidence chain, warning = intact but
degrading), a type, the artifact path it’s about, a human message, and a remediation. The
run’s overall drift status is FAIL on any error, WARN on any warning, else PASS. The card
shows the top 12 findings plus four summary chips (requirements / tasks / missing evidence / stale
references); an empty findings list renders as an honest “No drift detected.”
Try it
Open the Evidence Center from the Business Hub nav, or drive the same scanner from the CLI:--json/--all are real flags on the drift command (bin/rstack-agents.js); default exit
is warning-tolerant (non-zero only on error-severity findings) so a CI pipeline can adopt the
gate without breaking on cosmetic drift.Related
- Overview & Navigation — the six-destination shell this page lives in
- Operations Center — health/integrations/recovery, the sibling projection built the same server-owned way
