okf 2.1.0 → 2.2.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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/.okf/capabilities/agent-skill.md +112 -0
  3. data/.okf/capabilities/bundles-manager.md +144 -0
  4. data/.okf/capabilities/graph-server.md +678 -0
  5. data/.okf/capabilities/index.md +26 -0
  6. data/.okf/capabilities/library-api.md +82 -0
  7. data/.okf/capabilities/linter.md +83 -0
  8. data/.okf/capabilities/read-views.md +228 -0
  9. data/.okf/capabilities/render.md +66 -0
  10. data/.okf/capabilities/search.md +297 -0
  11. data/.okf/capabilities/validator.md +60 -0
  12. data/.okf/cli.md +214 -0
  13. data/.okf/design/browser-tests.md +211 -0
  14. data/.okf/design/core-shell-split.md +73 -0
  15. data/.okf/design/index.md +17 -0
  16. data/.okf/design/integration-first.md +140 -0
  17. data/.okf/design/packaging.md +65 -0
  18. data/.okf/design/ruby-floor.md +53 -0
  19. data/.okf/design/runtime-dependencies.md +82 -0
  20. data/.okf/design/search-engines.md +154 -0
  21. data/.okf/design/server-trust-boundary.md +139 -0
  22. data/.okf/index.md +40 -0
  23. data/.okf/log.md +724 -0
  24. data/.okf/model/bundle.md +47 -0
  25. data/.okf/model/concept.md +75 -0
  26. data/.okf/model/graph.md +59 -0
  27. data/.okf/model/index.md +9 -0
  28. data/.okf/model/skeleton.md +76 -0
  29. data/.okf/overview.md +87 -0
  30. data/.okf/registry.md +432 -0
  31. data/.okf/structure/format-layer.md +59 -0
  32. data/.okf/structure/index.md +22 -0
  33. data/.okf/structure/search.md +53 -0
  34. data/.okf/structure/the-analysers.md +60 -0
  35. data/.okf/structure/the-cli.md +99 -0
  36. data/.okf/structure/the-disk-shell.md +76 -0
  37. data/.okf/structure/the-model.md +81 -0
  38. data/.okf/structure/the-server.md +74 -0
  39. data/.okf/structure/the-skill.md +52 -0
  40. data/.okf/testing/adding-a-verb.md +76 -0
  41. data/.okf/testing/index.md +12 -0
  42. data/.okf/testing/the-harness.md +45 -0
  43. data/CHANGELOG.md +161 -16
  44. data/README.md +226 -17
  45. data/lib/okf/cli/command.rb +8 -3
  46. data/lib/okf/cli/registry.rb +306 -47
  47. data/lib/okf/cli.rb +1 -1
  48. data/lib/okf/registry.rb +448 -22
  49. data/lib/okf/render/graph/template.html.erb +6 -2
  50. data/lib/okf/server/hub.rb +6 -2
  51. data/lib/okf/skill/reference/cli/registry.md +49 -6
  52. data/lib/okf/skill/reference/cli.md +1 -1
  53. data/lib/okf/version.rb +1 -1
  54. metadata +46 -5
@@ -0,0 +1,47 @@
1
+ ---
2
+ type: Component
3
+ title: OKF::Bundle
4
+ description: The pure in-memory collection of concepts that validate, lint, and graph run over.
5
+ resource: gems/okf/lib/okf/bundle.rb
6
+ tags: [pure]
7
+ generated:
8
+ by: human:maintainer
9
+ at: 2026-08-13T12:00:00Z
10
+ sources:
11
+ - title: gems/okf/lib/okf/bundle.rb
12
+ resource: https://github.com/serradura/okf/blob/main/gems/okf/lib/okf/bundle.rb
13
+ ---
14
+
15
+ # Overview
16
+
17
+ `OKF::Bundle` is a set of [concepts](concept.md) held together in memory, with no
18
+ disk involved. It is the object the three judging capabilities operate on:
19
+ `#validate`, `#lint`, and `#graph` each hand the bundle to a dedicated pure
20
+ class and return a result. A bundle also carries the reserved files
21
+ (`index.md`, `log.md`) and — importantly — an `unparseable` list.
22
+
23
+ # Best-effort by construction
24
+
25
+ When a bundle is built from disk, files the reader cannot use do not vanish and do
26
+ not abort the build: they are collected in `bundle.unparseable` — frontmatter that
27
+ would not parse, and files that would not open, since a locked file is one file's
28
+ problem and not the bundle's. That is what
29
+ lets [graph](graph.md), the [server](../capabilities/graph-server.md), and the
30
+ [read views](../capabilities/read-views.md) render everything that *is* valid
31
+ while the [CLI](../cli.md) notes the skips on stderr — §11's best-effort posture,
32
+ made structural.
33
+
34
+ # Build it from data, not only from files
35
+
36
+ Because the bundle is pure, an embedding application can construct concepts
37
+ straight from its own records — no Markdown round-trip — and still get validate,
38
+ lint, and graph for free. This is the surface the
39
+ [library API](../capabilities/library-api.md) exposes to, say, a Rails store that
40
+ already holds knowledge as rows. It also feeds the shared `#catalog`, the data
41
+ behind every read view — and the rollups beside it, each kept in one home
42
+ after a hand copy diverged: `#stats` (whose `by_dir` reads the directory
43
+ index, so an empty directory reports its honest zero), `#tag_groups` (the
44
+ vocabulary-curation cut, within-group counts beside cross-set totals), and
45
+ `Bundle::References` (the §6.3 inventory — the one model that sees a bundle's
46
+ non-markdown files, fed a manifest by the shell). The CLI verbs and the MCP
47
+ tools consume the same three.
@@ -0,0 +1,75 @@
1
+ ---
2
+ type: Component
3
+ title: OKF::Concept
4
+ description: The pure in-memory model of a single OKF file — frontmatter, body, and a stable id.
5
+ resource: gems/okf/lib/okf/concept.rb
6
+ tags: [pure]
7
+ generated:
8
+ by: human:maintainer
9
+ at: 2026-08-14T12:00:00Z
10
+ sources:
11
+ - title: gems/okf/lib/okf/concept.rb
12
+ resource: https://github.com/serradura/okf/blob/main/gems/okf/lib/okf/concept.rb
13
+ ---
14
+
15
+ # Overview
16
+
17
+ `OKF::Concept` is the atomic node: a `path`, a parsed
18
+ `frontmatter` (`@okf-eco format/frontmatter`) hash, and a Markdown `body`. It is
19
+ [pure](../design/core-shell-split.md) — it holds no file handle and does no I/O.
20
+ The on-disk counterpart is `OKF::Concept::File`, part of the
21
+ [library API](../capabilities/library-api.md).
22
+
23
+ # The id is the concept's identity
24
+
25
+ `#id` is the concept's **stable identifier** across the whole system — the graph
26
+ node key, the link target, and the thing you name a concept for. By default it is
27
+ the `path` minus `.md` (e.g. `model/graph.md` → `model/graph`), which is why you
28
+ name a file for what it *is*, not where it sits. A frontmatter `id`, when set,
29
+ pins the identity explicitly — the path-derived name is the fallback, not the only
30
+ source — so a concept can keep its id across a move.
31
+
32
+ The override is this gem's **extension, not spec**: §2 defines the concept id
33
+ as the path minus `.md`, full stop. Pinning one puts the concept in two worlds
34
+ on purpose — the identity views ([catalog, hubs, `--dir`,
35
+ search](../capabilities/read-views.md)) follow the id, because the edges do,
36
+ while the physical views (`index`, `dirs`, stats' `by_dir`) keep the file where
37
+ it lives, because an index is a physical listing. An integration test holds a
38
+ concept whose id leaves its directory and asserts both worlds at once, so the
39
+ split stays a decision; the authoring advice is the default's — rename the
40
+ file, not the id.
41
+
42
+ # What it derives from its own content
43
+
44
+ The concept parses its body on demand into the structural facts the rest of the
45
+ gem consumes:
46
+
47
+ - `#type`, `#title`, `#description`, `#resource`, `#tags` — typed reads over
48
+ the frontmatter;
49
+ - the §5 families, each carrying §13.1's fallbacks inline: `#generated` /
50
+ `#generated_at` / `#generated_by` (a legacy `timestamp` lifts into `at`,
51
+ per-key, with no actor ever invented), `#sources` (the native list, or the
52
+ legacy `# Citations` (`@okf-eco format/citations`) body list whenever the native
53
+ value yields zero mappings), `#verified` (a bare mapping reads as a
54
+ one-element list; degenerate shapes fold to unverified), `#trust_tier` /
55
+ `#trust` (derived per §5.3, never stored), `#status`/`#declared_status`
56
+ (absent reads stable; both keep the producer's spelling, because every
57
+ surface that *displays* a status prints what was written — the fold belongs
58
+ to `.effective_status`, which is what narrowing compares through), `#stale_after_date`/`#stale_on?(today)` (§5.5 — stale
59
+ on the day itself; the clock is always an argument, never read);
60
+ - the §10 contract of an Attested Computation: `#runtime`, `#parameters`,
61
+ `#computation`, `#executor`, `#attester`, `#attested_computation?`;
62
+ - `#usage_window` — validated for shape, deliberately consumed by nothing: no
63
+ gem surface computes over usage counts, so an effective-window resolver
64
+ would be speculative;
65
+ - `#declared_generated?`, `#legacy_timestamp?`, `#legacy_citations?` — raw-key
66
+ detection for lint and the surfaces; detection never influences reading;
67
+ - `#links` — every raw cross-link (`@okf-eco format/cross-links`) target in the body, in order; the bundle-relative ones become graph edges;
68
+ - `#external_links` — the subset of those that are URLs or `mailto:` (not edges);
69
+ - `#to_markdown` — the inverse of the frontmatter parser (`#citations` is gone,
70
+ subsumed by `#sources`);
71
+ - `#lint` — the concept-scoped [lint](../capabilities/linter.md) checks in isolation.
72
+
73
+ A concept never decides conformance alone; a [bundle](bundle.md) does, because
74
+ some checks (duplicate titles, missing link targets) are only meaningful across
75
+ the set.
@@ -0,0 +1,59 @@
1
+ ---
2
+ type: Component
3
+ title: OKF::Bundle::Graph
4
+ description: The in-memory knowledge graph — concepts as nodes, cross-links as directed edges, with type and tag indexes.
5
+ resource: gems/okf/lib/okf/bundle/graph.rb
6
+ tags: [graph, pure]
7
+ generated:
8
+ by: human:maintainer
9
+ at: 2026-08-13T12:00:00Z
10
+ sources:
11
+ - title: gems/okf/lib/okf/bundle/graph.rb
12
+ resource: https://github.com/serradura/okf/blob/main/gems/okf/lib/okf/bundle/graph.rb
13
+ ---
14
+
15
+ # Overview
16
+
17
+ `OKF::Bundle::Graph` turns a [bundle](bundle.md) into nodes and edges:
18
+ [concepts](concept.md) become nodes keyed by id, and bundle-relative
19
+ cross-links (`@okf-eco format/cross-links`) become directed edges. It is pure — it
20
+ carries no presentation concerns; sizing and colour belong to a renderer like
21
+ the [graph server](../capabilities/graph-server.md).
22
+
23
+ # Fidelity is a build option
24
+
25
+ The same graph ships at three weights, so a client downloads only what it needs
26
+ and fetches the rest on demand:
27
+
28
+ | Build | Node payload |
29
+ |-------|--------------|
30
+ | default (`body: true`) | id, type, title, description, tags, **body** |
31
+ | `body: false` | everything but the body |
32
+ | `minimal: true` | just id and title — the leanest payload to draw |
33
+
34
+ # Indexes come free at every weight
35
+
36
+ Regardless of node fidelity, the graph exposes two inverted indexes computed from
37
+ every concept:
38
+
39
+ - `type_index` — `{ type => [id, …] }`, so even a minimal client can colour nodes
40
+ by [`type`](concept.md). A concept whose type is missing *or blank* indexes
41
+ under `Untyped`: §11 condition 2 rejects the two identically (the
42
+ [validator](../capabilities/validator.md) asks `OKF.blank?`, not `empty?`), so
43
+ sorting them into different buckets would give `type: " "` a row of its own,
44
+ labelled with spaces;
45
+ - `tag_index` — `{ tag => [id, …] }`, so it can filter by tag.
46
+
47
+ Those indexes, plus `unlinked_ids` (degree-0 nodes), are what the
48
+ [read views](../capabilities/read-views.md) — `types`, `tags`, `stats`, `loose`
49
+ — are built from, and what their `--type`/`--dir`/`--tag` filters match against.
50
+
51
+ # Reduced to a skeleton
52
+
53
+ Where this graph is the full wiring, the [skeleton](skeleton.md) is the same
54
+ graph made holdable: concepts collapsed into their directory, the links between
55
+ two directories collapsed into one weighted arc, and every link tagged with the
56
+ cut it survives. It is the reduction [`graph --traffic`](../capabilities/read-views.md)
57
+ reads for cohesion and the [graph page](../capabilities/graph-server.md) lays a
58
+ large bundle out on — built from this graph's own nodes and edges, and just as
59
+ pure.
@@ -0,0 +1,9 @@
1
+ # The model
2
+
3
+ The pure, in-memory data structures the gem builds a bundle out of — no disk, no
4
+ stdio. Everything else reads or renders these.
5
+
6
+ * [Concept](concept.md) - one file's worth of knowledge: frontmatter plus body, with a stable id.
7
+ * [Bundle](bundle.md) - a collection of concepts you validate, lint, and graph.
8
+ * [Graph](graph.md) - concepts as nodes, cross-links as edges, plus type and tag indexes.
9
+ * [Skeleton](skeleton.md) - the graph reduced: directories, the weighted arcs between them, and each link with the cut it survives.
@@ -0,0 +1,76 @@
1
+ ---
2
+ type: Component
3
+ title: OKF::Bundle::Skeleton
4
+ description: The graph reduced to what a reader can hold — directories, the weighted arcs between them, and each link with the cut it survives.
5
+ resource: gems/okf/lib/okf/bundle/skeleton.rb
6
+ tags: [graph, pure]
7
+ generated:
8
+ by: human:maintainer
9
+ at: 2026-07-23T12:00:00Z
10
+ sources:
11
+ - title: gems/okf/lib/okf/bundle/skeleton.rb
12
+ resource: https://github.com/serradura/okf/blob/main/gems/okf/lib/okf/bundle/skeleton.rb
13
+ - title: gems/okf/lib/okf/cli/graph.rb
14
+ resource: https://github.com/serradura/okf/blob/main/gems/okf/lib/okf/cli/graph.rb
15
+ ---
16
+
17
+ # Overview
18
+
19
+ `OKF::Bundle::Skeleton` is the [graph](graph.md) reduced to what a reader can
20
+ hold in their head. It is built from a graph's nodes and edges, does no I/O, and
21
+ decides nothing about how any of it is drawn — the same purity the graph keeps,
22
+ one level of abstraction up. Two consumers read it: [`graph
23
+ --traffic`](../capabilities/read-views.md) prints its directory reduction, and
24
+ the [graph page](../capabilities/graph-server.md)'s link layer lays a large
25
+ bundle out on the backbone it names.
26
+
27
+ A dense bundle is not dense the way a hub-and-spoke picture is. Measured on a
28
+ 47-concept bundle with 227 links, the top hub takes 13 inbound and the median 4 —
29
+ no 80/20 to exploit, so dropping two-thirds of the *concepts* still leaves 53
30
+ edges. The density lives **between directories**: 173 of those 227 links (76%)
31
+ cross a directory boundary. That is the reduction worth drawing, and it is why
32
+ this class exists at all.
33
+
34
+ # The two things it produces
35
+
36
+ - **`dirs` + `arcs`** — the reduction as counts: one row per directory, one
37
+ weighted arc per ordered pair of directories. This is what `graph --traffic`
38
+ prints, with **cohesion** (a directory's internal share of its own traffic)
39
+ derived from them. Directories come off the concept *id* (`OKF.dir_of`), not
40
+ the file path, so this agrees with the graph's own `catalog` and `--dir`
41
+ filter — the side that follows the id, because the edges do.
42
+ - **`edges`, each with its `keep_at`** — the smallest cut at which a link
43
+ survives. Nothing prints these; they are what lets the graph page lay a large
44
+ bundle out on its strongest links first, inlined into the page as `EDGE_CUT`
45
+ because the layout needs them before any `fetch()` could answer.
46
+
47
+ Both are emitted **unthresholded**, and `#suggested_cut` *names* where to cut
48
+ rather than cutting — so a caller narrows the picture without this class ever
49
+ having to know what a picture is.
50
+
51
+ # The cut is fitted, not fixed
52
+
53
+ A fixed arc cut cannot serve both ends of the size range: measured at weight 3
54
+ across ten bundles, it left **2 arcs on one and 136 on another** — too tight to
55
+ be a picture at one end, no reduction at all at the other. What stays roughly
56
+ constant as a bundle grows is not the arc count but the arcs *per box*, since a
57
+ node-link diagram reads at about one to two edges per node regardless of size. So
58
+ `#suggested_cut` targets 1.5 arcs per directory and reports the weight that
59
+ delivers it, floored at 8 for the small end. Cohesion is computed over **every**
60
+ arc regardless of the cut, so narrowing the drawn picture never moves the
61
+ evidence under it.
62
+
63
+ # The spine is a sparsifier, not a sample
64
+
65
+ The `keep_at === 0` set is the **spine**: each concept's single most-connected
66
+ neighbour. It is chosen, not sampled, and the property that earns it the name is
67
+ that it **touches every linked concept**, so a layout run over it alone strands
68
+ nothing and the arrangement is a real one rather than a sketch to be redone.
69
+
70
+ It is the local-degree sparsifier (Lindner et al.) with a union rule — an edge
71
+ survives if *either* endpoint kept it — which is what stops it from stranding the
72
+ quiet half of a bundle the way a global "drop the weakest edges" rule does. It is
73
+ **not** the disparity filter, the usual name in this territory: that reads an
74
+ edge's weight against its endpoint's total, and every link here weighs exactly 1,
75
+ which makes every proportion identical and the filter a coin toss. Weighted-graph
76
+ tools do not transfer to an unweighted graph just because both are graphs.
data/.okf/overview.md ADDED
@@ -0,0 +1,87 @@
1
+ ---
2
+ type: Overview
3
+ title: okf at a glance
4
+ description: A light Ruby gem that reads, searches, validates, lints, and serves Open Knowledge Format v0.2 bundles.
5
+ tags: [okf, gem, diagram]
6
+ generated:
7
+ by: human:maintainer
8
+ at: 2026-08-13T12:00:00Z
9
+ sources:
10
+ - title: README.md
11
+ resource: https://github.com/serradura/okf/blob/main/README.md
12
+ - title: AGENTS.md
13
+ resource: https://github.com/serradura/okf/blob/main/AGENTS.md
14
+ ---
15
+
16
+ # Overview
17
+
18
+ **okf** — the gem on RubyGems — operates on OKF v0.2 (`@okf-eco format/okf-format`)
19
+ bundles: directories of Markdown files with YAML frontmatter that humans and
20
+ agents both read from one source. It does not define new knowledge storage; it
21
+ gives you leverage over knowledge that already lives as Markdown.
22
+
23
+ ```mermaid
24
+ flowchart LR
25
+ skill["companion<br/>agent skill"] -. authors/curate .-> bundle[("OKF v0.2 bundle<br/>Markdown + YAML")]
26
+ bundle --> model["pure model<br/>Concept · Bundle · Graph"]
27
+ skill -. execute .-> cli
28
+ subgraph cli ["okf CLI"]
29
+ validate["validate — legal? §11"]
30
+ lint["lint — well-curated?"]
31
+ search["search — which concept?"]
32
+ server["server — explore"]
33
+ end
34
+ model --> cli
35
+ model --> library["library API<br/>embed in Ruby"]
36
+ ```
37
+
38
+ Over such a bundle the gem gives you seven capabilities behind one
39
+ [command-line tool](cli.md):
40
+
41
+ | Capability | What it answers | Verb |
42
+ | -------------------------------------------------------- | --------------------------------- | ---------------- |
43
+ | [Companion agent skill](capabilities/agent-skill.md) | Can an agent author it? | `skill` |
44
+ | [Conformance validator](capabilities/validator.md) | Is this a legal OKF bundle? (§11) | `validate` |
45
+ | [Curation linter](capabilities/linter.md) | Is it navigable, complete, fresh? | `lint` / `loose` |
46
+ | [Ranked text search](capabilities/search.md) | Which concept covers X? | `search` |
47
+ | [Interactive graph server](capabilities/graph-server.md) | Can I explore it visually? | `server` |
48
+ | [Static render](capabilities/render.md) | Can I ship a serverless snapshot? | `render` |
49
+ | [Library API](capabilities/library-api.md) | Can my Ruby program use it? | (in-process) |
50
+
51
+ Beside the gem's seven, the sibling surfaces. The
52
+ MCP server (`@okf-mcp design/the-tool-set`) (`okf-mcp`) projects the same kernel
53
+ onto the Model Context Protocol so any MCP-capable agent host reads these
54
+ bundles without a terminal — ten read-only tools, concepts as resources a host
55
+ can attach on its own, and the [skill's](capabilities/agent-skill.md) playbooks
56
+ as prompts. The enforcement layer (`@okf-eco gems/okf-pro`) (`okf-pro`)
57
+ goes the other way and is the only surface here that **writes**: it generates an
58
+ agent's knowledge repository — bundle, hooks, pre-commit, CI, skill — and then
59
+ holds it to a few invariants at all three doors, under a contract where a gate
60
+ that cannot check refuses rather than shrugs.
61
+
62
+ Alongside those, a family of [read views](capabilities/read-views.md) —
63
+ `index`, `catalog`, `files`, `types`, `tags`, `stats`, `graph` — print the bundle at a
64
+ glance so an agent reads it without a browser.
65
+
66
+ Knowledge rarely lives in one bundle, so `okf server` hosts one, several, or every
67
+ bundle in a per-user [registry](registry.md) — one hub, one switcher, no per-repo
68
+ server to remember.
69
+
70
+ # The two ideas it inherits from the format
71
+
72
+ - **Dual audience.** Every file serves a human skimming it _and_ an agent
73
+ extracting from it, so bodies are structural Markdown and
74
+ links (`@okf-eco format/cross-links`) are plain Markdown links — both readers already
75
+ understand them.
76
+ - **The graph is emergent.** Files are nodes, Markdown links are edges. You never
77
+ declare a graph; the gem [builds one](model/graph.md) from how concepts link.
78
+
79
+ # Design ethos
80
+
81
+ The gem is deliberately light so it runs on the Ruby an OS already ships. That
82
+ ethos is not incidental — it is enforced by [hard constraints](design/):
83
+ a [Ruby 2.4 floor](design/ruby-floor.md), exactly
84
+ [three runtime dependencies](design/runtime-dependencies.md), and a
85
+ [core/shell split](design/core-shell-split.md) that keeps all logic pure and
86
+ testable without disk. Everything else — no ActiveSupport, no build step, no
87
+ JavaScript toolchain — follows from those.