okf 2.0.0 → 2.1.1

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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/.okf/capabilities/agent-skill.md +112 -0
  3. data/.okf/capabilities/bundles-manager.md +133 -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 +295 -0
  11. data/.okf/capabilities/validator.md +60 -0
  12. data/.okf/cli.md +193 -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 +662 -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 +265 -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 +67 -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 +83 -16
  44. data/README.md +205 -21
  45. data/lib/okf/cli.rb +20 -2
  46. data/lib/okf/skill/SKILL.md +15 -7
  47. data/lib/okf/skill/playbooks/maintain.md +3 -1
  48. data/lib/okf/skill/playbooks/produce.md +4 -2
  49. data/lib/okf/skill/reference/authoring.md +23 -26
  50. data/lib/okf/skill/reference/cli/checks.md +171 -0
  51. data/lib/okf/skill/reference/cli/graph.md +49 -0
  52. data/lib/okf/skill/reference/cli/map.md +98 -0
  53. data/lib/okf/skill/reference/cli/registry.md +70 -0
  54. data/lib/okf/skill/reference/cli/search.md +130 -0
  55. data/lib/okf/skill/reference/cli/serve.md +83 -0
  56. data/lib/okf/skill/reference/cli/views.md +59 -0
  57. data/lib/okf/skill/reference/cli.md +33 -603
  58. data/lib/okf/skill/reference/spec-map.md +32 -0
  59. data/lib/okf/version.rb +1 -1
  60. metadata +54 -5
@@ -0,0 +1,45 @@
1
+ ---
2
+ type: Component
3
+ title: The test harness
4
+ description: One base class for the whole suite — plain Minitest plus `test "..."` and block setup/teardown — and it runs on 2.4, so the gem's API floor binds the tests exactly as it binds `lib/`.
5
+ resource: gems/okf/test/test_helper.rb
6
+ tags: [testing, minitest, ruby-floor]
7
+ generated:
8
+ by: human:maintainer
9
+ at: 2026-08-19T12:00:00Z
10
+ ---
11
+
12
+ # One base class
13
+
14
+ Every test in this gem subclasses `OKF::TestCase`, defined in
15
+ `test/test_helper.rb`. It is plain Minitest with two pieces of sugar:
16
+
17
+ * `test "a sentence" do … end` instead of `def test_a_sentence`, so a failure
18
+ names the behaviour in prose rather than in snake case;
19
+ * block `setup` / `teardown`, which compose rather than requiring `super`.
20
+
21
+ Nothing else is the base class. A test that reaches for `Minitest::Test`
22
+ directly loses both and reads unlike its neighbours, which is the whole reason
23
+ the sugar exists — the three siblings each ported the same class for the same
24
+ reason, so the suites read alike across the monorepo.
25
+
26
+ # It runs on 2.4, so the floor binds it
27
+
28
+ The suite runs on every supported Ruby, 2.4 included. That makes the
29
+ [Ruby floor](../design/ruby-floor.md)'s forbidden-API list a rule about `test/`
30
+ as much as about `lib/` — a `filter_map` in a test fails the floor container
31
+ exactly as one in a source file does, and RuboCop will not catch either.
32
+
33
+ SimpleCov is the one exception, and it is conditional rather than absent: it
34
+ needs 2.5+, so `test_helper.rb` loads it inside a `begin`/`rescue` and the suite
35
+ simply runs without coverage where it cannot load. Coverage is a reporting
36
+ convenience; the floor is a contract, and the contract wins.
37
+
38
+ # What a test may assume about the disk
39
+
40
+ Fixtures are real directories under `test/fixtures/`, not mocks. A branch that
41
+ no fixture can reach is a branch nobody has ever proven, so the answer to an
42
+ unreachable path is a new fixture rather than a bent assertion — the general
43
+ form of that obligation is `@okf-eco design/how-a-change-is-proven`, and how the
44
+ critical layer is organised is
45
+ [integration first](../design/integration-first.md).
data/CHANGELOG.md CHANGED
@@ -5,6 +5,71 @@ All notable changes to this project are documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [2.1.1] - 2026-08-20
9
+
10
+ ### Fixed
11
+
12
+ - **The package metadata follows the `gems/` move and the repository rename.**
13
+ Two URLs on the package page were about to be wrong at once. `changelog_uri`
14
+ named `blob/main/okf/CHANGELOG.md`, a path that stopped existing when the
15
+ four gems moved under `gems/`; `homepage` named `serradura/okf-gem`, and the
16
+ repository is now **`serradura/okf`** — the name the command has had all
17
+ along. rubygems.org serves whatever the last release published, so neither
18
+ corrects itself: only a release republishes metadata, and this is that
19
+ release. Both land in one round rather than two, which is the whole reason
20
+ the rename waited for the paths to stop moving.
21
+
22
+ `homepage_uri`, `source_code_uri` and `changelog_uri` are all derived from
23
+ `homepage`, so one line moves four pieces of this gem's metadata. GitHub
24
+ redirects the old URLs permanently, so anything already published keeps
25
+ resolving.
26
+
27
+ ### Added
28
+
29
+ - **The gem ships a file-level map of itself, at `.okf/`.** `structure/` names
30
+ every one of the fifty files under `lib/`, grouped by the layer that owns it,
31
+ and `testing/adding-a-verb.md` is the ordered walk a new command owes. It is
32
+ the gem's own documentation rather than a sample, so an installed okf now
33
+ carries a real bundle for a reader to open with the tool they just installed:
34
+ `okf server "$(gem contents okf --show-install-dir)/.okf"`.
35
+
36
+ It deliberately copies nothing from the repository's own bundle, which still
37
+ holds okf's format, model, capabilities and design constraints — a second copy
38
+ of a catalogue is worse than none.
39
+
40
+ `test/unit/bundle_catalog_test.rb` pins both halves: a file under `lib/` named
41
+ by no concept, a concept naming a file that is gone, or the repository
42
+ bundle's `cli.md` group table disagreeing with `OKF::CLI.builtins` all fail
43
+ the suite. That group table was code-derived and unchecked until now.
44
+ `test/unit/packaging_test.rb` pins that the bundle actually ships.
45
+
46
+ ## [2.1.0] - 2026-08-17
47
+
48
+ ### Changed
49
+
50
+ - **The skill's CLI reference is an index and seven leaves.** `reference/cli.md`
51
+ answered every question about every verb in one 46,234-byte file, so an agent
52
+ asking about `search` paid for `serve`, `registry` and the graph too. The
53
+ shared contract — refs, exit codes, `--json`, the filters — stays in `cli.md`
54
+ and routes to one file per surface under `reference/cli/`. Measured on the
55
+ worst question rather than the mean: 9,084 bytes for a routing question,
56
+ 19,533 for the heaviest leaf. `reference/spec-map.md` is the other half, a
57
+ pointer from a spec clause to the file that answers it, so the vendored
58
+ `SPEC.md` keeps its index beside it instead of cut into it.
59
+ - **Cross-file citations in the skill name a `rule:` marker**, not a section
60
+ anchor: the key travels with the paragraph the next time anything moves.
61
+ - **`okf help` prints one row per extension**, plus a pointer to
62
+ `okf <verb> --help` for the rest. An addon with an umbrella verb and several
63
+ subcommands used to dwarf the built-ins the map exists to teach.
64
+
65
+ ### Added
66
+
67
+ - **The skill teaches what a log records.** `log.md` carries durable knowledge
68
+ and shipped behavior, never the process that produced them —
69
+ `rule:okf-log-durable-only` in `reference/authoring.md`, cited by the maintain
70
+ playbook and the Closeout gate. It was the okf-gem repository's own contract
71
+ before this, which an agent maintaining any other bundle never reads.
72
+
8
73
  ## [2.0.0] - 2026-08-14
9
74
 
10
75
  > Major, not minor. The Breaking entries below change public shapes a shipped
@@ -1410,7 +1475,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1410
1475
  ### Added
1411
1476
 
1412
1477
  - Claude Code plugin. The repository now doubles as a plugin marketplace:
1413
- `/plugin marketplace add serradura/okf-gem`, then `/plugin install okf@okfgem`.
1478
+ `/plugin marketplace add serradura/okf`, then `/plugin install okf@okfgem`.
1414
1479
  The plugin carries the canonical skill (a generated copy; `rake plugin:sync`
1415
1480
  keeps it in lockstep with `lib/okf/skill`, and a test fails on drift), one
1416
1481
  front-door command (`/okf:gem`: no arguments orients on the CLI, the bundle,
@@ -1516,18 +1581,20 @@ Initial release.
1516
1581
 
1517
1582
  - Runs on Ruby >= 2.4 with two runtime dependencies: rack and webrick.
1518
1583
 
1519
- [2.0.0]: https://github.com/serradura/okf-gem/compare/v1.13.0...v2.0.0
1520
- [1.13.0]: https://github.com/serradura/okf-gem/compare/v1.12.0...v1.13.0
1521
- [1.12.0]: https://github.com/serradura/okf-gem/compare/v1.11.0...v1.12.0
1522
- [1.11.0]: https://github.com/serradura/okf-gem/compare/v1.10.0...v1.11.0
1523
- [1.10.0]: https://github.com/serradura/okf-gem/compare/v1.9.0...v1.10.0
1524
- [1.9.0]: https://github.com/serradura/okf-gem/compare/v1.8.0...v1.9.0
1525
- [1.8.0]: https://github.com/serradura/okf-gem/compare/v1.7.0...v1.8.0
1526
- [1.7.0]: https://github.com/serradura/okf-gem/compare/v1.6.0...v1.7.0
1527
- [1.6.0]: https://github.com/serradura/okf-gem/compare/v1.5.0...v1.6.0
1528
- [1.5.0]: https://github.com/serradura/okf-gem/compare/v1.4.0...v1.5.0
1529
- [1.4.0]: https://github.com/serradura/okf-gem/compare/v1.3.0...v1.4.0
1530
- [1.3.0]: https://github.com/serradura/okf-gem/compare/v1.2.0...v1.3.0
1531
- [1.2.0]: https://github.com/serradura/okf-gem/compare/v1.1.0...v1.2.0
1532
- [1.1.0]: https://github.com/serradura/okf-gem/compare/v1.0.0...v1.1.0
1533
- [1.0.0]: https://github.com/serradura/okf-gem/releases/tag/v1.0.0
1584
+ [2.1.1]: https://github.com/serradura/okf/compare/v2.1.0...v2.1.1
1585
+ [2.1.0]: https://github.com/serradura/okf/compare/v2.0.0...v2.1.0
1586
+ [2.0.0]: https://github.com/serradura/okf/compare/v1.13.0...v2.0.0
1587
+ [1.13.0]: https://github.com/serradura/okf/compare/v1.12.0...v1.13.0
1588
+ [1.12.0]: https://github.com/serradura/okf/compare/v1.11.0...v1.12.0
1589
+ [1.11.0]: https://github.com/serradura/okf/compare/v1.10.0...v1.11.0
1590
+ [1.10.0]: https://github.com/serradura/okf/compare/v1.9.0...v1.10.0
1591
+ [1.9.0]: https://github.com/serradura/okf/compare/v1.8.0...v1.9.0
1592
+ [1.8.0]: https://github.com/serradura/okf/compare/v1.7.0...v1.8.0
1593
+ [1.7.0]: https://github.com/serradura/okf/compare/v1.6.0...v1.7.0
1594
+ [1.6.0]: https://github.com/serradura/okf/compare/v1.5.0...v1.6.0
1595
+ [1.5.0]: https://github.com/serradura/okf/compare/v1.4.0...v1.5.0
1596
+ [1.4.0]: https://github.com/serradura/okf/compare/v1.3.0...v1.4.0
1597
+ [1.3.0]: https://github.com/serradura/okf/compare/v1.2.0...v1.3.0
1598
+ [1.2.0]: https://github.com/serradura/okf/compare/v1.1.0...v1.2.0
1599
+ [1.1.0]: https://github.com/serradura/okf/compare/v1.0.0...v1.1.0
1600
+ [1.0.0]: https://github.com/serradura/okf/releases/tag/v1.0.0
data/README.md CHANGED
@@ -5,7 +5,7 @@ ranked search, and a live knowledge graph. 100% local.**
5
5
 
6
6
  [Site](https://okfgem.com) · [Docs](https://okfgem.com/docs/) ·
7
7
  [Live demo](https://demo.okfgem.com) ·
8
- [Project README](https://github.com/serradura/okf-gem#readme)
8
+ [Project README](https://github.com/serradura/okf#readme)
9
9
 
10
10
  OKF (Open Knowledge Format) is portable project knowledge: Markdown files with
11
11
  YAML frontmatter that both humans and agents read from one source. This gem is
@@ -22,20 +22,41 @@ on every Ruby since **2.4** — the one your OS already ships.
22
22
 
23
23
  ## Install
24
24
 
25
+ > **In Claude Code**, the plugin is the fastest path: two commands install the whole
26
+ > toolchain (skill, `/okf:gem`, and the curation hook). See the
27
+ > [project README](https://github.com/serradura/okf#claude-code-plugin).
28
+ > Everywhere else, install the gem:
29
+
25
30
  ```bash
26
31
  gem install okf
27
32
  # or, in a project
28
33
  bundle add okf
29
34
  ```
30
35
 
31
- No Ruby? The official image carries the CLI:
36
+ Tested and supported on every Ruby from **2.4 through 4.0**. From a checkout,
37
+ `bundle exec rake install` builds and installs it locally.
38
+
39
+ ## No Ruby? Use Docker
40
+
41
+ The official image bundles the CLI, so every `okf` command runs against a bundle
42
+ you mount at `/data`:
32
43
 
33
44
  ```bash
34
45
  docker run --rm -v "$PWD:/data" ghcr.io/serradura/okf validate .
46
+ docker run --rm -v "$PWD:/data" -p 8808:8808 ghcr.io/serradura/okf server . --bind 0.0.0.0
35
47
  ```
36
48
 
37
- The Docker-backed [`okf` command](https://docker.okfgem.com) drops the prefix so
38
- every verb reads exactly like the native CLI.
49
+ Tired of the long line? The Docker-backed [`okf` command](https://docker.okfgem.com)
50
+ drops the prefix so every verb reads exactly like the native CLI:
51
+
52
+ ```bash
53
+ curl -fsSL https://docker.okfgem.com/install.sh | sh # PowerShell: irm https://docker.okfgem.com/install.ps1 | iex
54
+ okf validate .
55
+ okf server .
56
+ ```
57
+
58
+ Images are published for `linux/amd64` and `linux/arm64` on
59
+ [ghcr.io](https://github.com/serradura/okf/pkgs/container/okf).
39
60
 
40
61
  ## Four steps to your first bundle
41
62
 
@@ -55,6 +76,41 @@ okf server <folder> # 4. explore what you got, as a live graph
55
76
 
56
77
  Then `/okf maintain` keeps it in sync as the code changes.
57
78
 
79
+ What the gem does, and which verb does it. This table is the map; **[the
80
+ docs](https://okfgem.com/docs/)** are the manual.
81
+
82
+ | Capability | What it answers | Verb |
83
+ | ------------------------------------------------------------- | --------------------------------- | -------------------------- |
84
+ | [Companion agent skill](https://okfgem.com/docs/skill/) | Can an agent author it? | `skill` |
85
+ | [Conformance validator](https://okfgem.com/docs/cli/validate/) | Is this a legal OKF bundle? | `validate` |
86
+ | [Curation linter](https://okfgem.com/docs/cli/lint/) | Is it navigable, complete, fresh? | `lint` / `loose` |
87
+ | [Ranked text search](https://okfgem.com/docs/cli/search/) | Which concept covers X? | `search` |
88
+ | [Read views](https://okfgem.com/docs/cli/) | What is in here, and where? | `index` / `dirs` / `catalog` |
89
+ | [Interactive graph server](https://okfgem.com/docs/cli/server/) | Can I explore it visually? | `server` |
90
+ | [Static render](https://okfgem.com/docs/cli/render/) | Can I ship a serverless snapshot? | `render` |
91
+ | [Library API](https://okfgem.com/docs/library/) | Can my Ruby program use it? | in-process |
92
+
93
+ And because knowledge rarely lives in one bundle, a registry gives each one a
94
+ name — see [one registry, many bundles](#one-registry-many-bundles) below.
95
+
96
+ ## The graph
97
+
98
+ One page, from a phone to a desktop: the navigation rail becomes a drawer, the
99
+ toolbar folds into a `⚙` sheet, and a tap opens a preview card at the bottom edge
100
+ rather than a panel over the whole viewport, so the graph stays live while you
101
+ read. Drag the card up for the neighbourhood, tap a link and it walks there in
102
+ place.
103
+
104
+ It is keyboard-first: **`⌘/Ctrl-K`** opens a command palette that searches
105
+ concepts, jumps to a view, and — behind a [hub](#one-registry-many-bundles) —
106
+ switches bundles. **`/`** jumps to the current view's search, **`?`** answers with
107
+ every shortcut. Cluster mode boxes the graph by directory and nests as deep as
108
+ your tree does.
109
+
110
+ To skip the server entirely, **`okf render <dir>`** writes that same page as one
111
+ self-contained HTML file, the whole bundle baked in, so you can publish the graph
112
+ on GitHub Pages or any static host.
113
+
58
114
  ## The command line
59
115
 
60
116
  Written to be read by an **agent first and a person second** — that is what the
@@ -83,16 +139,130 @@ Exit codes: `0` success, `1` non-conformant bundle (or a `lint --fail-on`
83
139
  threshold crossed), `2` usage error. Every flag is in `okf <verb> --help` and in
84
140
  [the docs](https://okfgem.com/docs/).
85
141
 
86
- **A registry names your bundles.** `okf registry set ./docs --as handbook` once,
87
- then `@handbook` works anywhere a `<dir>` does, from any directory; `okf search
88
- @all rate limit` spans every one of them, and a bare `okf server` hosts them all
89
- behind one hub. `okf registry init` scopes one to a single project instead, and a
90
- committed `.okf-registry.json` travels with the repo.
142
+ ```bash
143
+ $ okf validate docs
144
+ OKF v0.2 conformance docs
145
+ concepts: 37 index.md: 10 log.md: 1
146
+ ! warn features/link-suggestions.md: cross-link target not found: `/graph-view.md` (tolerated under §6.1)
147
+
148
+ ✓ conformant (33 warning(s))
149
+
150
+ $ okf server docs
151
+ serving 37 concepts at http://127.0.0.1:8808 (Ctrl-C to stop)
152
+
153
+ $ okf render docs > public/index.html # the same page, static — host it anywhere
154
+ ```
91
155
 
92
- **A big bundle is read a level at a time.** `okf index --depth 1 --except
93
- body,listing` is the map an agent orients on — on a 414-concept bundle, 2.8 KB
94
- against the full 313 KB and `--dir` then opens one branch, bringing the
95
- ancestors that say what it is.
156
+ ## One registry, many bundles
157
+
158
+ The registry is a per-user, ordered list of bundles in one plain JSON file (`$OKF_HOME/registry.json`, default `~/.okf`) — hand-editable,
159
+ greppable, no database. It stores references, never content: the bundles stay in
160
+ the repos that own them.
161
+
162
+ ```bash
163
+ okf registry set ./docs --as handbook # give the bundle a name
164
+ okf lint @handbook # @slug works wherever a <dir> does, from anywhere
165
+ okf search @all rate limit # ranked retrieval across every registered bundle
166
+ okf server # no args: the whole registry behind one hub
167
+ ```
168
+
169
+ Related bundles can share a name: `okf registry group backend @handbook @runbooks`
170
+ makes `@backend` stand for the set (members can be groups too, so they nest), and
171
+ `okf search @backend rate limit` or `okf server @backend` then targets all of them
172
+ at once — a durable subset for the two verbs that take several bundles.
173
+
174
+ The registry lives under `$OKF_HOME` (default `~/.okf`) — one per user. For one
175
+ scoped to a single project instead, `okf registry init` drops a
176
+ `.okf-registry.json` in the current directory; okf then discovers it by walking up
177
+ from wherever you run, and every registry op — and every `@slug` — resolves through
178
+ it in place of the global one. So a bare `okf server` inside that repo serves *its*
179
+ bundles with no `$OKF_HOME` setup. The nearest registry wins, and
180
+ `OKF_NO_DISCOVERY=1` forces the global one.
181
+
182
+ Commit that file and it travels with the repo: a bundle under the project root is
183
+ stored relative to the registry, so a checkout on another machine — or a container
184
+ that mounts the repo — resolves the same bundles unchanged. (Bundles outside the
185
+ tree keep absolute paths, which do not travel.)
186
+
187
+ Behind the hub each bundle mounts at `/b/<slug>/`, `/b/` lists them all, and the
188
+ `⌘/Ctrl-K` palette both switches bundles and **searches every one at once** — type
189
+ a few words and the matching concepts appear with their bundle and a snippet, from
190
+ wherever you are.
191
+
192
+ The ⚙ rail opens **Bundles**, the registry on the graph page itself: make
193
+ default, rename, remove, where you are already reading. Those controls are the one
194
+ thing that does not follow you onto a network — bind anywhere but loopback and
195
+ they are refused outright, since `--bind 0.0.0.0` is how a personal tool becomes a
196
+ public one.
197
+
198
+ ## Reading a big bundle a level at a time
199
+
200
+ A few hundred concepts is a map nobody reads whole, so `index` and `dirs` descend
201
+ instead of dumping. `--dir` takes a directory **and everything under it**,
202
+ `--depth N` bounds how far below that it goes, and the two compose the way you
203
+ actually walk a tree:
204
+
205
+ ```bash
206
+ okf dirs @handbook # the shape: every dir, what it holds directly and below
207
+ okf index @handbook --depth 1 --no-body # the top of the map, no prose
208
+ okf index @handbook --dir platform/api # now open one branch — with the chain that places it
209
+ ```
210
+
211
+ Naming a `--dir` brings its ancestors along, marked `↑`, so a branch is never
212
+ shown adrift of the context that says what it is — the root `index.md`'s prose
213
+ first among it.
214
+
215
+ For an agent the saving is the whole point. On a 400-concept bundle the full
216
+ `okf index --json` is 313 KB; the skeleton it orients on is 2.8 KB:
217
+
218
+ ```bash
219
+ okf index @handbook --json --depth 1 --except body,listing
220
+ ```
221
+
222
+ ## The agent skill
223
+
224
+ The gem carries the [companion OKF agent skill](https://okfgem.com/docs/skill/):
225
+ a `SKILL.md` plus reference and template files that teach a coding agent to
226
+ author, maintain, and consume OKF bundles and to drive
227
+ [the command line](#the-command-line).
228
+ Because the skill ships inside the gem, installing the gem already puts the skill
229
+ on your machine, and the skill's CLI reference can never drift from the
230
+ executable it was released with.
231
+
232
+ The skill routes a small set of verbs. In Claude Code they run as `/okf:gem
233
+ <verb>`; used standalone, the skill infers the verb from your request.
234
+
235
+ | Verb | What it does |
236
+ | ---------------- | ----------------------------------------------------------------------------------------------------------- |
237
+ | _(none)_ | Orient on the bundle and recommend the highest-value next move |
238
+ | `search` | Answer a question from the bundle, token-lean: the map, the finder, only the winning bodies |
239
+ | `produce` | Create or extend a bundle from code, docs, or knowledge in people's heads |
240
+ | `migrate` | Adopt existing Markdown docs in place: frontmatter and reserved files added, bodies kept verbatim |
241
+ | `maintain` | Sync the bundle's content with reality after the code or docs change |
242
+ | `refine` | Restructure it for retrieval: evidence-first, cohesion over balance — proposes, never applies |
243
+ | `consume` | Use the bundle as context for a task, writing back what you learn |
244
+ | `curate` | Structural upkeep as it stands: `validate` + `lint` + `loose` |
245
+ | `doctor` | Install and verify the CLI, then doctor the bundle |
246
+ | `<okf-cli-verb>` | Run any CLI verb (`validate`, `lint`, `search`, `index`, `server`, the read views) and interpret its output |
247
+
248
+ Three of those look alike and are not, which is the distinction worth learning
249
+ first: **`curate`** keeps the bundle *sound* (the structure as it stands),
250
+ **`maintain`** keeps it *true* (the code changed, so the content must catch up),
251
+ and **`refine`** changes *where knowledge lives* — the folder a concept sits in,
252
+ a fact re-explained in three overviews. Reach for `refine` when nothing is wrong
253
+ and everything is hard to find. It reads the evidence, then hands you a proposal
254
+ — it never rearranges your bundle on its own.
255
+
256
+ Point it at your agent's config directory and the tree settles in its own
257
+ `skills/okf/` folder, so a shared skills directory never gets the files loose:
258
+
259
+ ```bash
260
+ okf skill .claude # Claude Code -> .claude/skills/okf
261
+ okf skill .agents # agent-agnostic -> .agents/skills/okf
262
+ ```
263
+
264
+ The resolved directory must be empty unless you pass `--force`, so a customized
265
+ skill is never clobbered.
96
266
 
97
267
  ## The library
98
268
 
@@ -158,9 +328,12 @@ without ever failing it.
158
328
  Publish a gem named `okf-*` carrying an `okf/plugin.rb` and installing it is the
159
329
  whole installation: your verb answers to `okf` and behaves like a built-in.
160
330
  Nothing an addon registers can displace one, and a broken addon is skipped rather
161
- than taking the CLI down. [`okf-mcp`](https://rubygems.org/gems/okf-mcp) is the
162
- first one install it and `okf mcp` serves your bundles over the Model Context
163
- Protocol, with nothing in this gem naming it.
331
+ than taking the CLI down. Three ship alongside this one, with nothing in this
332
+ gem naming any of them: [`okf-mcp`](https://rubygems.org/gems/okf-mcp) serves
333
+ your bundles over the Model Context Protocol,
334
+ [`okf-tui`](https://rubygems.org/gems/okf-tui) browses them full-screen in a
335
+ terminal, and [`okf-pro`](https://rubygems.org/gems/okf-pro) writes an
336
+ agent's knowledge repository and enforces it at three doors.
164
337
 
165
338
  The graph page treats a bundle as untrusted content: inlined data is escaped and
166
339
  every concept body is sanitized before it reaches the DOM, so a script hidden in
@@ -170,11 +343,22 @@ do not know.
170
343
 
171
344
  ## More
172
345
 
173
- The [project README](https://github.com/serradura/okf-gem#readme) carries the
174
- diagrams, the comparison with `CLAUDE.md`, agent auto-memory and wikis, and the
175
- Claude Code plugin. The [docs](https://okfgem.com/docs/) are the manual. And the
176
- repo documents *itself* in OKF — clone it and run `okf server .okf` to read this
177
- gem's own knowledge as a graph.
346
+ The [project README](https://github.com/serradura/okf#readme) carries the
347
+ diagrams, the comparison with `CLAUDE.md`, agent auto-memory and wikis, the
348
+ Claude Code plugin, and the way to install the skill into any agent without this
349
+ gem (`npx skills add serradura/okf`). The [docs](https://okfgem.com/docs/) are the manual.
350
+
351
+ And the gem documents *itself* in OKF. `.okf/` ships inside it — a map of what
352
+ every file under `lib/` does, and the walk a new verb owes — so from an
353
+ installed copy:
354
+
355
+ ```bash
356
+ okf server "$(gem contents okf --show-install-dir)/.okf"
357
+ ```
358
+
359
+ reads this gem's own knowledge as a graph, in this gem. That is the shortest
360
+ honest demonstration there is: the format is good enough that the tool's
361
+ maintainers use it on the tool.
178
362
 
179
363
  ## License
180
364
 
data/lib/okf/cli.rb CHANGED
@@ -92,7 +92,7 @@ module OKF
92
92
  [ :judge, nil ],
93
93
  [ :read, nil ],
94
94
  [ :graph, nil ],
95
- [ :extension, " installed extensions:" ]
95
+ [ :extension, " installed extensions (`okf <verb> --help` for each):" ]
96
96
  ].freeze
97
97
 
98
98
  # Everything the map's grammar column cannot say for itself. A test finds the
@@ -476,13 +476,31 @@ module OKF
476
476
  end
477
477
 
478
478
  def print_group(io, group, heading)
479
- rows = self.class.commands.reject(&:hidden?).select { |command| command.group == group }.flat_map(&:help_rows)
479
+ commands = self.class.commands.reject(&:hidden?).select { |command| command.group == group }
480
+ rows = group == :extension ? extension_rows(commands) : commands.flat_map(&:help_rows)
480
481
  return if rows.empty?
481
482
 
482
483
  io.puts heading if heading
483
484
  rows.each { |left, desc| io.puts " #{left.to_s.ljust(56)}#{desc}" }
484
485
  io.puts
485
486
  end
487
+
488
+ # One line per extension, whatever it declares — its FIRST row, which is why
489
+ # an addon's first row should be its summary.
490
+ #
491
+ # Enforced here rather than asked of each addon, because a rule an addon has
492
+ # to remember is a rule this map cannot rely on. An umbrella verb is a shape
493
+ # an addon legitimately has — the built-in `registry` has it too — and one
494
+ # arriving with eight subcommands made this block the longest section on the
495
+ # page, dwarfing the built-ins above it and burying the single-line addons
496
+ # beside it.
497
+ #
498
+ # A built-in keeps every row: those are this gem's own surface, they are what
499
+ # the map exists to teach, and there is no second place to read them. An
500
+ # extension has one — `okf <verb> --help`, which the heading points at.
501
+ def extension_rows(commands)
502
+ commands.map { |command| command.help_rows.first }.compact
503
+ end
486
504
  end
487
505
  end
488
506
 
@@ -96,11 +96,18 @@ The one trap worth carrying in your head: **the age cutoff is off by default**
96
96
  a plain `okf lint` reports concepts past their own declared `stale_after` (the
97
97
  `expired` check reads the clock the CLI supplies), but never judges *age*; pass
98
98
  `--stale-after <90d|12w|ISO-date>` when you want anything not touched since then
99
- flagged too. <!-- check:stale -->
100
-
101
- Read [cli.md](reference/cli.md) before *interpreting* a verb's output in depth:
102
- what `validate` may and may not reject, lint's categories and check ids, the JSON
103
- shapes, the tag-curation views, the server's trust boundary.
99
+ flagged too the two mechanisms share a spelling and nothing else
100
+ ([cli/checks.md](reference/cli/checks.md), rule `okf-two-clocks`).
101
+ <!-- check:stale -->
102
+
103
+ Read [cli.md](reference/cli.md) before *interpreting* a verb's output in depth.
104
+ It is the index and the shared contract — refs, exit codes, `--json`, the
105
+ filters — and its table routes to the one file the verb lives in:
106
+ [checks](reference/cli/checks.md) (what `validate` may and may not reject, lint's
107
+ categories and check ids), [search](reference/cli/search.md),
108
+ [map](reference/cli/map.md) (`index`/`dirs`), [views](reference/cli/views.md)
109
+ (the tag-curation views), [serve](reference/cli/serve.md) (the trust boundary),
110
+ [registry](reference/cli/registry.md), [graph](reference/cli/graph.md).
104
111
 
105
112
  ## Orient before you touch anything
106
113
 
@@ -123,8 +130,9 @@ playbooks (the Commands table below; no `okf` installed? read the root
123
130
  `consume` (use it as context) carry the judgment the executable can't — this is
124
131
  where the skill earns its keep. Each has a playbook (the Commands table below);
125
132
  read the modelling craft in [authoring.md](reference/authoring.md) before
126
- producing or maintaining, and the verbatim spec [SPEC.md](reference/SPEC.md)
127
- when you need chapter and verse.
133
+ producing or maintaining. When you need chapter and verse, go through
134
+ [spec-map.md](reference/spec-map.md) which § settles which question — into the
135
+ verbatim [SPEC.md](reference/SPEC.md).
128
136
 
129
137
  **No subcommand?** Infer intent: "document this / capture X" → `produce`;
130
138
  "convert / migrate / OKFy these existing docs into a bundle" → `migrate`; "the
@@ -25,7 +25,9 @@ lives in [authoring.md](../reference/authoring.md).
25
25
  silently deleting the context that explains them.
26
26
  4. **Update every enumeration that names what you changed — including `index.md`
27
27
  bodies**, not just the concept files: a new, renamed, or removed concept changes
28
- its directory's index listing too. Append a dated `log.md` entry. Step 1's map
28
+ its directory's index listing too. Append a dated `log.md` entry what changed and why it
29
+ matters, never the rounds it took (rule `okf-log-durable-only` in
30
+ [authoring.md](../reference/authoring.md)). Step 1's map
29
31
  is how you verify this — re-run `okf index` and confirm each listing matches
30
32
  reality.
31
33
  5. Run `validate`, then `lint` to catch the curation drift the change introduced —
@@ -4,7 +4,8 @@ The craft that makes these steps land well — granularity, choosing `type`, tag
4
4
  vocabulary, topology, links, sources — lives in
5
5
  [authoring.md](../reference/authoring.md). Read it before a non-trivial produce.
6
6
 
7
- 1. Read [SPEC.md](../reference/SPEC.md) if you are unsure of any rule.
7
+ 1. Unsure of a rule? [spec-map.md](../reference/spec-map.md) names the § that
8
+ settles it; read that section of [SPEC.md](../reference/SPEC.md).
8
9
  2. Pick the source(s): **code** (derive concepts from source, READMEs, docstrings,
9
10
  config), **docs/wiki** (distill pages into concepts; record the originals in
10
11
  `sources:` and key claims with `[^id]` footnotes), **manual** (decisions,
@@ -27,5 +28,6 @@ vocabulary, topology, links, sources — lives in
27
28
  `okf_version: "0.2"`. Append a dated entry to `log.md`.
28
29
  6. **Close out** — walk the
29
30
  [Closeout gate](../reference/authoring.md#closeout--the-finishing-gate)
30
- (`validate` + `lint` are part of it, see [cli.md](../reference/cli.md))
31
+ (`validate` + `lint` are part of it, see
32
+ [cli/checks.md](../reference/cli/checks.md))
31
33
  before finishing.
@@ -1,33 +1,13 @@
1
1
  # Authoring OKF well — the craft
2
2
 
3
- The spec ([SPEC.md](SPEC.md)) tells you what is *legal*. This file is what is
4
- *good* the modelling judgment that turns a pile of conformant files into
3
+ The spec ([SPEC.md](SPEC.md)) tells you what is *legal*, and
4
+ [spec-map.md](spec-map.md) says which § settles which question. This file is
5
+ what is *good* — the modelling judgment that turns a pile of conformant files into
5
6
  knowledge worth consuming. Read it before `produce` or `maintain`, and keep the
6
7
  §11 conformance rules in mind (parseable frontmatter, a non-empty `type`, and
7
8
  well-formed reserved files — the hard rules in [SKILL.md](../SKILL.md));
8
9
  everything else is guidance a consumer must tolerate.
9
10
 
10
- ## What each SPEC section governs
11
-
12
- Consult the right section on demand instead of re-reading all of [SPEC.md](SPEC.md):
13
-
14
- | § | Governs | Reach for it when |
15
- |---|---------|-------------------|
16
- | §3 | bundle structure, reserved filenames | laying out directories |
17
- | §4 | concept documents & frontmatter | writing or validating a concept |
18
- | §5.1 | provenance — `sources` and its credibility signals | any external or empirical claim |
19
- | §5.2 / §5.3 | trust — `generated`, `verified`, and the derived tiers | recording who wrote or confirmed content |
20
- | §5.4 / §5.5 | lifecycle — `status`, `stale_after` | marking drafts, deprecations, expiries |
21
- | §6 / §6.1 | cross-links; **broken links are tolerated** | linking; judging a "broken" link |
22
- | §6.2 / §6.3 | path-valued fields; the `references/` convention | pointing at non-concept assets |
23
- | §7 | the actor convention | filling any `by` |
24
- | §8 | index files & progressive disclosure | orienting; writing or synthesizing an index |
25
- | §9 | log files | recording history |
26
- | §10 | attested computations | a concept that *is* a sanctioned computation |
27
- | §11 | conformance — the hard gate | what `validate` may and may not reject |
28
- | §12 | versioning (`okf_version`) | the root index's one allowed field |
29
- | §13 | changes from v0.1 | migrating a bundle; reading an unmigrated one |
30
-
31
11
  ## Modelling principles
32
12
 
33
13
  These are the decisions that make or break a bundle. None are enforced by the
@@ -177,7 +157,8 @@ declare it only when it says something. `stale_after` is an absolute
177
157
  the day itself. Use it for knowledge with a known shelf life (a quota, a
178
158
  migration window); `lint`'s `expired` check reports the ones whose date has
179
159
  passed. It is a declared expiry, distinct from the `--stale-after` *flag*,
180
- which is a reader-supplied age cutoff — see [cli.md](cli.md).
160
+ which is a reader-supplied age cutoff — see [cli/checks.md](cli/checks.md),
161
+ rule `okf-two-clocks`.
181
162
 
182
163
  ### Capture the non-obvious — not what code already says <!-- rule:okf-non-obvious -->
183
164
  A bundle that restates function signatures or config keys goes stale the moment
@@ -217,6 +198,21 @@ concepts. Templates: [concept](../templates/concept.md),
217
198
  nested [index](../templates/index.md), bundle-root
218
199
  [root-index](../templates/root-index.md), [log](../templates/log.md).
219
200
 
201
+ ### The log records what shipped, not how it shipped <!-- rule:okf-log-durable-only -->
202
+ `log.md` carries durable knowledge and shipped behavior — never the process that
203
+ produced them. A bug fixed in a release is an entry; the review rounds that found
204
+ it are not. A capability that shipped is an entry; the iterations it took to
205
+ stabilize it before it shipped are not. When a change taught a lesson, the lesson
206
+ belongs in the concept it is about, stated as a principle, and the log entry
207
+ *points* at that concept instead of re-narrating the rounds — a reader finds it
208
+ where the subject lives, not by reading history.
209
+
210
+ The bar is what a reader six months out needs: *what changed and why it matters*,
211
+ never *how many passes it took to get there*. Watch for the shape this invites —
212
+ the newest entries sit at the top where every reader lands, so a stretch of work
213
+ stabilized by iteration accretes "round N found M defects" exactly where a durable
214
+ summary belongs.
215
+
220
216
  ## Migrating from v0.1 (§13)
221
217
 
222
218
  A v0.1 bundle is consumable forever — §13.1 sanctions reading `timestamp` as
@@ -237,7 +233,7 @@ verb (search, produce, migrate, maintain, consume, curate, doctor), routed by th
237
233
  Commands table in [SKILL.md](../SKILL.md). The Closeout below is their shared
238
234
  finishing gate.
239
235
 
240
- ## Closeout — the finishing gate
236
+ ## Closeout — the finishing gate <!-- rule:okf-closeout-gate -->
241
237
 
242
238
  `produce` step 6 and `maintain` steps 4–7 both land here: before calling an
243
239
  authoring task done, walk this once. It is the repo's "turn every task into a check
@@ -247,7 +243,8 @@ grep can't:
247
243
  - **Index enumerations** — every `index.md` that lists what you added, renamed, or
248
244
  removed is updated; re-run `okf index` and eyeball each listing against reality.
249
245
  Easy to skip, expensive to miss — this is the check that was missing.
250
- - **`log.md`** — a dated entry, newest first.
246
+ - **`log.md`** — a dated entry, newest first, and durable only (rule
247
+ `okf-log-durable-only` above).
251
248
  - **`generated.at`** bumped on the concepts you touched (and `generated.by` says
252
249
  who touched them — you, in §7's spelling).
253
250
  - **`validate`** — zero §11 errors.