@panaversity/ksor 0.0.9 → 0.0.11

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 CHANGED
@@ -1,5 +1,271 @@
1
1
  # @panaversity/ksor
2
2
 
3
+ ## 0.0.11
4
+
5
+ ### Patch Changes
6
+
7
+ - 0a0dd27: A record describes itself on the surface agents discover it through
8
+
9
+ `/.well-known/mcp/server.json` carried one hard-coded sentence — "The <name>
10
+ Knowledge System of Record: governed markdown served with citations and honest
11
+ abstention." — byte-identical in every ksor record ever scaffolded. An agent
12
+ choosing between records in a registry learned nothing from any of them, which
13
+ is the opposite of what that document exists for.
14
+
15
+ The description now comes from the record's own prose: its display title and the
16
+ first real sentence of `instance.md`, which is what the intake interview writes.
17
+ A record whose owner has not described it yet SAYS so rather than borrowing a
18
+ confident sentence it has not earned — the same answer the MCP door already
19
+ gives an agent that connects, so the two surfaces do not disagree about whether
20
+ this record knows what it is.
21
+
22
+ The scaffold's opening paragraphs are authoring guidance, not scope, so the
23
+ template is detected across the whole body rather than paragraph by paragraph:
24
+ publishing instructions-to-the-author as a description would be worse than
25
+ admitting there is none.
26
+
27
+ - 0fe759d: Three defects found by auditing 0.0.10 against a live record
28
+
29
+ **A repeated `sslmode` was read the wrong end.** `pinnedTlsDsn` took the FIRST
30
+ value of a repeated parameter; `pg` takes the LAST. So on
31
+ `?sslmode=require&sslmode=disable` — whose effective mode is `disable` — the pin
32
+ saw a weak mode, collapsed the duplicates into one `verify-full`, turned TLS on,
33
+ and printed "TLS verified" at an operator whose DSN ended in `disable`. The
34
+ direction was safe; silently overruling an explicit opt-out and then misreporting
35
+ it was not. All three TLS functions now read the mode the driver will use.
36
+
37
+ The same sweep asserted the larger worry the pin creates — that re-serializing a
38
+ connection string could alter a credential. Seventeen DSNs with the passwords
39
+ people actually paste (raw `@`, spaces, `%`, `+`, brackets, non-ASCII,
40
+ percent-encoded separators) are now checked field by field against `pg`'s own
41
+ resolved view: everything the driver derives is byte-identical, and so is the
42
+ TLS decision.
43
+
44
+ **The outline's `position` disclosed documents an audience may not see.** It was
45
+ the rank in the whole record, so a public caller received 1, 3, 4 — a gap exactly
46
+ where an internal sibling sat, telling them something exists and roughly where.
47
+ The same row's `child_count` was already computed over visible children only, so
48
+ one response object disagreed with itself. `position` is now the rank among the
49
+ siblings the caller can see, computed as a window over the filtered set so it
50
+ stays correct across pages and at every depth, and both it and `depth` say what
51
+ they are in the tool schema.
52
+
53
+ **`ksor serve` now says when the record has no identity yet.** The MCP door
54
+ already refused to pass an unedited `instance.md` to agents as instructions —
55
+ it substitutes a plain statement that the scope is unstated — but the operator
56
+ starting the server was told nothing, so a record serving with no declared
57
+ identity looked exactly like one that had been described. It is a boot line now,
58
+ beside the abstention posture: both answer "how much should I trust this".
59
+
60
+ - f5cd885: The bearer door's key line joins the boot block instead of interrupting it
61
+
62
+ In bearer mode the line naming where the signing keys were discovered printed
63
+ before the aligned posture block and in a different shape, so it read as a stray
64
+ log line rather than as part of what the server was telling you about itself. It
65
+ is a `keys` row in the block now, under `auth`, resolved at boot exactly as
66
+ before.
67
+
68
+ - 5f30b5f: The site build no longer fails when two evaluations of the record staging overlap
69
+
70
+ The scaffold stages a per-audience copy of the record before the site build
71
+ reads it, removing the previous stage first. `rmSync(..., { force: true })`
72
+ suppresses ENOENT but retries nothing: Node retries EBUSY / EMFILE / ENFILE /
73
+ ENOTEMPTY / EPERM only when `maxRetries` is set, and it defaults to zero. The
74
+ bundler evaluates the source config more than once when it wants it in more than
75
+ one place, so one run could remove the stage while another was still copying
76
+ into it — surfacing as `ENOTEMPTY` and failing the entire site build (seen once
77
+ in CI, 2026-08-21).
78
+
79
+ The removal now asks for those retries. Losing that race is safe: the stage is a
80
+ deterministic function of the record and the denylist, so redoing it produces
81
+ the same bytes.
82
+
83
+ Three claims in the scaffold's `AGENTS.md` that recent releases made false are
84
+ also corrected: `--actor` no longer "defaults to the operating user" (it is
85
+ required, and there is no default by design); the signing keys are discovered
86
+ from the SSO's own metadata rather than fetched from Better Auth's path; and the
87
+ `order:` key now drives the MCP `outline` tool alongside the sidebar and
88
+ `llms.txt`, which is what "one order drives every surface" was always supposed
89
+ to mean.
90
+
91
+ - 4a1c154: The shrink guard guards `ksor ingest --flip` again — it had stopped
92
+
93
+ `.env.example` documents `KSOR_MAX_SHRINK` as "a corpus that shrinks by more
94
+ than this FRACTION refuses to flip". In 0.0.10 it did not. Deleting eight of ten
95
+ documents and running `ksor ingest --flip` published the two that were left,
96
+ silently, exit 0.
97
+
98
+ The cause was the fix that stopped a refused ingest from publishing. That moved
99
+ the flip out of `buildGeneration` and into the command, so the governance gate
100
+ could run against the new generation BEFORE it became the active one — and the
101
+ shrink check, which lived inside the build's flip branch, was stepped straight
102
+ over. The library test that covers the guard stayed green throughout, because it
103
+ drives `buildGeneration` directly with `flip: true`, which is no longer the path
104
+ the CLI takes.
105
+
106
+ There is now one answer to "may this generation be activated" — `flipRefusal` —
107
+ and both flip paths ask it, in the same transaction as the flip itself. The new
108
+ test drives the command rather than the library, so a guard that only one of two
109
+ paths performs fails the tier that proves it.
110
+
111
+ Verified against a live record: a 10 → 2 node build now names all eight removed
112
+ documents, refuses with exit 1, and leaves the previous generation serving.
113
+
114
+ ## 0.0.10
115
+
116
+ ### Patch Changes
117
+
118
+ - c07a5db: `ksor calibrate` states what its measurement is worth, not just its result
119
+
120
+ The default door synthesizes in-corpus questions by asking a model to write one
121
+ FROM each sampled passage, then scores those questions against the corpus that
122
+ contains the passage. They share vocabulary a reader's question will not, so the
123
+ in-corpus distribution sits higher than real traffic will and the separation the
124
+ run reports is an UPPER BOUND. The `--queries-file` door carried a caveat about
125
+ its own distribution; the default door — the biased one — carried none.
126
+
127
+ Found live: a real record calibrated this way reported min in-corpus 0.682
128
+ against max out-of-corpus 0.580 and recommended `vector_floor: 0.631`. Questions
129
+ the record demonstrably answers then scored 0.530 to 0.606 — every one below the
130
+ recommended floor. Pasting it would have made the record abstain on questions
131
+ whose answers it had just cited, which is the failure abstention exists to
132
+ prevent, arrived at from the other side.
133
+
134
+ The block now carries that caveat, and prints the one number it always left the
135
+ reader to work out: the separation margin, with the probe counts behind it. A
136
+ margin of 0.054 over six in-corpus and four out-of-corpus probes is a different
137
+ claim from the same margin over sixty, and both figures were already on the
138
+ report without ever reaching the page. The mathematics and the recommended value
139
+ are unchanged.
140
+
141
+ It also names the generation it measured. With nothing pinned — the ordinary
142
+ case, calibrating what is being served — the report carried no generation at
143
+ all, so the provenance comment beside a pasted floor read `on generation unknown
144
+ (no generation pinned)`. A floor is a threshold inside ONE generation's embedding
145
+ space, and the query that counts the chunks had already resolved which one.
146
+
147
+ `runCalibration` had no test of any kind; it has one now, against real Postgres.
148
+
149
+ - d00f3a2: The signing keys are discovered, not guessed — any standards-compliant SSO now works.
150
+
151
+ `KSOR_SSO_URL` is documented as "the AS base", and the verifier appended one
152
+ vendor's layout to it (`/api/auth/jwks`, Better Auth's). Auth0, Okta, Entra,
153
+ Keycloak, Cognito and Google all publish elsewhere, so every one of them failed
154
+ the key fetch — which is classified transient, so the door booted clean and
155
+ returned 503 to every request with nothing naming the cause. The only posture an
156
+ operator could actually reach was `KSOR_ALLOW_PUBLIC_UNAUTHENTICATED=1`: the one
157
+ key we handed people was the one that props the door open.
158
+
159
+ `jwks_uri` is now read from the SSO's own metadata document — RFC 8414 first,
160
+ then OpenID Discovery — with `KSOR_JWKS_URL` kept as an explicit override, and
161
+ the vendor path kept as a last resort that reports itself as a guess. Where the
162
+ keys came from is stated on the boot line.
163
+
164
+ Verified against three real providers: Google (RFC 8414, cross-origin
165
+ `jwks_uri`), GitHub Actions OIDC, and Entra — whose issuer carries a path,
166
+ the case a naive `${sso}/.well-known/…` gets wrong.
167
+
168
+ Discovery never refuses to boot: an unreachable AS falls back and says so.
169
+
170
+ - 9062088: A loopback authorization server's keys are reachable again.
171
+
172
+ JWKS discovery refused a cleartext `jwks_uri`, which is right for a network AS
173
+ and wrong for a local one: a dev authorization server on loopback advertises
174
+ `http://127.0.0.1:…/jwks`, the resolver refused it, the vendor guess was used
175
+ instead, and every request returned 503. `assertHttpUrl` already exempts
176
+ loopback for the SSO base for exactly this reason; the resolver now does too.
177
+ Cleartext to any non-loopback host is still refused.
178
+
179
+ Found by writing the first test that boots the gateway in bearer mode.
180
+
181
+ - 995ec48: A governance act names its actor; the tool no longer guesses one.
182
+
183
+ `ksor takedown --actor` fell back to `$USER` / `$USERNAME` / `"operator"`, so a
184
+ ledger row read `runner` under CI and `root` in a container — a self-asserted
185
+ string wearing a schema, indistinguishable from a person who was never there.
186
+ `retrieval_log.actor` is `NOT NULL` with the comment "NO default: unset errors
187
+ loudly", and the fallback is precisely what stopped it erroring.
188
+
189
+ Denying or revoking now REFUSES without `--actor`, before the DSN is resolved:
190
+ a missing actor is an argument error (exit 1), not an environment one. The
191
+ read-only modes — `--list`, `--ledger`, `--export` — write no ledger row and
192
+ need nothing.
193
+
194
+ - 41b0c38: Reading order is one rule again: the MCP door now reads `order:`
195
+
196
+ `order:` is the only ordering key a record may declare — it is in the governed
197
+ frontmatter set the format checker closes, and the checker's own remedy for a
198
+ stray `meta.json` says so. The MCP door never read it. The tree adapter was
199
+ converted from the predecessor, whose ordering keys were Docusaurus's
200
+ `position:` / `sidebar_position:`, neither of which a compliant record may
201
+ declare — so `outline` reported the record's structure in filename order and
202
+ called it the reading order, while the website honoured `order:`. On a
203
+ curriculum, where reading order IS the content, an agent asking "what do I read
204
+ first" got a different answer from the two surfaces.
205
+
206
+ Four smaller disagreements went with it, each now a row in a shared decision
207
+ table: unordered documents sorted at 10 000 rather than after everything;
208
+ fractional orders were truncated; ties compared `example.md` against
209
+ `example-two.md`, where `.` sorts after `-`, reversing ordinary pairs; and one
210
+ side folded case while the other did not.
211
+
212
+ The rule now lives in one file, copied into the scaffold and asserted
213
+ byte-identical, with `ORDER_CASES` run against BOTH surfaces — so a surface that
214
+ drifts fails on the row it broke.
215
+
216
+ - 41b0c38: `ksor serve` reports its own posture instead of forwarding other people's warnings
217
+
218
+ Booting printed four alarming paragraphs at an operator who had done nothing
219
+ wrong: the driver's multi-line `SECURITY WARNING` about `sslmode` aliases, ksor's
220
+ own three-line restatement of the same thing, and the MCP SDK's note about a
221
+ `responseMode` ksor chose deliberately.
222
+
223
+ The driver's warning is correct and its remedy is one word, so ksor now applies
224
+ it: a remote `sslmode=require|prefer|verify-ca` is rewritten to `verify-full`
225
+ before the connection is made. The connection is unchanged today — pg 8 was
226
+ already resolving all three to full verification, which is the entire content of
227
+ its warning — and it can no longer be silently downgraded by a driver upgrade.
228
+ The SDK's note describes a recorded decision, not a defect, and is suppressed by
229
+ exact message so that anything else it says still reaches the operator.
230
+
231
+ What is left is the record's posture, aligned and in ksor's own voice, with the
232
+ two lines that decide whether to trust what happens next saying what they mean:
233
+ auth `DISABLED` now names the bind it is survivable on, and an absent abstention
234
+ floor says out-of-corpus questions will be answered rather than refused.
235
+
236
+ - bea7d80: `read` names every section it will accept, not just the top-level ones
237
+
238
+ `read` resolves a `heading` three ways: a full heading path, any prefix of one,
239
+ and a section's last segment when that segment is unique in the document. The
240
+ error for a section it could not find listed only the TOP-LEVEL segments — a
241
+ strict subset of its own vocabulary — so it reported real, reachable sections as
242
+ absent. Found live: a nested section was refused by name and served on the next
243
+ call under the same name.
244
+
245
+ The error now lists the full heading paths (the form that always resolves and
246
+ never collides), states the unique-last-segment shorthand rather than doubling
247
+ the list to enumerate it, and counts the tail past twenty instead of printing an
248
+ unbounded list. The `heading` input and the `sections` output now describe the
249
+ same vocabulary in the tool schema, where an agent reads it before making the
250
+ call rather than after failing one.
251
+
252
+ - 4631268: Correct every remaining document that said `pnpm serve` publishes.
253
+
254
+ `serve` was `pnpm schema && pnpm grant && pnpm ingest && ksor serve` and is now
255
+ `ksor serve` alone. Three adopter-facing places still described the old chain:
256
+ the scaffolded `instance.md`'s own comment ("copy .env.example to .env, then run
257
+ `pnpm serve`"), the scaffold README's file table ("the agent surface: schema →
258
+ grant → ingest → serve"), and AGENTS.md's runbook ("`pnpm serve` is the only
259
+ command this rung needs"). Following any of them serves an empty record.
260
+
261
+ All three now say the same thing the CLI does: `pnpm provision` once,
262
+ `pnpm refresh` to publish, `pnpm serve` to serve — and why publishing is
263
+ separate, since a restart or an autoscaling event must not republish a record.
264
+
265
+ A test now asserts the CLAIM rather than the command. The existing guard could
266
+ not catch this: it checks that a named command exists, and `pnpm serve` does
267
+ exist — what was wrong was the sentence attached to it.
268
+
3
269
  ## 0.0.9
4
270
 
5
271
  ### Patch Changes