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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 047d30628ab36abdfe517a2722753dc8f4a6eb60cea9a41d6015508d64784af6
4
- data.tar.gz: 637ce26fbb8375b9c4d0daeab6ef6bdc8b8cee0258838e73151944b772445c47
3
+ metadata.gz: 0aee1cdfc7b13016c423050f98de4994431fa68ae4afe3869b5555b5408c3315
4
+ data.tar.gz: '058369e3e6131e6af6de0b3e599bdc7716e3296f0ff1caacd59f905088e5c4ce'
5
5
  SHA512:
6
- metadata.gz: e30d74c10e269546cd81a433ff91b8f4f662540a81de9010de03c5e245d59eae3f7fff2743519379bbc2de53319501b2e517a6d7c613bf24d7e21a9ab447dd5d
7
- data.tar.gz: 862d1454d20f6ff1f51e0f17829676becdf1503df42f2d6ca5bf9b0004437ba43e65f6db381bba14b0126f2f40bb3704a6f29c8e12bafc237d405612604610a8
6
+ metadata.gz: 7d91fe567c067d175cfa3274773e984720384315d2eb8fb790da247ee256706b48e079ba4a4d9e5b909a4e6f480f968574d4526e6d6b80b4e443b57785d9ac36
7
+ data.tar.gz: e075baf062df84f4567200cfe4d375280dcf5cd9b76aad7d9802d1ae28cc00245982c6604e7e5fea080b15c47cee0644c5f75b7e2468379fc386de985ad1fe45
@@ -0,0 +1,112 @@
1
+ ---
2
+ type: Capability
3
+ title: Companion agent skill (skill)
4
+ description: A SKILL.md plus references and templates, shipped inside the gem, that teaches an agent to author OKF.
5
+ resource: gems/okf/lib/okf/skill.rb
6
+ tags: [agent, install]
7
+ generated:
8
+ by: human:maintainer
9
+ at: 2026-08-17T12:00:00Z
10
+ sources:
11
+ - title: gems/okf/lib/okf/skill.rb
12
+ resource: https://github.com/serradura/okf/blob/main/gems/okf/lib/okf/skill.rb
13
+ - title: gems/okf/lib/okf/skill/SKILL.md
14
+ resource: https://github.com/serradura/okf/blob/main/gems/okf/lib/okf/skill/SKILL.md
15
+ - title: gems/okf/test/plugin/sync_test.rb
16
+ resource: https://github.com/serradura/okf/blob/main/gems/okf/test/plugin/sync_test.rb
17
+ ---
18
+
19
+ # Overview
20
+
21
+ The gem carries the **OKF agent skill** — a `SKILL.md` with reference and
22
+ template files that teach a coding agent to *produce*, *migrate*, *maintain*,
23
+ *refine*, *consume*, *search*, *curate*, and *doctor* OKF (`@okf-eco format/okf-format`) bundles
24
+ and to drive the [CLI](../cli.md). The authoring judgment the executable can't encode lives here;
25
+ the executable handles the mechanics. Each verb routes to its own playbook
26
+ (`playbooks/`); the search playbook is progressive disclosure end to end —
27
+ ingest the index map, decide where to look, cut across with
28
+ [`okf search`](search.md), read only the winning bodies — and pointed questions
29
+ route to it first from the menu and consume playbooks. The two authoring
30
+ on-ramps stay distinct: `produce` distills sources into new concepts, while
31
+ `migrate` adopts existing documentation in place — frontmatter and reserved
32
+ files added, bodies kept **verbatim**, with `okf validate --json` as the
33
+ worklist — so a document survives conversion recognizably itself. Three more verbs
34
+ bound that loop rather than drive it: `curate` is structural upkeep as the bundle
35
+ *stands* — `validate` + `lint` + `loose` — and hands off to `maintain` the moment
36
+ the finding is that the *content*, not the structure, has drifted; `refine` is the
37
+ inverse hand-off — the content is right but the *shape* underserves retrieval —
38
+ and restructures for cohesion over the evidence the [read views](read-views.md)
39
+ compute (tag locality via `tags --by`, the hub origin test via `graph --hubs`),
40
+ always proposing before it applies; `doctor` is the
41
+ one playbook that assumes nothing, installing and verifying the [CLI](../cli.md)
42
+ before it examines the bundle. A no-argument run is a verb of its own — the menu
43
+ reads the signals and names the highest-value move without running one.
44
+
45
+ **Every playbook names the same first move**, and that took a correction. The
46
+ skill had prescribed one in seven places — bare `okf index`, `index --no-body`,
47
+ `dirs --depth 1` — across SKILL.md, four playbooks and the CLI reference, and
48
+ that disagreement *is* the deliberation an agent pays for on every retrieval. A
49
+ lookup table added to SKILL.md to pre-decide it made things worse: it duplicated
50
+ guidance already in the reference, contradicted the file it sat in, and carried
51
+ payload figures measured on one private bundle. It was reverted for the
52
+ subtraction it should have been. Every site now says `okf dirs` first, then
53
+ `okf index --dir <branch>` to descend — chosen structurally, because `dirs` emits
54
+ one row per directory where `index` emits one per concept even under `--no-body`,
55
+ so the two scale with different things. The reason is in the skill; the numbers
56
+ are not.
57
+
58
+ # `okf skill <dest>` installs it
59
+
60
+ `OKF::Skill.install` copies the skill into a destination you name — Claude Code's
61
+ `.claude`, an agent-agnostic `.agents`, wherever your agent looks. The rules are
62
+ deliberate:
63
+
64
+ - the **destination is required** — no magic default — so a user always decides
65
+ where the skill lands;
66
+ - it lands in a **`skills/okf/` folder** by default (`.claude` →
67
+ `.claude/skills/okf`), because an agent discovers a skill as
68
+ `<skills-dir>/<name>/SKILL.md` — so the skill settles in its own folder, not
69
+ loose among the others. A `<dest>` already ending in `skills` only gains the
70
+ `okf/` leaf; one already named `okf` is used as-is (idempotent); `--here`
71
+ pastes straight into `<dest>`, wherever it is;
72
+ - the resolved directory must be **empty unless `--force`**, so a customized
73
+ skill is never clobbered.
74
+
75
+ # One canonical copy, versioned with the gem
76
+
77
+ The skill ships **only** from `okf/lib/okf/skill/**` — that tree is the single source,
78
+ and `install` copies from it. Because the skill rides inside the gem, installing
79
+ the gem already puts the skill on the machine, and the skill's CLI reference can
80
+ **never drift** from the executable it was released with. Local installs
81
+ elsewhere are gitignored so they never masquerade as the source.
82
+
83
+ # Two more channels: the Claude Code plugin, and `skills/`
84
+
85
+ The repository doubles as a plugin marketplace, and the plugin carries a
86
+ **generated** copy of the same skill (`plugin/skills/okf`) — `rake skill:sync`
87
+ regenerates it after any skill edit or version bump, and a test fails the build
88
+ on drift, so the canonical-copy rule survives the second channel. Around the
89
+ skill the plugin adds a front-door command (`/okf:gem`) that is deliberately a
90
+ **pass-through shim**: it hands its arguments to the skill unchanged, so
91
+ `SKILL.md` stays the single router for every channel — the Commands table, the
92
+ intent inference, and the not-a-bundle `migrate` suggestion live only there,
93
+ where the drift test guards them, instead of in a second copy the test never
94
+ sees. The plugin also carries a PostToolUse hook that runs `okf validate` +
95
+ `okf lint` after every edit inside a bundle and hands the relevant findings
96
+ back as context. Nothing under `plugin/` ships in the gem.
97
+
98
+ The third channel is the generic one. A skill installer walks a repository's
99
+ `skills/` directory, so `skills/okf` is the same generated copy under the name
100
+ every such tool already looks for — `npx skills add serradura/okf` installs
101
+ it into any of the agents that tool supports, no gem and no Claude Code
102
+ required. It costs one line in `GENERATED_SKILL_COPIES` rather than a second
103
+ task, which is the point: **one canonical tree with N destinations is one
104
+ obligation, and a second task to remember is a second task to forget.** The
105
+ guard scales with it — `rake skill:verify` compares file lists and SHA-256
106
+ checksums for every destination and `build` depends on it, so a copy cannot be
107
+ released stale, whatever channel it feeds.
108
+
109
+ The channels are not equivalent, and the difference is the version they carry.
110
+ The gem's copy is the release's, pinned to the executable beside it; the plugin's
111
+ follows the marketplace; `skills/` tracks this repository's default branch. A
112
+ consumer who needs the skill and the CLI to agree should take the gem's.
@@ -0,0 +1,144 @@
1
+ ---
2
+ type: Capability
3
+ title: Bundles list (the hub's /b/ page)
4
+ description: The hub's /b/ page — every bundle it knows about with its size, health and default marker — and the four POST routes that change the registry, which the graph page's Bundles panel drives.
5
+ resource: gems/okf/lib/okf/server/hub.rb
6
+ tags: [server, registry, hub, ui]
7
+ generated:
8
+ by: human:maintainer
9
+ at: 2026-07-21T16:00:00Z
10
+ sources:
11
+ - title: gems/okf/lib/okf/server/hub.rb
12
+ resource: https://github.com/serradura/okf/blob/main/gems/okf/lib/okf/server/hub.rb
13
+ - title: gems/okf/lib/okf/cli/server.rb
14
+ resource: https://github.com/serradura/okf/blob/main/gems/okf/lib/okf/cli/server.rb
15
+ - title: gems/okf/lib/okf/registry.rb
16
+ resource: https://github.com/serradura/okf/blob/main/gems/okf/lib/okf/registry.rb
17
+ ---
18
+
19
+ # Why it exists
20
+
21
+ The [registry](../registry.md) has always been a terminal thing: `okf registry
22
+ set`, `del`, `default`, `rename`. That is the right surface for the people who
23
+ write the bundles, and the wrong one for the people who read them. A meeting with
24
+ non-technical readers settled it — the registry needed a point-and-click
25
+ surface, and the [server](graph-server.md) was already most of the way there.
26
+
27
+ The `/b/` page used to be a bare list of links. It became the browser
28
+ counterpart of the TUI's bundles view — every fact needed to choose between
29
+ bundles, and forms to change the set.
30
+
31
+ Then the [graph page](graph-server.md) grew a **Bundles panel** behind the rail's
32
+ ⚙, and for a while both surfaces carried the same four verbs. Two
33
+ implementations of one contract is the thing that drifts, so the forms came out
34
+ of `/b/` and the routes stayed. The split that survives is a clean one: `/b/`
35
+ answers *which bundles are there*, from a page you can land on with no bundle
36
+ open at all, and the panel answers *change this one* where the reader already
37
+ is. Managing a set is something you do while reading it, not on a detour to a
38
+ page you had to know existed.
39
+
40
+ # What a row says
41
+
42
+ One row per bundle the server *knows about*, which is deliberately not the same
43
+ as one per bundle it *hosts*. A registry entry whose folder was deleted cannot be
44
+ served, and leaving it off the page answers "where did my bundle go?" with
45
+ silence; it shows, muted, with `folder is gone` and no link.
46
+
47
+ Each row carries the title, the `@slug` you would type at the CLI, the folder,
48
+ the concept count, and a **health verdict** — `ok`, `warn`, `error` — drawn as a
49
+ 3px rule on the row's left edge. That rule is the accent bar under the page's own
50
+ heading, stood on end and put to work; colour only reinforces it, and the word
51
+ beside it is the message, so nothing on the page depends on being able to see
52
+ red. The verdict keeps the [validate/lint separation](validator.md): a curation
53
+ finding is a warning and the bundle stays open, and only a non-conformant bundle
54
+ reads as broken.
55
+
56
+ A bundle that arrived through a registry [link](../registry.md) is listed,
57
+ mounted and served like any other, and is **read-only**: the file that owns it is
58
+ another registry, so the model refuses `rename`, `remove` and `default` against
59
+ it. The page carries that in both halves, because one without the other is a
60
+ worse page than neither. The row shows `via @onm` and **offers no actions menu** —
61
+ three buttons that can only fail are not a menu — and the request is refused
62
+ anyway if it arrives, since the guard lives in `Registry` rather than here. That
63
+ is the same "hiding a button is a UI, refusing the request is the boundary" split
64
+ the read-only gate above keeps, reached from the other direction: there the
65
+ server declines to write at all, here one row does.
66
+
67
+ Rows are matched to entries by **directory**, not by slug. A rename changes the
68
+ slug and nothing else, and a row that lost its identity over a rename is the bug
69
+ that avoids.
70
+
71
+ # Writes, and the four locks on them
72
+
73
+ Four routes change anything — `POST /registry/default | rename | remove | add` —
74
+ and each passes four gates before it runs. They are the only non-GET routes the
75
+ server has:
76
+
77
+ 1. **Is this server writable at all?** A loopback bind is, without a flag: the
78
+ audience this page was built for should not need a command line to use the
79
+ page they were pointed at, and `okf server --read-only` is how they decline it.
80
+ Any other address is refused outright, with no flag that opens it: `--bind
81
+ 0.0.0.0` is how a personal tool becomes a public one, and a write surface does
82
+ not follow it there at all. A read-only server offers no controls *and*
83
+ refuses the request that skipped them — hiding a button is a UI, refusing the
84
+ request is the boundary.
85
+ 2. **Is there a registry to write to?** An ephemeral hub (`okf server ./a ./b`)
86
+ is serving directories somebody typed and has no list to edit — `409`, and the
87
+ page says so rather than leaving the missing controls a mystery.
88
+ 3. **Is the verb one of the four?** The path is user input, and "call whatever
89
+ method the path names" is how a router becomes an `eval`. A frozen list, not
90
+ a lookup.
91
+ 4. **Did this come from this page?** Same-origin *and* a per-boot token. Neither
92
+ alone is enough: the token lives in a page, and a page is a thing another
93
+ site can get a reader to submit; Origin alone would trust every
94
+ tab the browser has open on this host. An unstated origin is refused rather
95
+ than assumed. Per-boot rather than per-session because the hub has no sessions
96
+ and wants none — a cookie jar is a subsystem to defend for a page four people
97
+ see.
98
+
99
+ A success writes the file, **rebuilds the hub's bundles and apps from disk**, and
100
+ answers with the outcome. The rebuild is the part that is easy to skip and
101
+ impossible to skip safely: a write that leaves the running server on the old set
102
+ is a lie the next click believes.
103
+
104
+ Every answer is JSON, and Accept decides nothing. It used to be one of two
105
+ renderings — a `303` back to `/b/` for the forms, so a reload never re-posted,
106
+ and JSON for the panel's `fetch()`. With the forms gone there is one caller, so
107
+ there is one shape; asking for HTML does not resurrect a page-shaped answer that
108
+ nothing would read. The refusal messages are the core's own — a reserved slug, a
109
+ collision, a slug nothing carries all raise `OKF::Error` with a sentence written
110
+ for a person, and repeating that judgement in the server is how the two come to
111
+ disagree.
112
+
113
+ # The page carries no script, and nothing to post
114
+
115
+ No JavaScript, no build step, and now no forms and no token either: a page with
116
+ nothing to post has no business holding the credential. What is left is what only
117
+ this page can be — the list, the place `/` redirects from when no bundle is
118
+ named, the way back from the [404](graph-server.md), and the **empty state**. That
119
+ last one is why it cannot simply be deleted along with its forms: a hub with zero
120
+ bundles serves no graph page, so there is no page for the panel to live on, and
121
+ something still has to say what happened and how to fix it.
122
+
123
+ **There is no Add anywhere**, on either surface. A browser cannot hand over a
124
+ filesystem path — the File System Access API yields an opaque handle, never a
125
+ path, and is Chromium-only besides — and registering is the agent's act. The
126
+ route is still there because `okf registry set` is not the only caller that could
127
+ want it, but nothing in the UI reaches it.
128
+
129
+ # Verification
130
+
131
+ `okf/test/integration/server/hub_writes_test.rb` is the critical layer: every verb's
132
+ happy path, every refusal, and each of the four gates, asserting both that the
133
+ registry file changed *and* that the live hub reflects it without a restart.
134
+ `okf/test/browser/specs/bundles-panel.spec.js` drives the same verbs in Chromium
135
+ through the panel, against a registry-backed server with its own `$OKF_HOME` —
136
+ serial, and each spec puts back what it changed, because they share one live
137
+ registry.
138
+ <!-- rule:okf-prove-the-write-lands -->
139
+
140
+ A browser pass over these writes earned its place immediately, back when they
141
+ were forms: it caught an HTML `pattern` attribute whose character class was valid
142
+ as a Ruby regexp and invalid under the `v` flag a browser compiles it with, so
143
+ every keystroke in the rename field threw a `SyntaxError` no integration
144
+ assertion could see.