@panaversity/ksor 0.0.53 → 0.0.55
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 +328 -0
- package/README.md +30 -0
- package/dist/checker/check-main.mjs +5 -2
- package/dist/cli.mjs +319 -60
- package/dist/{gateway-api-Ck1l_Sgs-B-_wMYLB.mjs → gateway-api-C0vL3oOK-D24n786A.mjs} +221 -20
- package/dist/gateway.d.mts +2 -2
- package/dist/gateway.mjs +1 -1
- package/docs/deploying.md +48 -9
- package/docs/ingesting.md +30 -8
- package/package.json +1 -1
- package/templates/scaffold/.agents/skills/format-checker/check.mjs +3 -0
- package/templates/scaffold/.claude/skills/format-checker/check.mjs +3 -0
- package/templates/scaffold/AGENTS.md +42 -5
- package/templates/scaffold/README.md +79 -13
- package/templates/scaffold/env.example +6 -1
- package/templates/scaffold/gitignore +4 -3
- package/templates/scaffold/mcp.json +13 -0
- package/templates/scaffold/system/site/lib/lock.ts +8 -1
- package/templates/scaffold/system/site/lib/people-rule.ts +56 -0
- package/templates/scaffold/system/site/lib/people.ts +5 -24
- package/templates/scaffold/system/site/lib/stage-knowledge.ts +2 -0
- package/templates/scaffold/system/site/record/load.ts +11 -1
- package/templates/scaffold/system/site/record/lock.ts +12 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,333 @@
|
|
|
1
1
|
# @panaversity/ksor
|
|
2
2
|
|
|
3
|
+
## 0.0.55
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 4ecf549: Test infrastructure only — nothing an adopter installs behaves differently.
|
|
8
|
+
|
|
9
|
+
The behavioural evals scored a missing `top_cosine` as `-1`. When a provider
|
|
10
|
+
rate-limits, the read plane degrades to keyword-only by design, so searches
|
|
11
|
+
answer with no score — and the assertions then compared sentinels, reporting a
|
|
12
|
+
vendor outage as "the abstention floor is broken". Four CI failures in a day
|
|
13
|
+
read that way before anyone looked past the assertion. A missing score now
|
|
14
|
+
refuses, naming the cause, and never invents the number that is absent.
|
|
15
|
+
|
|
16
|
+
- e476197: Fix the hello-world tutorial, which could not be completed as written.
|
|
17
|
+
|
|
18
|
+
Three defects, all found by walking the published 0.0.54 rather than by reading:
|
|
19
|
+
|
|
20
|
+
- Step 3's document declared `type: Policy`. `Policy` is a reserved type, so the
|
|
21
|
+
record demands `sources` — `ksor build`, `npm run check` and the dev server all
|
|
22
|
+
refused it, and steps 4 through 10 were unreachable. It is now `type: Document`,
|
|
23
|
+
the type the profile promises never to reserve, with a note on why and on what
|
|
24
|
+
an agent should do when it reaches for a reserved one.
|
|
25
|
+
- Step 1 scaffolds with `npx`, which emits an **npm** project, and every command
|
|
26
|
+
after it said `pnpm`. On that project `pnpm install && pnpm dev` fails with
|
|
27
|
+
`sh: next: command not found`. All sixteen commands are npm's now, and the step
|
|
28
|
+
that explains manager detection says which one the rest of the tutorial speaks.
|
|
29
|
+
- The captured outputs had been trimmed after capture, in a document whose second
|
|
30
|
+
paragraph promises they were "pasted as it appeared": `ksor serve`'s boot report
|
|
31
|
+
was missing the `trust` line it has always printed, the build outputs were
|
|
32
|
+
missing their timestamp, `source:` and `wrote` lines, and the port-conflict
|
|
33
|
+
refusal was quoted offering `pnpm serve` where it says `ksor serve`.
|
|
34
|
+
|
|
35
|
+
The walk also surfaced that `ksor init` leaves a repo with no commits, so every
|
|
36
|
+
reader's first build prints `source: unspecified`. Rather than hide it, the
|
|
37
|
+
tutorial now shows it and folds `git commit` into the approval step — which is
|
|
38
|
+
where provenance belongs anyway, and which lets the second build print a real
|
|
39
|
+
commit sha.
|
|
40
|
+
|
|
41
|
+
The tutorial also said `.mcp.json`'s "first is Neon's" and named the second
|
|
42
|
+
server nowhere, and said nothing about the Neon server acting on the whole Neon
|
|
43
|
+
account. Both are fixed here for the tutorial; the emitted scaffold's copies of
|
|
44
|
+
the same two defects are fixed separately.
|
|
45
|
+
|
|
46
|
+
Only the tutorial and the test that pins its prompts changed; nothing an adopter
|
|
47
|
+
installs behaves differently.
|
|
48
|
+
|
|
49
|
+
- ae49524: Stop a spent OpenAI balance from quarantining content and flipping a generation,
|
|
50
|
+
and name the right variable when a provider key is missing.
|
|
51
|
+
|
|
52
|
+
**The serious one.** `insufficient_quota` — OpenAI's answer to an exhausted
|
|
53
|
+
balance, which arrives as 429 like an ordinary rate limit — was classified
|
|
54
|
+
non-retryable, correctly, because no amount of waiting adds credit. But
|
|
55
|
+
"non-retryable" is what the ingest drain reads as **poison chunk**: it
|
|
56
|
+
binary-splits the batch down to singletons and marks each `failed`. A spent
|
|
57
|
+
balance arrives on _every_ chunk, so a run walked the queue quarantining
|
|
58
|
+
everything it touched; if the failed fraction stayed under
|
|
59
|
+
`MAX_FAILED_FRACTION` (2%), `generationReady` admitted it and the generation
|
|
60
|
+
**flipped** — publishing a record in which exactly the passages the owner had
|
|
61
|
+
just edited were unsearchable, `ksor ingest` exit 0, the billing reason visible
|
|
62
|
+
only in `chunks.embed_error`. The same event on Gemini aborts the run, so
|
|
63
|
+
switching provider silently changed what a spent quota does.
|
|
64
|
+
|
|
65
|
+
The drain now has three answers instead of two: retryable (abort, chunks stay
|
|
66
|
+
pending), **fatal** (abort the same way, but without spending five backoffs
|
|
67
|
+
first — the account is what is wrong, not the passage), and everything else
|
|
68
|
+
(binary-split to the poison chunk). `isFatal` is optional on `EmbeddingProvider`,
|
|
69
|
+
so a provider that cannot tell keeps the old two-kind behaviour and Gemini's
|
|
70
|
+
path is unchanged.
|
|
71
|
+
|
|
72
|
+
**The missing-key refusal names the variable.** `ksor serve` on an
|
|
73
|
+
`embedding.provider: openai` record said `embedding provider "openai" needs an
|
|
74
|
+
API key and none was supplied` and stopped — while `ksor serve --help`,
|
|
75
|
+
`env.example` and `docs/deploying.md` all named `GEMINI_API_KEY`, which that
|
|
76
|
+
door does not read. The registry row already held `keyEnv`; it now reaches the
|
|
77
|
+
operator (`— set OPENAI_API_KEY`), and all three documents describe the choice
|
|
78
|
+
instead of one vendor.
|
|
79
|
+
|
|
80
|
+
**`ksor calibrate`'s Gemini requirement is stated rather than papered over.**
|
|
81
|
+
Question synthesis is Gemini-only today, so a record embedding with
|
|
82
|
+
`OPENAI_API_KEY` is still refused for a Google key when calibrating through the
|
|
83
|
+
synthesized door. That gap is now said plainly in the refusal and in
|
|
84
|
+
`docs/ingesting.md`, which taught calibration without mentioning it. The
|
|
85
|
+
`--queries-file` door avoids it entirely.
|
|
86
|
+
|
|
87
|
+
**The OpenAI live test announces itself.** It is gated on `OPENAI_API_KEY`, no
|
|
88
|
+
workflow supplied one, and a false `describe.runIf` contributes nothing to a run
|
|
89
|
+
— so the suite its own header calls "the tripwire for vendor drift" was absent
|
|
90
|
+
from CI and reported as absent by nobody. It now prints `skipped — set
|
|
91
|
+
OPENAI_API_KEY`, the way Gemini's does, and CI passes the secret so the tripwire
|
|
92
|
+
arms the moment one is added.
|
|
93
|
+
|
|
94
|
+
Found by an adversarial review of this week's commits.
|
|
95
|
+
|
|
96
|
+
- ff99eb5: Hash `.ksor/people.yaml` into `build_id`, so the two surfaces of one build
|
|
97
|
+
cannot publish different provenance.
|
|
98
|
+
|
|
99
|
+
The phone book added in 0.0.53 rewrites the actor printed on every Owner,
|
|
100
|
+
Approved, Withdrawn and Trust row — `displayActor` replaces `human:contractor-a`
|
|
101
|
+
with "Human: Jane Doe, VP Compliance", and the identifier does not appear on the
|
|
102
|
+
page at all. It was hashed by nothing. `.ksor/governance.yaml` and
|
|
103
|
+
`.ksor/takedowns.yaml` are both in `build_id`; this one was left out, on the
|
|
104
|
+
stated reasoning that including it would refuse the next site build after a
|
|
105
|
+
spelling correction.
|
|
106
|
+
|
|
107
|
+
That is the trade critical rule 1 forbids, and the consequence was reachable
|
|
108
|
+
without doing anything unusual: edit a name, `pnpm check` stays green,
|
|
109
|
+
`ksor build` emits a byte-identical lock, and the deployed page publishes an
|
|
110
|
+
approver the `/md/` twin stamped with that same `build_id` contradicts. An
|
|
111
|
+
auditor reconciling the page against the lock finds nothing wrong, because the
|
|
112
|
+
string they are auditing was never covered by it.
|
|
113
|
+
|
|
114
|
+
Now: `people_sha256` joins `policy_sha256` and `ledger_sha256` in the lock and
|
|
115
|
+
in `build_id`; `.ksor/people.yaml` joins the inputs that move `source_commit`;
|
|
116
|
+
and the site's staleness gate compares it like the other three, so an edit the
|
|
117
|
+
lock never saw refuses with `ksor-lock-stale` naming the file. Refusing until
|
|
118
|
+
`ksor build` is re-run is the behaviour, not a regression — it is what every
|
|
119
|
+
other published byte already does.
|
|
120
|
+
|
|
121
|
+
Two things found alongside it, in the same file:
|
|
122
|
+
|
|
123
|
+
- `people.ts` claimed "duplicate keys are refused by the parser rather than
|
|
124
|
+
resolved by whichever came last". They were not. `uniqueKeys: true` makes the
|
|
125
|
+
parser RECORD a duplicate; `toJS()` still resolves last-wins, and nothing read
|
|
126
|
+
the errors — so two entries for one actor published the second person's name
|
|
127
|
+
on the first person's approval, the precise collision the map replaced a name
|
|
128
|
+
derivation to avoid. A duplicate now drops the whole book, and identifiers are
|
|
129
|
+
published instead.
|
|
130
|
+
- The rule lived behind a module that reads `instance.md` on import, so it could
|
|
131
|
+
only be tested by building a record on disk — which is why it shipped asserted
|
|
132
|
+
by a comment. It is now a leaf, `lib/people-rule.ts`, with the shipped
|
|
133
|
+
function under test.
|
|
134
|
+
|
|
135
|
+
**Upgrading:** a lock written before this refuses with `ksor-lock-invalid`
|
|
136
|
+
naming `people_sha256`; run `ksor build` and commit the lock it writes.
|
|
137
|
+
|
|
138
|
+
Found by an adversarial review of this week's commits.
|
|
139
|
+
|
|
140
|
+
- b45d477: Say what the scaffold's `.mcp.json` attaches to an adopter's coding agent, and
|
|
141
|
+
stop the README telling them to destroy it.
|
|
142
|
+
|
|
143
|
+
`ksor init` emits `.mcp.json` with two servers. The emitted README and AGENTS.md
|
|
144
|
+
both said "the first is Neon" and named the second nowhere — so
|
|
145
|
+
`agentfactory-system-of-record`, a Panaversity-operated endpoint, was wired into
|
|
146
|
+
every adopter's coding agent with no emitted document mentioning it. `.mcp.json`
|
|
147
|
+
attaches servers to the agent that OPERATES the record; a server nobody
|
|
148
|
+
documented is a capability nobody reviewed.
|
|
149
|
+
|
|
150
|
+
Both are now named, with what each is and that either may be deleted. The second
|
|
151
|
+
is described as what it is: a read-only example record that is **not** the
|
|
152
|
+
adopter's and that nothing in the project depends on.
|
|
153
|
+
|
|
154
|
+
The Neon step also said only that the server exists. It acts on the Neon
|
|
155
|
+
_account_ — an agent holding it can create and delete projects and branches — so
|
|
156
|
+
the README and AGENTS.md now say that before handing over a prompt that runs
|
|
157
|
+
against real infrastructure, and point at Neon's own documentation for the
|
|
158
|
+
scopes rather than paraphrasing them.
|
|
159
|
+
|
|
160
|
+
And the "Test the door with an actual agent" section told the adopter to _write_
|
|
161
|
+
`.mcp.json` with a file containing only `test-record` — overwriting the Neon
|
|
162
|
+
entry the same README depends on two sections earlier — and then closed with
|
|
163
|
+
"Delete `.mcp.json`, or keep it". It now shows the entry to **add**, and says not
|
|
164
|
+
to delete the file.
|
|
165
|
+
|
|
166
|
+
A guard derived from `mcp.json` itself asserts every server key appears in both
|
|
167
|
+
emitted documents, so adding a server and saying nothing fails on the server
|
|
168
|
+
that was added. Mutation-tested: unnaming the second server turns both red.
|
|
169
|
+
|
|
170
|
+
Found by an adversarial review of this week's commits. Whether the scaffold
|
|
171
|
+
should ship a second, vendor-operated MCP record at all is an owner question and
|
|
172
|
+
is untouched here.
|
|
173
|
+
|
|
174
|
+
- 5283084: Test infrastructure only — nothing an adopter installs behaves differently.
|
|
175
|
+
|
|
176
|
+
A skill's `description` is its trigger and nothing measured it (#30). Every
|
|
177
|
+
prompt the hello world tells a reader to say is now matched to a shipped skill
|
|
178
|
+
or recorded as needing none, and each skill's trigger phrases are pinned — so
|
|
179
|
+
narrowing one, the failure mode where a skill silently stops firing, goes red
|
|
180
|
+
naming the phrase. The model-scored half of that issue is untouched.
|
|
181
|
+
|
|
182
|
+
## 0.0.54
|
|
183
|
+
|
|
184
|
+
### Patch Changes
|
|
185
|
+
|
|
186
|
+
- 3f55e91: `ksor calibrate` names the free-tier path when a quota refuses it, and the
|
|
187
|
+
calibration text model moves to `gemini-3.7-flash`.
|
|
188
|
+
|
|
189
|
+
Walked on a real free-tier key: embedding is free of charge and a first corpus
|
|
190
|
+
embeds fine (23 chunks, 0 failed), but the DEFAULT calibration door writes one
|
|
191
|
+
probe question per sampled passage with an LLM — and the free tier allows five
|
|
192
|
+
generations a minute. So the documented way to turn on the product's headline
|
|
193
|
+
feature failed, surfacing the vendor's sentence and nothing else.
|
|
194
|
+
|
|
195
|
+
Two quotas reach that code and they need opposite answers: the generation cap is
|
|
196
|
+
a wall no wait clears (the remedy is `--queries-file`, the zero-LLM door), and
|
|
197
|
+
the embedding cap is a per-minute window (the remedy is to wait, and the usual
|
|
198
|
+
cause is an ingest immediately before). Both are now named, with why. A 429 this
|
|
199
|
+
does not recognise is re-thrown untouched — an invented remedy is worse than the
|
|
200
|
+
vendor's own message.
|
|
201
|
+
|
|
202
|
+
`docs/ingesting.md` documents the zero-LLM door where the reader meets the
|
|
203
|
+
command, including how to choose the questions: the floor is set by the weakest
|
|
204
|
+
one, so a vague question drags it down and a question the record cannot answer
|
|
205
|
+
invalidates the measurement.
|
|
206
|
+
|
|
207
|
+
The text model moves `gemini-2.5-flash` → `gemini-3.7-flash`. Cheap, unlike the
|
|
208
|
+
embedding model: it only writes probe questions, so nothing stored is
|
|
209
|
+
re-computed and no floor is invalidated — and the door is recorded beside every
|
|
210
|
+
number, which is what stops two measurements being compared as one experiment.
|
|
211
|
+
|
|
212
|
+
**And calibration now embeds on the patient retry.** It used the READ plane's
|
|
213
|
+
door, which never retries a 429 — correct for a live search, which should
|
|
214
|
+
degrade to keyword-only in under a second rather than stall a reader behind
|
|
215
|
+
backoff, and wrong for a measurement nobody is waiting on. So a free-tier key
|
|
216
|
+
that rate-limited mid-run refused the whole calibration. The intent stays
|
|
217
|
+
`query` (a floor must be measured through the label the door searches with);
|
|
218
|
+
only the retry policy moves, to the one `isRetryable`'s own comment describes
|
|
219
|
+
for batch work. Calibration's text generation already took that path, so this
|
|
220
|
+
was the two halves of one act disagreeing.
|
|
221
|
+
|
|
222
|
+
- dd6371b: Two things: a false claim removed from a shipped page, and the scaffold gains
|
|
223
|
+
`.mcp.json`.
|
|
224
|
+
|
|
225
|
+
**The false claim.** `docs/deploying.md` told adopters "The MCP surface already
|
|
226
|
+
applies the audience scope **per request**", under the heading of the very
|
|
227
|
+
requirement it does not meet. It does not: `content-gateway/src/compose.ts`
|
|
228
|
+
reads `KSOR_AUDIENCE` from the environment once at boot into a per-process
|
|
229
|
+
viewer, and the request path never touches it — `docs/authorization.md` says so
|
|
230
|
+
plainly ("Any caller holding a valid token gets the whole record") and
|
|
231
|
+
`specs/ksor/serve/spec.md` names per-request visibility filtering as out of
|
|
232
|
+
scope. A reader who believed the page would point every caller at one door and
|
|
233
|
+
serve them the restricted half. The page now says what the door does — one
|
|
234
|
+
viewer per door, so one process per audience — and separates the audit it does
|
|
235
|
+
give (a `retrieval_log` row naming the verified caller) from the authorization
|
|
236
|
+
it does not. A docs-truth assertion now fails on the claim itself, not merely on
|
|
237
|
+
a command that no longer exists.
|
|
238
|
+
|
|
239
|
+
**`.mcp.json`.** The scaffold's closed root set gains one member: the MCP
|
|
240
|
+
servers a coding agent may reach from the project. It ships with Neon's, which
|
|
241
|
+
turns the step the tool could never do for an adopter — provision a Postgres,
|
|
242
|
+
enable pgvector, produce a connection string — into four real tool calls
|
|
243
|
+
(`create_project`, `run_sql`, `create_branch`, `get_connection_string`) and one
|
|
244
|
+
sentence to the agent. The scaffold's README and AGENTS.md carry that sentence,
|
|
245
|
+
and both now say plainly which step no agent can do at any price: the embedding
|
|
246
|
+
API key, which no vendor mints over a protocol. Committed rather than ignored,
|
|
247
|
+
because both entries authenticate interactively and the file carries no secret —
|
|
248
|
+
stated, because pasting an API key into it would change that.
|
|
249
|
+
|
|
250
|
+
- eebd777: OpenAI joins Gemini behind the embedding seam, and the wiring stops naming one
|
|
251
|
+
vendor (issue #25).
|
|
252
|
+
|
|
253
|
+
The seam was already vendor-neutral in shape — `EmbeddingProvider`, the
|
|
254
|
+
framework's normalization and degeneracy checks, and an embedding space
|
|
255
|
+
identified by `modelId` + column width and never by the vendor. What was
|
|
256
|
+
Gemini-bound was the WIRING: `GEMINI_API_KEY` was spelled into three composition
|
|
257
|
+
roots, so a second provider could not obtain a key even though the registry
|
|
258
|
+
would happily build it. Each registry row now names its own key variable, and
|
|
259
|
+
the roots ask, exactly as `instance.md` names the DSN variable rather than
|
|
260
|
+
hardcoding it.
|
|
261
|
+
|
|
262
|
+
`provider: openai` with `model: text-embedding-3-small` and `dim: 1536` reads
|
|
263
|
+
`OPENAI_API_KEY`. Over `fetch`, no SDK — the same call decision 12's 2026-08-22
|
|
264
|
+
revision made for Gemini, and for the same reason.
|
|
265
|
+
|
|
266
|
+
Two things a live call surfaced that a stub would not. Response items carry
|
|
267
|
+
their own `index` and the vendor does not promise array order, so they are
|
|
268
|
+
sorted before the framework pairs them positionally — a shuffled response is the
|
|
269
|
+
same count, the same width and all finite, so every downstream check passes
|
|
270
|
+
while every passage carries another's vector. And an exhausted balance arrives
|
|
271
|
+
as **429**, the same status as a rate limit: it is now read from the vendor's
|
|
272
|
+
`error.type` and never retried, because five exponential backoffs do not add
|
|
273
|
+
credit.
|
|
274
|
+
|
|
275
|
+
Switching provider is a re-embed of the whole corpus and a re-measured
|
|
276
|
+
`vector_floor`. A different provider is a different embedding space, and the
|
|
277
|
+
invariant against copying a calibrated constant applies across vendors with more
|
|
278
|
+
force, not less.
|
|
279
|
+
|
|
280
|
+
Verified live against the real API on a funded key: 1536-dimension vectors, a
|
|
281
|
+
paraphrase at cosine 0.812 against an unrelated sentence at 0.058, and the two
|
|
282
|
+
intents agreeing to 0.9997 — which is the symmetry that makes the empty task
|
|
283
|
+
labels correct. Then through the whole plane: a real record ingested to Postgres
|
|
284
|
+
under `embedding_model = text-embedding-3-small`, 23 chunks, 0 failed, stored at
|
|
285
|
+
the declared width of 1536 and L2-normalized as the framework promises.
|
|
286
|
+
|
|
287
|
+
One more defect the live call found: `buildShippedProvider` handed EVERY
|
|
288
|
+
provider Gemini's task labels from global config, so an OpenAI run logged its
|
|
289
|
+
space as `text-embedding-3-small/d1536/RETRIEVAL_DOCUMENT` — a label that vendor
|
|
290
|
+
has no concept of and never received. The labels moved onto the registry row,
|
|
291
|
+
where a vendor's shape belongs.
|
|
292
|
+
|
|
293
|
+
- eb54871: The npm page shows what the product does, on the first screen.
|
|
294
|
+
|
|
295
|
+
It asserted the headline behaviour — a cited answer, an honest refusal — and
|
|
296
|
+
demonstrated it nowhere. A reader had no way to tell a real mechanism from a
|
|
297
|
+
prompt instruction, which is exactly the skepticism this product exists to
|
|
298
|
+
answer. It now shows three things, all of them real output: the admitted count
|
|
299
|
+
moving when a human approves a draft, the `provenance` and `governance` a search
|
|
300
|
+
hit carries, and an abstention envelope. It also links the hello world.
|
|
301
|
+
|
|
302
|
+
Nothing was added to the tarball; this is the README npm renders.
|
|
303
|
+
|
|
304
|
+
- 6e5ff3e: Repo documentation and a test only — nothing an adopter installs changes.
|
|
305
|
+
|
|
306
|
+
`docs/status.md` named 0.0.42 while the published package was 0.0.53. Authority
|
|
307
|
+
rule 3 makes that file the only authority on what is built, and it is the first
|
|
308
|
+
thing an evaluator's coding agent reads. It is current now, and a docs-truth
|
|
309
|
+
assertion holds it equal to `packages/ksor/package.json` so a Version PR cannot
|
|
310
|
+
bump one without the other. It also records that the full kernel walk was re-run
|
|
311
|
+
against 0.0.53 — it had last run against 0.0.18, thirty-five releases earlier.
|
|
312
|
+
|
|
313
|
+
- ed947c1: The deploy runbook stops ruling out the one step Vercel calls required.
|
|
314
|
+
|
|
315
|
+
`docs/deploying.md` said the silent-404 failure "does not depend on the
|
|
316
|
+
Application Preset". Vercel's own guide says the opposite: a project builds as
|
|
317
|
+
services only when the preset is `Services` AND `vercel.json` carries a
|
|
318
|
+
`services` key, and "if either is missing, Vercel falls back to its default
|
|
319
|
+
framework detection and ignores your services configuration" — which is that
|
|
320
|
+
failure exactly, and no file in the repository can set a project setting.
|
|
321
|
+
|
|
322
|
+
One measurement of ours disagrees with that guide and is recorded rather than
|
|
323
|
+
reconciled: two projects read back from the API, one `Services` and one `Other`,
|
|
324
|
+
both built and served. Both facts are real; guessing between them is what
|
|
325
|
+
produced the sentence that steered adopters away from the fix.
|
|
326
|
+
|
|
327
|
+
The scaffold's runbook now sets the preset at step 2, and ends with the three
|
|
328
|
+
curls that tell a live deployment from a Ready-and-404 one — `/mcp` answering
|
|
329
|
+
405 is the door refusing a GET, which is how you know it is routed at all.
|
|
330
|
+
|
|
3
331
|
## 0.0.53
|
|
4
332
|
|
|
5
333
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -16,6 +16,36 @@ pnpm install
|
|
|
16
16
|
pnpm dev # the site, live at http://localhost:3000
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
+
Then write a document and publish it:
|
|
20
|
+
|
|
21
|
+
```console
|
|
22
|
+
$ pnpm exec ksor build
|
|
23
|
+
ksor build: 6 document(s), 5 admitted to a machine surface
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**Six documents, five admitted.** The one you just wrote is a `draft`, so it
|
|
27
|
+
reaches nothing an AI agent reads — not `llms.txt`, not the markdown twins —
|
|
28
|
+
until a human approves it. Approve it and the count moves. That is the whole
|
|
29
|
+
product, and it costs nothing: no database, no API key, no account.
|
|
30
|
+
|
|
31
|
+
Climb one rung and an agent asks the record a question. The answer carries
|
|
32
|
+
where it came from:
|
|
33
|
+
|
|
34
|
+
```json
|
|
35
|
+
"provenance": { "stable_id": "knowledge/refund-policy", "generation": 1 },
|
|
36
|
+
"governance": { "status": "stable", "approval": { "by": "human:you" } }
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
…and a question the record does not cover is declined rather than guessed at:
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{ "ok": false, "abstained": true, "gate": { "floor": 0.622 } }
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**[Hello world](https://github.com/panaversity/ksor/blob/main/docs/tutorials/00-hello-world.md)**
|
|
46
|
+
walks all of that in about fifteen minutes. Every command and output in it was
|
|
47
|
+
run and pasted as it appeared — including the ones above.
|
|
48
|
+
|
|
19
49
|
One command emits a complete governed project: the record (`knowledge/`,
|
|
20
50
|
plain CommonMark), a working documentation site with hot reload, offline
|
|
21
51
|
search and `llms.txt`, adopter CI, a dependency-free format checker
|
|
@@ -10892,7 +10892,7 @@ var require_public_api = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
10892
10892
|
exports.stringify = stringify;
|
|
10893
10893
|
}));
|
|
10894
10894
|
//#endregion
|
|
10895
|
-
//#region ../content/dist/check-
|
|
10895
|
+
//#region ../content/dist/check-6fB_kR4G.mjs
|
|
10896
10896
|
var import_dist = (/* @__PURE__ */ __commonJSMin(((exports) => {
|
|
10897
10897
|
var composer = require_composer();
|
|
10898
10898
|
var Document = require_Document();
|
|
@@ -12150,6 +12150,7 @@ function changedFields(before, after) {
|
|
|
12150
12150
|
const CONTROL_FILES = [
|
|
12151
12151
|
"instance.md",
|
|
12152
12152
|
".ksor/governance.yaml",
|
|
12153
|
+
".ksor/people.yaml",
|
|
12153
12154
|
".ksor/takedowns.yaml"
|
|
12154
12155
|
];
|
|
12155
12156
|
/** Files the operating system writes behind the author's back: ignored, never reported. */
|
|
@@ -13714,7 +13715,7 @@ function checkAgainstPolicy(concept, policy, refusals) {
|
|
|
13714
13715
|
}
|
|
13715
13716
|
}
|
|
13716
13717
|
//#endregion
|
|
13717
|
-
//#region ../content/dist/record-
|
|
13718
|
+
//#region ../content/dist/record-Cxw0SUG3.mjs
|
|
13718
13719
|
const hex64 = string().regex(/^[0-9a-f]{64}$/, "a sha256 hex digest");
|
|
13719
13720
|
const viewerList = array(string().min(1));
|
|
13720
13721
|
const lockSchema = object({
|
|
@@ -13732,6 +13733,7 @@ const lockSchema = object({
|
|
|
13732
13733
|
drafts: _enum(["hidden", "shown"]),
|
|
13733
13734
|
instance_sha256: hex64,
|
|
13734
13735
|
policy_sha256: hex64,
|
|
13736
|
+
people_sha256: hex64,
|
|
13735
13737
|
ledger_sha256: hex64,
|
|
13736
13738
|
ledger_entries: array(object({
|
|
13737
13739
|
id: string().min(1),
|
|
@@ -13928,6 +13930,7 @@ const INPUTS = [
|
|
|
13928
13930
|
"knowledge",
|
|
13929
13931
|
"instance.md",
|
|
13930
13932
|
".ksor/governance.yaml",
|
|
13933
|
+
".ksor/people.yaml",
|
|
13931
13934
|
".ksor/takedowns.yaml"
|
|
13932
13935
|
];
|
|
13933
13936
|
function gitFacts(root) {
|