okf 1.10.0 → 1.11.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: 48baf9115b68a210ae385dbddc1a9956dfd16f5424614629db9fd43ff7b40fc4
4
- data.tar.gz: 32824378ebd2856cf1272cc9bfd75b2d989ef87788f38759e794acf6c4594b99
3
+ metadata.gz: 7fae00b0ef2e7029cf0869ec73339321f6a54d58bd566aa6139cf6d5143999b9
4
+ data.tar.gz: e337a96fadc268338e4f23984bb85b7b6ad54cee9b13f79a8301ae0179d4eec0
5
5
  SHA512:
6
- metadata.gz: 8add8ee3eb50b29bb7169544a27135d8221a54bcd2f1a5b98c221ec90a3ac1e836fa2da76456da889f7b396958835ac4ee39cf308a4ea9be88c47e2d65b9636c
7
- data.tar.gz: 8a84bfd874afff0cdf002f6fcff57df2042e6f04598d4ce41553bd3bde800e8645568008b5ad8491c89d1d0317477bd54c3cd7d6ab576dc2535fb722517fa43d
6
+ metadata.gz: ec66b51cd4a4cba56998248a85bf0470ad62db32487f7c5d0c66780057bcdb839b0f6b521fe6649fb438a0010da2c9d3a82b2781fbeb59c90de5d96971b5ab39
7
+ data.tar.gz: 181388d7be611d1af0fb811a50a326f7d72b159d4ea09b632007d5a355042faf6bca7c27f6d5d67d396fad03f868450ec2ed0234655b3a7861074b43b3356135
data/CHANGELOG.md CHANGED
@@ -5,6 +5,208 @@ 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.11.0] - 2026-07-22
9
+
10
+ ### Added
11
+
12
+ - **`okf dirs <dir|@slug> [--json]`** — the bundle's directories (its clusters)
13
+ with the number of concepts living **directly** in each, root first and the
14
+ total last. Every dir the tree has, including the empty intermediates that
15
+ exist only to connect it — a dir holding nothing but sub-directories reads
16
+ `0`, not a hidden rollup, so the column sums to the bundle's concept count.
17
+ JSON: `{ bundle, total, count, dirs: [{ dir, count, subdirs }] }`.
18
+ - **`--depth N` on `index` and `dirs`** — how many directory levels below the
19
+ starting point to keep, where the starting point is the `--dir` when one is
20
+ given and the bundle root otherwise. Relative rather than absolute, so
21
+ `--dir a/b --depth 1` reads "a/b and one level under it" without first working
22
+ out how deep `a/b` is, and the two flags walk a tree a level at a time.
23
+ `--depth 0` is the starting point alone; anything but a whole number is a
24
+ usage error (exit 2). This is what makes `index` usable at scale — every
25
+ directory in it is a section, so a few hundred concepts is a map nobody reads
26
+ whole. On one 414-concept bundle `index --no-body` went 12.5 KB → 1.3 KB at
27
+ `--depth 1`, and `index --json` 313 KB → 2.8 KB with `--depth 1 --except
28
+ body,listing`.
29
+ - **`--dir` on `index` and `dirs` brings the chain up to the root with it**, so a
30
+ branch is never shown adrift of the authored context that says what it is —
31
+ the root `index.md`'s prose first among it. Those rows print with a leading
32
+ `↑`, carry `ancestor: true`, and stay out of `total`; `--no-ancestors` drops
33
+ them. Ascent and descent are separate axes, so `--depth` never bounds the
34
+ chain: `--dir X --depth 0` is X alone, plus how you get to X. A `--dir` that
35
+ names nothing gains no chain, since a lone root row would read as a partial
36
+ answer to a query that in fact matched nothing.
37
+ - **`dirs` gains `--dir` (repeatable) and a `subtree` count** per row: the
38
+ concepts at or below that directory, defined as exactly what `--dir` on the
39
+ row returns, so the number and the flag can never disagree. Without it a
40
+ truncated listing is all zeroes at the top of a deep tree — which is where
41
+ "where is the mass?" is actually asked. The human table shows the column only
42
+ where some directory nests; `--json` always carries it.
43
+ - **`--dir PATH`** joins the shared filter set on `search`, `catalog`, `files`,
44
+ `types` and `tags`, and `index` gains it as a repeatable selector. One rule:
45
+ a concept matches when its dir *is* the path or sits below it — so `--dir
46
+ platform` reaches `platform/services/api`, `--dir platform/services` narrows,
47
+ and `--dir .` means the root alone with no special case. `root` is the
48
+ unquoted spelling of `.`; matching folds case.
49
+ - **`tags --by dir`** cuts the tag index by the whole directory path, where
50
+ `--by area` only ever saw the first segment.
51
+ - **`stats` gains `dirs` and `by_dir`** (the full-path cut, direct counts), and
52
+ its human breakdown now reads **By dir**. Both are read off the same map `okf
53
+ dirs` lists and `--dir` is answered against, so the two verbs cannot report a
54
+ different number of directories — a directory holding nothing directly appears
55
+ at `0` rather than being dropped, since it is still one `--dir` addresses.
56
+ - **Search rows carry `dir`** — the full path, `.` at the root — beside the
57
+ first-segment `area` they already had.
58
+ - **`dirs` takes `--fields`/`--except`** like the other list views, over the row
59
+ shape it already declared.
60
+ - **A single-bundle `okf server` answers `GET /search?q=`.** The route was the
61
+ hub's alone — conceived as the *cross-bundle* one — which left `okf server
62
+ ./docs` with a ⌘K palette that could find nothing, though one bundle is a legal
63
+ one-element set. `App` owns the payload and the hub calls it, so the shape is
64
+ defined once; a row from a single-bundle server carries no `slug`, which is how
65
+ it avoids answering as if it were a set. A static `okf render` still advertises
66
+ no endpoint: there is no server behind it to ask.
67
+ - **The route always answers; advertising it is the caller's call.** The page
68
+ resolves the endpoint *relative to the URL the reader is on*, so only whoever
69
+ mounted the app knows what to call it: `okf server` mounts at the root and
70
+ passes `search`, while `App.new(folder)` on its own advertises nothing, since
71
+ a default would point an app mounted at `/knowledge` back at its host's root.
72
+ - **The search index is built once and held.** Every request used to rebuild the
73
+ whole corpus — measured 1.45 s per search on a 414-concept bundle, flat across
74
+ repeats, with the build ~95% of it. `Search.prepare` holds a corpus (documents,
75
+ the key→concept map, the built index) and `Search.with` queries it: **0.016 –
76
+ 0.052 s** per search, with the 1.39 s build moved into boot, where `okf server`
77
+ warms it deliberately. An engine opts in by exposing `prepare`; the scan
78
+ declares none and is handed none, so no engine or addon had to change. The
79
+ trade is staleness — a corpus is a snapshot, like the graph — and the hub drops
80
+ its corpus on any registry write, since a held index outliving the set it was
81
+ built from is a wrong answer rather than a slow one.
82
+ - **`⌥ drag` moves a cluster box**, and is listed in the `?` sheet.
83
+
84
+ ### Changed
85
+
86
+ - **A bundle is named by its slug, everywhere it is chosen.** The ⌘K switcher,
87
+ the Bundles panel and the hub's `/b/` page all led with the derived
88
+ `parent/dir` label and left the slug in muted grey beside it — the address in
89
+ the name's place, when a bundle is addressed by `@okf-gem` and `/b/okf-gem/`.
90
+ Rows now carry `@slug` as the name and the folder as the fact under it, shown
91
+ only where it is not the name repeated. `/b/` drops the short label outright:
92
+ it only ever stood in for the full path, which is on the row already.
93
+ - **A `.okf` directory is labelled by the project that holds it.**
94
+ `Bundle::Folder.label` reads `repo/.okf` as `repo`. The `parent/dir` pair
95
+ exists because a bundle directory's own name is rarely unique — except when it
96
+ is `.okf`, the conventional container, and then a registry of eight projects
97
+ was eight rows all saying `.okf`. This also reaches `okf registry list` and the
98
+ default `okf server` title. (A `.okf` with no parent to borrow keeps its own
99
+ name; that case used to compose into `//.okf`.)
100
+ - **A force layout settles, then moves once.** `animate:true` reads to these
101
+ engines as "render every tick of the simulation" — the visible bounce, and
102
+ hundreds of full re-renders for one settle. It is `'end'` now: the same
103
+ simulation run headless, the nodes moved once into the same final positions.
104
+ Past 250 nodes even that transition is dropped, because at that size the move
105
+ itself is the jank.
106
+ - **A cluster box is scenery, not a handle.** Its empty interior is the largest
107
+ drag target on the canvas, so dragging to look around dragged the *directory*
108
+ instead of the view — worse the bigger the cluster. It takes `grabbable:false`
109
+ **and** `pannable:true`: ungrabbable alone stops the box moving, but the node
110
+ still swallows the drag, and it is `pannable` that hands the gesture to the
111
+ canvas the way empty background does. `Alt`+drag gives the handle back, since
112
+ moving a box is a real gesture, just not the constant one — `Alt` rather than
113
+ `Ctrl`, which on macOS is the system secondary click. A tap still opens the
114
+ directory's map.
115
+ - **`f` is no longer a shortcut.** A bare letter bound globally fires on every
116
+ keystroke the page did not route into an input, and fullscreen is not a mode to
117
+ enter by accident. The button stays and is now the only way in; the shortcut
118
+ sheet no longer advertises a key nothing is bound to.
119
+ - **The skill names one first move.** It had prescribed three different ones
120
+ across seven places — SKILL.md, four playbooks and the CLI reference — and that
121
+ disagreement is the deliberation an agent pays for on every retrieval. Every
122
+ site now says `okf dirs` first, then `okf index --dir <branch>` to descend,
123
+ chosen structurally: `dirs` emits one row per *directory* where `index` emits
124
+ one listing row per *concept* even under `--no-body`, so the two scale with
125
+ different things.
126
+
127
+ - **Cluster mode nests.** The graph page grouped concepts into one flat row of
128
+ boxes, one per *first path segment* — the same lossy projection `--area` was.
129
+ A cluster is a directory now, and the boxes nest as the directories do, to a
130
+ depth picked from a select beside the layout one. Depth **1** is the default
131
+ and draws exactly the old view; a flat bundle is offered no control at all.
132
+ At depth N every directory of depth ≤ N gets a box (intermediates that hold no
133
+ concepts of their own included, since they hold sub-boxes), and a concept
134
+ attaches to its own directory's box truncated to N. The root box still holds
135
+ direct-root concepts and never nests another. Box ids carry the directory
136
+ verbatim (`box::platform/services`, `box::.`), so a tap opens that directory's
137
+ map with no label to unmangle.
138
+ - **The page speaks `dir` too**: the filter group is **Dirs**, listing every
139
+ directory (not just first segments) and filtering by the same
140
+ directory-and-below rule `--dir` uses — in the graph, catalog and tags views —
141
+ and the Stats panel's breakdown is **By dir**, keyed by the whole path.
142
+
143
+ ### Fixed
144
+
145
+ - **The rail marks Index while the root map is open.** Index is a shortcut into
146
+ Files, so the two share one `data-view` — and the rail read only that, lighting
147
+ **Files** on the one screen a reader reached by clicking **Index**. The open
148
+ file is what distinguishes them, so it is what the rail reads; a *nested*
149
+ `index.md` is still Files.
150
+ - **`--dir` accepts the label the views print.** `fold_dir` never stripped a
151
+ trailing slash, while `okf index` labels a row `tables/` — so pasting a printed
152
+ row back into the flag matched nothing and exited 0, an empty result under a
153
+ count that agreed with it.
154
+ - **The `--dir` chain keeps its case.** It was walked over case-folded paths and
155
+ then matched against the map with `include?`, which does not fold, so every
156
+ ancestor of a directory spelled with a capital vanished from the chain that
157
+ exists to place the branch.
158
+ - **`--area` with `--depth` or `--dir` is refused (exit 2)** instead of unioning
159
+ the area with what the other flag selects. The deprecated flag is exact: with
160
+ `--depth` it names no starting point to be relative to, and with `--dir` one
161
+ side is exact where the other is a prefix, so the map came back with the area
162
+ *and* the subtree — an answer to neither question. A deprecated flag that
163
+ quietly widens is worse than one that is merely old.
164
+ - **A cleared filter no longer leaves a cluster unlaid.** The tiling runs over
165
+ the visible elements only (fcose throws on a node whose label went
166
+ `display:none` mid-run), but nothing re-tiled when a filter was later loosened —
167
+ so concepts hidden when clustering began came back at their pre-cluster
168
+ coordinates and stretched their box across the canvas. Worst case the filter
169
+ matched nothing, the layout returned early, and clearing it showed a view
170
+ nothing had laid out at all.
171
+ - **A palette hit in a single-bundle server no longer 404s or reloads the page.**
172
+ A row with no `slug` was read as naming a *foreign* bundle, so the href became
173
+ `../undefined/`, the row rendered an "undefined" chip, and the click took the
174
+ page-load branch — reloading the whole index to reach a node already on screen.
175
+ Three sites, one absent field.
176
+ - **A focused form field no longer zooms the page on iOS.** Safari zooms whenever
177
+ a focused control is under 16px and never zooms back out, so on a phone every
178
+ `/` left the reader pinching to recover. Keyed on `(max-width:768px)` *or*
179
+ `(pointer:coarse)`, because neither covers the other — a phone is narrow, a
180
+ tablet in landscape is not and zooms just the same.
181
+ - **A nested cluster no longer throws when a filter empties it mid-layout.**
182
+ fcose measures every node it is handed, so hiding nodes while its tiling
183
+ animation ran threw on a label it could no longer measure. The layout is
184
+ handed the visible elements only — which is also the right answer, since a
185
+ hidden concept has no business influencing where the visible ones land.
186
+ - **An intermediate directory box no longer takes its branch off the canvas.**
187
+ The empty-box rule read a compound's direct children, and a box holding only
188
+ sub-boxes has none, so it always counted as empty. It reads leaf descendants
189
+ now.
190
+
191
+ ### Removed
192
+
193
+ - **`OKF::Server::Hub::SEARCH_LIMIT` and `Hub::SEARCH_ENGINE`.** Both moved to
194
+ `OKF::Server::App`, which now defines the `/search` payload both hosts answer
195
+ with (`App.search_payload`). The hub's copies were left behind unreferenced —
196
+ two constants for one cap is two places to raise it and one of them silently
197
+ losing. Use `OKF::Server::App::SEARCH_LIMIT` / `App::SEARCH_ENGINE`.
198
+
199
+ ### Deprecated
200
+
201
+ - **`--area`, and `tags --by area`.** OKF's own vocabulary for grouping is
202
+ *directories* (`grep -ci area SPEC.md` → 0); "area" was this gem's invention,
203
+ and defining it as a concept id's first path segment threw away every level
204
+ below it. `dir` is now the only machine word — full path, `.` at the root,
205
+ rendered `(root)` for humans — and "cluster" stays prose for what a dir
206
+ groups. Both deprecated spellings keep their **old behavior exactly** and warn
207
+ 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.
209
+
8
210
  ## [1.10.0] - 2026-07-21
9
211
 
10
212
  ### Added
@@ -930,6 +1132,7 @@ Initial release.
930
1132
 
931
1133
  - Runs on Ruby >= 2.4 with two runtime dependencies: rack and webrick.
932
1134
 
1135
+ [1.11.0]: https://github.com/serradura/okf-gem/compare/v1.10.0...v1.11.0
933
1136
  [1.10.0]: https://github.com/serradura/okf-gem/compare/v1.9.0...v1.10.0
934
1137
  [1.9.0]: https://github.com/serradura/okf-gem/compare/v1.8.0...v1.9.0
935
1138
  [1.8.0]: https://github.com/serradura/okf-gem/compare/v1.7.0...v1.8.0