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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +50 -0
- data/README.md +15 -15
- data/lib/okf/bundle/search.rb +155 -0
- data/lib/okf/bundle.rb +2 -2
- data/lib/okf/cli.rb +68 -4
- data/lib/okf/server/app.rb +29 -0
- data/lib/okf/server/graph/template.html.erb +256 -40
- data/lib/okf/skill/SKILL.md +17 -11
- data/lib/okf/skill/playbooks/consume.md +4 -2
- data/lib/okf/skill/playbooks/maintain.md +6 -4
- data/lib/okf/skill/playbooks/menu.md +10 -6
- data/lib/okf/skill/playbooks/search.md +42 -0
- data/lib/okf/skill/reference/authoring.md +2 -2
- data/lib/okf/skill/reference/cli.md +55 -17
- data/lib/okf/version.rb +1 -1
- data/lib/okf.rb +1 -0
- metadata +3 -26
- data/.okf/capabilities/agent-skill.md +0 -46
- data/.okf/capabilities/graph-server.md +0 -63
- data/.okf/capabilities/index.md +0 -20
- data/.okf/capabilities/library-api.md +0 -72
- data/.okf/capabilities/linter.md +0 -49
- data/.okf/capabilities/read-views.md +0 -84
- data/.okf/capabilities/validator.md +0 -40
- data/.okf/cli.md +0 -52
- data/.okf/design/core-shell-split.md +0 -58
- data/.okf/design/index.md +0 -10
- data/.okf/design/ruby-floor.md +0 -45
- data/.okf/design/runtime-dependencies.md +0 -44
- data/.okf/design/server-trust-boundary.md +0 -40
- data/.okf/format/citations.md +0 -33
- data/.okf/format/cross-links.md +0 -52
- data/.okf/format/frontmatter.md +0 -38
- data/.okf/format/index.md +0 -9
- data/.okf/format/okf-format.md +0 -43
- data/.okf/index.md +0 -18
- data/.okf/log.md +0 -10
- data/.okf/model/bundle.md +0 -38
- data/.okf/model/concept.md +0 -44
- data/.okf/model/graph.md +0 -44
- data/.okf/model/index.md +0 -8
- 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
|
|
18
|
-
|
|
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)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
|
71
|
-
|
|
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`,
|
|
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
|
|
114
|
-
|
|
115
|
-
|
|
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.
|
|
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.
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
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
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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
data/lib/okf.rb
CHANGED
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
|
+
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.
|
data/.okf/capabilities/index.md
DELETED
|
@@ -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.
|
data/.okf/capabilities/linter.md
DELETED
|
@@ -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.
|