Add sources
Getting knowledge into the record is the everyday work of this project. The
rules that make it governed are in AGENTS.md → "Writing knowledge" (shape,
frontmatter, audience, draft until the owner approves, copy values exactly,
never invent). This file is the ACT: how a source becomes a document that
passes those rules, whatever kind of source it is.
The source is one of two kinds — and usually both
A file — PDF, Word, slides, HTML, a Notion export, pasted text — is
knowledge that already exists somewhere. Your job is to move it without losing
anything: convert it, structure it, and prove every load-bearing value survived.
A person — "it's just how we do it" — is knowledge that exists nowhere
yet. Your job is to draw it out by asking, write it as the record and not as a
transcript, and record only what they confirm.
A real owner has both: the policy PDF, and the exception everyone knows that
the PDF never mentions. So the person step runs EVERY time, after the file:
"what does this not cover?" is the question that finds the pages nobody wrote.
When the source is a file
Extract the text first, into a scratch file outside knowledge/. The
extraction is what you convert from and what you verify against, so it has
to be a file you can grep, not something you remember reading:
| format |
extractor |
| PDF |
pdftotext -layout in.pdf /tmp/in.txt (poppler) |
| docx / odt / html / epub |
pandoc in.docx -t gfm -o /tmp/in.md |
| docx on macOS |
textutil -convert txt in.docx -output /tmp/in.txt (built in) |
| anything |
markitdown in.pdf > /tmp/in.md, if installed |
| Notion / Obsidian export |
already markdown — no extraction needed; the file IS the extraction |
None on PATH? Read the file directly (your Read tool opens PDFs) and say so
in your report — there is then no extraction to verify against, and step 5
degrades to re-reading the source by eye. That is a weaker check, and the
owner must be told it was the check that ran.
Empty extraction — whitespace, form-feeds, nothing — means the PDF has no
text layer. Stop and tell the owner: "This is a scanned image. I can read it
as a picture, but I cannot promise the numbers in it are right, and a wrong
threshold in a system of record is worse than a missing page. Give me a text
PDF, or paste the section you need." Do not OCR it and hope.
Decide the shape of the RECORD, not of one file. A 200-page manual is not
one document. One document per topic — the unit someone would ask a question
about — placed where its path is the identity it should have, with order:
set to reading order. Show the owner the proposed tree before writing it.
Convert to CommonMark a person would have written. Real headings from
the document's own structure (never an # h1 — the title is the frontmatter),
real lists, real tables; images extracted and placed beside the document
with relative links. Strip page furniture: running headers and footers, page
numbers, "Page 4 of 12", the table of contents. Keep the source's own words
for anything load-bearing.
Name the source precisely in sources — a URL where one exists,
otherwise the descriptor that governs ("Finance policy manual §4.2, 2025
edition"; "internal docs" governs nothing) — and cite it from the claim with
a footnote whose label is that source's id.
Verify — do not trust yourself. Run the shipped check against the
extraction from step 1:
node .agents/skills/add-sources/verify.mjs /tmp/in.txt knowledge/<path>.md
It lists every load-bearing token in the document's body — numbers, dates,
thresholds, codes, capitalised names — that does not appear in the
extraction. Each one is either a value you changed (fix it, verbatim) or a
value you introduced (delete it, or turn it into an Open question: line
for the owner). A value that passes was in the source; nothing more is
claimed. Frontmatter is exempt, because its title and description are your
words by design.
When the source is a person
Ask, one question at a time, in their words. Who triggers this? What
happens first, then next? Who has to approve, and at what threshold? What
goes wrong, and what is the exception? Follow up until each answer is
concrete enough that someone who was not in the room could act on it —
"what would someone actually ask this?" gets further than "define the
boundary".
Draft as the record, not as a transcript. One document per topic. Their
sentences, tightened — never your inference about what they must have
meant. Anything they did not say, or said they were not sure of, becomes an
Open question: line in the document. It does not become prose.
The source is the conversation, and it is named like any other. No
provenance: key exists; the attestation goes in sources:
sources:
- id: ops-interview-2026-08-21
title: Interview with J. Smith, Head of Operations
resource: "Interview with human:jsmith (Head of Operations), 2026-08-21T10:00:00Z, conducted by human:you"
Who, their role, the instant, and who asked. That is a claim nobody can
check against a file, and the pull request that adds it is the only thing
standing behind it — say so if the owner asks what "verified" would mean
here. No transcript is kept: raw unreviewed speech does not belong in a
governed record, and the checker refuses a .txt there anyway.
Two people describe one process differently — surface it, never smooth
it. Two cited statements, each with its own footnote, and the
disagreement flagged to the owner. Which one becomes stable is an
approval, not an edit.
Finish — every batch, either kind
pnpm check, and obey what it prints.
ksor build: it regenerates every folder's index.md and writes the lock.
- Read it back on the site.
pnpm dev renders the real page, drafts
marked; that page is what the owner confirms against, not a message in a
terminal. "Their words, tightened — never your invention" is the standard,
and it is theirs to say whether you met it.
- Then ask them to approve it — and write down what they said. A draft
reaches no machine surface: no
llms.txt, no /md/ twin, nothing for an
agent to cite. Until the owner says "approved" and you record
ksor.approval: { by: <their handle>, at: <now> } with status: stable,
the record still publishes nothing of theirs. Never record an approval
nobody gave.
- Commit the documents, the indexes and the lock together.
1---2name: add-sources3description: Turn what the owner has into governed knowledge in knowledge/ — a document, a page, pasted text, notes, or something nobody ever wrote down that they tell you. Use when the owner shares material to add, says "add this to the knowledge base", asks how to get existing content in, or wants to write down what they know from memory with no source to hand. Not for editing the site.4---56# Add sources78Getting knowledge into the record is the everyday work of this project. The9rules that make it _governed_ are in `AGENTS.md` → "Writing knowledge" (shape,10frontmatter, audience, `draft` until the owner approves, copy values exactly,11never invent). This file is the ACT: how a source becomes a document that12passes those rules, whatever kind of source it is.1314## The source is one of two kinds — and usually both1516**A file** — PDF, Word, slides, HTML, a Notion export, pasted text — is17knowledge that already exists somewhere. Your job is to move it without losing18anything: convert it, structure it, and prove every load-bearing value survived.1920**A person** — "it's just how we do it" — is knowledge that exists nowhere21yet. Your job is to draw it out by asking, write it as the record and not as a22transcript, and record only what they confirm.2324A real owner has both: the policy PDF, and the exception everyone knows that25the PDF never mentions. So the person step runs EVERY time, after the file:26"what does this not cover?" is the question that finds the pages nobody wrote.2728## When the source is a file29301. **Extract the text first, into a scratch file outside `knowledge/`.** The31 extraction is what you convert from and what you verify against, so it has32 to be a file you can grep, not something you remember reading:3334 | format | extractor |35 | ------------------------ | ------------------------------------------------------------------- |36 | PDF | `pdftotext -layout in.pdf /tmp/in.txt` (poppler) |37 | docx / odt / html / epub | `pandoc in.docx -t gfm -o /tmp/in.md` |38 | docx on macOS | `textutil -convert txt in.docx -output /tmp/in.txt` (built in) |39 | anything | `markitdown in.pdf > /tmp/in.md`, if installed |40 | Notion / Obsidian export | already markdown — no extraction needed; the file IS the extraction |4142 None on `PATH`? Read the file directly (your Read tool opens PDFs) and say so43 in your report — there is then no extraction to verify against, and step 544 degrades to re-reading the source by eye. That is a weaker check, and the45 owner must be told it was the check that ran.4647 **Empty extraction — whitespace, form-feeds, nothing — means the PDF has no48 text layer.** Stop and tell the owner: "This is a scanned image. I can read it49 as a picture, but I cannot promise the numbers in it are right, and a wrong50 threshold in a system of record is worse than a missing page. Give me a text51 PDF, or paste the section you need." Do not OCR it and hope.52532. **Decide the shape of the RECORD, not of one file.** A 200-page manual is not54 one document. One document per topic — the unit someone would ask a question55 about — placed where its path is the identity it should have, with `order:`56 set to reading order. Show the owner the proposed tree before writing it.57583. **Convert to CommonMark a person would have written.** Real headings from59 the document's own structure (never an `# h1` — the title is the frontmatter),60 real lists, real tables; images extracted and placed beside the document61 with relative links. Strip page furniture: running headers and footers, page62 numbers, "Page 4 of 12", the table of contents. Keep the source's own words63 for anything load-bearing.64654. **Name the source precisely** in `sources` — a URL where one exists,66 otherwise the descriptor that governs ("Finance policy manual §4.2, 202567 edition"; "internal docs" governs nothing) — and cite it from the claim with68 a footnote whose label is that source's `id`.69705. **Verify — do not trust yourself.** Run the shipped check against the71 extraction from step 1:7273 ```sh74 node .agents/skills/add-sources/verify.mjs /tmp/in.txt knowledge/<path>.md75 ```7677 It lists every load-bearing token in the document's body — numbers, dates,78 thresholds, codes, capitalised names — that does not appear in the79 extraction. Each one is either a value you changed (fix it, verbatim) or a80 value you introduced (delete it, or turn it into an `Open question:` line81 for the owner). A value that passes was in the source; nothing more is82 claimed. Frontmatter is exempt, because its title and description are your83 words by design.8485## When the source is a person86871. **Ask, one question at a time, in their words.** Who triggers this? What88 happens first, then next? Who has to approve, and at what threshold? What89 goes wrong, and what is the exception? Follow up until each answer is90 concrete enough that someone who was not in the room could act on it —91 "what would someone actually ask this?" gets further than "define the92 boundary".93942. **Draft as the record, not as a transcript.** One document per topic. Their95 sentences, tightened — never your inference about what they must have96 meant. Anything they did not say, or said they were not sure of, becomes an97 `Open question:` line in the document. It does not become prose.98993. **The source is the conversation, and it is named like any other.** No100 `provenance:` key exists; the attestation goes in `sources`:101102 ```yaml103 sources:104 - id: ops-interview-2026-08-21105 title: Interview with J. Smith, Head of Operations106 resource: "Interview with human:jsmith (Head of Operations), 2026-08-21T10:00:00Z, conducted by human:you"107 ```108109 Who, their role, the instant, and who asked. That is a claim nobody can110 check against a file, and the pull request that adds it is the only thing111 standing behind it — say so if the owner asks what "verified" would mean112 here. No transcript is kept: raw unreviewed speech does not belong in a113 governed record, and the checker refuses a `.txt` there anyway.1141154. **Two people describe one process differently — surface it, never smooth116 it.** Two cited statements, each with its own footnote, and the117 disagreement flagged to the owner. Which one becomes `stable` is an118 approval, not an edit.119120## Finish — every batch, either kind121122- `pnpm check`, and obey what it prints.123- `ksor build`: it regenerates every folder's `index.md` and writes the lock.124- **Read it back on the site.** `pnpm dev` renders the real page, drafts125 marked; that page is what the owner confirms against, not a message in a126 terminal. "Their words, tightened — never your invention" is the standard,127 and it is theirs to say whether you met it.128- **Then ask them to approve it — and write down what they said.** A draft129 reaches no machine surface: no `llms.txt`, no `/md/` twin, nothing for an130 agent to cite. Until the owner says "approved" and you record131 `ksor.approval: { by: <their handle>, at: <now> }` with `status: stable`,132 the record still publishes nothing of theirs. Never record an approval133 nobody gave.134- Commit the documents, the indexes and the lock together.