Skip to content

Latest commit

 

History

17 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

provenance

Designing a system that will not state what it cannot prove.

A generator writes job application documents, traces every factual claim to the record behind it, and blocks the ones that have none. The subject matter is a job search; the problem is not. Anything that produces text someone will act on — a summary, a report, an answer pulled from a pile of documents — has to know which parts it can back and which it cannot.

What that looks like in practice, and what this repository is actually demonstrating:

  • Provenance is the schema, not a feature. A claim carries its label and its source together. The kind of record decides which label is available: a self-report can only support "the subject said it", never "a record confirms it".
  • Refusal is the main path. A claim with no record is blocked before it reaches a document. The pipeline reports how many records it stopped, not how many it sent — 11 of 14 here.
  • The invariants are executable. npm run seed throws rather than emit a claim whose label and record disagree, and npm run check fails the build if one ever appears. None of this relies on remembering the rules.
  • Refusal survives export. The PDF a reader downloads contains no blocked claim text, and a test generates the file and asserts that.

The page makes it visible: the record library on the left, the document in the middle, the job requirement on the right, and a line connecting each claim to both.

The demo data is generated. No real record enters this repository. Every company, person, place and file name is invented. The page belongs to Russell W. Hild / consultruss.com, and deploys to consultruss.com/provenance.

Commands

npm run seed     # regenerate data/generated/* and src/js/data.js from data/seed.config.json
npm run build    # write dist/
npm run check    # run the acceptance checks
npm run serve    # preview dist/ at http://127.0.0.1:8137

No dependencies. Node is used for the four scripts only; the page itself is plain HTML, CSS and ES modules.

Preview with npm run serve rather than opening index.html from the filesystem: module scripts are CORS-checked and a file:// origin is opaque, so browsers refuse to load them. The server sends Cache-Control: no-store, because a browser will otherwise keep an old js/data.js after npm run seed and quietly show you stale numbers. If you serve dist/ some other way, send that header or you will debug a page that is not the one you built.

The labels

Label Meaning Ships
a said directly by the subject yes
b confirmed against a record yes
c asserted by a tool, never confirmed no
d exists only in a derived document no

A claim labelled c or d carries sourceId: "s0", the null source. There is nothing to cite, so it draws no wire on the left. npm run check fails if that is ever untrue.

Layout

data/seed.config.json      every invented name and every authored string
data/generated/*.json      generator output, committed, meant to be opened and read
scripts/generate.mjs       writes the four JSON files and src/js/data.js
scripts/build.mjs          copies src/ to dist/
scripts/check.mjs          the acceptance checks
src/                       the page
design/provenance-mock.html  the visual reference this was built against, not shipped
dist/                      build output, committed, deployed

Three files sit outside the original brief and are worth explaining:

  • src/js/data.js is generated alongside the JSON. The page imports it instead of fetching it, so the page issues no data request at all and needs no base-path logic to work from a subdirectory. The JSON files stay the artifact a reviewer opens; this is the same content as a module.
  • src/js/pdf.js writes PDFs directly, with no library. The documents are short and text-only, and the base-14 fonts need no embedding, so a few hundred lines replaces a ~350 KB dependency and keeps the no-CDN rule literally true.
  • src/js/dom.js is a fifteen-line createElement helper. Nothing on the page is built from interpolated HTML strings.

The export

The download button writes a PDF of the document on screen, and the export performs the refusal rather than working around it:

  • A blocked claim is never written to the file. It is replaced in place by [ claim withheld — no source on file ].
  • Every claim that ships carries a footnote naming the source file it came from, so the document keeps its provenance once detached from the page.
  • The creation date is pinned to seed.config.json, so the same document always produces a byte-identical PDF.

This was added after the brief was written; the brief listed PDF export as out of scope, and that decision was reversed deliberately.

Determinism

npm run seed is a pure function of data/seed.config.json. There is no Date.now(), no Math.random(), and no Intl/locale formatting anywhere in the generator — locale output depends on the ICU build Node was compiled with, which would make the seed machine-dependent. The generator asserts its own totals and throws rather than emitting a corpus that does not add up.

What the checks cover

npm run check decides these automatically:

Check What it proves
16.1 npm run seed twice is byte-identical
16.2 no real company name, no name belonging to another project on this machine
16.3 every claim labelled c or d cites s0, and no shipping claim cites it
16.6 the first document carries a blocked claim to open on
16.8 the gate data contains at least one exit 1 run
16.9 at least one requirement lands on do not have
16.11 nothing in src/ or dist/ references an external host
16.12 the exported PDF draws no blocked claim text, and footnotes every claim that ships

The denylist in check 16.2 is a list, not a guess. Extend it in scripts/check.mjs rather than relying on memory.

These four are checked by hand in a browser, because a script cannot decide them:

  • 16.4 — click a claim, then a source, then a requirement, then an inspector chip to return to a claim. The console stays clean at every step.
  • 16.5 — switching documents leaves both rails unchanged and updates every requirement dot.
  • 16.7 — resizing the window redraws the wires onto the correct anchors.
  • 16.10 — keyboard alone reaches and activates every claim, source and requirement.

Deploy

npm run build writes dist/. Every asset path is relative, so the folder works from any subdirectory. Deployment copies dist/ into the site's deploy folder under /provenance. Nothing in this repository touches the site repository.

About

Grounding, citation and refusal in a document generator: every claim carries the record behind it, and claims with no record are blocked before they ship. Demo data.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages