okf 1.4.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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +50 -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 +256 -40
  9. data/lib/okf/skill/SKILL.md +17 -11
  10. data/lib/okf/skill/playbooks/consume.md +4 -2
  11. data/lib/okf/skill/playbooks/maintain.md +6 -4
  12. data/lib/okf/skill/playbooks/menu.md +10 -6
  13. data/lib/okf/skill/playbooks/search.md +42 -0
  14. data/lib/okf/skill/reference/authoring.md +2 -2
  15. data/lib/okf/skill/reference/cli.md +55 -17
  16. data/lib/okf/version.rb +1 -1
  17. data/lib/okf.rb +1 -0
  18. metadata +3 -26
  19. data/.okf/capabilities/agent-skill.md +0 -46
  20. data/.okf/capabilities/graph-server.md +0 -63
  21. data/.okf/capabilities/index.md +0 -20
  22. data/.okf/capabilities/library-api.md +0 -72
  23. data/.okf/capabilities/linter.md +0 -49
  24. data/.okf/capabilities/read-views.md +0 -84
  25. data/.okf/capabilities/validator.md +0 -40
  26. data/.okf/cli.md +0 -52
  27. data/.okf/design/core-shell-split.md +0 -58
  28. data/.okf/design/index.md +0 -10
  29. data/.okf/design/ruby-floor.md +0 -45
  30. data/.okf/design/runtime-dependencies.md +0 -44
  31. data/.okf/design/server-trust-boundary.md +0 -40
  32. data/.okf/format/citations.md +0 -33
  33. data/.okf/format/cross-links.md +0 -52
  34. data/.okf/format/frontmatter.md +0 -38
  35. data/.okf/format/index.md +0 -9
  36. data/.okf/format/okf-format.md +0 -43
  37. data/.okf/index.md +0 -18
  38. data/.okf/log.md +0 -10
  39. data/.okf/model/bundle.md +0 -38
  40. data/.okf/model/concept.md +0 -44
  41. data/.okf/model/graph.md +0 -44
  42. data/.okf/model/index.md +0 -8
  43. data/.okf/overview.md +0 -67
@@ -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.4.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.4.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.
@@ -1,84 +0,0 @@
1
- ---
2
- type: Capability
3
- title: Read views (index, catalog, files, types, tags, stats, loose, graph)
4
- description: The server's browser panels reproduced on the CLI, plus the index map, so an agent reads a bundle at a glance without a browser.
5
- tags: [read, cli, json]
6
- timestamp: 2026-07-12T12:00:00Z
7
- ---
8
-
9
- # Overview
10
-
11
- The [graph server](graph-server.md) renders a bundle in a browser; these verbs
12
- render the same knowledge as text, so an agent (or a terminal) reads it directly.
13
- Each prints a scannable human view by default and machine JSON with `--json`, and
14
- all are advisory reads that exit `0`. They share
15
- [`OKF::Bundle#catalog`](../model/bundle.md) and the
16
- [graph indexes](../model/graph.md) for their data.
17
-
18
- # The views
19
-
20
- | Verb | Shows | Grouped by |
21
- |------|-------|------------|
22
- | `index` | each directory's index body, type/tag rollup, child dirs, and concept listing | directory (root first) |
23
- | `catalog` | concepts with type, tags, link counts, status | area |
24
- | `files` | files with titles | folder |
25
- | `types` | [types](../format/frontmatter.md) with their concepts | count |
26
- | `tags` | [tags](../format/frontmatter.md) with their concepts | count |
27
- | `stats` | rollups: concepts, areas, types, cross-links, tags | — |
28
- | `loose` | degree-0 concepts (no [links](../format/cross-links.md) in or out) | folder |
29
- | `graph` | the raw nodes and edges | — (`--minimal` / `--no-body`) |
30
-
31
- # `index` is the orient-first map (§6)
32
-
33
- Alone among the read views, `index` shows the reserved `index.md` layer: the
34
- concept views skip those structural files, so only `index` renders the
35
- [progressive-disclosure map](../format/okf-format.md) — one entry per directory
36
- (root first) with its authored index body, a type/tag rollup over the concepts
37
- living directly there, its child directories, and the concept listing. `--area`
38
- narrows to one directory and repeats (`root` names the bundle root); `--no-body`
39
- drops the prose to a skeleton. It is the cheapest orientation when picking up a
40
- bundle, and the only view that exposes *enumeration drift* — a listing entry that
41
- should exist but is missing, which no grep can find. A directory that holds
42
- concepts but no `index.md` gets its listing synthesized and tagged `(no index.md)`,
43
- a prompt to write a real map rather than a defect.
44
-
45
- # JSON output — compact, and projectable
46
-
47
- `--json` is **compact by default** — single-line, the token-efficient substrate an
48
- agent consumes; `--pretty` (which implies `--json`) indents the same JSON for a
49
- human. On the per-item list views — `index`, `catalog`, `files` — `--fields a,b`
50
- keeps only those properties and `--except a,b` drops them (mutually exclusive; an
51
- unknown name is a usage error that lists the valid ones). Projection runs before
52
- emission, so an agent never pays tokens for a field it dropped: `okf index <dir>
53
- --except body,listing` is the lean directory skeleton, the difference between a few
54
- hundred bytes and hundreds of KB on a large bundle.
55
-
56
- # Narrowing and regrouping
57
-
58
- The four list views — `catalog`, `files`, `types`, `tags` — accept the filters
59
- *orthogonal* to how they group, so you ask a narrow question instead of paging
60
- the whole bundle (matching is case-insensitive):
61
-
62
- | View | Filters it accepts |
63
- |------|--------------------|
64
- | `catalog`, `files` | `--type`, `--area`, `--tag` |
65
- | `types` | `--area`, `--tag` (it already groups by type) |
66
- | `tags` | `--type`, `--area` (it already groups by tag) |
67
-
68
- `tags --by type|area` regroups the tag index under each concept dimension with
69
- within-group counts — the view for curating a
70
- [tag](../format/frontmatter.md) vocabulary: which tags cluster in which area,
71
- which type leans on which tags.
72
-
73
- # `loose` is a curation lens, not an error
74
-
75
- `loose` is the folder-grouped view over [lint](linter.md)'s `unlinked` check —
76
- distinct from `orphan`. An `index.md` listing makes a file *reachable* (not an
77
- orphan) but is **not a graph edge**, so a listed file can still float here. A
78
- loose file may be perfectly fine — a terminal leaf like a backlog item is loose
79
- by design — so `loose` surfaces the set for a human or agent to judge and always
80
- exits `0`.
81
-
82
- # Citations
83
-
84
- [1] [cli.md — read views](https://github.com/serradura/okf-gem/blob/main/lib/okf/skill/reference/cli.md) — the views and their flags.
@@ -1,40 +0,0 @@
1
- ---
2
- type: Capability
3
- title: Conformance validator (validate)
4
- description: Implements the spec's §9 conformance definition exactly — three hard conditions, everything else a warning.
5
- resource: lib/okf/bundle/validator.rb
6
- tags: [validation, conformance, cli]
7
- timestamp: 2026-07-11T12:00:00Z
8
- ---
9
-
10
- # Overview
11
-
12
- `okf validate` answers one question: *is this a legal [OKF](../format/okf-format.md)
13
- bundle?* `OKF::Bundle::Validator` implements §9 exactly and is the **only**
14
- capability that can fail a bundle — exit `1` on any hard error, `0` otherwise.
15
-
16
- # The three hard conditions (errors)
17
-
18
- | Rule | Condition |
19
- |------|-----------|
20
- | §9.1 | every non-reserved file has a parseable [frontmatter](../format/frontmatter.md) block |
21
- | §9.2 | every such block has a **non-empty `type`** |
22
- | §9.3 | every `index.md` / `log.md` present is well-formed (nested index has no frontmatter, root index carries only `okf_version`, log dates are ISO) |
23
-
24
- # Everything else is a warning
25
-
26
- The validator is **forbidden by §9** from rejecting a bundle for soft issues, so
27
- these are warnings that never change conformance:
28
-
29
- - missing recommended fields, non-list `tags`, an unparseable `timestamp`;
30
- - **broken [cross-links](../format/cross-links.md)** (§5.3) — consumers MUST
31
- tolerate them.
32
-
33
- Judging those is the [linter](linter.md)'s job, and keeping the two apart is a
34
- [hard design contract](../design/core-shell-split.md). The
35
- [writer](library-api.md) runs this validator *before* publishing, so a saved
36
- bundle is never written non-conformant.
37
-
38
- # Citations
39
-
40
- [1] [lib/okf/bundle/validator.rb](https://github.com/serradura/okf-gem/blob/main/lib/okf/bundle/validator.rb) — the §9 implementation.