Files
evidence-record/README.md
T
Paul HittandClaude Opus 5.5 03c8e4e4da Tell npm where the @extant2000 scope lives before installing
The package is on the GitLab registry, and a bare npm install looks on
npmjs.org and fails. The install section now sets the scope's registry
first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 16:44:36 -04:00

141 lines
5.1 KiB
Markdown

# @extant2000/evidence-record
A small vocabulary for checks that have to show their work: four-way
determinations, cited evidence, and reports that derive their own verdict.
Zero runtime dependencies. Pure functions. Ships ESM and CommonJS.
## Why it exists
Automated checks tend to fail their readers in the same few ways. This package
gives every check one shared way to avoid them.
1. **A boolean hides the case that matters most.** `passed: true` can mean "we
looked and it held" or "there was nothing to look at". Those need to stay
apart, so a determination has four values, and three of them are not a
pass.
2. Findings that do not say where they came from ask the reader to take them
on trust. Every finding here carries `Evidence`: a file and line, a database
row, a command, a measurement, or a document.
3. A measurement without its sample count is not evidence. An availability of
100% over zero samples and one over 8,640 samples are different claims, so
the sample count travels with the number.
4. A report with zero findings is not a pass unless something was examined.
The verdict is computed from the report, and `examined === 0` always gives
`not-assessed`.
The unflattering outputs stay unflattering. A missing value is not a pass, and
nothing in this package turns one into a pass.
## Determinations
```ts
type Determination = 'pass' | 'fail' | 'not-applicable' | 'not-assessed'
```
`rollUp` combines several. Any fail wins. An empty list is `not-assessed`,
because `[].every(...)` being true is the trap this type closes. One unassessed
item keeps the roll-up at `not-assessed`, so healthy items cannot dilute it.
```ts
rollUp([]) // 'not-assessed'
rollUp(['pass', 'not-assessed']) // 'not-assessed'
rollUp(['pass', 'pass', 'fail']) // 'fail'
```
`label(d)` gives a short badge (`MET`, `NOT MET`, `N/A`, `NOT ASSESSED`).
`describe(d, subject)` gives a sentence that still makes sense when pasted into
an email on its own.
## Evidence
```ts
import { evidence, cite, describeMeasurement } from '@extant2000/evidence-record'
evidence.file('docker-compose.yml', 42, 'ports: ["2222:22"]')
evidence.record('requirements', 'a1b2c3', 'parent_id', 'null')
evidence.command('docker compose config', 0)
evidence.measurement('uptime', null, 0, { unit: '%', window: '2026-07' })
evidence.document('NIST SP 800-53 Rev 5', 'AC-2')
```
`cite()` renders each kind in the form people already read it in: `path:line`
for source, `table#id (field)` for rows, `$ command → exit 0` for commands.
`citeVerbose()` adds the excerpt and note. `describeMeasurement()` reads the
null case out loud: "No samples for Availability in 2026-07; it cannot be
computed."
## Reports
```ts
import { evidence, overall, validateReport, formatReport } from '@extant2000/evidence-record'
const report = {
capability: 'PortCheck',
scope: '12 compose services',
examined: 12,
findings: [
{ id: 'f1', summary: 'SSH published on a host port', determination: 'fail',
severity: 'critical', evidence: [evidence.file('docker-compose.yml', 42, 'ports: ["2222:22"]')] },
{ id: 'f2', summary: 'Admin UI bound to loopback only', determination: 'pass',
severity: 'info', evidence: [evidence.file('docker-compose.yml', 88)] },
],
}
overall(report) // 'fail', computed from the findings
validateReport(report) // [] when every conclusion carries a citation
formatReport(report) // worst first, one citation per finding
```
Output of `formatReport(report)`:
```
PortCheck — 12 compose services
NOT MET · 12 items examined · 2 findings
✗ CRITICAL SSH published on a host port [NOT MET]
evidence: docker-compose.yml:42
◦ info Admin UI bound to loopback only [MET]
evidence: docker-compose.yml:88
```
The same report with nothing examined:
```
PortCheck — no services found
NOT ASSESSED · 0 items examined · 0 findings
PortCheck's scope was not assessed. This is not a pass — an unassessed control is not an implemented control.
```
`validateReport` flags a finding that reaches `pass`, `fail` or
`not-applicable` with no evidence, plus duplicate ids and empty summaries. A
`not-assessed` finding may have no evidence, since having nothing to cite is
what it reports. `formatReport` accepts `{ evidence: 'inline' | 'full' | 'none',
minSeverity }`. `formatRollUp(reports)` gives one line per report and a
combined verdict, where an unrun report pulls the total to `not-assessed`.
## Conformance
A check built on this package conforms when its own tests assert that:
- `validateReport(report)` is empty for every report it can produce;
- an empty input produces `not-assessed`, and the rendered output contains
neither `✓` nor the word "clean";
- every `fail` finding carries at least one `Evidence`;
- the rendered output of a failure names a file, a row, or a command.
## Install
The package is published to the GitLab package registry, so point the
`@extant2000` scope there first:
```
npm config set @extant2000:registry https://gitlab.com/api/v4/packages/npm/
npm install @extant2000/evidence-record
```
## License
MIT. Copyright (c) 2026 Extant 2000 LLC.