@saasontools/strauss-kb 0.1.13 → 0.1.14

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/ARCHITECTURE.md CHANGED
@@ -1,43 +1,42 @@
1
1
  # Architecture
2
2
 
3
- The README says what the format is and how to use it. This says why it is
4
- shaped that way, and which alternatives were tried and dropped — the decisions
5
- a later reader would otherwise reopen.
3
+ The README is the entry point. This is why the format is shaped as it is.
6
4
 
7
5
  ## One record per file
8
6
 
9
- The filename is the identity, so two writers never merge; they only choose
10
- distinct names. Publication uses `link`, which fails when the name is taken, and
11
- a collision surfaces as a 409 the caller has to answer rather than a silent
12
- last-write-wins.
7
+ The filename is the identity, so two writers never merge they choose distinct
8
+ names. Publication uses `link`, so a collision surfaces as a 409 rather than a
9
+ silent last-write-wins.
13
10
 
14
11
  Read-modify-write (`setStatus`, `answer`) checks a content digest immediately
15
- before publishing. That narrows the lost-update window to two adjacent syscalls
16
- rather than closing it. A lock would close it and add a stale-hold failure worse
17
- than the residue: a crashed holder blocks every later writer, where a lost
18
- update costs one retry.
12
+ before publishing, narrowing the lost-update window to two adjacent syscalls
13
+ rather than closing it. A lock would close it and add a worse failure: a crashed holder blocks every later writer, where a lost update costs one retry.
19
14
 
20
15
  ## Read for a question, not for a session
21
16
 
22
- A base loaded at the start of a long conversation is summarised away by the end
23
- of it, and nothing keeps it alive. So no consumer loads it that way:
17
+ A base loaded at the start of a long conversation is summarised away by the end,
18
+ so no consumer loads it that way:
24
19
 
25
- | Consumer | How it reads |
26
- | ------------------------------- | ---------------------------------------------------------------------------- |
27
- | Diff annotation | `matchToDiff` — deterministic, no context involved |
28
- | "Has this been decided?" | a fresh short-lived reader, given the base and the question, discarded after |
29
- | An implementor writing a record | a point query at the moment of writing, not a load an hour earlier |
20
+ | Consumer | How it reads |
21
+ | ------------------------------- | --------------------------------------------------- |
22
+ | Diff annotation | `matchToDiff` — deterministic, no context |
23
+ | "Has this been decided?" | a fresh short-lived reader, given base and question |
24
+ | An implementor writing a record | a point query at the moment of writing |
30
25
 
31
- Each has clean context by construction. Where a base genuinely must stay
32
- resident it will drift, and there is no defence — the mitigation is that
33
- reloading costs about three thousand tokens, so read it again at the point of
34
- use rather than trying to keep it.
26
+ Reloading costs about three thousand tokens for twenty records.
27
+
28
+ ## Load beats retrieval while the base fits
29
+
30
+ On nine questions whose wording appears in no record, a reader holding the whole
31
+ base answered eight; embedding search over the same records answered four. Two
32
+ differences are structural: a reader can say no record answers, where vector
33
+ search returns its nearest neighbour whatever the distance; and it picks the
34
+ record that answers rather than the one nearest the topic.
35
35
 
36
36
  ## What happens when a base outgrows a context
37
37
 
38
- Loading stops working somewhere above a few hundred records. What replaces it is
39
- not a different answer to the same question — it is the same reader, given
40
- candidates instead of everything:
38
+ Loading stops working above a few hundred records; what replaces it is the same
39
+ reader given candidates, not everything:
41
40
 
42
41
  ```
43
42
  vector recall → top ~20 candidates, with their scores
@@ -45,24 +44,18 @@ vector recall → top ~20 candidates, with their scores
45
44
  reader judges → picks what answers, or says nothing does
46
45
  ```
47
46
 
48
- The reader stays the judge in both regimes, which is what preserves the two
49
- structural wins the README reports: it can say no record answers the question,
50
- and it picks the record that answers rather than the one nearest the topic.
51
- Neither survives if a ranker's top hit is taken as the answer.
47
+ The reader stays the judge, preserving the two structural wins above.
52
48
 
53
49
  **A score threshold is not the growth path.** The wrong `audit trail` hit scored
54
50
  0.318 against the correct `race condition` hit at 0.295; any cut that drops the
55
51
  first drops the second. A threshold excludes the absurd — an unrelated query
56
52
  scored 0.091 — and nothing else.
57
53
 
58
- **Tags are not the growth path either.** The field exists, is written, and is
59
- read by nothing but an index line, deliberately. Free-text tags drift the way
60
- `auth` / `authentication` / `authn` drift, which is the failure this format was
61
- rewritten to remove; enforcing a vocabulary would make them a closed enum, which
62
- `type` already is. The labels that matter here are already verifiable —
63
- `strauss_anchors` names a file and a symbol, which either match the repository
64
- or do not, where a tag can be wrong forever. If narrowing ever matters, measure
65
- tag narrowing against vector recall on a real base rather than adding both.
54
+ **Tags are not the growth path either.** The field exists, is written, and read by
55
+ nothing but an index line. Free-text tags drift the way `auth` /
56
+ `authentication` / `authn` drift; enforcing a vocabulary would make them a
57
+ closed enum, which `type` already is. `strauss_anchors` names a file and symbol,
58
+ which either match the repository or not.
66
59
 
67
60
  ## Typed links
68
61
 
@@ -81,11 +74,10 @@ record's declared dependencies are not obligations anyone still owes.
81
74
 
82
75
  ## Rejected: a format that needs a parser
83
76
 
84
- This was broken twice. A hand-rolled frontmatter reader could not express nested
85
- maps, so it misread every OKF `generated`, `sources[]`, and `verified[]`. Its
86
- replacement's first log format was `·`-delimited, with a splitter to read it
87
- back. Both are gone: the log is JSONL and the schema is emitted from Zod, so
88
- `strauss-kb schema` is the contract rather than a description of one.
77
+ A hand-rolled frontmatter reader could not express nested maps, misreading every
78
+ OKF `generated`, `sources[]`, and `verified[]`; its replacement's first log
79
+ format was `·`-delimited. Both are gone: the log is JSONL, the schema is emitted
80
+ from Zod, and `strauss-kb schema` is the contract.
89
81
 
90
82
  ## Anchor resolution
91
83
 
@@ -195,12 +187,9 @@ local git client actually performs.
195
187
 
196
188
  ## Rejected for now: a base registry
197
189
 
198
- Cross-base questions are unaskable by construction — supersession, traces, and
199
- search stop at the directory boundary. That is the price of a base that can be
200
- copied, deleted, or handed over whole, and it is what keeps the search index
201
- disposable.
190
+ Cross-base questions are unaskable: supersession, traces, and search stop at the
191
+ directory boundary. That is the price of a base that can be copied or handed
192
+ over whole, and it keeps the search index disposable.
202
193
 
203
- If cross-base ever becomes the common case, the cheap escape is a registry: a
204
- list of paths a caller may name explicitly, queried one at a time and merged
205
- only for display. It is deliberately unbuilt. Adding it early would drag back
206
- the cross-scope machinery this model exists to avoid.
194
+ The cheap escape, if that becomes the common case, is a registry: paths a caller
195
+ names explicitly, queried one at a time and merged only for display.