Spec Studio

Every validator ships evidence in both directions

spec:validation.validator-self-testingBusiness Rule · featureready floor ready

Packs: pack:self-hosting-v1

Design: 5 design entries · 5 pinned declarations · no decisions

Findings: no errors, no warnings

specs/validation/validator-self-testing.sdp.md

No open questions and no blocking questions; implementation binding present, verifier binding present, and expected-outcome oracle present.

A validator that stops firing reports nothing, and an empty report looks like a clean graph unless a test asserts the refusal. A validator that refuses every input is just as useless. Refusing and accepting inputs, one probe world per direction, tell the two failures apart, so the check families are themselves checked. No validator checks this discipline, because the Protocol's checks concern conformance and honesty, never the delivery process.

An audit keeps the discipline whole. Scattered tests can cover every check today and miss the next one, so the audit holds one probe pair per finding id in one table and fails when the table and the engine's registries disagree. Extraction refusals count: they carry the conformance family, refuse carriers in the same validation run, and go silent in the same way. The accepting probe is a near miss rather than an empty input, because an empty input passes a validator that refuses too much.

Intent
outcome
Keep a validator that has silently stopped firing from reading as a clean build.
Rule

The world runs the audit function on the shipped probe table, on that table without the pair for the named check, or on that table with the named check's pair written twice, over the shipped registries and the scanned source. A check that behaves silently has its findings removed from every probe's run; one that behaves overeagerly has a finding added to every probe's run. A source that also writes an id adds it to the scanned source ids, and nothing adds none. The named ids are joined by a comma and a space, or nothing when the audit names none.

rule.bothDirections line 19 · specs/validation/validator-self-testing.sdp.md:19
Each validator ships evidence in both directions: at least one input it must report and at least one near miss it must leave unreported.

spec:validation.validator-self-testing.overeager-check-named · enabled, spec:validation.validator-self-testing.shipped-checks-paired · enabled, spec:validation.validator-self-testing.silent-check-named · enabled

rule.neverAValidator line 20 · specs/validation/validator-self-testing.sdp.md:20
The discipline is evidence over the two check families, never a check of its own; no validator checks whether another validator carries tests, and no report, graph fact or delivery fact records the audit.

no example exercises it

rule.auditScope line 21 · specs/validation/validator-self-testing.sdp.md:21
The audit covers every finding id a validation run can report: each graph validator's and each extraction finding's, the Markdown and Gherkin carriers' included. Contract generation and import report outside a validation run and stay outside the audit.

spec:validation.validator-self-testing.shipped-checks-paired · enabled

rule.probePairs line 22 · specs/validation/validator-self-testing.sdp.md:22
The audit holds one probe pair per finding id: a refusing probe the id must report, and an accepting probe, a near miss that differs from the refusing probe only in the construct the check reads, that the id must leave unreported.

spec:validation.validator-self-testing.duplicate-pair-named · enabled, spec:validation.validator-self-testing.shipped-checks-paired · enabled, spec:validation.validator-self-testing.unpaired-check-named · enabled

rule.closedTable line 23 · specs/validation/validator-self-testing.sdp.md:23
The audit's table and the engine's registries agree exactly: a registered id without a pair, an id paired twice, a pair for an id no registry lists, a finding id written in the engine's source outside every registry, and a finding id a probe run reports outside every registry each fail the audit.

spec:validation.validator-self-testing.duplicate-pair-named · enabled, spec:validation.validator-self-testing.shipped-checks-paired · enabled, spec:validation.validator-self-testing.unpaired-check-named · enabled, spec:validation.validator-self-testing.unregistered-id-named · enabled

rule.sourceReadWhole line 24 · specs/validation/validator-self-testing.sdp.md:24
The audit reads the engine's source as code, so a comment never counts and every quote form does, and it fails on a finding id it cannot read whole, such as an id prefix joined to a value at run time.

no example exercises it

rule.silentAndOvereager line 25 · specs/validation/validator-self-testing.sdp.md:25
The audit runs every probe through the entry a validation run uses, graph validation for a graph probe and extraction for a carrier probe, and fails for each id its refusing probe does not report and each id its accepting probe reports; it reads the finding id alone, never the message.

spec:validation.validator-self-testing.overeager-check-named · enabled, spec:validation.validator-self-testing.shipped-checks-paired · enabled, spec:validation.validator-self-testing.silent-check-named · enabled

rule.auditInGate line 26 · specs/validation/validator-self-testing.sdp.md:26
The audit is a test in the repository's own suite, so the gate runs it on every change. It fails by naming each id at fault, writes no tracked file and no durable result, and may create and remove a temporary extraction root for a carrier probe.

spec:validation.validator-self-testing.shipped-checks-paired · enabled

0 unkeyed rules.

Verifying examples

A check paired twice is named · spec:validation.validator-self-testing.duplicate-pair-named · ready floor ready

Executable Spec

Given the audit reads {table: "the shipped table with one pair twice"}
Given the check {findingId: "conformance/orphans"} behaves {behavior: "as shipped"}
Given the engine source also writes {extraSourceId: "nothing"}
When the audit runs
Then the audit {verdict: "fails"}
Then the audit names {named: "conformance/orphans"} as {list: "duplicatePairs"}

Open example

A check that reports its near miss is named overeager · spec:validation.validator-self-testing.overeager-check-named · ready floor ready

Executable Spec

Given the audit reads {table: "the shipped probe table"}
Given the check {findingId: "conformance/orphans"} behaves {behavior: "overeagerly"}
Given the engine source also writes {extraSourceId: "nothing"}
When the audit runs
Then the audit {verdict: "fails"}
Then the audit names {named: "conformance/orphans"} as {list: "overeager"}

Open example

Every shipped check reports its refusing probe and leaves its near miss alone · spec:validation.validator-self-testing.shipped-checks-paired · ready floor ready

Executable Spec

Given the audit reads {table: "the shipped probe table"}
Given the check {findingId: "conformance/orphans"} behaves {behavior: "as shipped"}
Given the engine source also writes {extraSourceId: "nothing"}
When the audit runs
Then the audit {verdict: "passes"}
Then the audit names {named: "nothing"} as {list: "none"}

Open example

A check that stops reporting is named silent · spec:validation.validator-self-testing.silent-check-named · ready floor ready

Executable Spec

Given the audit reads {table: "the shipped probe table"}
Given the check {findingId: "conformance/orphans"} behaves {behavior: "silently"}
Given the engine source also writes {extraSourceId: "nothing"}
When the audit runs
Then the audit {verdict: "fails"}
Then the audit names {named: "conformance/orphans"} as {list: "silent"}

Open example

A registered check without a probe pair is named unpaired · spec:validation.validator-self-testing.unpaired-check-named · ready floor ready

Executable Spec

Given the audit reads {table: "the shipped table without one pair"}
Given the check {findingId: "conformance/orphans"} behaves {behavior: "as shipped"}
Given the engine source also writes {extraSourceId: "nothing"}
When the audit runs
Then the audit {verdict: "fails"}
Then the audit names {named: "conformance/orphans"} as {list: "unpaired"}

Open example

A finding id the source writes outside every registry is named · spec:validation.validator-self-testing.unregistered-id-named · ready floor ready

Executable Spec

Given the audit reads {table: "the shipped probe table"}
Given the check {findingId: "conformance/orphans"} behaves {behavior: "as shipped"}
Given the engine source also writes {extraSourceId: "conformance/unlisted-check"}
When the audit runs
Then the audit {verdict: "fails"}
Then the audit names {named: "conformance/unlisted-check"} as {list: "unregistered"}

Open example

Example space

Given the audit reads {table:"the shipped probe table"|"the shipped table without one pair"|"the shipped table with one pair twice"}
Given the check {findingId:string} behaves {behavior:"as shipped"|"silently"|"overeagerly"}
Given the engine source also writes {extraSourceId:string}
When the audit runs
Then the audit {verdict:"passes"|"fails"}
Then the audit names {named:string} as {list:"none"|"unpaired"|"duplicatePairs"|"unregistered"|"silent"|"overeager"}
Design
registries line 29 · specs/validation/validator-self-testing.sdp.md:29
graphValidatorIds and extractFindingIds; the Markdown extractor's extract/invalid-markdown-structure, extract/unrecognized-heading and extract/invalid-frontmatter join extractFindingIds, so one registry lists every extraction finding id.

no citation recorded

probeEntry line 30 · specs/validation/validator-self-testing.sdp.md:30
{ id: string; refusing: Probe; accepting: Probe }, with type Probe = { graph: GraphSchema } | { carriers: Readonly<Record<string, string>> }, a graph for a graph validator and root-relative carrier files for an extraction finding.

no citation recorded

auditFunction line 31 · specs/validation/validator-self-testing.sdp.md:31
auditValidatorEvidence(table: readonly ProbeEntry[], run: (probe: Probe) => readonly Finding[], registered: readonly string[], source: SourceScan): AuditAnswer, where run is graph validation or extraction as the probe needs, so a world can wrap it to silence one id or report it everywhere; every finding id any probe run reports outside registered is unregistered.

no citation recorded

auditAnswer line 32 · specs/validation/validator-self-testing.sdp.md:32
{ unpaired: string[]; duplicatePairs: string[]; strayPairs: string[]; unregistered: string[]; unresolved: string[]; silent: string[]; overeager: string[] }, each sorted in code-unit order and each id listed once; the audit passes when all seven are empty and otherwise fails with the non-empty lists in its message.

no citation recorded

sourceScan line 33 · specs/validation/validator-self-testing.sdp.md:33
scanFindingIds(srcRoot: string): SourceScan, with type SourceScan = { ids: string[]; unresolved: string[] }, reads every .ts file under src/ with the TypeScript scanner, so comments are skipped. A string literal or a template literal without substitutions whose whole text matches ^(conformance|honesty|extract)/[a-z][a-z-]*$ is an id, and one outside every registry is unregistered. A literal whose text matches ^(conformance|honesty|extract)/[a-z-]*$ but is no whole id, or a template literal whose text before its first substitution matches it, is unresolved, named <file>:<line>. The gate suite scans the real source tree.

no citation recorded

Verification

Verifier bindings

Example space

  • table · "the shipped probe table" | "the shipped table without one pair" | "the shipped table with one pair twice" · declared by the audit reads {table}
  • findingId · string · declared by the check {findingId} behaves {behavior}
  • behavior · "as shipped" | "silently" | "overeagerly" · declared by the check {findingId} behaves {behavior}
  • extraSourceId · string · declared by the engine source also writes {extraSourceId}

Bound points

ExampletablefindingIdbehaviorextraSourceId
spec:validation.validator-self-testing.duplicate-pair-named"the shipped table with one pair twice""conformance/orphans""as shipped""nothing"
spec:validation.validator-self-testing.overeager-check-named"the shipped probe table""conformance/orphans""overeagerly""nothing"
spec:validation.validator-self-testing.shipped-checks-paired"the shipped probe table""conformance/orphans""as shipped""nothing"
spec:validation.validator-self-testing.silent-check-named"the shipped probe table""conformance/orphans""silently""nothing"
spec:validation.validator-self-testing.unpaired-check-named"the shipped table without one pair""conformance/orphans""as shipped""nothing"
spec:validation.validator-self-testing.unregistered-id-named"the shipped probe table""conformance/orphans""as shipped""conformance/unlisted-check"

Oracle binding: present · oracle:protocol.validator-self-testing · test/validator-audit.test.ts:100

Coverage

findingId: "conformance/orphans" · spec:validation.validator-self-testing.duplicate-pair-named, spec:validation.validator-self-testing.overeager-check-named, spec:validation.validator-self-testing.shipped-checks-paired, spec:validation.validator-self-testing.silent-check-named, spec:validation.validator-self-testing.unpaired-check-named, spec:validation.validator-self-testing.unregistered-id-named · not enumerable

extraSourceId: "nothing" · spec:validation.validator-self-testing.duplicate-pair-named, spec:validation.validator-self-testing.overeager-check-named, spec:validation.validator-self-testing.shipped-checks-paired, spec:validation.validator-self-testing.silent-check-named, spec:validation.validator-self-testing.unpaired-check-named; "conformance/unlisted-check" · spec:validation.validator-self-testing.unregistered-id-named · not enumerable

Unbound

  • table: None
  • findingId: None
  • behavior: None
  • extraSourceId: None
Design view

Requirement drawing

Requirement drawing for spec:validation.validator-self-testing
The diagram as text
  • spec:validation.validator-self-testing: Every validator ships evidence in both directions · declared Spec
  • spec:validation.validator-self-testing.duplicate-pair-named: A check paired twice is named · declared Spec
  • spec:validation.validator-self-testing.overeager-check-named: A check that reports its near miss is named overeager · declared Spec
  • spec:validation.validator-self-testing.shipped-checks-paired: Every shipped check reports its refusing probe and leaves its near miss alone · declared Spec
  • spec:validation.validator-self-testing.silent-check-named: A check that stops reporting is named silent · declared Spec
  • spec:validation.validator-self-testing.unpaired-check-named: A registered check without a probe pair is named unpaired · declared Spec
  • spec:validation.validator-self-testing.unregistered-id-named: A finding id the source writes outside every registry is named · declared Spec
  • spec:validation.two-check-families: Validation separates well-formedness from non-pretending · declared Spec
  • impl:protocol.validator-probe-table: declares the refusing and accepting probes for every registered finding id · anchored code · test/helpers/validator-probes.ts:372
  • impl:protocol.validator-self-testing: audits the finding registries against source literals and paired probes · anchored code · test/helpers/validator-audit.ts:86
  • oracle:protocol.validator-self-testing: oracle:protocol.validator-self-testing · anchored oracle binding · test/validator-audit.test.ts:100
  • test:protocol.validator-self-testing.duplicate-pair-named: test:protocol.validator-self-testing.duplicate-pair-named · anchored test binding · binding present · test/validator-audit.test.ts:202
  • test:protocol.validator-self-testing.overeager-check-named: test:protocol.validator-self-testing.overeager-check-named · anchored test binding · binding present · test/validator-audit.test.ts:172
  • test:protocol.validator-self-testing.shipped-checks-paired: test:protocol.validator-self-testing.shipped-checks-paired · anchored test binding · binding present · test/validator-audit.test.ts:142
  • test:protocol.validator-self-testing.silent-check-named: test:protocol.validator-self-testing.silent-check-named · anchored test binding · binding present · test/validator-audit.test.ts:157
  • test:protocol.validator-self-testing.unpaired-check-named: test:protocol.validator-self-testing.unpaired-check-named · anchored test binding · binding present · test/validator-audit.test.ts:187
  • test:protocol.validator-self-testing.unregistered-id-named: test:protocol.validator-self-testing.unregistered-id-named · anchored test binding · binding present · test/validator-audit.test.ts:217
  • spec:validation.validator-self-testing refines spec:validation.two-check-families · declared
  • spec:validation.validator-self-testing.duplicate-pair-named refines spec:validation.validator-self-testing · declared
  • spec:validation.validator-self-testing.duplicate-pair-named verifies spec:validation.validator-self-testing · declared
  • spec:validation.validator-self-testing.overeager-check-named refines spec:validation.validator-self-testing · declared
  • spec:validation.validator-self-testing.overeager-check-named verifies spec:validation.validator-self-testing · declared
  • spec:validation.validator-self-testing.shipped-checks-paired refines spec:validation.validator-self-testing · declared
  • spec:validation.validator-self-testing.shipped-checks-paired verifies spec:validation.validator-self-testing · declared
  • spec:validation.validator-self-testing.silent-check-named refines spec:validation.validator-self-testing · declared
  • spec:validation.validator-self-testing.silent-check-named verifies spec:validation.validator-self-testing · declared
  • spec:validation.validator-self-testing.unpaired-check-named refines spec:validation.validator-self-testing · declared
  • spec:validation.validator-self-testing.unpaired-check-named verifies spec:validation.validator-self-testing · declared
  • spec:validation.validator-self-testing.unregistered-id-named refines spec:validation.validator-self-testing · declared
  • spec:validation.validator-self-testing.unregistered-id-named verifies spec:validation.validator-self-testing · declared
  • impl:protocol.validator-probe-table satisfies spec:validation.validator-self-testing · anchored
  • impl:protocol.validator-self-testing satisfies spec:validation.validator-self-testing · anchored
  • oracle:protocol.validator-self-testing models spec:validation.validator-self-testing · anchored
  • test:protocol.validator-self-testing.duplicate-pair-named verifies spec:validation.validator-self-testing.duplicate-pair-named · anchored
  • test:protocol.validator-self-testing.overeager-check-named verifies spec:validation.validator-self-testing.overeager-check-named · anchored
  • test:protocol.validator-self-testing.shipped-checks-paired verifies spec:validation.validator-self-testing.shipped-checks-paired · anchored
  • test:protocol.validator-self-testing.silent-check-named verifies spec:validation.validator-self-testing.silent-check-named · anchored
  • test:protocol.validator-self-testing.unpaired-check-named verifies spec:validation.validator-self-testing.unpaired-check-named · anchored
  • test:protocol.validator-self-testing.unregistered-id-named verifies spec:validation.validator-self-testing.unregistered-id-named · anchored
Mermaid source

Copyable source; the Studio draws static SVG.

Preferred syntax: requirementDiagram. Chosen syntax: flowchart TB because models, folds require exact labels unavailable in requirement syntax.

Decisions

No decision record named.

Runtime
  • impl:protocol.validator-probe-table · declares the refusing and accepting probes for every registered finding id · test/helpers/validator-probes.ts:372

    Uses

    None.

  • impl:protocol.validator-self-testing · audits the finding registries against source literals and paired probes · test/helpers/validator-audit.ts:86

    Uses

    None.

Runtime observation: not tracked.

Evidence

Runtime observation: not tracked.

Build attestations: not tracked.

Deployments: not tracked.

Bills of materials: not tracked.

Impact
Relations of spec:validation.validator-self-testing
Relations of spec:validation.validator-self-testingrefines · declaredrefines · declaredverifies · declaredrefines · declaredverifies · declaredrefines · declaredverifies · declaredrefines · declaredverifies · declaredrefines · declaredverifies · declaredrefines · declaredverifies · declaredOutgoing refinesspec:validation.two-check-familiesValidation separateswell-formedness fromnon-pretending…two-check-familiesSubjectspec:validation.validator-self-testingEvery validator shipsevidence in both directions…validator-self-testingIncoming refinesspec:validation.validator-self-testing.duplicate-pair-namedA check paired twice isnamed…validator-self-testing.duplicate-pair-namedspec:validation.validator-self-testing.overeager-check-namedA check that reports itsnear miss is named overeager…validator-self-testing.overeager-check-namedspec:validation.validator-self-testing.shipped-checks-pairedEvery shipped check reportsits refusing probe andleaves its near miss alone…validator-self-testing.shipped-checks-pairedspec:validation.validator-self-testing.silent-check-namedA check that stopsreporting is named silent…validator-self-testing.silent-check-namedspec:validation.validator-self-testing.unpaired-check-namedA registered check withouta probe pair is namedunpaired…validator-self-testing.unpaired-check-namedspec:validation.validator-self-testing.unregistered-id-namedA finding id the sourcewrites outside everyregistry is named…validator-self-testing.unregistered-id-named
The diagram as text
  • spec:validation.two-check-families: Validation separates well-formedness from non-pretending
  • spec:validation.validator-self-testing: Every validator ships evidence in both directions
  • spec:validation.validator-self-testing.duplicate-pair-named: A check paired twice is named
  • spec:validation.validator-self-testing.overeager-check-named: A check that reports its near miss is named overeager
  • spec:validation.validator-self-testing.shipped-checks-paired: Every shipped check reports its refusing probe and leaves its near miss alone
  • spec:validation.validator-self-testing.silent-check-named: A check that stops reporting is named silent
  • spec:validation.validator-self-testing.unpaired-check-named: A registered check without a probe pair is named unpaired
  • spec:validation.validator-self-testing.unregistered-id-named: A finding id the source writes outside every registry is named
  • spec:validation.validator-self-testing refines spec:validation.two-check-families · declared
  • spec:validation.validator-self-testing.duplicate-pair-named refines spec:validation.validator-self-testing · declared
  • spec:validation.validator-self-testing.overeager-check-named refines spec:validation.validator-self-testing · declared
  • spec:validation.validator-self-testing.shipped-checks-paired refines spec:validation.validator-self-testing · declared
  • spec:validation.validator-self-testing.silent-check-named refines spec:validation.validator-self-testing · declared
  • spec:validation.validator-self-testing.unpaired-check-named refines spec:validation.validator-self-testing · declared
  • spec:validation.validator-self-testing.unregistered-id-named refines spec:validation.validator-self-testing · declared
  • spec:validation.validator-self-testing.duplicate-pair-named verifies spec:validation.validator-self-testing · declared
  • spec:validation.validator-self-testing.overeager-check-named verifies spec:validation.validator-self-testing · declared
  • spec:validation.validator-self-testing.shipped-checks-paired verifies spec:validation.validator-self-testing · declared
  • spec:validation.validator-self-testing.silent-check-named verifies spec:validation.validator-self-testing · declared
  • spec:validation.validator-self-testing.unpaired-check-named verifies spec:validation.validator-self-testing · declared
  • spec:validation.validator-self-testing.unregistered-id-named verifies spec:validation.validator-self-testing · declared
Mermaid source

Copyable source; the Studio draws static SVG.

Preferred syntax: flowchart. Chosen syntax: labelled flowchart, preserving exact relations, claims and folds.

Rests on

Rested on by

Distance 1

Relations

Relation and directionOther endClaimReasonExercised keys
refines · outgoingValidation separates well-formedness from non-pretending
spec:validation.two-check-families · resolves
declaredNoneNone
refines · incomingA check paired twice is named
spec:validation.validator-self-testing.duplicate-pair-named · resolves
declaredNoneNone
refines · incomingA check that reports its near miss is named overeager
spec:validation.validator-self-testing.overeager-check-named · resolves
declaredNoneNone
refines · incomingEvery shipped check reports its refusing probe and leaves its near miss alone
spec:validation.validator-self-testing.shipped-checks-paired · resolves
declaredNoneNone
refines · incomingA check that stops reporting is named silent
spec:validation.validator-self-testing.silent-check-named · resolves
declaredNoneNone
refines · incomingA registered check without a probe pair is named unpaired
spec:validation.validator-self-testing.unpaired-check-named · resolves
declaredNoneNone
refines · incomingA finding id the source writes outside every registry is named
spec:validation.validator-self-testing.unregistered-id-named · resolves
declaredNoneNone
verifies · incomingA check paired twice is named
spec:validation.validator-self-testing.duplicate-pair-named · resolves
declaredNoneclosedTable, probePairs
verifies · incomingA check that reports its near miss is named overeager
spec:validation.validator-self-testing.overeager-check-named · resolves
declaredNonesilentAndOvereager, bothDirections
verifies · incomingEvery shipped check reports its refusing probe and leaves its near miss alone
spec:validation.validator-self-testing.shipped-checks-paired · resolves
declaredNonebothDirections, auditScope, probePairs, closedTable, silentAndOvereager, auditInGate
verifies · incomingA check that stops reporting is named silent
spec:validation.validator-self-testing.silent-check-named · resolves
declaredNonesilentAndOvereager, bothDirections
verifies · incomingA registered check without a probe pair is named unpaired
spec:validation.validator-self-testing.unpaired-check-named · resolves
declaredNoneclosedTable, probePairs
verifies · incomingA finding id the source writes outside every registry is named
spec:validation.validator-self-testing.unregistered-id-named · resolves
declaredNoneclosedTable

Bindings

Implementation binding: present

  • impl:protocol.validator-probe-table · declares the refusing and accepting probes for every registered finding id · test/helpers/validator-probes.ts:372 · role contract
  • impl:protocol.validator-self-testing · audits the finding registries against source literals and paired probes · test/helpers/validator-audit.ts:86 · role validator

Design reference: none

Verifier binding: present

Expected-outcome oracle: present

  • oracle:protocol.validator-self-testing · test/validator-audit.test.ts:100

Runtime observation: not tracked.

Run results: not recorded in the graph.

Findings

None.

Composing (0)

Composing needs the script.