@panaversity/ksor 0.0.29 → 0.0.31
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 +108 -0
- package/README.md +24 -0
- package/dist/cli.mjs +11 -3
- package/dist/{gateway-api-8lNruq9e-CuohjtoK.mjs → gateway-api-6nC9x54K-BWFTI_6U.mjs} +1 -1
- package/dist/gateway.mjs +1 -1
- package/package.json +1 -1
- package/templates/scaffold/.agents/skills/format-checker/check.mjs +11 -1
- package/templates/scaffold/.agents/skills/make-slides/SKILL.md +160 -0
- package/templates/scaffold/.claude/skills/format-checker/check.mjs +11 -1
- package/templates/scaffold/.claude/skills/make-slides/SKILL.md +160 -0
- package/templates/scaffold/AGENTS.md +91 -5
- package/templates/scaffold/README.md +24 -1
- package/templates/scaffold/knowledge/what-is-a-ksor.quiz.yaml +90 -0
- package/templates/scaffold/knowledge/what-is-a-ksor.slides.yaml +65 -0
- package/templates/scaffold/system/site/app/docs/[[...slug]]/page.tsx +17 -2
- package/templates/scaffold/system/site/app/global.css +113 -0
- package/templates/scaffold/system/site/components/deck-viewer.tsx +195 -0
- package/templates/scaffold/system/site/components/quiz.tsx +321 -0
- package/templates/scaffold/system/site/components/slides.tsx +128 -0
- package/templates/scaffold/system/site/components/study-aids.tsx +1 -1
- package/templates/scaffold/system/site/lib/attachment-rule.ts +14 -0
- package/templates/scaffold/system/site/lib/attachments.ts +88 -3
- package/templates/scaffold/system/site/lib/deck.ts +3 -12
- package/templates/scaffold/system/site/lib/identity.ts +55 -0
- package/templates/scaffold/system/site/lib/quiz-audit.ts +306 -0
- package/templates/scaffold/system/site/lib/quiz-round.ts +57 -0
- package/templates/scaffold/system/site/lib/quiz.ts +84 -0
- package/templates/scaffold/system/site/lib/slides-embed.ts +93 -0
- package/templates/scaffold/system/site/lib/slides.ts +123 -0
- package/templates/scaffold/system/site/source.config.ts +25 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,113 @@
|
|
|
1
1
|
# @panaversity/ksor
|
|
2
2
|
|
|
3
|
+
## 0.0.31
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- c69232d: Adversarial coverage for the MCP door (issue #33), first slice: the governance
|
|
8
|
+
leak sweep and cross-replica snapshot behaviour.
|
|
9
|
+
|
|
10
|
+
**A withdrawn document must not appear in any field of any reachable response.**
|
|
11
|
+
The existing takedown test proves each serving arm behaves at the arms someone
|
|
12
|
+
thought of. This one plants an unguessable marker inside the withdrawn document
|
|
13
|
+
— in its body _and its title_ — and asserts the marker appears nowhere in the
|
|
14
|
+
serialized result, across eighteen request shapes: search by body, by marker, by
|
|
15
|
+
title words, at several limits, keyword search, `topOneScore`, read by
|
|
16
|
+
stable_id / slug / qualified path, and outline at every anchor and page. A leak
|
|
17
|
+
into a field the test has never heard of still fails it.
|
|
18
|
+
|
|
19
|
+
It carries a **positive control**, because every other assertion is a
|
|
20
|
+
not-contains and a probe that could never see the marker would pass them all
|
|
21
|
+
while proving nothing: each shape runs before the takedown and the ones that
|
|
22
|
+
testify are required to have found it first.
|
|
23
|
+
|
|
24
|
+
It also covers the subtlest case, which carries no content at all — `topOneScore`
|
|
25
|
+
feeds the abstention gate, so a withdrawn document scoring there would let a
|
|
26
|
+
record claim coverage on the strength of text it refuses to show.
|
|
27
|
+
|
|
28
|
+
**Cross-replica snapshot tokens**, listed in #33 as "documented, untested" and
|
|
29
|
+
since found on a real deployment: two processes with no `KSOR_SNAPSHOT_KEYS`
|
|
30
|
+
produce tokens neither can verify from the other, and the verdict is `invalid`
|
|
31
|
+
rather than `unknown_key` — the key _id_ matches and only the secret differs,
|
|
32
|
+
which is why the failure is invisible until you read it. Also pins rotation
|
|
33
|
+
(outstanding tokens survive while the old key is listed, and die when it is
|
|
34
|
+
dropped) and cross-deployment refusal.
|
|
35
|
+
|
|
36
|
+
- d96b139: Presentations, as governed attachments of a document.
|
|
37
|
+
|
|
38
|
+
A document in `knowledge/` may now carry `<doc>.slides.yaml`. It renders at the
|
|
39
|
+
top of that document's page — before the prose, because a deck is the shape of
|
|
40
|
+
the thing and gives the detail somewhere to land.
|
|
41
|
+
|
|
42
|
+
**Ask your coding agent and it writes the deck.** `make slides for
|
|
43
|
+
knowledge/expenses/approvals.md` runs the new `make-slides` skill, which reads
|
|
44
|
+
the document whole, writes the slides, checks every claim and every number back
|
|
45
|
+
against it, and tells you what it left out because the document did not support
|
|
46
|
+
it — which is usually how you find out a document has a gap. No browser, no
|
|
47
|
+
third-party tool, no step where a person takes over.
|
|
48
|
+
|
|
49
|
+
**The record owns the deck by default.** `deck:` carries the slides themselves
|
|
50
|
+
and the site renders them, which is the only mode where a presentation is
|
|
51
|
+
governed: reviewed in the same pull request as its document, versioned with it,
|
|
52
|
+
withdrawn when it is withdrawn, and incapable of rotting into a dead link. Every
|
|
53
|
+
slide ships in the server-rendered HTML, so a reader without JavaScript, a
|
|
54
|
+
crawler and an agent parsing the page all get the whole deck. Presenter notes
|
|
55
|
+
render outside the slide, so they are not projected in fullscreen.
|
|
56
|
+
|
|
57
|
+
**A deck you keep elsewhere** can be embedded instead — `slides.url:`, with the
|
|
58
|
+
embed url derived for Google Slides, Canva and SlideShare. Its frame is
|
|
59
|
+
click-to-load: nothing is requested from the host until a reader asks for it, so
|
|
60
|
+
a page still makes zero external requests and a reader who only wanted the
|
|
61
|
+
policy never announces that to a slide host. Declaring both modes is refused
|
|
62
|
+
(`ksor-slides-two-sources`) — two presentations with nothing to say which one
|
|
63
|
+
governs is the disagreement a system of record exists to settle. `http` urls are
|
|
64
|
+
refused too, since a browser blocks a mixed-content frame silently.
|
|
65
|
+
|
|
66
|
+
Like every attachment, a deck has no URL, no sidebar row, no `llms.txt` line and
|
|
67
|
+
no id an agent can cite, and it takes its `visibility:` and any takedown from its
|
|
68
|
+
parent.
|
|
69
|
+
|
|
70
|
+
## 0.0.30
|
|
71
|
+
|
|
72
|
+
### Patch Changes
|
|
73
|
+
|
|
74
|
+
- fbf149b: Quizzes, as governed attachments of a document.
|
|
75
|
+
|
|
76
|
+
A document in `knowledge/` may now carry `<doc>.quiz.yaml` beside its summary
|
|
77
|
+
and its flashcard deck. It renders at the end of the document's page, under the
|
|
78
|
+
deck: choose an option, see immediately whether you were right, and read the
|
|
79
|
+
explanation before moving on. There is no pass mark — a quiz here checks
|
|
80
|
+
understanding of the record, it does not certify anybody — and answers stay in
|
|
81
|
+
the reader's own browser.
|
|
82
|
+
|
|
83
|
+
A quiz is **part of its document, not a document**: no URL, no sidebar row, no
|
|
84
|
+
`llms.txt` line, no markdown twin, no search entry, and no stable id. That last
|
|
85
|
+
one settles a question worth being explicit about: because `ksor ingest` creates
|
|
86
|
+
no node for a quiz, **the answer key cannot reach the MCP surface at all**.
|
|
87
|
+
There is nothing for an agent to search and nothing for it to read — not by a
|
|
88
|
+
filter that could be forgotten, but because the row does not exist. Governance
|
|
89
|
+
inherits from the parent exactly as the summary and the deck already do.
|
|
90
|
+
|
|
91
|
+
**`pnpm check` and `pnpm build` refuse a quiz a reader could pass without
|
|
92
|
+
reading it**, naming the questions to fix:
|
|
93
|
+
|
|
94
|
+
- `ksor-quiz-answer-bias` — more than 60% of answers at one option position
|
|
95
|
+
- `ksor-quiz-length-bias` — picking the longest or shortest option usually wins
|
|
96
|
+
- `ksor-quiz-answer-run` — four or more questions in a row share an answer
|
|
97
|
+
- `ksor-quiz-contradiction` — an explanation calls the marked answer wrong
|
|
98
|
+
- `ksor-quiz-duplicate-stem` — two questions open with the same 60 characters
|
|
99
|
+
|
|
100
|
+
These are carried from the predecessor, where the same mistakes shipped and were
|
|
101
|
+
found by readers rather than by the project — one quiz put every correct answer
|
|
102
|
+
in the same position across 451 questions. There they lived in a script that was
|
|
103
|
+
run once; here they are part of loading the file, so a quiz that fails them
|
|
104
|
+
cannot be published. The ratio rules do not apply below five questions, where
|
|
105
|
+
enforcing a spread would mean choosing an author's answers for them.
|
|
106
|
+
|
|
107
|
+
`ksor init` ships a quiz on the seed document, so a first `pnpm dev` shows the
|
|
108
|
+
shape. Its own first draft was refused for putting four of five answers at
|
|
109
|
+
option B — the check catching exactly what it was carried for.
|
|
110
|
+
|
|
3
111
|
## 0.0.29
|
|
4
112
|
|
|
5
113
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -32,6 +32,30 @@ scaffold is deterministic and offline, and every refusal explains itself.
|
|
|
32
32
|
> and the released version number are authoritative for the exact released
|
|
33
33
|
> functionality.
|
|
34
34
|
|
|
35
|
+
### Study attachments
|
|
36
|
+
|
|
37
|
+
A document may carry companions named after it, and the site renders each on
|
|
38
|
+
that document's page and nowhere else:
|
|
39
|
+
|
|
40
|
+
| File | What it is |
|
|
41
|
+
| ----------------------- | ------------------------------------------------------ |
|
|
42
|
+
| `<doc>.summary.md` | a précis, shown as a second tab beside the document |
|
|
43
|
+
| `<doc>.slides.yaml` | a presentation, at the top of the page |
|
|
44
|
+
| `<doc>.flashcards.yaml` | a recall deck, at the end |
|
|
45
|
+
| `<doc>.quiz.yaml` | a multiple-choice check, at the end |
|
|
46
|
+
|
|
47
|
+
Ask your coding agent — `make slides for knowledge/expenses/approvals.md` —
|
|
48
|
+
and the `make-slides` skill writes the deck from the document, checks every
|
|
49
|
+
claim and number back against it, and reports what it left out because the
|
|
50
|
+
document did not support it.
|
|
51
|
+
|
|
52
|
+
An attachment is **part of its document**: no URL, no sidebar row, no
|
|
53
|
+
`llms.txt` line, and no id an agent can cite. It takes its `visibility:` and
|
|
54
|
+
any takedown from its parent, so restricting the document restricts them all.
|
|
55
|
+
A quiz whose answers are guessable is refused by the build, and because ingest
|
|
56
|
+
creates no node for an attachment, a quiz's answer key can never reach the
|
|
57
|
+
agent surface at all.
|
|
58
|
+
|
|
35
59
|
Full concept, design goals, and project status:
|
|
36
60
|
**<https://github.com/panaversity/ksor>**
|
|
37
61
|
|
package/dist/cli.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { n as resolveCommand, r as verbs, t as exitCodes } from "./src-pl4aOpVs.mjs";
|
|
3
|
-
import { A as runProbe$1, C as keyRingFromEnv, D as prewarmPool, E as pooledEndpointFor, F as withPgRetry$1, I as withProbeDeadline$1, L as z$1, M as storedTextSearchConfig, N as tlsPosture, O as readHandler, P as visibleTiers$1, S as embedQueryVlit, T as parseInstanceText$1, _ as buildShippedProvider$1, a as MAX_OUTLINE_LIMIT, b as contentPool$1, c as MissingProviderKeyError$1, d as READ_OUTPUT, f as SEARCH_OUTPUT, g as assertSchemaCompatible, h as assertGovernanceServable$1, i as GovernanceGateError$1, j as searchHandler, k as recordIsUndescribed, l as OUTLINE_OUTPUT, m as TextSearchConfigMismatch, n as EmbeddingSpaceMismatch$1, p as SchemaVersionError, r as FLOOR, s as McpServer$1, t as ContentStoreError$1, u as READ_ONLY, v as checkEmbeddingSpace$1, w as outlineHandler, x as contentPoolMin, y as composeInstructions } from "./gateway-api-
|
|
3
|
+
import { A as runProbe$1, C as keyRingFromEnv, D as prewarmPool, E as pooledEndpointFor, F as withPgRetry$1, I as withProbeDeadline$1, L as z$1, M as storedTextSearchConfig, N as tlsPosture, O as readHandler, P as visibleTiers$1, S as embedQueryVlit, T as parseInstanceText$1, _ as buildShippedProvider$1, a as MAX_OUTLINE_LIMIT, b as contentPool$1, c as MissingProviderKeyError$1, d as READ_OUTPUT, f as SEARCH_OUTPUT, g as assertSchemaCompatible, h as assertGovernanceServable$1, i as GovernanceGateError$1, j as searchHandler, k as recordIsUndescribed, l as OUTLINE_OUTPUT, m as TextSearchConfigMismatch, n as EmbeddingSpaceMismatch$1, p as SchemaVersionError, r as FLOOR, s as McpServer$1, t as ContentStoreError$1, u as READ_ONLY, v as checkEmbeddingSpace$1, w as outlineHandler, x as contentPoolMin, y as composeInstructions } from "./gateway-api-6nC9x54K-BWFTI_6U.mjs";
|
|
4
4
|
import { chmodSync, copyFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
6
6
|
import { InMemoryTransport, LATEST_PROTOCOL_VERSION, createMcpHandler } from "@modelcontextprotocol/server";
|
|
@@ -16,7 +16,7 @@ import { bodyLimit } from "hono/body-limit";
|
|
|
16
16
|
import { execFileSync, spawnSync } from "node:child_process";
|
|
17
17
|
import { parseArgs } from "node:util";
|
|
18
18
|
import { readFile, readdir, stat } from "node:fs/promises";
|
|
19
|
-
//#region ../content-gateway/dist/main-
|
|
19
|
+
//#region ../content-gateway/dist/main-DdIuyneg.mjs
|
|
20
20
|
/**
|
|
21
21
|
* The default registration — and the ORIGINAL of the file `ksor init` emits.
|
|
22
22
|
*
|
|
@@ -1583,7 +1583,7 @@ async function withPgRetry(op, options = {}) {
|
|
|
1583
1583
|
throw lastError;
|
|
1584
1584
|
}
|
|
1585
1585
|
//#endregion
|
|
1586
|
-
//#region ../content/dist/commands-
|
|
1586
|
+
//#region ../content/dist/commands-mZxv9bLG.mjs
|
|
1587
1587
|
/**
|
|
1588
1588
|
* EVAL-LOCKED constants, quarried verbatim from the oracle
|
|
1589
1589
|
* (sor-agentfactory @ b554f91, config.py) — changing any of these is a
|
|
@@ -4208,6 +4208,14 @@ const ATTACHMENT_SUFFIXES = [
|
|
|
4208
4208
|
{
|
|
4209
4209
|
suffix: ".flashcards.yaml",
|
|
4210
4210
|
kind: "deck"
|
|
4211
|
+
},
|
|
4212
|
+
{
|
|
4213
|
+
suffix: ".quiz.yaml",
|
|
4214
|
+
kind: "quiz"
|
|
4215
|
+
},
|
|
4216
|
+
{
|
|
4217
|
+
suffix: ".slides.yaml",
|
|
4218
|
+
kind: "slides"
|
|
4211
4219
|
}
|
|
4212
4220
|
];
|
|
4213
4221
|
/**
|
|
@@ -5,7 +5,7 @@ import { z, z as z$1 } from "zod";
|
|
|
5
5
|
import pg from "pg";
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { createHash, createHmac, randomBytes, timingSafeEqual } from "node:crypto";
|
|
8
|
-
//#region ../content-gateway/dist/gateway-api-
|
|
8
|
+
//#region ../content-gateway/dist/gateway-api-6nC9x54K.mjs
|
|
9
9
|
/**
|
|
10
10
|
* The record's system prompt: instance.md's body, preserved beneath a framework
|
|
11
11
|
* floor. Lifted out of server.ts so the public gateway surface can re-export
|
package/dist/gateway.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { L as z$1, O as readHandler, a as MAX_OUTLINE_LIMIT, d as READ_OUTPUT, f as SEARCH_OUTPUT, j as searchHandler, k as recordIsUndescribed, l as OUTLINE_OUTPUT, o as MAX_SEARCH_K, r as FLOOR, s as McpServer$1, u as READ_ONLY, w as outlineHandler, y as composeInstructions } from "./gateway-api-
|
|
1
|
+
import { L as z$1, O as readHandler, a as MAX_OUTLINE_LIMIT, d as READ_OUTPUT, f as SEARCH_OUTPUT, j as searchHandler, k as recordIsUndescribed, l as OUTLINE_OUTPUT, o as MAX_SEARCH_K, r as FLOOR, s as McpServer$1, u as READ_ONLY, w as outlineHandler, y as composeInstructions } from "./gateway-api-6nC9x54K-BWFTI_6U.mjs";
|
|
2
2
|
export { FLOOR, MAX_OUTLINE_LIMIT, MAX_SEARCH_K, McpServer$1 as McpServer, OUTLINE_OUTPUT, READ_ONLY, READ_OUTPUT, SEARCH_OUTPUT, composeInstructions, outlineHandler, readHandler, recordIsUndescribed, searchHandler, z$1 as z };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@panaversity/ksor",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.31",
|
|
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",
|
|
@@ -41,7 +41,13 @@ const ASSET_EXTENSIONS = new Set([".png", ".jpg", ".jpeg", ".gif", ".svg", ".web
|
|
|
41
41
|
// rules below. This mirrors packages/content/src/lib/attachment-rule.ts, which
|
|
42
42
|
// this dependency-free file cannot import; ATTACHMENT_CASES there is the table
|
|
43
43
|
// both are held to.
|
|
44
|
-
const ATTACHMENT_SUFFIXES = [
|
|
44
|
+
const ATTACHMENT_SUFFIXES = [
|
|
45
|
+
".summary.md",
|
|
46
|
+
".summary.mdx",
|
|
47
|
+
".flashcards.yaml",
|
|
48
|
+
".quiz.yaml",
|
|
49
|
+
".slides.yaml",
|
|
50
|
+
];
|
|
45
51
|
// One character off a real attachment, refused BY NAME: `.yml` reaches the
|
|
46
52
|
// site bundler's `Unknown file type` throw, which names the path and nothing
|
|
47
53
|
// about the rule.
|
|
@@ -49,6 +55,10 @@ const ATTACHMENT_NEAR_MISSES = [
|
|
|
49
55
|
[".flashcards.yml", ".flashcards.yaml"],
|
|
50
56
|
[".flashcards.json", ".flashcards.yaml"],
|
|
51
57
|
[".summary.markdown", ".summary.md"],
|
|
58
|
+
[".quiz.yml", ".quiz.yaml"],
|
|
59
|
+
[".quiz.json", ".quiz.yaml"],
|
|
60
|
+
[".slides.yml", ".slides.yaml"],
|
|
61
|
+
[".slides.json", ".slides.yaml"],
|
|
52
62
|
];
|
|
53
63
|
|
|
54
64
|
/** The attachment suffix this name carries, or null. A dotfile has no stem. */
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: make-slides
|
|
3
|
+
description: Generate a presentation from one document and attach it, so it renders on that document's page. Use when the owner says "make slides for X", "turn this into a deck", "I need to present this", asks for a teaching aid or a slideshow, or when onboarding needs a session rather than a page.
|
|
4
|
+
metadata:
|
|
5
|
+
version: "2.0.0"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Generating a presentation for a document
|
|
9
|
+
|
|
10
|
+
You write the slides. Not an outline for somebody else to build — the actual
|
|
11
|
+
deck, into `<doc>.slides.yaml`, which the site renders on that document's page.
|
|
12
|
+
No browser, no third-party tool, no step where a human takes over.
|
|
13
|
+
|
|
14
|
+
Run it end to end: read the document, write the deck, check every line back
|
|
15
|
+
against the document, verify it builds. **The check is not optional** — it is
|
|
16
|
+
the step that keeps the record's guarantee true.
|
|
17
|
+
|
|
18
|
+
## The one rule everything else serves
|
|
19
|
+
|
|
20
|
+
**A slide may only say what the document says.**
|
|
21
|
+
|
|
22
|
+
The deck is a way of presenting the record, never a second source. A slide
|
|
23
|
+
asserting a threshold the document does not contain is a claim nothing governs
|
|
24
|
+
and no agent can cite — and because the deck is an attachment, the record now
|
|
25
|
+
stands behind it. Every number, date, name and rule is copied from the
|
|
26
|
+
document exactly, units included.
|
|
27
|
+
|
|
28
|
+
If the document does not say something you want on a slide, there are two
|
|
29
|
+
honest options: leave it out, or tell the owner the document is missing it.
|
|
30
|
+
Never a third.
|
|
31
|
+
|
|
32
|
+
## 1 · Read the document whole, first
|
|
33
|
+
|
|
34
|
+
Read `<doc>.md` completely before writing anything. Note as you go:
|
|
35
|
+
|
|
36
|
+
- **the decision it settles** — the reason it exists
|
|
37
|
+
- **the rule, in its own words** — usually one or two sentences
|
|
38
|
+
- **the numbers** — thresholds, deadlines, limits, and their units
|
|
39
|
+
- **the cases** — what happens in each situation it names
|
|
40
|
+
- **the boundary** — what it explicitly does NOT cover
|
|
41
|
+
- **its governance** — `owner`, `effective`, `status` from the frontmatter
|
|
42
|
+
|
|
43
|
+
If the document carries `<doc>.summary.md`, read that too: it is a reviewed
|
|
44
|
+
compression of the same thing, and it tells you what the author thought was
|
|
45
|
+
load-bearing.
|
|
46
|
+
|
|
47
|
+
## 2 · Write the deck
|
|
48
|
+
|
|
49
|
+
Write `<doc>.slides.yaml` beside the document:
|
|
50
|
+
|
|
51
|
+
```yaml
|
|
52
|
+
slides:
|
|
53
|
+
title: Expense approvals
|
|
54
|
+
description: The 15-minute version, for a room.
|
|
55
|
+
deck:
|
|
56
|
+
- heading: What this settles
|
|
57
|
+
lead: One sentence, in the document's own words.
|
|
58
|
+
note: What to say here. Spoken, never shown.
|
|
59
|
+
|
|
60
|
+
- heading: The rule
|
|
61
|
+
bullets:
|
|
62
|
+
- Two approvers above the threshold, always
|
|
63
|
+
- The threshold is per invoice, including tax
|
|
64
|
+
note: Pause here. This is the slide people remember wrong.
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Per slide:**
|
|
68
|
+
|
|
69
|
+
| Field | Use |
|
|
70
|
+
| --------- | ------------------------------------------------------------ |
|
|
71
|
+
| `heading` | required — a statement, not a label. "The rule", not "Rules" |
|
|
72
|
+
| `lead` | one sentence, for a slide making a single point |
|
|
73
|
+
| `bullets` | three to five. Six is the cap, and six is already too many |
|
|
74
|
+
| `note` | what the presenter SAYS — never a repeat of the slide |
|
|
75
|
+
|
|
76
|
+
**Per deck** — 8 to 14 slides for an ordinary policy document:
|
|
77
|
+
|
|
78
|
+
1. What this settles, and for whom
|
|
79
|
+
2. Why it exists — the decision behind it
|
|
80
|
+
3. The rule itself, stated once
|
|
81
|
+
4. One slide per case, with the real numbers
|
|
82
|
+
5. What people get wrong, and what is true instead
|
|
83
|
+
6. The boundary — what this document does not cover
|
|
84
|
+
7. Where to find it: the route, the owner, the effective date
|
|
85
|
+
|
|
86
|
+
**Habits that decide whether it is any good:**
|
|
87
|
+
|
|
88
|
+
- **A heading is a claim.** "Recency is not authority" teaches; "Authority"
|
|
89
|
+
does not.
|
|
90
|
+
- **A bullet is one thought.** If it needs a comma splice, it is two bullets.
|
|
91
|
+
- **The note carries the argument.** The slide holds the shape; the presenter
|
|
92
|
+
holds the reasoning. A note repeating the bullets is a wasted field.
|
|
93
|
+
- **Do not pad to a target.** Five slides of substance beat twelve with three
|
|
94
|
+
that exist to reach twelve.
|
|
95
|
+
|
|
96
|
+
## 3 · Check every line against the document
|
|
97
|
+
|
|
98
|
+
Go back through slide by slide with the document open. For each:
|
|
99
|
+
|
|
100
|
+
- Is every claim in the document? Name where.
|
|
101
|
+
- Is every number identical, same units, same rounding?
|
|
102
|
+
- Does any slide imply a rule the document does not state?
|
|
103
|
+
- Does the boundary slide match what the document actually excludes?
|
|
104
|
+
|
|
105
|
+
This pass finds real errors, reliably. A transcription slip in a deck outlives
|
|
106
|
+
the session it was made for, because the next presenter trusts it.
|
|
107
|
+
|
|
108
|
+
## 4 · Verify it
|
|
109
|
+
|
|
110
|
+
```sh
|
|
111
|
+
pnpm check # refuses an orphan, frontmatter, or a malformed deck
|
|
112
|
+
pnpm dev # look at the page — the deck renders at the end
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
`pnpm build` refuses:
|
|
116
|
+
|
|
117
|
+
- `ksor-slides-empty` — neither `deck:` nor `slides.url:`; nothing to show
|
|
118
|
+
- `ksor-slides-two-sources` — both, so nothing says which one governs
|
|
119
|
+
- `ksor-attachment-orphan` — no `<doc>.md` beside it
|
|
120
|
+
- `ksor-attachment-frontmatter` — an attachment carries none of its own
|
|
121
|
+
|
|
122
|
+
## 5 · Tell the owner what you did
|
|
123
|
+
|
|
124
|
+
Which document, how many slides, and **anything you left out because the
|
|
125
|
+
document did not support it**. That last part is the useful half: it is how an
|
|
126
|
+
owner finds out their document has a gap.
|
|
127
|
+
|
|
128
|
+
## Embedding a deck made elsewhere
|
|
129
|
+
|
|
130
|
+
If the owner already has a deck in Google Slides, Canva or SlideShare, use the
|
|
131
|
+
linked mode — `slides.url:` and no `deck:`:
|
|
132
|
+
|
|
133
|
+
```yaml
|
|
134
|
+
slides:
|
|
135
|
+
title: Expense approvals
|
|
136
|
+
url: https://docs.google.com/presentation/d/<id>/edit?usp=sharing
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
The embed url is derived for those three providers; for anything else add
|
|
140
|
+
`embed:` explicitly or it renders as a link. `url` must be https — a browser
|
|
141
|
+
blocks a mixed-content frame silently, so an http one publishes a panel that
|
|
142
|
+
never loads.
|
|
143
|
+
|
|
144
|
+
**Prefer the record-owned deck.** A linked deck is not reviewed in a pull
|
|
145
|
+
request, not versioned with its document, not withdrawn when the document is,
|
|
146
|
+
and can rot to a dead link with nothing going red. Use the link when the deck
|
|
147
|
+
already exists and somebody else maintains it — not as the default.
|
|
148
|
+
|
|
149
|
+
## What NOT to do
|
|
150
|
+
|
|
151
|
+
- **Do not put the deck in the document.** No `<iframe>`, no raw HTML.
|
|
152
|
+
`knowledge/` is CommonMark and must read cleanly in any markdown viewer.
|
|
153
|
+
- **Do not write a slide the document cannot support**, even a true one. If it
|
|
154
|
+
is not in the record, the record cannot stand behind it.
|
|
155
|
+
- **Do not make one deck for several documents.** A deck belongs to one
|
|
156
|
+
document, the way a summary does. A deck spanning five policies has no
|
|
157
|
+
document to be governed by and nothing to be withdrawn with.
|
|
158
|
+
- **Do not patch a stale deck.** When the document changes materially,
|
|
159
|
+
regenerate from it. Patching is how a deck and its document drift, and a
|
|
160
|
+
deck that drifts starts winning arguments it should lose.
|
|
@@ -41,7 +41,13 @@ const ASSET_EXTENSIONS = new Set([".png", ".jpg", ".jpeg", ".gif", ".svg", ".web
|
|
|
41
41
|
// rules below. This mirrors packages/content/src/lib/attachment-rule.ts, which
|
|
42
42
|
// this dependency-free file cannot import; ATTACHMENT_CASES there is the table
|
|
43
43
|
// both are held to.
|
|
44
|
-
const ATTACHMENT_SUFFIXES = [
|
|
44
|
+
const ATTACHMENT_SUFFIXES = [
|
|
45
|
+
".summary.md",
|
|
46
|
+
".summary.mdx",
|
|
47
|
+
".flashcards.yaml",
|
|
48
|
+
".quiz.yaml",
|
|
49
|
+
".slides.yaml",
|
|
50
|
+
];
|
|
45
51
|
// One character off a real attachment, refused BY NAME: `.yml` reaches the
|
|
46
52
|
// site bundler's `Unknown file type` throw, which names the path and nothing
|
|
47
53
|
// about the rule.
|
|
@@ -49,6 +55,10 @@ const ATTACHMENT_NEAR_MISSES = [
|
|
|
49
55
|
[".flashcards.yml", ".flashcards.yaml"],
|
|
50
56
|
[".flashcards.json", ".flashcards.yaml"],
|
|
51
57
|
[".summary.markdown", ".summary.md"],
|
|
58
|
+
[".quiz.yml", ".quiz.yaml"],
|
|
59
|
+
[".quiz.json", ".quiz.yaml"],
|
|
60
|
+
[".slides.yml", ".slides.yaml"],
|
|
61
|
+
[".slides.json", ".slides.yaml"],
|
|
52
62
|
];
|
|
53
63
|
|
|
54
64
|
/** The attachment suffix this name carries, or null. A dotfile has no stem. */
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: make-slides
|
|
3
|
+
description: Generate a presentation from one document and attach it, so it renders on that document's page. Use when the owner says "make slides for X", "turn this into a deck", "I need to present this", asks for a teaching aid or a slideshow, or when onboarding needs a session rather than a page.
|
|
4
|
+
metadata:
|
|
5
|
+
version: "2.0.0"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Generating a presentation for a document
|
|
9
|
+
|
|
10
|
+
You write the slides. Not an outline for somebody else to build — the actual
|
|
11
|
+
deck, into `<doc>.slides.yaml`, which the site renders on that document's page.
|
|
12
|
+
No browser, no third-party tool, no step where a human takes over.
|
|
13
|
+
|
|
14
|
+
Run it end to end: read the document, write the deck, check every line back
|
|
15
|
+
against the document, verify it builds. **The check is not optional** — it is
|
|
16
|
+
the step that keeps the record's guarantee true.
|
|
17
|
+
|
|
18
|
+
## The one rule everything else serves
|
|
19
|
+
|
|
20
|
+
**A slide may only say what the document says.**
|
|
21
|
+
|
|
22
|
+
The deck is a way of presenting the record, never a second source. A slide
|
|
23
|
+
asserting a threshold the document does not contain is a claim nothing governs
|
|
24
|
+
and no agent can cite — and because the deck is an attachment, the record now
|
|
25
|
+
stands behind it. Every number, date, name and rule is copied from the
|
|
26
|
+
document exactly, units included.
|
|
27
|
+
|
|
28
|
+
If the document does not say something you want on a slide, there are two
|
|
29
|
+
honest options: leave it out, or tell the owner the document is missing it.
|
|
30
|
+
Never a third.
|
|
31
|
+
|
|
32
|
+
## 1 · Read the document whole, first
|
|
33
|
+
|
|
34
|
+
Read `<doc>.md` completely before writing anything. Note as you go:
|
|
35
|
+
|
|
36
|
+
- **the decision it settles** — the reason it exists
|
|
37
|
+
- **the rule, in its own words** — usually one or two sentences
|
|
38
|
+
- **the numbers** — thresholds, deadlines, limits, and their units
|
|
39
|
+
- **the cases** — what happens in each situation it names
|
|
40
|
+
- **the boundary** — what it explicitly does NOT cover
|
|
41
|
+
- **its governance** — `owner`, `effective`, `status` from the frontmatter
|
|
42
|
+
|
|
43
|
+
If the document carries `<doc>.summary.md`, read that too: it is a reviewed
|
|
44
|
+
compression of the same thing, and it tells you what the author thought was
|
|
45
|
+
load-bearing.
|
|
46
|
+
|
|
47
|
+
## 2 · Write the deck
|
|
48
|
+
|
|
49
|
+
Write `<doc>.slides.yaml` beside the document:
|
|
50
|
+
|
|
51
|
+
```yaml
|
|
52
|
+
slides:
|
|
53
|
+
title: Expense approvals
|
|
54
|
+
description: The 15-minute version, for a room.
|
|
55
|
+
deck:
|
|
56
|
+
- heading: What this settles
|
|
57
|
+
lead: One sentence, in the document's own words.
|
|
58
|
+
note: What to say here. Spoken, never shown.
|
|
59
|
+
|
|
60
|
+
- heading: The rule
|
|
61
|
+
bullets:
|
|
62
|
+
- Two approvers above the threshold, always
|
|
63
|
+
- The threshold is per invoice, including tax
|
|
64
|
+
note: Pause here. This is the slide people remember wrong.
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Per slide:**
|
|
68
|
+
|
|
69
|
+
| Field | Use |
|
|
70
|
+
| --------- | ------------------------------------------------------------ |
|
|
71
|
+
| `heading` | required — a statement, not a label. "The rule", not "Rules" |
|
|
72
|
+
| `lead` | one sentence, for a slide making a single point |
|
|
73
|
+
| `bullets` | three to five. Six is the cap, and six is already too many |
|
|
74
|
+
| `note` | what the presenter SAYS — never a repeat of the slide |
|
|
75
|
+
|
|
76
|
+
**Per deck** — 8 to 14 slides for an ordinary policy document:
|
|
77
|
+
|
|
78
|
+
1. What this settles, and for whom
|
|
79
|
+
2. Why it exists — the decision behind it
|
|
80
|
+
3. The rule itself, stated once
|
|
81
|
+
4. One slide per case, with the real numbers
|
|
82
|
+
5. What people get wrong, and what is true instead
|
|
83
|
+
6. The boundary — what this document does not cover
|
|
84
|
+
7. Where to find it: the route, the owner, the effective date
|
|
85
|
+
|
|
86
|
+
**Habits that decide whether it is any good:**
|
|
87
|
+
|
|
88
|
+
- **A heading is a claim.** "Recency is not authority" teaches; "Authority"
|
|
89
|
+
does not.
|
|
90
|
+
- **A bullet is one thought.** If it needs a comma splice, it is two bullets.
|
|
91
|
+
- **The note carries the argument.** The slide holds the shape; the presenter
|
|
92
|
+
holds the reasoning. A note repeating the bullets is a wasted field.
|
|
93
|
+
- **Do not pad to a target.** Five slides of substance beat twelve with three
|
|
94
|
+
that exist to reach twelve.
|
|
95
|
+
|
|
96
|
+
## 3 · Check every line against the document
|
|
97
|
+
|
|
98
|
+
Go back through slide by slide with the document open. For each:
|
|
99
|
+
|
|
100
|
+
- Is every claim in the document? Name where.
|
|
101
|
+
- Is every number identical, same units, same rounding?
|
|
102
|
+
- Does any slide imply a rule the document does not state?
|
|
103
|
+
- Does the boundary slide match what the document actually excludes?
|
|
104
|
+
|
|
105
|
+
This pass finds real errors, reliably. A transcription slip in a deck outlives
|
|
106
|
+
the session it was made for, because the next presenter trusts it.
|
|
107
|
+
|
|
108
|
+
## 4 · Verify it
|
|
109
|
+
|
|
110
|
+
```sh
|
|
111
|
+
pnpm check # refuses an orphan, frontmatter, or a malformed deck
|
|
112
|
+
pnpm dev # look at the page — the deck renders at the end
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
`pnpm build` refuses:
|
|
116
|
+
|
|
117
|
+
- `ksor-slides-empty` — neither `deck:` nor `slides.url:`; nothing to show
|
|
118
|
+
- `ksor-slides-two-sources` — both, so nothing says which one governs
|
|
119
|
+
- `ksor-attachment-orphan` — no `<doc>.md` beside it
|
|
120
|
+
- `ksor-attachment-frontmatter` — an attachment carries none of its own
|
|
121
|
+
|
|
122
|
+
## 5 · Tell the owner what you did
|
|
123
|
+
|
|
124
|
+
Which document, how many slides, and **anything you left out because the
|
|
125
|
+
document did not support it**. That last part is the useful half: it is how an
|
|
126
|
+
owner finds out their document has a gap.
|
|
127
|
+
|
|
128
|
+
## Embedding a deck made elsewhere
|
|
129
|
+
|
|
130
|
+
If the owner already has a deck in Google Slides, Canva or SlideShare, use the
|
|
131
|
+
linked mode — `slides.url:` and no `deck:`:
|
|
132
|
+
|
|
133
|
+
```yaml
|
|
134
|
+
slides:
|
|
135
|
+
title: Expense approvals
|
|
136
|
+
url: https://docs.google.com/presentation/d/<id>/edit?usp=sharing
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
The embed url is derived for those three providers; for anything else add
|
|
140
|
+
`embed:` explicitly or it renders as a link. `url` must be https — a browser
|
|
141
|
+
blocks a mixed-content frame silently, so an http one publishes a panel that
|
|
142
|
+
never loads.
|
|
143
|
+
|
|
144
|
+
**Prefer the record-owned deck.** A linked deck is not reviewed in a pull
|
|
145
|
+
request, not versioned with its document, not withdrawn when the document is,
|
|
146
|
+
and can rot to a dead link with nothing going red. Use the link when the deck
|
|
147
|
+
already exists and somebody else maintains it — not as the default.
|
|
148
|
+
|
|
149
|
+
## What NOT to do
|
|
150
|
+
|
|
151
|
+
- **Do not put the deck in the document.** No `<iframe>`, no raw HTML.
|
|
152
|
+
`knowledge/` is CommonMark and must read cleanly in any markdown viewer.
|
|
153
|
+
- **Do not write a slide the document cannot support**, even a true one. If it
|
|
154
|
+
is not in the record, the record cannot stand behind it.
|
|
155
|
+
- **Do not make one deck for several documents.** A deck belongs to one
|
|
156
|
+
document, the way a summary does. A deck spanning five policies has no
|
|
157
|
+
document to be governed by and nothing to be withdrawn with.
|
|
158
|
+
- **Do not patch a stale deck.** When the document changes materially,
|
|
159
|
+
regenerate from it. Patching is how a deck and its document drift, and a
|
|
160
|
+
deck that drifts starts winning arguments it should lose.
|