@panaversity/ksor 0.0.18 → 0.0.19
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 +54 -0
- package/dist/cli.mjs +33 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,59 @@
|
|
|
1
1
|
# @panaversity/ksor
|
|
2
2
|
|
|
3
|
+
## 0.0.19
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- aa4bdce: Record why the vector index is unused, and what fixing it would cost
|
|
8
|
+
|
|
9
|
+
Diagnosis only — no serving behaviour changes. Answers are unaffected, and were
|
|
10
|
+
already correct: the query plans a sequential scan, and a sequential scan is
|
|
11
|
+
EXACT. What grows with the corpus is the work, not the error.
|
|
12
|
+
|
|
13
|
+
The cause recorded until now — a window function, then joins and predicates
|
|
14
|
+
Postgres cannot estimate — was incomplete. Testing each clause on its own shows
|
|
15
|
+
a cost mispricing underneath: a full sequential pass over 20,000 chunks,
|
|
16
|
+
including 20,000 1536-dimension distance computations, is priced at 1904 for
|
|
17
|
+
work that takes ~130 ms, while the HNSW scan's startup cost alone is 2137.
|
|
18
|
+
|
|
19
|
+
A restructured arm reaches 36 ms against 648 ms — but only with `ef_search` at
|
|
20
|
+
pgvector's default, which is the setting where the index missed the true nearest
|
|
21
|
+
neighbour for 1 query in 100 on a bed with real cluster structure, dropping the
|
|
22
|
+
top-1 similarity by 0.99. Against this record's ~0.01 abstention separation,
|
|
23
|
+
that flips an abstention: the corpus holds the answer and the door says it does
|
|
24
|
+
not. The speed and the approximation cannot be separated, so taking them is an
|
|
25
|
+
owner decision rather than a tuning change.
|
|
26
|
+
|
|
27
|
+
Both the current plan and the fix path are now pinned by tests, so neither can
|
|
28
|
+
drift unnoticed.
|
|
29
|
+
|
|
30
|
+
- b9f3d00: A citation pin no longer outlives a restriction
|
|
31
|
+
|
|
32
|
+
A snapshot token pins a generation so a citation keeps resolving to the same
|
|
33
|
+
bytes. It was also deciding the _audience_ question — evaluating `visibility` on
|
|
34
|
+
the pinned row — so a document restricted after the token was issued kept reading
|
|
35
|
+
in full for the token's life, to a caller the record had just closed it to.
|
|
36
|
+
|
|
37
|
+
Three routes refused it and one served it, on the same surface, in the same
|
|
38
|
+
second: `outline` omitted it, `search` filtered it, an unpinned `read` refused it,
|
|
39
|
+
and `read` with a pre-flip token returned the whole document.
|
|
40
|
+
|
|
41
|
+
The generation pointers are why the obvious guard missed it. A flip sets
|
|
42
|
+
`rollback_generation` to the generation just superseded, so a pre-flip pin is
|
|
43
|
+
exactly the rollback pointer — servable by design, and the check that narrows a
|
|
44
|
+
pin to {active, rollback} passed it.
|
|
45
|
+
|
|
46
|
+
**Governance is now read from the record as it stands.** A pin still decides
|
|
47
|
+
which generation's content is served; it no longer decides whether the caller may
|
|
48
|
+
have it. A document the record no longer contains cannot be resurrected by one
|
|
49
|
+
either. Unpinned reads are unaffected — with nothing pinned, the two generations
|
|
50
|
+
are the same one and the check is an identity.
|
|
51
|
+
|
|
52
|
+
The cost is deliberate: a citation can stop resolving within the token's 30
|
|
53
|
+
minutes when the record restricts what it points at. That is what "the record
|
|
54
|
+
changed" should look like. The alternative is a window in which a withdrawal is
|
|
55
|
+
not a withdrawal.
|
|
56
|
+
|
|
3
57
|
## 0.0.18
|
|
4
58
|
|
|
5
59
|
### Patch Changes
|
package/dist/cli.mjs
CHANGED
|
@@ -15,7 +15,7 @@ import { bodyLimit } from "hono/body-limit";
|
|
|
15
15
|
import { execFileSync, spawnSync } from "node:child_process";
|
|
16
16
|
import { parseArgs } from "node:util";
|
|
17
17
|
import { readFile, readdir, stat } from "node:fs/promises";
|
|
18
|
-
//#region ../content-gateway/dist/main-
|
|
18
|
+
//#region ../content-gateway/dist/main-DDeyGVjK.mjs
|
|
19
19
|
/**
|
|
20
20
|
* A connection could not be ESTABLISHED in time — retryable.
|
|
21
21
|
*
|
|
@@ -2053,6 +2053,28 @@ g AS (
|
|
|
2053
2053
|
(SELECT active_generation FROM corpora
|
|
2054
2054
|
WHERE tenant_id = $1 AND corpus_id = $2)
|
|
2055
2055
|
) AS gen
|
|
2056
|
+
),
|
|
2057
|
+
-- The generation the record is on RIGHT NOW, which decides governance even when
|
|
2058
|
+
-- content is served from a pinned one.
|
|
2059
|
+
--
|
|
2060
|
+
-- A snapshot pin exists so a citation keeps resolving to the same bytes. It used
|
|
2061
|
+
-- to decide the audience question too, by evaluating visibility on the pinned
|
|
2062
|
+
-- row — so a document restricted after the token was issued kept reading in full
|
|
2063
|
+
-- for the token's life, while outline, search and an unpinned read all
|
|
2064
|
+
-- refused it in the same second. servableGenerations could not catch it: a
|
|
2065
|
+
-- flip sets rollback_generation to the generation just superseded, so a pre-flip
|
|
2066
|
+
-- pin IS the rollback pointer and is servable by design (issue #87).
|
|
2067
|
+
--
|
|
2068
|
+
-- Pins yield. A citation may stop resolving within the token's life, which is
|
|
2069
|
+
-- what "the record changed" should look like — the alternative is a window in
|
|
2070
|
+
-- which a withdrawal is not a withdrawal, and decision 19 says a surface that
|
|
2071
|
+
-- refuses must refuse everywhere, which includes its own fourth route.
|
|
2072
|
+
--
|
|
2073
|
+
-- When nothing is pinned this is the SAME generation as g, so the join is an
|
|
2074
|
+
-- identity and no unpinned read changes behaviour.
|
|
2075
|
+
live AS (
|
|
2076
|
+
SELECT active_generation AS gen FROM corpora
|
|
2077
|
+
WHERE tenant_id = $1 AND corpus_id = $2
|
|
2056
2078
|
)`;
|
|
2057
2079
|
/** Candidates by LEAF slug ($4), each with its full root path (for suffix disambiguation). */
|
|
2058
2080
|
const NODE_BY_SLUG_SQL = `
|
|
@@ -2082,7 +2104,12 @@ SELECT n.node_id, n.slug, n.title, n.stable_id, n.path, n.generation, n.permalin
|
|
|
2082
2104
|
FROM tree n
|
|
2083
2105
|
JOIN content_nodes self ON self.node_id = n.node_id AND self.tenant_id = $1
|
|
2084
2106
|
AND self.generation = n.generation
|
|
2085
|
-
|
|
2107
|
+
-- INNER join, so a document the record no longer contains cannot be
|
|
2108
|
+
-- resurrected by a pin either: no live row, no read.
|
|
2109
|
+
JOIN live ON TRUE
|
|
2110
|
+
JOIN content_nodes now ON now.tenant_id = $1 AND now.generation = live.gen
|
|
2111
|
+
AND now.stable_id = self.stable_id
|
|
2112
|
+
WHERE n.slug = $4 AND ${DENY$1} AND ${audienceAllowed$1("now")}
|
|
2086
2113
|
ORDER BY n.path`;
|
|
2087
2114
|
const ALIAS_SQL = `
|
|
2088
2115
|
WITH ${GEN}
|
|
@@ -2099,8 +2126,11 @@ const NODE_BY_STABLE_ID_SQL = `
|
|
|
2099
2126
|
WITH RECURSIVE ${GEN}, ${DENIED_CTE$1}
|
|
2100
2127
|
SELECT n.node_id, n.slug, n.title, n.stable_id, n.stable_id::text AS path, n.generation, n.permalink
|
|
2101
2128
|
FROM content_nodes n JOIN g ON n.generation = g.gen
|
|
2129
|
+
JOIN live ON TRUE
|
|
2130
|
+
JOIN content_nodes now ON now.tenant_id = $1 AND now.generation = live.gen
|
|
2131
|
+
AND now.stable_id = n.stable_id
|
|
2102
2132
|
WHERE n.tenant_id = $1 AND n.stable_id = $4 AND n.status = 'published' AND ${DENY$1}
|
|
2103
|
-
AND ${
|
|
2133
|
+
AND ${audienceAllowed$1("now")}`;
|
|
2104
2134
|
const DOCUMENT_CHUNKS_SQL = `
|
|
2105
2135
|
WITH ${GEN}
|
|
2106
2136
|
SELECT c.ordinal, COALESCE(c.heading_path_text, ''), c.content
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@panaversity/ksor",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.19",
|
|
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",
|