okf 1.3.0 → 1.5.0

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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +68 -0
  3. data/README.md +15 -15
  4. data/lib/okf/bundle/search.rb +155 -0
  5. data/lib/okf/bundle.rb +2 -2
  6. data/lib/okf/cli.rb +68 -4
  7. data/lib/okf/server/app.rb +29 -0
  8. data/lib/okf/server/graph/template.html.erb +385 -50
  9. data/lib/okf/server/graph.rb +2 -2
  10. data/lib/okf/skill/SKILL.md +17 -11
  11. data/lib/okf/skill/playbooks/consume.md +4 -2
  12. data/lib/okf/skill/playbooks/maintain.md +6 -4
  13. data/lib/okf/skill/playbooks/menu.md +10 -6
  14. data/lib/okf/skill/playbooks/search.md +42 -0
  15. data/lib/okf/skill/reference/authoring.md +2 -2
  16. data/lib/okf/skill/reference/cli.md +55 -17
  17. data/lib/okf/version.rb +1 -1
  18. data/lib/okf.rb +1 -0
  19. metadata +3 -26
  20. data/.okf/capabilities/agent-skill.md +0 -46
  21. data/.okf/capabilities/graph-server.md +0 -63
  22. data/.okf/capabilities/index.md +0 -20
  23. data/.okf/capabilities/library-api.md +0 -72
  24. data/.okf/capabilities/linter.md +0 -49
  25. data/.okf/capabilities/read-views.md +0 -84
  26. data/.okf/capabilities/validator.md +0 -40
  27. data/.okf/cli.md +0 -52
  28. data/.okf/design/core-shell-split.md +0 -58
  29. data/.okf/design/index.md +0 -10
  30. data/.okf/design/ruby-floor.md +0 -45
  31. data/.okf/design/runtime-dependencies.md +0 -44
  32. data/.okf/design/server-trust-boundary.md +0 -40
  33. data/.okf/format/citations.md +0 -33
  34. data/.okf/format/cross-links.md +0 -52
  35. data/.okf/format/frontmatter.md +0 -38
  36. data/.okf/format/index.md +0 -9
  37. data/.okf/format/okf-format.md +0 -43
  38. data/.okf/index.md +0 -18
  39. data/.okf/log.md +0 -10
  40. data/.okf/model/bundle.md +0 -38
  41. data/.okf/model/concept.md +0 -44
  42. data/.okf/model/graph.md +0 -44
  43. data/.okf/model/index.md +0 -8
  44. data/.okf/overview.md +0 -67
@@ -21,8 +21,8 @@ module OKF
21
21
  LAYOUTS = %w[cose concentric breadthfirst circle grid].freeze
22
22
 
23
23
  # Node-diameter range in px; the template scales within it by node degree.
24
- MIN_SIZE = 24
25
- MAX_SIZE = 70
24
+ MIN_SIZE = 14
25
+ MAX_SIZE = 44
26
26
 
27
27
  # The 6-character JSON unicode escape for `<` (backslash, u, 0, 0, 3, c),
28
28
  # built from the backslash code point so no literal escape appears here.
@@ -7,14 +7,16 @@ description: >-
7
7
  what the tools report — and routes every mechanical question (validation,
8
8
  linting, views, the graph server) to the installed `okf` CLI. Use whenever
9
9
  capturing project knowledge (services, APIs, schemas, metrics, runbooks,
10
- decisions) into a bundle, updating one after code or docs change, checking a
11
- bundle's conformance or curation quality, rendering it as a graph, or working in
10
+ decisions) into a bundle, retrieving an answer from a bundle without reading
11
+ it whole, updating one after code or docs change, checking a bundle's
12
+ conformance or curation quality, rendering it as a graph, or working in
12
13
  a repo that contains an OKF bundle — a `.okf/` directory or a root `index.md`
13
14
  carrying `okf_version`. Triggers on: "document this in OKF", "update the
14
- knowledge bundle", "capture this as a concept", "validate/lint/serve the
15
+ knowledge bundle", "capture this as a concept", "what do we know about X?",
16
+ "where is X documented?", "search the bundle", "validate/lint/serve the
15
17
  bundle", or a task needing knowledge from an OKF bundle already in the repo.
16
18
  user-invocable: true
17
- argument-hint: "[produce|maintain|consume|<okf-cli-verb>] [dir] [--flags]"
19
+ argument-hint: "[search|produce|maintain|consume|<okf-cli-verb>] [dir] [--flags]"
18
20
  allowed-tools: Read Write Edit Grep Glob Bash
19
21
  ---
20
22
 
@@ -79,9 +81,12 @@ Don't memorize the surface — `okf --help` maps every verb, `okf <verb> --help`
79
81
  flags. The division of labour is the whole game:
80
82
 
81
83
  - **Shell out — never eyeball —** anything a verb computes: conformance (§9), what
82
- exists, what links where, what's stale, the map. Every read verb takes `--json`
83
- and the list views filter by type/area/tag, so ask the narrow question instead of
84
- paging the bundle.
84
+ exists, what links where, where a term lives, what's stale, the map. Every read
85
+ verb takes `--json` and the list views filter by type/area/tag, so ask the narrow
86
+ question instead of paging the bundle.
87
+ - **Skeleton first, bodies last.** `index --no-body`, `search`, `graph --minimal`,
88
+ and `--fields` projections each answer for a fraction of a dump's bytes; full
89
+ bodies are the final step of a retrieval, never the first. <!-- rule:okf-skeleton-first -->
85
90
  - **You judge — the CLI can't —** meaning: contradictions, semantic staleness
86
91
  (parses fine, no longer true), whether a loose file is terminal-by-design, whether
87
92
  a singleton tag is a deliberate marker. Tool output is evidence, never a verdict.
@@ -116,10 +121,10 @@ producing or maintaining, and the verbatim spec [SPEC.md](reference/SPEC.md)
116
121
  when you need chapter and verse.
117
122
 
118
123
  **No subcommand?** Infer intent: "document this / capture X" → `produce`; "the
119
- code changed, update the docs" → `maintain`; a repo already carrying a bundle
120
- plus a task needing its knowledge → `consume`; "check / graph / preview it"
121
- run the matching CLI verb and interpret the result. When genuinely ambiguous,
122
- ask.
124
+ code changed, update the docs" → `maintain`; "what do we know about X / where
125
+ is X documented" → `search`; a repo already carrying a bundle plus a task
126
+ needing its knowledge `consume`; "check / graph / preview it" → run the
127
+ matching CLI verb and interpret the result. When genuinely ambiguous, ask.
123
128
 
124
129
  **Which directory?** Use the path given. Otherwise default to `.okf/` at the repo
125
130
  root, but first detect whether the project already keeps its bundle elsewhere
@@ -136,6 +141,7 @@ Read the referenced playbook before executing — it *is* the procedure.
136
141
  | Verb | Category | What it does | Reference |
137
142
  |------|----------|--------------|-----------|
138
143
  | *(none)* | Orient | recommend the highest-value next move; never auto-run | [playbooks/menu.md](playbooks/menu.md) |
144
+ | `search` | Use | answer a question from the bundle: map → finder → only the winning bodies | [playbooks/search.md](playbooks/search.md) |
139
145
  | `produce` | Author | create or extend a bundle | [playbooks/produce.md](playbooks/produce.md) |
140
146
  | `maintain` | Author | sync the bundle's content with reality after a change | [playbooks/maintain.md](playbooks/maintain.md) |
141
147
  | `consume` | Use | use the bundle as context for a task | [playbooks/consume.md](playbooks/consume.md) |
@@ -3,8 +3,10 @@
3
3
  1. **Orient first** (the [SKILL.md](../SKILL.md) reflex): `okf index <dir>` maps the
4
4
  whole bundle in one pass — every directory's index body, rollups, and listings —
5
5
  and `log.md` gives recent history. Then follow links only into the concepts the
6
- task needs. For a large bundle, `okf graph --json` gives the whole link structure
7
- at once so you can plan a traversal without opening every file.
6
+ task needs. For a *pointed question* rather than broad context, switch to the
7
+ [search playbook](search.md): map finder (`okf search`) only the winning
8
+ bodies. For a large bundle, `okf graph --json --minimal` gives the whole link
9
+ structure at once so you can plan a traversal without opening every file.
8
10
  2. Treat broken links as not-yet-written knowledge, not errors.
9
11
  3. **Write-back reflex:** if you learn something durable while working — a fact the
10
12
  bundle lacks, a link it is missing, a concept that no longer matches reality —
@@ -11,10 +11,12 @@ up. The modelling craft behind steps 3 and 7 lives in
11
11
  catch enumeration drift, because **grep cannot find an index entry that is
12
12
  missing.** (This is the always-on reflex in [SKILL.md](../SKILL.md).)
13
13
  2. **Find *every* affected concept** — the failure mode is fixing only the obvious
14
- one. Don't rely on reading the whole bundle; that only scales on tiny ones. Grep
15
- the changed asset's `resource` URI across the bundle, grep its path/name, and use
16
- `okf graph --json` to pull the concepts that link *to* the ones you're touching.
17
- Let grep and the graph find them so nothing drifts silently.
14
+ one. Don't rely on reading the whole bundle; that only scales on tiny ones. Run
15
+ `okf search <dir> <resource-URI-or-name>` it hits frontmatter *and* bodies and
16
+ returns ranked concept ids, not line noise; grep is the backstop for what search
17
+ cannot express and `okf graph <dir> --json --minimal` to pull the edges (the
18
+ concepts that link *to* the ones you're touching) without paying for every body.
19
+ Let search and the graph find them so nothing drifts silently.
18
20
  3. Update bodies and `timestamp`; fix or add cross-links; create new concepts for
19
21
  new assets; mark retired assets with a `**Deprecation**` note rather than
20
22
  silently deleting the context that explains them.
@@ -14,18 +14,22 @@ is the lede.
14
14
  - **No bundle** → lead with **`produce`** (create the first bundle from the
15
15
  code, docs, or what lives only in people's heads). Nothing else applies yet.
16
16
  3. **Read the bundle's state** from the CLI, not by eyeballing:
17
- `okf validate <root> --json`, `okf lint <root> --json`, `okf loose <root>
18
- --json`. Then recommend by what they report, most-blocking first:
17
+ `okf validate <root>`, `okf lint <root>`, `okf loose <root>` — the plain
18
+ text views, which are lighter than `--json` when you are reading a report
19
+ rather than extracting structure from it. Then recommend by what they
20
+ report, most-blocking first:
19
21
  - **`validate` has errors** → lead with **`curate`**: §9 conformance errors are
20
22
  the only hard failures, and curate fixes them before anything else.
21
23
  - **clean `validate`, but `lint`/`loose` findings** → lead with **`curate`** to
22
24
  settle the curation debt (reachability, backlog, completeness, hygiene),
23
25
  naming the top one or two categories from the report.
24
26
  - **clean across the board** → the bundle is healthy, so lead with **`consume`**
25
- (put it to work on the task at hand) and offer **`maintain`** as the move for
26
- when the code or docs have since changed. If the working tree has uncommitted
27
- changes to the code the bundle describes (`git status`), prefer **`maintain`**:
28
- that is exactly the drift it exists to close.
27
+ (put it to work on the task at hand) or **`search`** when what the user
28
+ actually has is a question ("what do we know about X?") and offer
29
+ **`maintain`** as the move for when the code or docs have since changed. If
30
+ the working tree has uncommitted changes to the code the bundle describes
31
+ (`git status`), prefer **`maintain`**: that is exactly the drift it exists
32
+ to close.
29
33
  4. **Freshness is off by default.** If the bundle carries timestamps, note that a
30
34
  plain `lint` said nothing about staleness and `okf lint <root> --stale-after
31
35
  90d` is the check that would.
@@ -0,0 +1,42 @@
1
+ # Playbook: search — retrieve knowledge without paying for the whole bundle
2
+
3
+ Reached by `search <query…>` — the query is everything after the verb; given no
4
+ query, ask what to find. Retrieval matters as much as curation: a bundle nobody
5
+ can query cheaply is dead weight. The discipline is progressive disclosure
6
+ (spec §6): every step pays a few hundred bytes to decide what the next step
7
+ reads, and full bodies are read last, and only the winners.
8
+
9
+ 1. **Guard once**: `command -v okf`. Missing → [doctor](doctor.md). No CLI at
10
+ all → read the root `index.md`, then each relevant area's `index.md`, by hand.
11
+ 2. **Ingest the map and decide where to look.** `okf index <dir> --no-body` is
12
+ the skeleton: every directory with its concept count, types, tags, children.
13
+ *You* do the semantic matching here — the question names a meaning, the map
14
+ names areas; connect them by judgment, not string equality. When an area
15
+ looks right, `okf index <dir> --area <name>` buys its authored index body and
16
+ listing (titles + descriptions) for the price of one directory.
17
+ <!-- rule:okf-search-map-first -->
18
+ 3. **Cut across with the finder when the question is lexical.** An exact
19
+ symbol, an error code, a column name, a phrase — things structure won't
20
+ surface — go to `okf search <dir> <terms>` (terms AND together; `--regexp`
21
+ for patterns like `err_[a-z]+_409`). Scope it with what the map taught you:
22
+ `--area billing`, `--type Decision`, `--tag idempotency`, `--in body`.
23
+ Matches rank by where they hit, and the snippet often *is* the answer.
24
+ 4. **Read only the winners.** A match row's `id` is its file: `<dir>/<id>.md`.
25
+ Read that file — not its folder, never the whole tree. Follow its links (§5)
26
+ one hop at a time; check `log.md` when freshness matters.
27
+ 5. **Answer, then write back.** Cite the concept ids you used. If the answer
28
+ was missing, stale, or needlessly hard to find — a gap, a broken link, an
29
+ index entry that should exist — switch to [maintain](maintain.md) and record
30
+ it. Retrieval friction is curation signal. <!-- rule:okf-search-write-back -->
31
+
32
+ Anti-patterns, each a real token bill:
33
+
34
+ - **The dump.** `okf graph --json` with bodies, or `cat`-ing the tree "for
35
+ context", costs more than every step above combined. Retrieval needs at most
36
+ `graph --json --minimal`, and only to plan a multi-hop traversal.
37
+ - **Grep before map.** Grep cannot find the entry that is *missing*, and it
38
+ returns line noise where `search` returns ranked concepts. Grep is the
39
+ fallback when the CLI is absent, not the first move.
40
+ - **Mechanical synonym retries.** The finder is exact by design; *you* are the
41
+ fuzzy layer. When terms miss, learn the bundle's vocabulary — `okf tags
42
+ <dir>`, `okf types <dir>` — and re-ask in its own words.
@@ -67,8 +67,8 @@ the domain.
67
67
  ### `resource` is the bridge to reality <!-- rule:okf-resource-bridge -->
68
68
  Set `resource` (a canonical URI) **only** when a concept *is* a real, addressable
69
69
  asset — a table (`bigquery://…`), a service repo, a dashboard, an endpoint. Its
70
- presence is what lets `maintain` find every concept affected by a changed asset by
71
- grepping for that URI. Abstract concepts — a decision, a principle, a metric
70
+ presence is what lets `maintain` find every concept affected by a changed asset in
71
+ one `okf search <dir> <uri>` call. Abstract concepts — a decision, a principle, a metric
72
72
  definition — have no resource, and **omitting it is meaningful**, not laziness. Do
73
73
  not invent placeholder URIs.
74
74
 
@@ -1,7 +1,7 @@
1
1
  # OKF tool verbs — the `okf` CLI
2
2
 
3
- `validate`, `lint`, `loose`, `index`, `catalog`, `files`, `tags`, `types`, `stats`,
4
- `server`, and `graph` are **not** eyeball passes and are not
3
+ `validate`, `lint`, `loose`, `search`, `index`, `catalog`, `files`, `tags`, `types`,
4
+ `stats`, `server`, and `graph` are **not** eyeball passes and are not
5
5
  reimplemented in this skill. They run the deterministic `okf` executable shipped by
6
6
  the companion gem — the single source of truth for OKF mechanics. Your job is to
7
7
  invoke it correctly and interpret the result, not to reason out conformance by hand.
@@ -108,11 +108,34 @@ defect — a terminal leaf (a backlog item, a spec reference) can be loose by de
108
108
  `loose` surfaces the set so you can judge intent (see the
109
109
  [maintain playbook](../playbooks/maintain.md)).
110
110
 
111
+ ## search — ranked text retrieval (metadata + body)
112
+
113
+ The browser page's search brought to the CLI and extended to bodies, so "which
114
+ concept covers X?" costs rows, not body reads. `okf search <dir> <term…>`:
115
+ terms AND together — every term must hit at least one searched field, not
116
+ necessarily the same one — as case-insensitive substrings, or as Ruby regular
117
+ expressions with `--regexp`/`-e` (an invalid pattern is a usage error, exit 2).
118
+ `--in a,b` restricts the searched fields (title, id, tags, type, description,
119
+ body); the shared `--type/--area/--tag` filters narrow the candidates *first*,
120
+ so a search scoped by what `index` taught you stays surgical.
121
+
122
+ Rows rank by **where** they hit — title 5, id 4, tags 3, type/description 2,
123
+ body 1, summed over matched fields — and carry one bounded context snippet from
124
+ the strongest match that needs context (description or body). Deliberately not
125
+ fuzzy: the consuming agent is the fuzzy layer — when terms miss, learn the
126
+ bundle's vocabulary from `tags`/`types` and re-ask in its own words, rather
127
+ than hammering synonyms. Advisory read: **exit 0 even with zero matches**.
128
+ JSON: `{ bundle, query, count, matches: [{ id, title, type, area, tags,
129
+ matched, score, snippet }] }`, projectable with `--fields/--except`. The
130
+ retrieval procedure that puts this verb in sequence — map first, finder second,
131
+ bodies last — is the [search playbook](../playbooks/search.md).
132
+
111
133
  ## index — the progressive-disclosure map (§6)
112
134
 
113
- The "orient before you read" view, and the one read verb that sees the layer the
114
- others can't: `index.md` files are reserved/structural, so `catalog`/`files`/… (all
115
- concept views) never show them. `okf index <dir>` prints one entry per directory
135
+ The "orient before you read" view, and the read verb that sees the layer the
136
+ concept views can't: `index.md` files are reserved/structural, so
137
+ `catalog`/`files`/… never show them (in the browser, the Indexes tab and
138
+ folder clicks render this same map). `okf index <dir>` prints one entry per directory
116
139
  that holds concepts or carries an `index.md`, root first — the authored index body
117
140
  (frontmatter stripped), a `type`/`tag` rollup over the concepts that live directly
118
141
  there, its child directories, and the concept listing. Run it first when picking up
@@ -177,21 +200,36 @@ Starts a local HTTP server (`okf server <dir>`; `-p`/`--port`, default 8808, and
177
200
  `--bind`) and prints its URL — stop it with Ctrl-C. The page boots from a lean
178
201
  payload (nodes carry only `id` and `title`, plus compact type/tag indexes) and
179
202
  fetches each concept's markdown body **live from disk** as you click it, so the
180
- initial load stays small and edits show without a restart. Concepts render as nodes
203
+ initial load stays small and edits show without a restart. Mermaid code blocks
204
+ in a body render as diagrams, and a click (or tap) opens the diagram full
205
+ screen with drag-to-pan and wheel/pinch zoom. Concepts render as nodes
181
206
  coloured by `type` and sized by degree, links as edges, with a detail panel
182
207
  (rendered markdown, "Links to" / "Linked from" backlinks), layout switching,
183
- type/area/tag filters on every view, and search. It is a Rack app, so the same
184
- server can be mounted in a host app (e.g. Rails).
185
-
186
- **Trust boundary:** the page loads Cytoscape and marked from a CDN and
187
- renders each concept's markdown body **without sanitization**, so only serve
188
- bundles you trust. Inlined graph data cannot break out of its `<script>` (every
189
- `<` is escaped), but the fetched markdown is rendered unsanitized.
208
+ type/area/tag filters on every view, and search. The authored layer is in the
209
+ UI too: the Files view carries **Files | Indexes** tabs — the Indexes tab
210
+ lists the log first (the chronological index), then every `index.md` — and
211
+ folder nodes in file-tree mode and area boxes in cluster mode open a
212
+ directory's §6 map in the inspector (authored, or synthesized when none
213
+ exists). Links to an `index.md`, `log.md`, or bare directory navigate instead
214
+ of dead-ending, and the log is fetched fresh on every read, so a
215
+ just-appended entry shows without a restart. `?view=index` jumps straight to
216
+ the Indexes tab. It is a Rack app, so the same server can be mounted in a
217
+ host app (e.g. Rails).
218
+
219
+ **Trust boundary:** the page renders each fetched markdown body through
220
+ DOMPurify and escapes everything it inlines (every `<` in the graph data is
221
+ escaped, so it cannot break out of its `<script>`), but it still loads its
222
+ viewer libraries (Cytoscape, marked, DOMPurify — plus Mermaid and Panzoom,
223
+ lazy-loaded on first use) from a CDN and renders whatever
224
+ links the bundle carries — so only serve bundles you trust.
190
225
 
191
226
  ## graph — the raw structure
192
227
 
193
228
  Prints the node/edge graph. `--json` emits a machine-readable dump (`nodes` with
194
- `id`/`type`/`title`/`description`/`tags`, and `edges`) you can pipe into other
195
- analysis or use to plan a traversal before consuming a large bundle. `--no-body`
196
- drops each node's body; `--minimal` ships only `id`/`title` plus the type/tag
197
- indexes the lean shape the `server` page boots from.
229
+ `id`/`type`/`title`/`description`/`tags` **and, by default, every `body`** the
230
+ part that dominates the bytes on a real bundle plus `edges`) you can pipe into
231
+ other analysis. To *plan* a traversal, structure is all you need: `--no-body`
232
+ drops each node's body, and `--minimal` ships only `id`/`title` plus the type/tag
233
+ indexes — the lean shape the `server` page boots from. Reach for the full dump
234
+ only when the task truly consumes every body; for one question, the
235
+ [search verb](#search--ranked-text-retrieval-metadata--body) is orders cheaper.
data/lib/okf/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OKF
4
- VERSION = "1.3.0"
4
+ VERSION = "1.5.0"
5
5
  end
data/lib/okf.rb CHANGED
@@ -39,6 +39,7 @@ module OKF
39
39
  require "okf/concept"
40
40
  require "okf/bundle"
41
41
  require "okf/bundle/graph"
42
+ require "okf/bundle/search"
42
43
  require "okf/bundle/validator"
43
44
  require "okf/bundle/validator/result"
44
45
  require "okf/bundle/linter"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: okf
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rodrigo Serradura
@@ -52,31 +52,6 @@ executables:
52
52
  extensions: []
53
53
  extra_rdoc_files: []
54
54
  files:
55
- - ".okf/capabilities/agent-skill.md"
56
- - ".okf/capabilities/graph-server.md"
57
- - ".okf/capabilities/index.md"
58
- - ".okf/capabilities/library-api.md"
59
- - ".okf/capabilities/linter.md"
60
- - ".okf/capabilities/read-views.md"
61
- - ".okf/capabilities/validator.md"
62
- - ".okf/cli.md"
63
- - ".okf/design/core-shell-split.md"
64
- - ".okf/design/index.md"
65
- - ".okf/design/ruby-floor.md"
66
- - ".okf/design/runtime-dependencies.md"
67
- - ".okf/design/server-trust-boundary.md"
68
- - ".okf/format/citations.md"
69
- - ".okf/format/cross-links.md"
70
- - ".okf/format/frontmatter.md"
71
- - ".okf/format/index.md"
72
- - ".okf/format/okf-format.md"
73
- - ".okf/index.md"
74
- - ".okf/log.md"
75
- - ".okf/model/bundle.md"
76
- - ".okf/model/concept.md"
77
- - ".okf/model/graph.md"
78
- - ".okf/model/index.md"
79
- - ".okf/overview.md"
80
55
  - CHANGELOG.md
81
56
  - CODE_OF_CONDUCT.md
82
57
  - LICENSE.txt
@@ -90,6 +65,7 @@ files:
90
65
  - lib/okf/bundle/linter.rb
91
66
  - lib/okf/bundle/linter/report.rb
92
67
  - lib/okf/bundle/reader.rb
68
+ - lib/okf/bundle/search.rb
93
69
  - lib/okf/bundle/validator.rb
94
70
  - lib/okf/bundle/validator/result.rb
95
71
  - lib/okf/bundle/writer.rb
@@ -112,6 +88,7 @@ files:
112
88
  - lib/okf/skill/playbooks/maintain.md
113
89
  - lib/okf/skill/playbooks/menu.md
114
90
  - lib/okf/skill/playbooks/produce.md
91
+ - lib/okf/skill/playbooks/search.md
115
92
  - lib/okf/skill/reference/APACHE-2.0.txt
116
93
  - lib/okf/skill/reference/SPEC.md
117
94
  - lib/okf/skill/reference/authoring.md
@@ -1,46 +0,0 @@
1
- ---
2
- type: Capability
3
- title: Companion agent skill (skill)
4
- description: A SKILL.md plus references and templates, shipped inside the gem, that teaches an agent to author OKF.
5
- resource: lib/okf/skill.rb
6
- tags: [skill, agent, install]
7
- timestamp: 2026-07-11T12:00:00Z
8
- ---
9
-
10
- # Overview
11
-
12
- The gem carries the **OKF agent skill** — a `SKILL.md` with reference and
13
- template files that teach a coding agent to *produce*, *maintain*, and *consume*
14
- [OKF](../format/okf-format.md) bundles and to drive the [CLI](../cli.md). The
15
- authoring judgment the executable can't encode lives here; the executable handles
16
- the mechanics.
17
-
18
- # `okf skill <dest>` installs it
19
-
20
- `OKF::Skill.install` copies the skill into a destination you name — Claude Code's
21
- `.claude`, an agent-agnostic `.agents`, wherever your agent looks. The rules are
22
- deliberate:
23
-
24
- - the **destination is required** — no magic default — so a user always decides
25
- where the skill lands;
26
- - it lands in a **`skills/okf/` folder** by default (`.claude` →
27
- `.claude/skills/okf`), because an agent discovers a skill as
28
- `<skills-dir>/<name>/SKILL.md` — so the skill settles in its own folder, not
29
- loose among the others. A `<dest>` already ending in `skills` only gains the
30
- `okf/` leaf; one already named `okf` is used as-is (idempotent); `--here`
31
- pastes straight into `<dest>`, wherever it is;
32
- - the resolved directory must be **empty unless `--force`**, so a customized
33
- skill is never clobbered.
34
-
35
- # One canonical copy, versioned with the gem
36
-
37
- The skill ships **only** from `lib/okf/skill/**` — that tree is the single source,
38
- and `install` copies from it. Because the skill rides inside the gem, installing
39
- the gem already puts the skill on the machine, and the skill's CLI reference can
40
- **never drift** from the executable it was released with. Local installs
41
- elsewhere are gitignored so they never masquerade as the source.
42
-
43
- # Citations
44
-
45
- [1] [lib/okf/skill.rb](https://github.com/serradura/okf-gem/blob/main/lib/okf/skill.rb) — the installer.
46
- [2] [lib/okf/skill/SKILL.md](https://github.com/serradura/okf-gem/blob/main/lib/okf/skill/SKILL.md) — the skill itself.
@@ -1,63 +0,0 @@
1
- ---
2
- type: Capability
3
- title: Interactive graph server (server)
4
- description: A self-contained HTML knowledge graph served over HTTP, and a mountable Rack app.
5
- resource: lib/okf/server/app.rb
6
- tags: [server, graph, rack, diagram]
7
- timestamp: 2026-07-12T12:00:00Z
8
- ---
9
-
10
- # Overview
11
-
12
- `okf server` boots an interactive view of the [graph](../model/graph.md):
13
- `OKF::Server::App` is a Rack app that serves one self-contained HTML page which
14
- draws the bundle with Cytoscape and renders concept bodies with marked, sanitized
15
- by DOMPurify. Because
16
- it is a plain Rack app, it also mounts inside a host application (e.g. a Rails
17
- route) — the built-in WEBrick runner is just the default, injected so tests drive
18
- it without opening a socket.
19
-
20
- # The page stays self-contained
21
-
22
- One ERB template, inline CSS and JS, no build step and no bundler. The only
23
- external assets are Cytoscape, marked, and DOMPurify from a CDN — plus Mermaid,
24
- lazy-loaded only when a concept body actually contains a diagram; everything else
25
- is inlined.
26
- The graph draws from a **minimal** node payload and pulls each concept's body
27
- **on demand** via `fetch()`, which is why even a large bundle loads fast.
28
-
29
- # Request flow
30
-
31
- ```mermaid
32
- sequenceDiagram
33
- participant B as Browser
34
- participant A as okf server (Rack app)
35
- B->>A: GET /
36
- A-->>B: HTML page + inlined minimal graph data
37
- Note over A,B: angle brackets escaped (json_for_script) — safe
38
- B->>A: GET /node?id=… (on demand)
39
- A-->>B: concept Markdown body
40
- Note over A,B: marked renders it, DOMPurify sanitizes it — safe
41
- ```
42
-
43
- # Endpoints
44
-
45
- | Path | Serves |
46
- |------|--------|
47
- | `/` | the HTML page (graph + inlined minimal data) |
48
- | `/node?id=` | one concept's rendered body |
49
- | `/node/meta?id=` | one concept's metadata |
50
- | `/catalog`, `/tags`, `/types` | the JSON behind the browser panels |
51
-
52
- # Trust boundary
53
-
54
- Both paths into the page are guarded. Inlined data goes through `json_for_script`,
55
- which escapes `<` so it cannot break out of its `<script>`; each fetched body is
56
- run through `DOMPurify.sanitize(marked.parse(...))`, which strips any script or
57
- handler before it reaches the DOM. See the
58
- [server trust boundary](../design/server-trust-boundary.md) for what that does and
59
- does not cover.
60
-
61
- # Citations
62
-
63
- [1] [lib/okf/server/app.rb](https://github.com/serradura/okf-gem/blob/main/lib/okf/server/app.rb) — the Rack app and its routes.
@@ -1,20 +0,0 @@
1
- # Capabilities
2
-
3
- The five things the gem does over a bundle, plus the read views that print it at
4
- a glance. All of them run over the same [pure model](../model/) and are fronted
5
- by the [CLI](../cli.md).
6
-
7
- # Judge
8
-
9
- * [Conformance validator](validator.md) - the §9 legal check; the only capability that can fail a bundle.
10
- * [Curation linter](linter.md) - advisory quality report across six categories; never rejects.
11
-
12
- # Serve & read
13
-
14
- * [Interactive graph server](graph-server.md) - a self-contained HTML graph over HTTP, mountable as a Rack app.
15
- * [Read views](read-views.md) - `index`, `catalog`, `files`, `types`, `tags`, `stats`, `loose`, `graph` — the browser views as text, plus the `index` map.
16
-
17
- # Use & author
18
-
19
- * [Library API](library-api.md) - the Ruby surface: pure model plus on-disk handles.
20
- * [Companion agent skill](agent-skill.md) - the skill shipped inside the gem that teaches an agent to author OKF.
@@ -1,72 +0,0 @@
1
- ---
2
- type: Capability
3
- title: Library API
4
- description: The Ruby surface — a pure in-memory model plus on-disk handles, an "ActiveRecord for the filesystem".
5
- resource: lib/okf.rb
6
- tags: [library, api, ruby, diagram]
7
- timestamp: 2026-07-12T12:00:00Z
8
- ---
9
-
10
- # Overview
11
-
12
- Everything the [CLI](../cli.md) does is available in-process. `require "okf"`
13
- gives you two layers, split cleanly by the [core/shell rule](../design/core-shell-split.md):
14
-
15
- - **pure, in-memory** — [`OKF::Concept`](../model/concept.md) and
16
- [`OKF::Bundle`](../model/bundle.md), which you build, interrogate, validate,
17
- lint, and graph with **no disk involved**;
18
- - **on-disk handles** — `OKF::Concept::File` and `OKF::Bundle::Folder`, which add
19
- `load` / `save` / `reload` / `delete` on top of the pure model.
20
-
21
- `require "okf"` stops at those two layers: the [CLI](../cli.md) and the skill
22
- installer load only when asked for (from `exe/okf`, or an explicit
23
- `require "okf/cli"` / `require "okf/skill"`), so an app embedding the library
24
- never drags in the command-line machinery.
25
-
26
- ```mermaid
27
- classDiagram
28
- class Concept {
29
- <<pure>>
30
- }
31
- class Bundle {
32
- <<pure>>
33
- }
34
- class Graph {
35
- <<pure>>
36
- }
37
- class ConceptFile {
38
- <<on-disk>>
39
- }
40
- class Folder {
41
- <<on-disk>>
42
- }
43
- Bundle "1" o-- "*" Concept : contains
44
- Bundle ..> Graph : builds
45
- ConceptFile ..> Concept : wraps
46
- Folder ..> Bundle : wraps
47
- ```
48
-
49
- `ConceptFile` is `OKF::Concept::File`; `Folder` is `OKF::Bundle::Folder` — each
50
- on-disk handle wraps a pure counterpart and adds load/save/reload/delete.
51
-
52
- # Build knowledge without touching Markdown
53
-
54
- The pure layer is the surface an embedding app uses to reuse the gem over
55
- knowledge it already holds as records. Construct concepts from data, assemble a
56
- bundle, and call `#validate`, `#lint`, or `#graph` — no Markdown round-trip
57
- needed. The lower-level pieces work standalone too:
58
- `OKF::Bundle::Validator.call`, `OKF::Bundle::Linter.call`,
59
- `OKF::Bundle::Graph.build`, `OKF::Markdown::Frontmatter.parse`.
60
-
61
- # Folder is an ActiveRecord for the filesystem
62
-
63
- `OKF::Bundle::Folder.load(dir)` reads a directory into a pure bundle;
64
- `Folder.new(bundle:, root:).save` materializes one back — and **validates §9
65
- before publishing** through an atomic writer, so it never leaves a broken bundle
66
- on disk. `OKF::Server::App.new(folder)` turns a folder straight into the
67
- [graph server](graph-server.md).
68
-
69
- # Citations
70
-
71
- [1] [README.md — Library](https://github.com/serradura/okf-gem/blob/main/README.md) — worked examples of both layers.
72
- [2] [lib/okf.rb](https://github.com/serradura/okf-gem/blob/main/lib/okf.rb) — the require surface.
@@ -1,49 +0,0 @@
1
- ---
2
- type: Capability
3
- title: Curation linter (lint)
4
- description: An advisory curation-quality report across six categories and sixteen checks — it never rejects a bundle.
5
- resource: lib/okf/bundle/linter.rb
6
- tags: [linting, curation, cli]
7
- timestamp: 2026-07-11T12:00:00Z
8
- ---
9
-
10
- # Overview
11
-
12
- `okf lint` answers the question [validate](validator.md) is forbidden to touch:
13
- *is this bundle well-curated — navigable, complete, trustworthy?* It reports over
14
- exactly the soft things §9 tolerates, has its own `OKF::Bundle::Linter` and
15
- report, and never emits a conformance error. It is **advisory**: exit `0` even
16
- with findings unless you opt in with `--fail-on warn`.
17
-
18
- # Six categories, sixteen checks
19
-
20
- | Category | Checks |
21
- |----------|--------|
22
- | Reachability | `orphan`, `not_in_index`, `disconnected_component`, `unlinked` |
23
- | Backlog | `missing_concept`, `broken_index_entry` |
24
- | Completeness | `stub`, `missing_title`, `missing_description`, `missing_timestamp` |
25
- | Freshness | `stale` |
26
- | Provenance | `uncited_external`, `broken_citation` |
27
- | Hygiene | `duplicate_title`, `unused_reference_def`, `undefined_reference`, `self_link` |
28
-
29
- Select with `--only` / `--except` (by check id), tune the stub threshold with
30
- `--min-body`, and get the whole report as a machine substrate with `--json`.
31
-
32
- # The freshness gotcha
33
-
34
- Freshness is **off by default** — a plain `lint` never reports `stale`. Pass
35
- `--stale-after <90d | 12w | 2026-01-01>` when concepts carry a
36
- [`timestamp`](../format/frontmatter.md). The CLI resolves that to an absolute
37
- cutoff so the pure linter never reads the clock.
38
-
39
- # Where lint stops and an agent begins
40
-
41
- `lint` is structural: it cannot judge **contradictions** or **semantic**
42
- staleness (a concept that parses fine but no longer matches reality). Those need
43
- meaning. `lint --json` is precisely the structured input an agent reasons over to
44
- close that gap. The [`loose`](read-views.md) view is a folder-grouped lens over
45
- the single `unlinked` check.
46
-
47
- # Citations
48
-
49
- [1] [lib/okf/bundle/linter.rb](https://github.com/serradura/okf-gem/blob/main/lib/okf/bundle/linter.rb) — the sixteen checks and their categories.