okf 1.11.0 → 1.12.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7fae00b0ef2e7029cf0869ec73339321f6a54d58bd566aa6139cf6d5143999b9
4
- data.tar.gz: e337a96fadc268338e4f23984bb85b7b6ad54cee9b13f79a8301ae0179d4eec0
3
+ metadata.gz: 9a83101e62e3b3bc97d7351580d50ecc017be391a5edbde2e8f52388de529111
4
+ data.tar.gz: 040be5295642da56cbeed342424872469d1c2b15efcb1b477f10076d7c90fafc
5
5
  SHA512:
6
- metadata.gz: ec66b51cd4a4cba56998248a85bf0470ad62db32487f7c5d0c66780057bcdb839b0f6b521fe6649fb438a0010da2c9d3a82b2781fbeb59c90de5d96971b5ab39
7
- data.tar.gz: 181388d7be611d1af0fb811a50a326f7d72b159d4ea09b632007d5a355042faf6bca7c27f6d5d67d396fad03f868450ec2ed0234655b3a7861074b43b3356135
6
+ metadata.gz: 47fb84d8dffc04eee30eb65adfaca756ae43315e2d152e62692084a5bd04c6a7992529d243b2375412a8339a55db891c3ae18d679e171d5709e9426b93686047
7
+ data.tar.gz: 30390e1eecc1570b28531c374b21200309630aad87f821a7c3f25b631ca5285e03b91baab9048f69127931a09368da1ab38a089fc9bc25473c05da3c0fadfe2b
data/CHANGELOG.md CHANGED
@@ -5,6 +5,97 @@ 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
+ ## [1.12.0] - 2026-07-24
9
+
10
+ ### Added
11
+
12
+ - **`okf graph --traffic`** — the link graph read one grain coarser. `--hubs`
13
+ measures concepts, but the refine playbook's directory judgements ("does this
14
+ directory prune? a concern, or a container?") had nothing measured at their
15
+ grain. `--traffic` collapses each concept into its directory and the links
16
+ between two directories into one weighted arc, so every row carries its
17
+ internal / out / in traffic and a **cohesion** — its internal share of that
18
+ total. That is cohesion versus coupling on a knowledge tree: near-zero cohesion
19
+ under heavy inbound is a shared vocabulary doing its job, heavy outbound with
20
+ nothing back is a projection wearing a directory. The rows sort by cohesion
21
+ ascending, so the directories with a case to answer come first, and a directory
22
+ with no traffic at all prints `—` rather than a `0%` it did not earn. The arc
23
+ **cut** is fitted to the bundle, not fixed — at weight 3, ten bundles ranged
24
+ from 2 arcs to 136 — and `--cut N` overrides it; cohesion is computed over
25
+ *every* arc regardless, so narrowing the drawn picture never moves the
26
+ evidence. JSON: `{ bundle, cut, fitted, dirs, arcs, total_arcs }`. The pure
27
+ model underneath is `Bundle::Skeleton` — concepts folded to directories, links
28
+ to weighted arcs, every edge tagged with the cut it survives (a local-degree
29
+ sparsifier, union rule, so no linked concept is ever stranded) — and it does no
30
+ I/O and draws nothing: it names a cut rather than taking one, so both the graph
31
+ page and the CLI narrow the same emission their own way.
32
+ - **The graph page draws links in three amounts, and opens dense on its spine.**
33
+ 227 links over 47 concepts at degree 9.7 is unreadable because of its arrows,
34
+ not its dots — so links become a layer: **every** link, the **spine** (each
35
+ concept's strongest edge — the Skeleton's `keep_at===0` set, chosen so it
36
+ touches every linked concept), or **none**, with a selected concept's own links
37
+ always shown in full. A dense bundle now opens on its spine rather than greeting
38
+ the reader with the thicket; the trigger is undirected degree above a floor set
39
+ between a tree's ~2 and the 9.7 that drove it, and `--map` overrides to none
40
+ with the directories boxed. Above 800 edges the first layout runs on the spine
41
+ alone and the rest arrive a frame later with no re-layout — **6.25 s → 3.29 s**
42
+ on 414 concepts, nothing on screen moving. Proven in both render modes on a
43
+ committed 110-concept / 880-link fixture, the only one over both floors.
44
+ - **Registry groups — a named, recursive set of bundles.** A group is a registry
45
+ slug that names a list of members (bundle *or* group slugs, so they nest) and
46
+ resolves recursively, path-deduped, to its bundle leaves. `okf registry group
47
+ <slug> <@member…>` creates one or adds to it, `ungroup` removes members (and
48
+ emptying a group deletes it), and `del`/`rename` now span a group slug too —
49
+ one name cascades across every member list, one `del` cascade-drops the slug and
50
+ deletes any group it empties. Groups live in their own list in the registry
51
+ JSON, so the first-is-default rule and the `File.directory?` guards never meet a
52
+ pathless entry. `okf search @backend` merges the members into one ranking and
53
+ `okf server @backend` mounts each (the first at `/`), both skipping a vanished
54
+ member with a note, exactly as `@all` does. Every single-bundle verb **refuses**
55
+ a `@group` (exit 2) — the same rule that refuses a second bundle, through the
56
+ same `resolve_registered` seam.
57
+ - **`okf registry init`** — create a project-local `.okf-registry.json` in the
58
+ current directory. Once it exists, okf discovers it by walking up from the
59
+ working directory, and every registry operation — and every `@slug` — resolves
60
+ through it in place of the global `$OKF_HOME` registry, so a bare `okf server`
61
+ inside a repo serves that repo's bundles with no global setup. The nearest one
62
+ on the path wins (nested registries resolve nearest-first), `okf registry list`
63
+ names the local file it found, and `OKF_NO_DISCOVERY=1` forces the global one —
64
+ the escape hatch for a fixed-cwd caller (CI, a tool).
65
+ - **A local registry stores portable, relative paths.** A bundle inside the
66
+ registry's own tree is written relative to the `.okf-registry.json`, so the file
67
+ can be committed and travels with the repo — a checkout on another machine, or a
68
+ container that mounts it, resolves the same bundles unchanged. A bundle outside
69
+ the tree keeps an absolute path (it cannot travel). Paths still read back
70
+ absolute everywhere the CLI reports them; the relative form lives only on disk,
71
+ and an existing absolute local entry migrates to relative on its next write. The
72
+ global `$OKF_HOME` registry is unchanged — it stores absolute paths as before.
73
+
74
+ ### Changed
75
+
76
+ - **The derived `area` field is renamed `top_dir`** — the first-path-segment
77
+ rollup the catalog, search, `stats`, and `graph --hubs` carry. `area` was never
78
+ the OKF spec's word (the spec speaks only of `dir`), so the rollup now names
79
+ itself in the spec's vocabulary: it is the `dir` at the top level. The `--json`
80
+ keys move with it — catalog/search rows carry `top_dir` in place of `area`,
81
+ `stats` emits `top_dirs`/`by_top_dir`, and `graph --hubs` emits
82
+ `top_dir`/`by_top_dir`. The **deprecated `--area`/`--by area` input flags are
83
+ unchanged** — they still warn and map to `--dir`/`--by dir`, and now source the
84
+ renamed field internally. No behavior changes; the well-homed-hub numbers are
85
+ identical.
86
+
87
+ ### Fixed
88
+
89
+ - **A local-registry server preserves its relative-path anchor across a
90
+ re-open.** The hub re-read its boot registry with `OKF::Registry.new(path)`,
91
+ which drops the `relative_base` a discovered `.okf-registry.json` carries — so
92
+ on a project-local server the Bundles panel matched each mounted bundle's
93
+ absolute root against the re-read entry's *relative* path, missed, and drew
94
+ every in-tree bundle as "folder is gone", while a browser add flattened the new
95
+ bundle to an absolute path, undoing the portability relative storage exists for.
96
+ `Registry#reopen` re-reads the same file anchored the same way, and both hub
97
+ re-open sites use it.
98
+
8
99
  ## [1.11.0] - 2026-07-22
9
100
 
10
101
  ### Added
@@ -205,7 +296,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
205
296
  rendered `(root)` for humans — and "cluster" stays prose for what a dir
206
297
  groups. Both deprecated spellings keep their **old behavior exactly** and warn
207
298
  once per run on stderr (`--json` on stdout is unaffected); they go in a later
208
- release, along with `by_area` and the `area` row field.
299
+ release.
209
300
 
210
301
  ## [1.10.0] - 2026-07-21
211
302
 
data/README.md CHANGED
@@ -227,72 +227,26 @@ okf server .
227
227
  Images are published for `linux/amd64` and `linux/arm64` on
228
228
  [ghcr.io](https://github.com/serradura/okf-gem/pkgs/container/okf).
229
229
 
230
- ## Command line
231
-
232
- These verbs are written to be read by an **agent first and a person second**
233
- that is what the skill drives, with no wrapper in between. Every read verb takes
234
- `--json`, the list views project down to the fields you ask for
235
- (`--fields`/`--except`), so nothing pays for output it will not read, and the
236
- exit codes are stable enough to branch on in CI. The same commands render as
237
- scannable plain text when a human is the one looking.
238
-
239
- ```bash
240
- okf validate <dir|@slug> # is this legal OKF?
241
- okf lint <dir|@slug> [--fail-on warn] # is it navigable, complete, fresh?
242
- okf loose <dir|@slug> # concepts with no links in or out
243
- okf search <dir|@slug…|@all> <term…> # ranked retrieval; @all spans every bundle
244
- okf index <dir|@slug> [--dir D] [--depth N] # the §6 map: index bodies, rollups, listings
245
- okf dirs <dir|@slug> [--dir D] [--depth N] # the shape: every directory and what it holds
246
- okf catalog | files | tags | types | stats <dir|@slug> # the browser views, on the CLI
247
- okf graph <dir|@slug> [--hubs] # the raw graph; --hubs ranks by inbound links
248
- okf server [DIR|@slug…] [-p PORT] [--bind ADDR] # the live graph: one bundle, or all of them
249
- okf render <dir|@slug> [-o FILE] # the same page as one static, self-contained file
250
- okf registry list | set | del | default | rename # name your bundles (see below)
251
- okf skill <dest> # install the companion agent skill
252
- okf --version
253
- ```
254
-
255
- Exit codes: `0` success, `1` non-conformant bundle (or a `lint --fail-on`
256
- threshold crossed), `2` usage error. Every flag is in `okf <verb> --help` and in
257
- [the docs](https://okfgem.com/docs/).
258
-
259
- ```bash
260
- $ okf validate docs
261
- OKF v0.1 conformance — docs
262
- concepts: 37 index.md: 10 log.md: 1
263
- ! warn features/link-suggestions.md: cross-link target not found: `/graph-view.md` (tolerated under §5.3)
264
-
265
- ✓ conformant (33 warning(s))
266
-
267
- $ okf server docs
268
- serving 37 concepts at http://127.0.0.1:8808 (Ctrl-C to stop)
269
-
270
- $ okf render docs > public/index.html # the same page, static — host it anywhere
271
- ```
272
-
273
- ### Reading a big bundle a level at a time
274
-
275
- A few hundred concepts is a map nobody reads whole, so `index` and `dirs` descend
276
- instead of dumping. `--dir` takes a directory **and everything under it**,
277
- `--depth N` bounds how far below that it goes, and the two compose the way you
278
- actually walk a tree:
279
-
280
- ```bash
281
- okf dirs @handbook # the shape: every dir, what it holds directly and below
282
- okf index @handbook --depth 1 --no-body # the top of the map, no prose
283
- okf index @handbook --dir platform/api # now open one branch — with the chain that places it
284
- ```
285
-
286
- Naming a `--dir` brings its ancestors along, marked `↑`, so a branch is never
287
- shown adrift of the context that says what it is — the root `index.md`'s prose
288
- first among it.
289
-
290
- For an agent the saving is the whole point. On a 400-concept bundle the full
291
- `okf index --json` is 313 KB; the skeleton it orients on is 2.8 KB:
292
-
293
- ```bash
294
- okf index @handbook --json --depth 1 --except body,listing
295
- ```
230
+ ## Where to go next
231
+
232
+ Installed. The rest of this page is each surface the gem gives you over a bundle,
233
+ in the order most people meet them:
234
+
235
+ - **[The graph](#the-graph)** the whole bundle on one page, live or baked into a
236
+ single HTML file you can host anywhere. Start here if you want to *see* it.
237
+ - **[Agent skill](#agent-skill)** the verbs your agent runs to author, maintain
238
+ and answer from a bundle, so you stay the editor rather than the typist.
239
+ - **[Claude Code plugin](#claude-code-plugin)** — that skill, a slash command and a
240
+ post-edit curation hook, in two lines.
241
+ - **[Command line](#command-line)** every view as scannable text or as JSON, with
242
+ exit codes stable enough to gate CI on.
243
+ - **[Library](#library)** `OKF::Bundle` in your own Ruby, and the graph as a Rack
244
+ app you can mount in an app you already have.
245
+ - **[Extending okf](#extending-okf-and-running-it-safely)** ship a verb as a gem,
246
+ and what the page does and does not trust in a bundle you did not write.
247
+
248
+ Full reference for every verb and flag lives in
249
+ **[the docs](https://okfgem.com/docs/)**.
296
250
 
297
251
  ## The graph
298
252
 
@@ -335,6 +289,24 @@ okf search @all rate limit # ranked retrieval across every register
335
289
  okf server # no args: the whole registry behind one hub
336
290
  ```
337
291
 
292
+ Related bundles can share a name: `okf registry group backend @handbook @runbooks`
293
+ makes `@backend` stand for the set (members can be groups too, so they nest), and
294
+ `okf search @backend rate limit` or `okf server @backend` then targets all of them
295
+ at once — a durable subset for the two verbs that take several bundles.
296
+
297
+ The registry lives under `$OKF_HOME` (default `~/.okf`) — one per user. For one
298
+ scoped to a single project instead, `okf registry init` drops a
299
+ `.okf-registry.json` in the current directory; okf then discovers it by walking up
300
+ from wherever you run, and every registry op — and every `@slug` — resolves through
301
+ it in place of the global one. So a bare `okf server` inside that repo serves *its*
302
+ bundles with no `$OKF_HOME` setup. The nearest registry wins, and
303
+ `OKF_NO_DISCOVERY=1` forces the global one.
304
+
305
+ Commit that file and it travels with the repo: a bundle under the project root is
306
+ stored relative to the registry, so a checkout on another machine — or a container
307
+ that mounts the repo — resolves the same bundles unchanged. (Bundles outside the
308
+ tree keep absolute paths, which do not travel.)
309
+
338
310
  Behind the hub each bundle mounts at `/b/<slug>/`, `/b/` lists them all, and the
339
311
  `⌘/Ctrl-K` palette both switches bundles and **searches every one at once** — type
340
312
  a few words and the matching concepts appear with their bundle and a snippet, from
@@ -350,7 +322,8 @@ public one.
350
322
 
351
323
  The gem carries the [companion OKF agent skill](.okf/capabilities/agent-skill.md):
352
324
  a `SKILL.md` plus reference and template files that teach a coding agent to
353
- author, maintain, and consume OKF bundles and to drive the commands above.
325
+ author, maintain, and consume OKF bundles and to drive the
326
+ [commands below](#command-line).
354
327
  Because the skill ships inside the gem, installing the gem already puts the skill
355
328
  on your machine, and the skill's CLI reference can never drift from the
356
329
  executable it was released with.
@@ -390,6 +363,99 @@ okf skill .agents # agent-agnostic -> .agents/skills/okf
390
363
  The resolved directory must be empty unless you pass `--force`, so a customized
391
364
  skill is never clobbered.
392
365
 
366
+ ## Claude Code plugin
367
+
368
+ This repository doubles as a Claude Code plugin marketplace, so the whole
369
+ toolchain installs with two commands inside Claude Code:
370
+
371
+ ```
372
+ /plugin marketplace add serradura/okf-gem
373
+ /plugin install okf@okfgem
374
+ ```
375
+
376
+ The plugin carries three pieces: the [`okf` skill](#agent-skill) above;
377
+ **`/okf:gem`**, a front door that hands its arguments to the skill unchanged (no
378
+ arguments: it orients on your bundle and recommends the next move, never
379
+ auto-runs); and a **curation hook** that runs `okf validate` + `okf lint` after
380
+ every edit inside a bundle and returns the findings as context. The checks are
381
+ the CLI's own, so the feedback is deterministic.
382
+
383
+ The hook stays silent outside bundles, and it is config-free to switch off:
384
+ `OKF_CURATE_DISABLED=1` turns it off, `OKF_CURATE_QUIET=1` keeps the findings
385
+ without the install suggestion, and an `<!-- okf-disable -->` comment skips one
386
+ file.
387
+
388
+ Prefer no plugin? `gem install okf && okf skill .claude` installs the skill
389
+ alone, and the skill itself instructs the agent to run the same checks after
390
+ editing a bundle.
391
+
392
+ ## Command line
393
+
394
+ These verbs are written to be read by an **agent first and a person second** —
395
+ that is what the skill drives, with no wrapper in between. Every read verb takes
396
+ `--json`, the list views project down to the fields you ask for
397
+ (`--fields`/`--except`), so nothing pays for output it will not read, and the
398
+ exit codes are stable enough to branch on in CI. The same commands render as
399
+ scannable plain text when a human is the one looking.
400
+
401
+ ```bash
402
+ okf validate <dir|@slug> # is this legal OKF?
403
+ okf lint <dir|@slug> [--fail-on warn] # is it navigable, complete, fresh?
404
+ okf loose <dir|@slug> # concepts with no links in or out
405
+ okf search <dir|@slug…|@all> <term…> # ranked retrieval; @all spans every bundle
406
+ okf index <dir|@slug> [--dir D] [--depth N] # the §6 map: index bodies, rollups, listings
407
+ okf dirs <dir|@slug> [--dir D] [--depth N] # the shape: every directory and what it holds
408
+ okf catalog | files | tags | types | stats <dir|@slug> # the browser views, on the CLI
409
+ okf graph <dir|@slug> [--hubs] [--traffic] # the raw graph; --hubs ranks concepts, --traffic dirs
410
+ okf server [DIR|@slug…] [-p PORT] [--bind ADDR] # the live graph: one bundle, or all of them
411
+ okf render <dir|@slug> [-o FILE] # the same page as one static, self-contained file
412
+ okf registry init | list | set | del | default | rename | group | ungroup # name & group your bundles; @slug works anywhere
413
+ okf skill <dest> # install the companion agent skill
414
+ okf --version
415
+ ```
416
+
417
+ Exit codes: `0` success, `1` non-conformant bundle (or a `lint --fail-on`
418
+ threshold crossed), `2` usage error. Every flag is in `okf <verb> --help` and in
419
+ [the docs](https://okfgem.com/docs/).
420
+
421
+ ```bash
422
+ $ okf validate docs
423
+ OKF v0.1 conformance — docs
424
+ concepts: 37 index.md: 10 log.md: 1
425
+ ! warn features/link-suggestions.md: cross-link target not found: `/graph-view.md` (tolerated under §5.3)
426
+
427
+ ✓ conformant (33 warning(s))
428
+
429
+ $ okf server docs
430
+ serving 37 concepts at http://127.0.0.1:8808 (Ctrl-C to stop)
431
+
432
+ $ okf render docs > public/index.html # the same page, static — host it anywhere
433
+ ```
434
+
435
+ ### Reading a big bundle a level at a time
436
+
437
+ A few hundred concepts is a map nobody reads whole, so `index` and `dirs` descend
438
+ instead of dumping. `--dir` takes a directory **and everything under it**,
439
+ `--depth N` bounds how far below that it goes, and the two compose the way you
440
+ actually walk a tree:
441
+
442
+ ```bash
443
+ okf dirs @handbook # the shape: every dir, what it holds directly and below
444
+ okf index @handbook --depth 1 --no-body # the top of the map, no prose
445
+ okf index @handbook --dir platform/api # now open one branch — with the chain that places it
446
+ ```
447
+
448
+ Naming a `--dir` brings its ancestors along, marked `↑`, so a branch is never
449
+ shown adrift of the context that says what it is — the root `index.md`'s prose
450
+ first among it.
451
+
452
+ For an agent the saving is the whole point. On a 400-concept bundle the full
453
+ `okf index --json` is 313 KB; the skeleton it orients on is 2.8 KB:
454
+
455
+ ```bash
456
+ okf index @handbook --json --depth 1 --except body,listing
457
+ ```
458
+
393
459
  ## Library
394
460
 
395
461
  `require "okf"` gives you the whole thing as Ruby objects — two layers: pure
@@ -412,17 +478,17 @@ OKF::Server::App.new(folder) # => a Rack app: the interactive graph, mountab
412
478
 
413
479
  That last line is the point of the Rack app: the graph mounts inside an app you
414
480
  already have, auth included. The [Rails guide](https://okfgem.com/docs/guides/rails/)
415
- walks it, and the [library API](.okf/capabilities/library-api.md) concept covers
481
+ walks it, and the [library API](https://okfgem.com/docs/library/) covers
416
482
  the pure layer, the writer, and the lower-level pieces.
417
483
 
418
484
  ### validate and lint are two different questions
419
485
 
420
- `validate` (the [conformance validator](.okf/capabilities/validator.md)) asks
486
+ `validate` (the [conformance validator](https://okfgem.com/docs/cli/validate/)) asks
421
487
  _"is this legal OKF?"_ and implements the spec's
422
488
  [§9](lib/okf/skill/reference/SPEC.md#9-conformance) exactly — which means it is
423
489
  *forbidden* to reject a bundle for a broken link or a missing optional field.
424
490
 
425
- `lint` (the [curation linter](.okf/capabilities/linter.md)) asks the
491
+ `lint` (the [curation linter](https://okfgem.com/docs/cli/lint/)) asks the
426
492
  complementary question, _"is this well-curated, navigable, trustworthy?"_, over
427
493
  exactly those tolerated things: reachability, backlog, completeness, freshness,
428
494
  provenance, hygiene. It is advisory and exits `0` even with findings unless you
@@ -447,32 +513,6 @@ treat an unfamiliar bundle the way you would treat any document from a source yo
447
513
  do not know. Full write-up:
448
514
  [server trust boundary](.okf/design/server-trust-boundary.md).
449
515
 
450
- ## Claude Code plugin
451
-
452
- This repository doubles as a Claude Code plugin marketplace, so the whole
453
- toolchain installs with two commands inside Claude Code:
454
-
455
- ```
456
- /plugin marketplace add serradura/okf-gem
457
- /plugin install okf@okfgem
458
- ```
459
-
460
- The plugin carries three pieces: the [`okf` skill](#agent-skill) above;
461
- **`/okf:gem`**, a front door that hands its arguments to the skill unchanged (no
462
- arguments: it orients on your bundle and recommends the next move, never
463
- auto-runs); and a **curation hook** that runs `okf validate` + `okf lint` after
464
- every edit inside a bundle and returns the findings as context. The checks are
465
- the CLI's own, so the feedback is deterministic.
466
-
467
- The hook stays silent outside bundles, and it is config-free to switch off:
468
- `OKF_CURATE_DISABLED=1` turns it off, `OKF_CURATE_QUIET=1` keeps the findings
469
- without the install suggestion, and an `<!-- okf-disable -->` comment skips one
470
- file.
471
-
472
- Prefer no plugin? `gem install okf && okf skill .claude` installs the skill
473
- alone, and the skill itself instructs the agent to run the same checks after
474
- editing a bundle.
475
-
476
516
  ## Development
477
517
 
478
518
  ```bash
@@ -42,6 +42,10 @@ module OKF
42
42
  @bundle.graph(minimal: minimal, body: body)
43
43
  end
44
44
 
45
+ def skeleton
46
+ @bundle.skeleton
47
+ end
48
+
45
49
  def catalog
46
50
  @bundle.catalog
47
51
  end
@@ -279,7 +279,7 @@ module OKF
279
279
  end
280
280
 
281
281
  # Ranked match rows, catalog-style identity plus where the terms hit:
282
- # [{ slug:, id:, title:, type:, dir:, area:, tags:, matched: [field, …], score:, snippet: }, …]
282
+ # [{ slug:, id:, title:, type:, dir:, top_dir:, tags:, matched: [field, …], score:, snippet: }, …]
283
283
  # ordered by score descending, then slug, then id. `slug` is present only
284
284
  # when searching across bundles. No terms means no matches.
285
285
  def results
@@ -367,7 +367,7 @@ module OKF
367
367
  title: (concept.title || concept.id).to_s,
368
368
  type: concept.type.to_s,
369
369
  dir: OKF.dir_of(concept.id),
370
- area: area_of(concept.id),
370
+ top_dir: top_dir_of(concept.id),
371
371
  tags: Array(concept.tags).map(&:to_s),
372
372
  matched: matched,
373
373
  score: score.round(4),
@@ -421,9 +421,9 @@ module OKF
421
421
  end
422
422
  end
423
423
 
424
- # A concept's top-level area, mirroring the catalog's definition. Deprecated
425
- # in favour of OKF.dir_of, which keeps the levels this one throws away.
426
- def area_of(id)
424
+ # A concept's top-level dir, mirroring the catalog's definition — the first
425
+ # path segment. OKF.dir_of keeps the levels this one rolls up.
426
+ def top_dir_of(id)
427
427
  id.include?("/") ? id.split("/").first : "(root)"
428
428
  end
429
429
  end