@credence/matrix
v0.6.0
Published
Credence review matrix: requirements × evidence × findings, with deterministic render.
Readme
@credence/matrix
The review matrix: requirements × subjects, each cell's state derived from the ledger using the same vocabulary as the claims.
import { deriveMatrix, renderMatrix, summarizeMatrix } from "@credence/matrix";
const matrix = await deriveMatrix(ledger, caseId, {
id: "parties",
title: "Party checks",
requirements: [{ key: "cnpj", label: "Tax ID" }, { key: "lawsuits", label: "Lawsuits" }],
subjects: { mode: "entities", entityType: "company" },
});
console.log(renderMatrix(matrix)); // deterministic markdown table
summarizeMatrix(matrix); // { total, ok, reported, unknown, attention }Cell states: ok · reported · unknown · absent · disputed · conflict.
The unknown (never investigated) vs absent (verified nothing there) distinction
is first-class — that's the whole point. The summary buckets are exhaustive and
disjoint: ok + reported + unknown + attention === total, so hearsay cannot fall
through and read as settled.
The value and the rule are different questions
A cell's state answers "what does the ledger hold for this field?" — and that
can be impeccable while a rule still objects to it. A verified, evidenced debt of
5M is ok and a blocking risk at the same time.
So a cell also carries attention (an open finding touches it) and findingRefs.
renderMatrix shows ✓ ok ⚑, summarizeMatrix counts it under attention, and
MatrixTable marks it. A surface that rendered only the value would tell a human
everything is fine while the Case Index said otherwise.
Conversely, a conflict that somebody dismissed stops shouting — but only on a dismissed conflict finding, never inferred from a missing one, so silence is always an explicit, attributed act.
Relation rows
A requirement can be a link rather than a value:
{ key: "owner_of", kind: "relation", direction: "incoming", cardinality: "many" }cardinality decides whether several distinct objects are a contradiction.
one (the default, and the conservative reading) is right for spouse_of;
many is right for owner_of, where co-ownership is not a disagreement.
deriveState(claims, minStatus, { adjudicatedBenign }) is exported as a pure
function for unit testing. MatrixResult is versioned (schemaVersion) JSON for a
UI to consume.
unsourced, and what changed in schemaVersion 4
A cell is unsourced when it has a value that nothing backs.
Through schemaVersion 3 that meant "no evidence row points at this claim".
From schemaVersion 4 it means "no evidence row points at this claim without
explicitly declining to back it". Evidence carries an
EvidenceStance — supports, contradicts or cited —
and a claim whose only link is cited ("the agent read this and it turned out
not to bear on the claim") now reads unsourced. So does one backed solely by a
contradicts link.
Evidence with no stance counts as backing, exactly as it did before. Every row written before 0.5.0 is unstated, and reading that silence as a refusal would mark the entire existing corpus unsourced overnight — the mirror image of the conflation stance exists to end.
No field moved. This is a semantic change, not a shape change, which is precisely why it takes a version bump: a consumer pinned to 3 would otherwise receive a different answer to the same question with no signal that it had.
