@panaversity/ksor 0.0.55 → 0.0.57
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +208 -0
- package/README.md +9 -1
- package/package.json +1 -1
- package/templates/scaffold/.agents/skills/add-sources/SKILL.md +129 -84
- package/templates/scaffold/.agents/skills/add-sources/verify.mjs +45 -0
- package/templates/scaffold/.agents/skills/format-checker/SKILL.md +18 -46
- package/templates/scaffold/.agents/skills/intake-interview/SKILL.md +16 -8
- package/templates/scaffold/.claude/skills/add-sources/SKILL.md +129 -84
- package/templates/scaffold/.claude/skills/add-sources/verify.mjs +45 -0
- package/templates/scaffold/.claude/skills/format-checker/SKILL.md +18 -46
- package/templates/scaffold/.claude/skills/intake-interview/SKILL.md +16 -8
- package/templates/scaffold/AGENTS.md +12 -9
- package/templates/scaffold/README.md +22 -9
- package/templates/scaffold/.agents/skills/make-slides/SKILL.md +0 -162
- package/templates/scaffold/.agents/skills/make-summary/SKILL.md +0 -153
- package/templates/scaffold/.claude/skills/make-slides/SKILL.md +0 -162
- package/templates/scaffold/.claude/skills/make-summary/SKILL.md +0 -153
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,213 @@
|
|
|
1
1
|
# @panaversity/ksor
|
|
2
2
|
|
|
3
|
+
## 0.0.57
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 07cddea: The tutorials are numbered in reading order, and the package README lists all
|
|
8
|
+
three.
|
|
9
|
+
|
|
10
|
+
The introduction is now `00`, hello world `01`, and _Make it yours_ stays `02`
|
|
11
|
+
— so the two hands-on tutorials that follow each other are no longer separated
|
|
12
|
+
by the 1,800-line introduction, and every existing cross-reference ("tutorial
|
|
13
|
+
2", "tutorial 4") stays true. The introduction's title drops "Tutorial 1"; it is
|
|
14
|
+
the introduction. A one-line pointer stays at the old `00-hello-world.md` for
|
|
15
|
+
one release, because the README that shipped in 0.0.56 links it.
|
|
16
|
+
|
|
17
|
+
Both READMEs gain a three-row table keyed on who each tutorial is for, and a
|
|
18
|
+
test now holds every link into `docs/tutorials/` — relative or GitHub URL —
|
|
19
|
+
to a file that exists.
|
|
20
|
+
|
|
21
|
+
## 0.0.56
|
|
22
|
+
|
|
23
|
+
### Patch Changes
|
|
24
|
+
|
|
25
|
+
- bf35e2f: `add-sources` 2.0.0: a file or a person, one skill — with a check the agent
|
|
26
|
+
runs instead of a rule it is asked to follow.
|
|
27
|
+
|
|
28
|
+
Two issues asked for two paths into the record. #31: an owner with a folder of
|
|
29
|
+
PDFs had no path — the skill stated the rules and converted nothing. #50: an
|
|
30
|
+
owner whose knowledge is only in their head had no path — the interview scoped
|
|
31
|
+
the record and stopped, leaving `knowledge/` full of samples about KSoR.
|
|
32
|
+
|
|
33
|
+
**They are one skill, not two.** The record draws no line between the kinds:
|
|
34
|
+
an interview attestation in `sources[].resource` passes `ksor build` today,
|
|
35
|
+
the fidelity rules read the same for both, and a real owner has BOTH — the
|
|
36
|
+
policy PDF and the exception everyone knows that the PDF never mentions. So
|
|
37
|
+
the person step runs after every file: "what does this not cover?" is the
|
|
38
|
+
question that finds the pages nobody wrote. A sibling skill would have made
|
|
39
|
+
the agent choose before it knew, and #50's own three-way trigger collision
|
|
40
|
+
vanishes.
|
|
41
|
+
|
|
42
|
+
**The source is a file.** Extract first, into a scratch file outside
|
|
43
|
+
`knowledge/` — the skill names the extractor per format (`pdftotext`,
|
|
44
|
+
`pandoc`, macOS `textutil`, `markitdown`) and what to do with none on `PATH`:
|
|
45
|
+
read the file directly and SAY the verification that follows is weaker. An
|
|
46
|
+
empty extraction is a scanned image, and the skill stops and tells the owner
|
|
47
|
+
rather than OCR and hope. Then decide the shape of the record, convert to
|
|
48
|
+
CommonMark a person would have written, name the source precisely, and run
|
|
49
|
+
the shipped check:
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
node .agents/skills/add-sources/verify.mjs /tmp/in.txt knowledge/<path>.md
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`verify.mjs` — plain Node, no dependencies, the owner's to keep — lists every
|
|
56
|
+
number, date, threshold, code and capitalised name in the document's body that
|
|
57
|
+
does not appear in the extraction, case-folded and whitespace-collapsed.
|
|
58
|
+
Frontmatter and footnote ids are exempt, because they are the agent's words by
|
|
59
|
+
design. It is a floor, and says so: a value that passes was in the source; it
|
|
60
|
+
cannot see a value that was dropped, and it cannot tell a paraphrase from an
|
|
61
|
+
invention. Model-driven conversion is highest-fidelity for layout and
|
|
62
|
+
lowest for exact values, and this is the mechanical half of "copy load-bearing
|
|
63
|
+
values exactly".
|
|
64
|
+
|
|
65
|
+
**The source is a person.** Ask one question at a time in their words — who
|
|
66
|
+
triggers it, the steps, who approves and at what threshold, what goes wrong,
|
|
67
|
+
the exception — until someone who was not in the room could act on it. Draft
|
|
68
|
+
as the record, not a transcript; anything unconfirmed is an `Open question:`
|
|
69
|
+
line, never prose. The attestation goes in `sources` (who, role, instant,
|
|
70
|
+
conducted by) — there is no `provenance:` key, and no transcript is kept.
|
|
71
|
+
Two people describing one process differently stay two cited statements.
|
|
72
|
+
|
|
73
|
+
**Both end the same way, and the ending is new.** Read it back on `pnpm dev`,
|
|
74
|
+
then ask the owner to approve and write down what they said. A draft reaches
|
|
75
|
+
no machine surface, so a skill that stopped at the draft left every `llms.txt`
|
|
76
|
+
and every door empty — found on the journey walk: `1 document(s), 0 admitted`.
|
|
77
|
+
|
|
78
|
+
The rules that were restated here (placement, frontmatter, audience,
|
|
79
|
+
deprecation) now point at the emitted AGENTS.md instead. The trigger test that
|
|
80
|
+
asserted "no skill claims dictated knowledge" flips: add-sources claims it by
|
|
81
|
+
name, and no other skill may. Eleven cases hold `verify.mjs` to what it does
|
|
82
|
+
and, in three of them, to what it does not claim.
|
|
83
|
+
|
|
84
|
+
- 73530e6: Prune the scaffold's skills to the three that make a record, and fix the two
|
|
85
|
+
seams every adopter hits on the way to one.
|
|
86
|
+
|
|
87
|
+
**Removed: `make-slides` and `make-summary`.** They were 45% of all shipped
|
|
88
|
+
skill text and 21.6% identical to each other — their own commit says
|
|
89
|
+
"make-summary is make-slides' discipline applied to prose". A companion is
|
|
90
|
+
downstream of a record existing and invisible to the agent surface (no route,
|
|
91
|
+
no `llms.txt` line, no MCP node); no fixture and no tutorial ever fired either;
|
|
92
|
+
and neither was ever shown to beat its absence, which is the bar AGENTS.md sets
|
|
93
|
+
for keeping a skill at all. Their one real rule — a card may only say what its
|
|
94
|
+
document says — already lives in the emitted AGENTS.md, and the site renders
|
|
95
|
+
companions exactly as before. Verified: the emitted checker passes with both
|
|
96
|
+
gone. `format-checker/SKILL.md` is cut to what AGENTS.md does not say; the
|
|
97
|
+
program it names is unchanged.
|
|
98
|
+
|
|
99
|
+
**Fixed: `intake-interview` was contradicting itself in its trigger.** The
|
|
100
|
+
always-resident description promised "seven questions"; the body has asked
|
|
101
|
+
three since 2026-08-26. The body handed off to "question 4" and "question 5",
|
|
102
|
+
neither of which exists, and claimed `add-sources` writes `verified:` entries —
|
|
103
|
+
nothing does. 1.6.0 says three, hands off to add-sources with whatever material
|
|
104
|
+
the owner has, and drops the false claim.
|
|
105
|
+
|
|
106
|
+
**Fixed: the recommended path turned a green record red.** Walked on the
|
|
107
|
+
published package:
|
|
108
|
+
|
|
109
|
+
- The README said run the interview, then "delete each starter as your own
|
|
110
|
+
knowledge arrives". Delete the five first and the build refuses
|
|
111
|
+
`ksor-record-empty` and writes nothing — a slug named by no document an
|
|
112
|
+
adopter reads. The README now says to write and approve one document of
|
|
113
|
+
your own before the last starter goes, and names the refusal.
|
|
114
|
+
- The hello-world tutorial approves as `human:you`. The interview then retires
|
|
115
|
+
`human:you` from the policy, and the tutorial's own document — still approved
|
|
116
|
+
by an actor the policy no longer names — refuses `ksor-approver-unauthorised`.
|
|
117
|
+
The interview now re-attributes every act recorded under the placeholder to
|
|
118
|
+
the owner's handle in the same change: it is the same person.
|
|
119
|
+
|
|
120
|
+
**Consolidated:** "the record says only what its source says — a gap is an
|
|
121
|
+
open question, never filled from general knowledge" lived only inside
|
|
122
|
+
`add-sources`; it is now stated once in the emitted AGENTS.md where every
|
|
123
|
+
other writing rule is.
|
|
124
|
+
|
|
125
|
+
Two deterministic gates hold all of this: a skill-consistency lint (a trigger's
|
|
126
|
+
question count matches its body; every "question N" resolves; every refusal
|
|
127
|
+
slug a document names is one the product raises; every skill a document names
|
|
128
|
+
ships) and a journey walk against the built CLI (interview → one draft →
|
|
129
|
+
approve → delete starters → retire the starter actor, plus the exact state
|
|
130
|
+
each refusal fires on). Every lint assertion was mutation-tested.
|
|
131
|
+
|
|
132
|
+
Found by four independent reviews of the plan to build a system of record,
|
|
133
|
+
before building any of it.
|
|
134
|
+
|
|
135
|
+
- ab6a3ed: The agent tier: a shipped skill, run by a real coding agent, with the skill
|
|
136
|
+
and without it — the comparison AGENTS.md has always demanded and nothing had
|
|
137
|
+
ever run (issue #30).
|
|
138
|
+
|
|
139
|
+
`pnpm test:agent` scaffolds a fresh record, installs it, drops a real two-page
|
|
140
|
+
PDF in `src/`, and hands `claude -p` the prompt tutorial 2 hands the reader.
|
|
141
|
+
Once with `add-sources` present, once with it removed. What the agent leaves
|
|
142
|
+
behind is graded, and the split is the Testing contract's own: deterministic
|
|
143
|
+
behavioural graders GATE the with-skill arm — exactly one new document, under
|
|
144
|
+
`finance/`; `.ksor/*` and `instance.md` untouched; the record builds; `status:
|
|
145
|
+
draft`, `sources` present, no `id:`/`name:`; page furniture gone; every
|
|
146
|
+
number, date and name in the body found in the extraction by the shipped
|
|
147
|
+
`verify.mjs`. Cost, turns, duration and the baseline arm are REPORTED, so the
|
|
148
|
+
delta is visible and a skill that stops winning is seen.
|
|
149
|
+
|
|
150
|
+
"Checker passes" is deliberately not a grader: while this was being designed a
|
|
151
|
+
baseline run passed the checker by hand-authoring `index.md` and editing
|
|
152
|
+
`.ksor/people.yaml` — the worse behaviour scoring better. Files touched is the
|
|
153
|
+
discriminating assertion.
|
|
154
|
+
|
|
155
|
+
It spends model tokens, so it is gated like the database tier: on
|
|
156
|
+
`ANTHROPIC_API_KEY` in CI (a repository secret the owner has not yet added; the
|
|
157
|
+
tier runs and reports itself skipped until then) or a logged-in `claude`
|
|
158
|
+
locally, pins a mid-tier model by default (a one-word reply on the default
|
|
159
|
+
model measured $0.25), and runs from `skill-evals.yml` on push to main and by
|
|
160
|
+
hand — never per pull request.
|
|
161
|
+
|
|
162
|
+
What it cannot measure is written in the suite rather than implied: a
|
|
163
|
+
conversational skill needs a scripted owner, "reads as a finished page" needs
|
|
164
|
+
a browser, and the adopter's own model is whatever they run.
|
|
165
|
+
|
|
166
|
+
Decision 31 records the three choices this week made about the skill surface —
|
|
167
|
+
pruned to three, one skill for a file and a person, and this harness shape
|
|
168
|
+
over the Python trigger script that was proposed and measured wanting.
|
|
169
|
+
|
|
170
|
+
- c34cc3a: Tutorial 2, _Make it yours_: the walk from hello world's record to one that is
|
|
171
|
+
only the owner's — every output run and pasted as it appeared.
|
|
172
|
+
|
|
173
|
+
The intake interview and what it does to the placeholder approver; one policy
|
|
174
|
+
brought in from a real PDF, with the shipped check catching the one number the
|
|
175
|
+
conversion got wrong; one procedure that only ever lived in someone's head,
|
|
176
|
+
written with the thing they were not sure of as an open question rather than
|
|
177
|
+
prose; the read-back on the site and the approval act; then the samples go and
|
|
178
|
+
the tool that approved them leaves the policy. Two refusals do work on the way,
|
|
179
|
+
and the tutorial says exactly which state each fires on.
|
|
180
|
+
|
|
181
|
+
The prompt-accounting test now covers both tutorials from one table, so a new
|
|
182
|
+
prompt in either fails until someone names the skill that answers it. Only that
|
|
183
|
+
test changes under `packages/`; nothing an adopter installs behaves differently.
|
|
184
|
+
|
|
185
|
+
- 87a3542: Correct a claim the tutorial made about `build_id`, and guard the general rule.
|
|
186
|
+
|
|
187
|
+
`buildIdOf` hashes `ksor_version` along with the record — deliberately, because
|
|
188
|
+
"what produced this" is part of what a publication is. So a captured `build_id`
|
|
189
|
+
is correct for exactly one release, and the sentence 0.0.54's tutorial fix added
|
|
190
|
+
— "Your timestamp will differ; the `build_id` will not" — was already false when
|
|
191
|
+
0.0.55 published it. A reader on any later ksor sees a hash that does not match
|
|
192
|
+
theirs and nothing saying why.
|
|
193
|
+
|
|
194
|
+
Found by walking the published package rather than by reading the diff: the same
|
|
195
|
+
practice that caught the tutorial being uncompletable caught the correction being
|
|
196
|
+
wrong.
|
|
197
|
+
|
|
198
|
+
The tutorial now says the id carries the toolchain, names the version its
|
|
199
|
+
outputs were captured on, and points at the reproducibility a reader can
|
|
200
|
+
actually check — run `ksor build` twice on one tree and the id is identical.
|
|
201
|
+
Both captured blocks are re-taken from a 0.0.55 walk.
|
|
202
|
+
|
|
203
|
+
A guard in `docs-truth.integration.test.ts` holds the general rule rather than
|
|
204
|
+
the sentence: a document printing a concrete `build_id` must say what moves one,
|
|
205
|
+
within 700 characters of the id. It is PROXIMITY rather than presence — the
|
|
206
|
+
first version asked whether "toolchain" appeared anywhere in the file, the file
|
|
207
|
+
already used the word once for an unrelated reason, and removing the caveat left
|
|
208
|
+
it green. Caught by mutation, and the tightened version immediately found a
|
|
209
|
+
second uncaveated id in the same document.
|
|
210
|
+
|
|
3
211
|
## 0.0.55
|
|
4
212
|
|
|
5
213
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -42,10 +42,18 @@ where it came from:
|
|
|
42
42
|
{ "ok": false, "abstained": true, "gate": { "floor": 0.622 } }
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
-
**[Hello world](https://github.com/panaversity/ksor/blob/main/docs/tutorials/
|
|
45
|
+
**[Hello world](https://github.com/panaversity/ksor/blob/main/docs/tutorials/01-hello-world.md)**
|
|
46
46
|
walks all of that in about fifteen minutes. Every command and output in it was
|
|
47
47
|
run and pasted as it appeared — including the ones above.
|
|
48
48
|
|
|
49
|
+
The three so far, in reading order — pick by what you want from it:
|
|
50
|
+
|
|
51
|
+
| | read this if |
|
|
52
|
+
| --- | --- |
|
|
53
|
+
| [00 · Introduction](https://github.com/panaversity/ksor/blob/main/docs/tutorials/00-introduction-to-ksor.md) | you want to understand why this exists — no technical background needed |
|
|
54
|
+
| [01 · Hello world](https://github.com/panaversity/ksor/blob/main/docs/tutorials/01-hello-world.md) | you want to see it work in fifteen minutes — Node only, nothing else |
|
|
55
|
+
| [02 · Make it yours](https://github.com/panaversity/ksor/blob/main/docs/tutorials/02-make-it-yours.md) | you finished hello world and want a record that is only yours — a file in, a person's knowledge in, the samples out |
|
|
56
|
+
|
|
49
57
|
One command emits a complete governed project: the record (`knowledge/`,
|
|
50
58
|
plain CommonMark), a working documentation site with hot reload, offline
|
|
51
59
|
search and `llms.txt`, adopter CI, a dependency-free format checker
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@panaversity/ksor",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.57",
|
|
4
4
|
"description": "Knowledge System of Record — compile governed markdown into a static site for people and an MCP server for AI agents, with citations and measured abstention.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"abstention",
|
|
@@ -1,91 +1,136 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: add-sources
|
|
3
|
-
description: Turn
|
|
3
|
+
description: 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
4
|
metadata:
|
|
5
|
-
version: "
|
|
5
|
+
version: "2.0.0"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Add sources
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
10
|
+
Getting knowledge into the record is the everyday work of this project. The
|
|
11
|
+
rules that make it _governed_ are in `AGENTS.md` → "Writing knowledge" (shape,
|
|
12
|
+
frontmatter, audience, `draft` until the owner approves, copy values exactly,
|
|
13
|
+
never invent). This file is the ACT: how a source becomes a document that
|
|
14
|
+
passes those rules, whatever kind of source it is.
|
|
15
|
+
|
|
16
|
+
## The source is one of two kinds — and usually both
|
|
17
|
+
|
|
18
|
+
**A file** — PDF, Word, slides, HTML, a Notion export, pasted text — is
|
|
19
|
+
knowledge that already exists somewhere. Your job is to move it without losing
|
|
20
|
+
anything: convert it, structure it, and prove every load-bearing value survived.
|
|
21
|
+
|
|
22
|
+
**A person** — "it's just how we do it" — is knowledge that exists nowhere
|
|
23
|
+
yet. Your job is to draw it out by asking, write it as the record and not as a
|
|
24
|
+
transcript, and record only what they confirm.
|
|
25
|
+
|
|
26
|
+
A real owner has both: the policy PDF, and the exception everyone knows that
|
|
27
|
+
the PDF never mentions. So the person step runs EVERY time, after the file:
|
|
28
|
+
"what does this not cover?" is the question that finds the pages nobody wrote.
|
|
29
|
+
|
|
30
|
+
## When the source is a file
|
|
31
|
+
|
|
32
|
+
1. **Extract the text first, into a scratch file outside `knowledge/`.** The
|
|
33
|
+
extraction is what you convert from and what you verify against, so it has
|
|
34
|
+
to be a file you can grep, not something you remember reading:
|
|
35
|
+
|
|
36
|
+
| format | extractor |
|
|
37
|
+
| ------------------------ | ------------------------------------------------------------------- |
|
|
38
|
+
| PDF | `pdftotext -layout in.pdf /tmp/in.txt` (poppler) |
|
|
39
|
+
| docx / odt / html / epub | `pandoc in.docx -t gfm -o /tmp/in.md` |
|
|
40
|
+
| docx on macOS | `textutil -convert txt in.docx -output /tmp/in.txt` (built in) |
|
|
41
|
+
| anything | `markitdown in.pdf > /tmp/in.md`, if installed |
|
|
42
|
+
| Notion / Obsidian export | already markdown — no extraction needed; the file IS the extraction |
|
|
43
|
+
|
|
44
|
+
None on `PATH`? Read the file directly (your Read tool opens PDFs) and say so
|
|
45
|
+
in your report — there is then no extraction to verify against, and step 5
|
|
46
|
+
degrades to re-reading the source by eye. That is a weaker check, and the
|
|
47
|
+
owner must be told it was the check that ran.
|
|
48
|
+
|
|
49
|
+
**Empty extraction — whitespace, form-feeds, nothing — means the PDF has no
|
|
50
|
+
text layer.** Stop and tell the owner: "This is a scanned image. I can read it
|
|
51
|
+
as a picture, but I cannot promise the numbers in it are right, and a wrong
|
|
52
|
+
threshold in a system of record is worse than a missing page. Give me a text
|
|
53
|
+
PDF, or paste the section you need." Do not OCR it and hope.
|
|
54
|
+
|
|
55
|
+
2. **Decide the shape of the RECORD, not of one file.** A 200-page manual is not
|
|
56
|
+
one document. One document per topic — the unit someone would ask a question
|
|
57
|
+
about — placed where its path is the identity it should have, with `order:`
|
|
58
|
+
set to reading order. Show the owner the proposed tree before writing it.
|
|
59
|
+
|
|
60
|
+
3. **Convert to CommonMark a person would have written.** Real headings from
|
|
61
|
+
the document's own structure (never an `# h1` — the title is the frontmatter),
|
|
62
|
+
real lists, real tables; images extracted and placed beside the document
|
|
63
|
+
with relative links. Strip page furniture: running headers and footers, page
|
|
64
|
+
numbers, "Page 4 of 12", the table of contents. Keep the source's own words
|
|
65
|
+
for anything load-bearing.
|
|
66
|
+
|
|
67
|
+
4. **Name the source precisely** in `sources` — a URL where one exists,
|
|
68
|
+
otherwise the descriptor that governs ("Finance policy manual §4.2, 2025
|
|
69
|
+
edition"; "internal docs" governs nothing) — and cite it from the claim with
|
|
70
|
+
a footnote whose label is that source's `id`.
|
|
71
|
+
|
|
72
|
+
5. **Verify — do not trust yourself.** Run the shipped check against the
|
|
73
|
+
extraction from step 1:
|
|
74
|
+
|
|
75
|
+
```sh
|
|
76
|
+
node .agents/skills/add-sources/verify.mjs /tmp/in.txt knowledge/<path>.md
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
It lists every load-bearing token in the document's body — numbers, dates,
|
|
80
|
+
thresholds, codes, capitalised names — that does not appear in the
|
|
81
|
+
extraction. Each one is either a value you changed (fix it, verbatim) or a
|
|
82
|
+
value you introduced (delete it, or turn it into an `Open question:` line
|
|
83
|
+
for the owner). A value that passes was in the source; nothing more is
|
|
84
|
+
claimed. Frontmatter is exempt, because its title and description are your
|
|
85
|
+
words by design.
|
|
86
|
+
|
|
87
|
+
## When the source is a person
|
|
88
|
+
|
|
89
|
+
1. **Ask, one question at a time, in their words.** Who triggers this? What
|
|
90
|
+
happens first, then next? Who has to approve, and at what threshold? What
|
|
91
|
+
goes wrong, and what is the exception? Follow up until each answer is
|
|
92
|
+
concrete enough that someone who was not in the room could act on it —
|
|
93
|
+
"what would someone actually ask this?" gets further than "define the
|
|
94
|
+
boundary".
|
|
95
|
+
|
|
96
|
+
2. **Draft as the record, not as a transcript.** One document per topic. Their
|
|
97
|
+
sentences, tightened — never your inference about what they must have
|
|
98
|
+
meant. Anything they did not say, or said they were not sure of, becomes an
|
|
99
|
+
`Open question:` line in the document. It does not become prose.
|
|
100
|
+
|
|
101
|
+
3. **The source is the conversation, and it is named like any other.** No
|
|
102
|
+
`provenance:` key exists; the attestation goes in `sources`:
|
|
103
|
+
|
|
104
|
+
```yaml
|
|
105
|
+
sources:
|
|
106
|
+
- id: ops-interview-2026-08-21
|
|
107
|
+
title: Interview with J. Smith, Head of Operations
|
|
108
|
+
resource: "Interview with human:jsmith (Head of Operations), 2026-08-21T10:00:00Z, conducted by human:you"
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Who, their role, the instant, and who asked. That is a claim nobody can
|
|
112
|
+
check against a file, and the pull request that adds it is the only thing
|
|
113
|
+
standing behind it — say so if the owner asks what "verified" would mean
|
|
114
|
+
here. No transcript is kept: raw unreviewed speech does not belong in a
|
|
115
|
+
governed record, and the checker refuses a `.txt` there anyway.
|
|
116
|
+
|
|
117
|
+
4. **Two people describe one process differently — surface it, never smooth
|
|
118
|
+
it.** Two cited statements, each with its own footnote, and the
|
|
119
|
+
disagreement flagged to the owner. Which one becomes `stable` is an
|
|
120
|
+
approval, not an edit.
|
|
121
|
+
|
|
122
|
+
## Finish — every batch, either kind
|
|
123
|
+
|
|
124
|
+
- `pnpm check`, and obey what it prints.
|
|
125
|
+
- `ksor build`: it regenerates every folder's `index.md` and writes the lock.
|
|
126
|
+
- **Read it back on the site.** `pnpm dev` renders the real page, drafts
|
|
127
|
+
marked; that page is what the owner confirms against, not a message in a
|
|
128
|
+
terminal. "Their words, tightened — never your invention" is the standard,
|
|
129
|
+
and it is theirs to say whether you met it.
|
|
130
|
+
- **Then ask them to approve it — and write down what they said.** A draft
|
|
131
|
+
reaches no machine surface: no `llms.txt`, no `/md/` twin, nothing for an
|
|
132
|
+
agent to cite. Until the owner says "approved" and you record
|
|
133
|
+
`ksor.approval: { by: <their handle>, at: <now> }` with `status: stable`,
|
|
134
|
+
the record still publishes nothing of theirs. Never record an approval
|
|
135
|
+
nobody gave.
|
|
136
|
+
- Commit the documents, the indexes and the lock together.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Did the document keep its source's load-bearing values?
|
|
3
|
+
//
|
|
4
|
+
// node verify.mjs <extraction.txt> <document.md>
|
|
5
|
+
//
|
|
6
|
+
// Exit 0 when every load-bearing token in the document's BODY appears in the
|
|
7
|
+
// extraction; exit 1 and print each one that does not, one per line.
|
|
8
|
+
//
|
|
9
|
+
// What "load-bearing" means here: numbers (with their separators — 10,000 and
|
|
10
|
+
// 10000 are different claims about the source), dates, codes, and runs of two
|
|
11
|
+
// or more capitalised words (a name). Matched case-folded and with whitespace
|
|
12
|
+
// collapsed, because an extraction shouts its headings and wraps its lines.
|
|
13
|
+
//
|
|
14
|
+
// What this proves, and no more: a token that PASSES is present in the source.
|
|
15
|
+
// A token that FAILS was changed or introduced — either way, look at it. It
|
|
16
|
+
// cannot tell a paraphrase from an invention, and it cannot see a value that
|
|
17
|
+
// was dropped. It is a floor under model-driven conversion, which is highest
|
|
18
|
+
// fidelity for layout and lowest for exact values (issue #31).
|
|
19
|
+
//
|
|
20
|
+
// Plain Node, no dependencies, safe to copy: `.agents/skills/` is the owner's.
|
|
21
|
+
|
|
22
|
+
import { readFileSync } from "node:fs";
|
|
23
|
+
|
|
24
|
+
const [, , extractionPath, documentPath] = process.argv;
|
|
25
|
+
if (!extractionPath || !documentPath) {
|
|
26
|
+
console.error("usage: node verify.mjs <extraction.txt> <document.md>");
|
|
27
|
+
process.exit(2);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const fold = (s) => s.toLowerCase().replace(/\s+/g, " ");
|
|
31
|
+
|
|
32
|
+
const extraction = fold(readFileSync(extractionPath, "utf8"));
|
|
33
|
+
const raw = readFileSync(documentPath, "utf8");
|
|
34
|
+
|
|
35
|
+
// Body only: frontmatter is the agent's own words by design (title,
|
|
36
|
+
// description, ids). Footnote labels and definition prefixes are ids too.
|
|
37
|
+
const body = raw.replace(/^---\n[\s\S]*?\n---\n?/, "").replace(/\[\^[^\]]+\]:?/g, " ");
|
|
38
|
+
|
|
39
|
+
const tokens = new Set();
|
|
40
|
+
for (const m of body.matchAll(/\d[\d,.:/-]*\d|\d/g)) tokens.add(m[0]);
|
|
41
|
+
for (const m of body.matchAll(/\b[A-Z][a-z]+(?:\s+[A-Z][a-z]+)+\b/g)) tokens.add(m[0]);
|
|
42
|
+
|
|
43
|
+
const missing = [...tokens].filter((t) => !extraction.includes(fold(t))).sort();
|
|
44
|
+
for (const t of missing) console.log(t);
|
|
45
|
+
process.exit(missing.length === 0 ? 0 : 1);
|
|
@@ -2,54 +2,26 @@
|
|
|
2
2
|
name: format-checker
|
|
3
3
|
description: The record's format rules as a runnable check — frontmatter, filenames, links, structure. Use before handing off any change to knowledge/, when a check fails and you need to fix it, or when unsure whether a document is well-formed. Run with `pnpm check` (or node .agents/skills/format-checker/check.mjs).
|
|
4
4
|
metadata:
|
|
5
|
-
version: "
|
|
5
|
+
version: "3.0.0"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Format checker
|
|
9
9
|
|
|
10
|
-
`pnpm check` runs `check.mjs
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
record is. Do not edit it: `ksor init` writes it and `ksor migrate` rewrites
|
|
14
|
-
both copies of it when you upgrade the tool, so an edit is overwritten rather
|
|
15
|
-
than kept. It is read-only in the other direction too — it reports, and never
|
|
16
|
-
rewrites a file.
|
|
10
|
+
`pnpm check` runs `check.mjs`. The rules it holds the record to are the ones in
|
|
11
|
+
`AGENTS.md` → "Writing knowledge"; this file says only what that section does
|
|
12
|
+
not, which is how to relate to the program.
|
|
17
13
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
record
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
- `.ksor/governance.yaml` exists and names approval and takedown authorities;
|
|
33
|
-
`.ksor/takedowns.yaml` is append-only, every entry by a takedown authority,
|
|
34
|
-
and never names a concept that no longer exists.
|
|
35
|
-
- `index.md` files are generated by `ksor build`, never authored — a stale or
|
|
36
|
-
missing one is refused (`ksor-index-stale`; run `ksor build`). `log.md` and
|
|
37
|
-
`README.md` are reserved names.
|
|
38
|
-
- Filenames are portable identities: lowercase ascii, no spaces, no
|
|
39
|
-
case-collisions, no `x.md` beside `x/`, no parentheses or leading
|
|
40
|
-
underscore. `knowledge/` holds `.md`, companions (`<doc>.summary.md`,
|
|
41
|
-
`<doc>.{flashcards,quiz,slides}.yaml`) and images only.
|
|
42
|
-
- Links resolve inside `knowledge/` — inline, `<angle-bracketed>` and
|
|
43
|
-
reference-style alike; code is never a link — and never reach a document
|
|
44
|
-
that not every reader of the source may read (the widening rule).
|
|
45
|
-
- `instance.md` is `format: 2` with the closed key set the profile defines.
|
|
46
|
-
- `CLAUDE.md` stays a one-line pointer; `.agents/skills/` and
|
|
47
|
-
`.claude/skills/` hold the same files byte for byte in both directions; the
|
|
48
|
-
site contains no content files.
|
|
49
|
-
|
|
50
|
-
Every failure prints where, the rule's slug (`problem: ksor-…`), why the rule
|
|
51
|
-
exists, and the fix — obey the printed fix literally; if it doesn't resolve
|
|
52
|
-
the problem, that is a bug worth reporting to ksor.
|
|
53
|
-
|
|
54
|
-
When you edit any skill under `.agents/skills/`, re-copy it to
|
|
55
|
-
`.claude/skills/` — the checker holds the two trees identical.
|
|
14
|
+
- **It is generated, and it is the same rule set** `ksor build` and
|
|
15
|
+
`ksor ingest` run, so the three can never disagree about a well-formed
|
|
16
|
+
record. Do not edit it: `ksor init` writes it and `ksor migrate` rewrites both
|
|
17
|
+
copies when you upgrade, so an edit is overwritten rather than kept. It
|
|
18
|
+
reports and never rewrites a file.
|
|
19
|
+
- **Obey the printed fix literally.** Every refusal prints where, the rule's
|
|
20
|
+
slug (`problem: ksor-…`), why the rule exists, and the fix. If the fix does
|
|
21
|
+
not resolve the problem, that is a bug worth reporting to ksor.
|
|
22
|
+
- **If its refusals contradict `AGENTS.md`, the checker is older than the
|
|
23
|
+
record**: upgrade `@panaversity/ksor` and re-run `ksor migrate`. Never "fix"
|
|
24
|
+
the record by undoing what a migration wrote.
|
|
25
|
+
- **When you edit any skill under `.agents/skills/`, re-copy it to
|
|
26
|
+
`.claude/skills/`** — the checker holds the two trees byte-identical in both
|
|
27
|
+
directions.
|