okf 1.0.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 +7 -0
- data/.okf/capabilities/agent-skill.md +46 -0
- data/.okf/capabilities/graph-server.md +60 -0
- data/.okf/capabilities/index.md +20 -0
- data/.okf/capabilities/library-api.md +67 -0
- data/.okf/capabilities/linter.md +49 -0
- data/.okf/capabilities/read-views.md +84 -0
- data/.okf/capabilities/validator.md +40 -0
- data/.okf/cli.md +52 -0
- data/.okf/design/core-shell-split.md +58 -0
- data/.okf/design/index.md +10 -0
- data/.okf/design/ruby-floor.md +45 -0
- data/.okf/design/runtime-dependencies.md +44 -0
- data/.okf/design/server-trust-boundary.md +35 -0
- data/.okf/format/citations.md +33 -0
- data/.okf/format/cross-links.md +52 -0
- data/.okf/format/frontmatter.md +38 -0
- data/.okf/format/index.md +9 -0
- data/.okf/format/okf-format.md +43 -0
- data/.okf/index.md +18 -0
- data/.okf/log.md +9 -0
- data/.okf/model/bundle.md +38 -0
- data/.okf/model/concept.md +44 -0
- data/.okf/model/graph.md +44 -0
- data/.okf/model/index.md +8 -0
- data/.okf/overview.md +66 -0
- data/CHANGELOG.md +54 -0
- data/CODE_OF_CONDUCT.md +10 -0
- data/LICENSE.txt +201 -0
- data/NOTICE +10 -0
- data/README.md +276 -0
- data/exe/okf +6 -0
- data/lib/okf/bundle/folder.rb +94 -0
- data/lib/okf/bundle/graph.rb +118 -0
- data/lib/okf/bundle/linter/report.rb +56 -0
- data/lib/okf/bundle/linter.rb +416 -0
- data/lib/okf/bundle/reader.rb +60 -0
- data/lib/okf/bundle/validator/result.rb +35 -0
- data/lib/okf/bundle/validator.rb +131 -0
- data/lib/okf/bundle/writer.rb +137 -0
- data/lib/okf/bundle.rb +216 -0
- data/lib/okf/cli.rb +910 -0
- data/lib/okf/concept/file.rb +63 -0
- data/lib/okf/concept.rb +101 -0
- data/lib/okf/markdown/citations.rb +49 -0
- data/lib/okf/markdown/frontmatter.rb +55 -0
- data/lib/okf/markdown/links.rb +98 -0
- data/lib/okf/path.rb +34 -0
- data/lib/okf/server/app.rb +120 -0
- data/lib/okf/server/graph.rb +112 -0
- data/lib/okf/server/runner.rb +78 -0
- data/lib/okf/server/templates/graph.html.erb +803 -0
- data/lib/okf/skill/SKILL.md +133 -0
- data/lib/okf/skill/reference/APACHE-2.0.txt +202 -0
- data/lib/okf/skill/reference/SPEC.md +460 -0
- data/lib/okf/skill/reference/authoring.md +218 -0
- data/lib/okf/skill/reference/cli.md +196 -0
- data/lib/okf/skill/templates/concept.md +24 -0
- data/lib/okf/skill/templates/index.md +8 -0
- data/lib/okf/skill/templates/log.md +6 -0
- data/lib/okf/skill/templates/root-index.md +12 -0
- data/lib/okf/skill.rb +82 -0
- data/lib/okf/version.rb +5 -0
- data/lib/okf.rb +55 -0
- metadata +142 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: f1f08bf7a4a1a49ef0bea5953b380df85bdbe79e1b30998e0d168d122d4a20c2
|
|
4
|
+
data.tar.gz: 97aff0312275def9b514c0e50bcc3872a73e3a304efa7ac8d85046a846711a7f
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: '09c1784bee88f5a74087345fa3ee26a13ba2875ff9447559852c23cf294f6f05ffa457eabdd67cbbac4bc5e255b3cc91f128863fab92d9b5a88f3ae41062e871'
|
|
7
|
+
data.tar.gz: d26885ca8683d2de6ad036d7a158089ba4b7e12437131b617687bad51bd15157f64dd148bcd7f8f502a95ae48d644f9c5674464e82ff3f97bc34faf61a24dc24
|
|
@@ -0,0 +1,46 @@
|
|
|
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: lib/okf/skill.rb
|
|
6
|
+
tags: [skill, agent, install]
|
|
7
|
+
timestamp: 2026-07-11T12:00:00Z
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Overview
|
|
11
|
+
|
|
12
|
+
The gem carries the **OKF agent skill** — a `SKILL.md` with reference and
|
|
13
|
+
template files that teach a coding agent to *produce*, *maintain*, and *consume*
|
|
14
|
+
[OKF](../format/okf-format.md) bundles and to drive the [CLI](../cli.md). The
|
|
15
|
+
authoring judgment the executable can't encode lives here; the executable handles
|
|
16
|
+
the mechanics.
|
|
17
|
+
|
|
18
|
+
# `okf skill <dest>` installs it
|
|
19
|
+
|
|
20
|
+
`OKF::Skill.install` copies the skill into a destination you name — Claude Code's
|
|
21
|
+
`.claude`, an agent-agnostic `.agents`, wherever your agent looks. The rules are
|
|
22
|
+
deliberate:
|
|
23
|
+
|
|
24
|
+
- the **destination is required** — no magic default — so a user always decides
|
|
25
|
+
where the skill lands;
|
|
26
|
+
- it lands in a **`skills/okf/` folder** by default (`.claude` →
|
|
27
|
+
`.claude/skills/okf`), because an agent discovers a skill as
|
|
28
|
+
`<skills-dir>/<name>/SKILL.md` — so the skill settles in its own folder, not
|
|
29
|
+
loose among the others. A `<dest>` already ending in `skills` only gains the
|
|
30
|
+
`okf/` leaf; one already named `okf` is used as-is (idempotent); `--here`
|
|
31
|
+
pastes straight into `<dest>`, wherever it is;
|
|
32
|
+
- the resolved directory must be **empty unless `--force`**, so a customized
|
|
33
|
+
skill is never clobbered.
|
|
34
|
+
|
|
35
|
+
# One canonical copy, versioned with the gem
|
|
36
|
+
|
|
37
|
+
The skill ships **only** from `lib/okf/skill/**` — that tree is the single source,
|
|
38
|
+
and `install` copies from it. Because the skill rides inside the gem, installing
|
|
39
|
+
the gem already puts the skill on the machine, and the skill's CLI reference can
|
|
40
|
+
**never drift** from the executable it was released with. Local installs
|
|
41
|
+
elsewhere are gitignored so they never masquerade as the source.
|
|
42
|
+
|
|
43
|
+
# Citations
|
|
44
|
+
|
|
45
|
+
[1] [lib/okf/skill.rb](https://github.com/serradura/okf-gem/blob/main/lib/okf/skill.rb) — the installer.
|
|
46
|
+
[2] [lib/okf/skill/SKILL.md](https://github.com/serradura/okf-gem/blob/main/lib/okf/skill/SKILL.md) — the skill itself.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: Capability
|
|
3
|
+
title: Interactive graph server (server)
|
|
4
|
+
description: A self-contained HTML knowledge graph served over HTTP, and a mountable Rack app.
|
|
5
|
+
resource: lib/okf/server/app.rb
|
|
6
|
+
tags: [server, graph, rack, diagram]
|
|
7
|
+
timestamp: 2026-07-11T12:00:00Z
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Overview
|
|
11
|
+
|
|
12
|
+
`okf server` boots an interactive view of the [graph](../model/graph.md):
|
|
13
|
+
`OKF::Server::App` is a Rack app that serves one self-contained HTML page which
|
|
14
|
+
draws the bundle with Cytoscape and renders concept bodies with marked. Because
|
|
15
|
+
it is a plain Rack app, it also mounts inside a host application (e.g. a Rails
|
|
16
|
+
route) — the built-in WEBrick runner is just the default, injected so tests drive
|
|
17
|
+
it without opening a socket.
|
|
18
|
+
|
|
19
|
+
# The page stays self-contained
|
|
20
|
+
|
|
21
|
+
One ERB template, inline CSS and JS, no build step and no bundler. The only
|
|
22
|
+
external assets are Cytoscape and marked from a CDN — plus Mermaid, lazy-loaded
|
|
23
|
+
only when a concept body actually contains a diagram; everything else is inlined.
|
|
24
|
+
The graph draws from a **minimal** node payload and pulls each concept's body
|
|
25
|
+
**on demand** via `fetch()`, which is why even a large bundle loads fast.
|
|
26
|
+
|
|
27
|
+
# Request flow
|
|
28
|
+
|
|
29
|
+
```mermaid
|
|
30
|
+
sequenceDiagram
|
|
31
|
+
participant B as Browser
|
|
32
|
+
participant A as okf server (Rack app)
|
|
33
|
+
B->>A: GET /
|
|
34
|
+
A-->>B: HTML page + inlined minimal graph data
|
|
35
|
+
Note over A,B: angle brackets escaped (json_for_script) — safe
|
|
36
|
+
B->>A: GET /node?id=… (on demand)
|
|
37
|
+
A-->>B: concept Markdown body
|
|
38
|
+
Note over A,B: marked renders it UNSANITIZED — trust boundary
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
# Endpoints
|
|
42
|
+
|
|
43
|
+
| Path | Serves |
|
|
44
|
+
|------|--------|
|
|
45
|
+
| `/` | the HTML page (graph + inlined minimal data) |
|
|
46
|
+
| `/node?id=` | one concept's rendered body |
|
|
47
|
+
| `/node/meta?id=` | one concept's metadata |
|
|
48
|
+
| `/catalog`, `/tags`, `/types` | the JSON behind the browser panels |
|
|
49
|
+
|
|
50
|
+
# Trust boundary
|
|
51
|
+
|
|
52
|
+
Fetched Markdown bodies are rendered **without sanitization**, so only serve
|
|
53
|
+
bundles you trust. Data inlined into the page is safe — it goes through
|
|
54
|
+
`json_for_script`, which escapes `<` so it cannot break out of its `<script>` —
|
|
55
|
+
but the on-demand body is not. See the
|
|
56
|
+
[server trust boundary](../design/server-trust-boundary.md) for the full picture.
|
|
57
|
+
|
|
58
|
+
# Citations
|
|
59
|
+
|
|
60
|
+
[1] [lib/okf/server/app.rb](https://github.com/serradura/okf-gem/blob/main/lib/okf/server/app.rb) — the Rack app and its routes.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Capabilities
|
|
2
|
+
|
|
3
|
+
The five things the gem does over a bundle, plus the read views that print it at
|
|
4
|
+
a glance. All of them run over the same [pure model](../model/) and are fronted
|
|
5
|
+
by the [CLI](../cli.md).
|
|
6
|
+
|
|
7
|
+
# Judge
|
|
8
|
+
|
|
9
|
+
* [Conformance validator](validator.md) - the §9 legal check; the only capability that can fail a bundle.
|
|
10
|
+
* [Curation linter](linter.md) - advisory quality report across six categories; never rejects.
|
|
11
|
+
|
|
12
|
+
# Serve & read
|
|
13
|
+
|
|
14
|
+
* [Interactive graph server](graph-server.md) - a self-contained HTML graph over HTTP, mountable as a Rack app.
|
|
15
|
+
* [Read views](read-views.md) - `index`, `catalog`, `files`, `types`, `tags`, `stats`, `loose`, `graph` — the browser views as text, plus the `index` map.
|
|
16
|
+
|
|
17
|
+
# Use & author
|
|
18
|
+
|
|
19
|
+
* [Library API](library-api.md) - the Ruby surface: pure model plus on-disk handles.
|
|
20
|
+
* [Companion agent skill](agent-skill.md) - the skill shipped inside the gem that teaches an agent to author OKF.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: Capability
|
|
3
|
+
title: Library API
|
|
4
|
+
description: The Ruby surface — a pure in-memory model plus on-disk handles, an "ActiveRecord for the filesystem".
|
|
5
|
+
resource: lib/okf.rb
|
|
6
|
+
tags: [library, api, ruby, diagram]
|
|
7
|
+
timestamp: 2026-07-11T12:00:00Z
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Overview
|
|
11
|
+
|
|
12
|
+
Everything the [CLI](../cli.md) does is available in-process. `require "okf"`
|
|
13
|
+
gives you two layers, split cleanly by the [core/shell rule](../design/core-shell-split.md):
|
|
14
|
+
|
|
15
|
+
- **pure, in-memory** — [`OKF::Concept`](../model/concept.md) and
|
|
16
|
+
[`OKF::Bundle`](../model/bundle.md), which you build, interrogate, validate,
|
|
17
|
+
lint, and graph with **no disk involved**;
|
|
18
|
+
- **on-disk handles** — `OKF::Concept::File` and `OKF::Bundle::Folder`, which add
|
|
19
|
+
`load` / `save` / `reload` / `delete` on top of the pure model.
|
|
20
|
+
|
|
21
|
+
```mermaid
|
|
22
|
+
classDiagram
|
|
23
|
+
class Concept {
|
|
24
|
+
<<pure>>
|
|
25
|
+
}
|
|
26
|
+
class Bundle {
|
|
27
|
+
<<pure>>
|
|
28
|
+
}
|
|
29
|
+
class Graph {
|
|
30
|
+
<<pure>>
|
|
31
|
+
}
|
|
32
|
+
class ConceptFile {
|
|
33
|
+
<<on-disk>>
|
|
34
|
+
}
|
|
35
|
+
class Folder {
|
|
36
|
+
<<on-disk>>
|
|
37
|
+
}
|
|
38
|
+
Bundle "1" o-- "*" Concept : contains
|
|
39
|
+
Bundle ..> Graph : builds
|
|
40
|
+
ConceptFile ..> Concept : wraps
|
|
41
|
+
Folder ..> Bundle : wraps
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`ConceptFile` is `OKF::Concept::File`; `Folder` is `OKF::Bundle::Folder` — each
|
|
45
|
+
on-disk handle wraps a pure counterpart and adds load/save/reload/delete.
|
|
46
|
+
|
|
47
|
+
# Build knowledge without touching Markdown
|
|
48
|
+
|
|
49
|
+
The pure layer is the surface an embedding app uses to reuse the gem over
|
|
50
|
+
knowledge it already holds as records. Construct concepts from data, assemble a
|
|
51
|
+
bundle, and call `#validate`, `#lint`, or `#graph` — no Markdown round-trip
|
|
52
|
+
needed. The lower-level pieces work standalone too:
|
|
53
|
+
`OKF::Bundle::Validator.call`, `OKF::Bundle::Linter.call`,
|
|
54
|
+
`OKF::Bundle::Graph.build`, `OKF::Markdown::Frontmatter.parse`.
|
|
55
|
+
|
|
56
|
+
# Folder is an ActiveRecord for the filesystem
|
|
57
|
+
|
|
58
|
+
`OKF::Bundle::Folder.load(dir)` reads a directory into a pure bundle;
|
|
59
|
+
`Folder.new(bundle:, root:).save` materializes one back — and **validates §9
|
|
60
|
+
before publishing** through an atomic writer, so it never leaves a broken bundle
|
|
61
|
+
on disk. `OKF::Server::App.new(folder)` turns a folder straight into the
|
|
62
|
+
[graph server](graph-server.md).
|
|
63
|
+
|
|
64
|
+
# Citations
|
|
65
|
+
|
|
66
|
+
[1] [README.md — Library](https://github.com/serradura/okf-gem/blob/main/README.md) — worked examples of both layers.
|
|
67
|
+
[2] [lib/okf.rb](https://github.com/serradura/okf-gem/blob/main/lib/okf.rb) — the require surface.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: Capability
|
|
3
|
+
title: Curation linter (lint)
|
|
4
|
+
description: An advisory curation-quality report across six categories and sixteen checks — it never rejects a bundle.
|
|
5
|
+
resource: lib/okf/bundle/linter.rb
|
|
6
|
+
tags: [linting, curation, cli]
|
|
7
|
+
timestamp: 2026-07-11T12:00:00Z
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Overview
|
|
11
|
+
|
|
12
|
+
`okf lint` answers the question [validate](validator.md) is forbidden to touch:
|
|
13
|
+
*is this bundle well-curated — navigable, complete, trustworthy?* It reports over
|
|
14
|
+
exactly the soft things §9 tolerates, has its own `OKF::Bundle::Linter` and
|
|
15
|
+
report, and never emits a conformance error. It is **advisory**: exit `0` even
|
|
16
|
+
with findings unless you opt in with `--fail-on warn`.
|
|
17
|
+
|
|
18
|
+
# Six categories, sixteen checks
|
|
19
|
+
|
|
20
|
+
| Category | Checks |
|
|
21
|
+
|----------|--------|
|
|
22
|
+
| Reachability | `orphan`, `not_in_index`, `disconnected_component`, `unlinked` |
|
|
23
|
+
| Backlog | `missing_concept`, `broken_index_entry` |
|
|
24
|
+
| Completeness | `stub`, `missing_title`, `missing_description`, `missing_timestamp` |
|
|
25
|
+
| Freshness | `stale` |
|
|
26
|
+
| Provenance | `uncited_external`, `broken_citation` |
|
|
27
|
+
| Hygiene | `duplicate_title`, `unused_reference_def`, `undefined_reference`, `self_link` |
|
|
28
|
+
|
|
29
|
+
Select with `--only` / `--except` (by check id), tune the stub threshold with
|
|
30
|
+
`--min-body`, and get the whole report as a machine substrate with `--json`.
|
|
31
|
+
|
|
32
|
+
# The freshness gotcha
|
|
33
|
+
|
|
34
|
+
Freshness is **off by default** — a plain `lint` never reports `stale`. Pass
|
|
35
|
+
`--stale-after <90d | 12w | 2026-01-01>` when concepts carry a
|
|
36
|
+
[`timestamp`](../format/frontmatter.md). The CLI resolves that to an absolute
|
|
37
|
+
cutoff so the pure linter never reads the clock.
|
|
38
|
+
|
|
39
|
+
# Where lint stops and an agent begins
|
|
40
|
+
|
|
41
|
+
`lint` is structural: it cannot judge **contradictions** or **semantic**
|
|
42
|
+
staleness (a concept that parses fine but no longer matches reality). Those need
|
|
43
|
+
meaning. `lint --json` is precisely the structured input an agent reasons over to
|
|
44
|
+
close that gap. The [`loose`](read-views.md) view is a folder-grouped lens over
|
|
45
|
+
the single `unlinked` check.
|
|
46
|
+
|
|
47
|
+
# Citations
|
|
48
|
+
|
|
49
|
+
[1] [lib/okf/bundle/linter.rb](https://github.com/serradura/okf-gem/blob/main/lib/okf/bundle/linter.rb) — the sixteen checks and their categories.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: Capability
|
|
3
|
+
title: Read views (index, catalog, files, types, tags, stats, loose, graph)
|
|
4
|
+
description: The server's browser panels reproduced on the CLI, plus the index map, so an agent reads a bundle at a glance without a browser.
|
|
5
|
+
tags: [read, cli, json]
|
|
6
|
+
timestamp: 2026-07-12T12:00:00Z
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Overview
|
|
10
|
+
|
|
11
|
+
The [graph server](graph-server.md) renders a bundle in a browser; these verbs
|
|
12
|
+
render the same knowledge as text, so an agent (or a terminal) reads it directly.
|
|
13
|
+
Each prints a scannable human view by default and machine JSON with `--json`, and
|
|
14
|
+
all are advisory reads that exit `0`. They share
|
|
15
|
+
[`OKF::Bundle#catalog`](../model/bundle.md) and the
|
|
16
|
+
[graph indexes](../model/graph.md) for their data.
|
|
17
|
+
|
|
18
|
+
# The views
|
|
19
|
+
|
|
20
|
+
| Verb | Shows | Grouped by |
|
|
21
|
+
|------|-------|------------|
|
|
22
|
+
| `index` | each directory's index body, type/tag rollup, child dirs, and concept listing | directory (root first) |
|
|
23
|
+
| `catalog` | concepts with type, tags, link counts, status | area |
|
|
24
|
+
| `files` | files with titles | folder |
|
|
25
|
+
| `types` | [types](../format/frontmatter.md) with their concepts | count |
|
|
26
|
+
| `tags` | [tags](../format/frontmatter.md) with their concepts | count |
|
|
27
|
+
| `stats` | rollups: concepts, areas, types, cross-links, tags | — |
|
|
28
|
+
| `loose` | degree-0 concepts (no [links](../format/cross-links.md) in or out) | folder |
|
|
29
|
+
| `graph` | the raw nodes and edges | — (`--minimal` / `--no-body`) |
|
|
30
|
+
|
|
31
|
+
# `index` is the orient-first map (§6)
|
|
32
|
+
|
|
33
|
+
Alone among the read views, `index` shows the reserved `index.md` layer: the
|
|
34
|
+
concept views skip those structural files, so only `index` renders the
|
|
35
|
+
[progressive-disclosure map](../format/okf-format.md) — one entry per directory
|
|
36
|
+
(root first) with its authored index body, a type/tag rollup over the concepts
|
|
37
|
+
living directly there, its child directories, and the concept listing. `--area`
|
|
38
|
+
narrows to one directory and repeats (`root` names the bundle root); `--no-body`
|
|
39
|
+
drops the prose to a skeleton. It is the cheapest orientation when picking up a
|
|
40
|
+
bundle, and the only view that exposes *enumeration drift* — a listing entry that
|
|
41
|
+
should exist but is missing, which no grep can find. A directory that holds
|
|
42
|
+
concepts but no `index.md` gets its listing synthesized and tagged `(no index.md)`,
|
|
43
|
+
a prompt to write a real map rather than a defect.
|
|
44
|
+
|
|
45
|
+
# JSON output — compact, and projectable
|
|
46
|
+
|
|
47
|
+
`--json` is **compact by default** — single-line, the token-efficient substrate an
|
|
48
|
+
agent consumes; `--pretty` (which implies `--json`) indents the same JSON for a
|
|
49
|
+
human. On the per-item list views — `index`, `catalog`, `files` — `--fields a,b`
|
|
50
|
+
keeps only those properties and `--except a,b` drops them (mutually exclusive; an
|
|
51
|
+
unknown name is a usage error that lists the valid ones). Projection runs before
|
|
52
|
+
emission, so an agent never pays tokens for a field it dropped: `okf index <dir>
|
|
53
|
+
--except body,listing` is the lean directory skeleton, the difference between a few
|
|
54
|
+
hundred bytes and hundreds of KB on a large bundle.
|
|
55
|
+
|
|
56
|
+
# Narrowing and regrouping
|
|
57
|
+
|
|
58
|
+
The four list views — `catalog`, `files`, `types`, `tags` — accept the filters
|
|
59
|
+
*orthogonal* to how they group, so you ask a narrow question instead of paging
|
|
60
|
+
the whole bundle (matching is case-insensitive):
|
|
61
|
+
|
|
62
|
+
| View | Filters it accepts |
|
|
63
|
+
|------|--------------------|
|
|
64
|
+
| `catalog`, `files` | `--type`, `--area`, `--tag` |
|
|
65
|
+
| `types` | `--area`, `--tag` (it already groups by type) |
|
|
66
|
+
| `tags` | `--type`, `--area` (it already groups by tag) |
|
|
67
|
+
|
|
68
|
+
`tags --by type|area` regroups the tag index under each concept dimension with
|
|
69
|
+
within-group counts — the view for curating a
|
|
70
|
+
[tag](../format/frontmatter.md) vocabulary: which tags cluster in which area,
|
|
71
|
+
which type leans on which tags.
|
|
72
|
+
|
|
73
|
+
# `loose` is a curation lens, not an error
|
|
74
|
+
|
|
75
|
+
`loose` is the folder-grouped view over [lint](linter.md)'s `unlinked` check —
|
|
76
|
+
distinct from `orphan`. An `index.md` listing makes a file *reachable* (not an
|
|
77
|
+
orphan) but is **not a graph edge**, so a listed file can still float here. A
|
|
78
|
+
loose file may be perfectly fine — a terminal leaf like a backlog item is loose
|
|
79
|
+
by design — so `loose` surfaces the set for a human or agent to judge and always
|
|
80
|
+
exits `0`.
|
|
81
|
+
|
|
82
|
+
# Citations
|
|
83
|
+
|
|
84
|
+
[1] [cli.md — read views](https://github.com/serradura/okf-gem/blob/main/lib/okf/skill/reference/cli.md) — the views and their flags.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: Capability
|
|
3
|
+
title: Conformance validator (validate)
|
|
4
|
+
description: Implements the spec's §9 conformance definition exactly — three hard conditions, everything else a warning.
|
|
5
|
+
resource: lib/okf/bundle/validator.rb
|
|
6
|
+
tags: [validation, conformance, cli]
|
|
7
|
+
timestamp: 2026-07-11T12:00:00Z
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Overview
|
|
11
|
+
|
|
12
|
+
`okf validate` answers one question: *is this a legal [OKF](../format/okf-format.md)
|
|
13
|
+
bundle?* `OKF::Bundle::Validator` implements §9 exactly and is the **only**
|
|
14
|
+
capability that can fail a bundle — exit `1` on any hard error, `0` otherwise.
|
|
15
|
+
|
|
16
|
+
# The three hard conditions (errors)
|
|
17
|
+
|
|
18
|
+
| Rule | Condition |
|
|
19
|
+
|------|-----------|
|
|
20
|
+
| §9.1 | every non-reserved file has a parseable [frontmatter](../format/frontmatter.md) block |
|
|
21
|
+
| §9.2 | every such block has a **non-empty `type`** |
|
|
22
|
+
| §9.3 | every `index.md` / `log.md` present is well-formed (nested index has no frontmatter, root index carries only `okf_version`, log dates are ISO) |
|
|
23
|
+
|
|
24
|
+
# Everything else is a warning
|
|
25
|
+
|
|
26
|
+
The validator is **forbidden by §9** from rejecting a bundle for soft issues, so
|
|
27
|
+
these are warnings that never change conformance:
|
|
28
|
+
|
|
29
|
+
- missing recommended fields, non-list `tags`, an unparseable `timestamp`;
|
|
30
|
+
- **broken [cross-links](../format/cross-links.md)** (§5.3) — consumers MUST
|
|
31
|
+
tolerate them.
|
|
32
|
+
|
|
33
|
+
Judging those is the [linter](linter.md)'s job, and keeping the two apart is a
|
|
34
|
+
[hard design contract](../design/core-shell-split.md). The
|
|
35
|
+
[writer](library-api.md) runs this validator *before* publishing, so a saved
|
|
36
|
+
bundle is never written non-conformant.
|
|
37
|
+
|
|
38
|
+
# Citations
|
|
39
|
+
|
|
40
|
+
[1] [lib/okf/bundle/validator.rb](https://github.com/serradura/okf-gem/blob/main/lib/okf/bundle/validator.rb) — the §9 implementation.
|
data/.okf/cli.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: Component
|
|
3
|
+
title: The okf command-line front end
|
|
4
|
+
description: The only layer that parses argv, prints, writes files, and decides exit codes.
|
|
5
|
+
resource: lib/okf/cli.rb
|
|
6
|
+
tags: [cli, shell]
|
|
7
|
+
timestamp: 2026-07-12T12:00:00Z
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Overview
|
|
11
|
+
|
|
12
|
+
`OKF::CLI` is the executable's front end and the single place where the gem
|
|
13
|
+
touches the outside world for a command: it parses `argv`, prints, writes files,
|
|
14
|
+
and chooses the exit code. Every library class beneath it just returns data — the
|
|
15
|
+
CLI is the [shell half](design/core-shell-split.md) of the architecture. Output
|
|
16
|
+
streams are injected (`out:`/`err:`) so the whole surface is driven in tests
|
|
17
|
+
without a real terminal or socket.
|
|
18
|
+
|
|
19
|
+
# Subcommands
|
|
20
|
+
|
|
21
|
+
Dispatch is a single `case` on the first argument. The verbs fall into three
|
|
22
|
+
groups:
|
|
23
|
+
|
|
24
|
+
| Group | Verbs | Notes |
|
|
25
|
+
|-------|-------|-------|
|
|
26
|
+
| Judge | `validate`, `lint`, `loose` | [validate](capabilities/validator.md) and [lint](capabilities/linter.md) answer different questions and stay separate. |
|
|
27
|
+
| Read | `index`, `catalog`, `files`, `types`, `tags`, `stats`, `graph` | the [browser views as text](capabilities/read-views.md), plus the `index` map. |
|
|
28
|
+
| Act | `server`, `skill` | boot the [graph server](capabilities/graph-server.md); install the [agent skill](capabilities/agent-skill.md). |
|
|
29
|
+
|
|
30
|
+
Plus `version` / `--version` / `-v` and `help` / `--help` / `-h`.
|
|
31
|
+
|
|
32
|
+
# Exit codes
|
|
33
|
+
|
|
34
|
+
The contract every verb keeps:
|
|
35
|
+
|
|
36
|
+
| Code | Meaning |
|
|
37
|
+
|------|---------|
|
|
38
|
+
| `0` | success — including a bundle with lint findings (`lint` is advisory) |
|
|
39
|
+
| `1` | a non-conformant bundle (`validate`) or a `lint --fail-on warn` threshold crossed |
|
|
40
|
+
| `2` | usage error — unknown command, missing directory, bad flag |
|
|
41
|
+
|
|
42
|
+
# Best-effort reads
|
|
43
|
+
|
|
44
|
+
`graph`, `server`, and the read views are best-effort under §9: a file with
|
|
45
|
+
invalid frontmatter is kept in `bundle.unparseable`, skipped, and *noted on
|
|
46
|
+
stderr* (so JSON on stdout stays clean) rather than aborting the whole command.
|
|
47
|
+
One bad file never breaks the rest. Run [validate](capabilities/validator.md) for
|
|
48
|
+
the details of what was skipped.
|
|
49
|
+
|
|
50
|
+
# Citations
|
|
51
|
+
|
|
52
|
+
[1] [lib/okf/cli.rb](https://github.com/serradura/okf-gem/blob/main/lib/okf/cli.rb) — the dispatch, option parsing, and printers.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: Constraint
|
|
3
|
+
title: The core/shell split
|
|
4
|
+
description: A pure functional core that never touches disk or stdio, and a thin shell that owns all I/O — enforced by a test.
|
|
5
|
+
resource: test/unit/boundary_test.rb
|
|
6
|
+
tags: [architecture, purity, testing, diagram]
|
|
7
|
+
timestamp: 2026-07-11T12:00:00Z
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Overview
|
|
11
|
+
|
|
12
|
+
The gem is two halves. The **core** is pure — [`Concept`](../model/concept.md),
|
|
13
|
+
[`Bundle`](../model/bundle.md), [`Graph`](../model/graph.md), the
|
|
14
|
+
[validator](../capabilities/validator.md), the [linter](../capabilities/linter.md),
|
|
15
|
+
the [format layer](../format/) — logic that returns data and does no I/O. The
|
|
16
|
+
**shell** owns everything that touches the world: the on-disk handles
|
|
17
|
+
(`Concept::File`, `Bundle::{Reader,Writer,Folder}`), the
|
|
18
|
+
[server](../capabilities/graph-server.md), and the [CLI](../cli.md).
|
|
19
|
+
|
|
20
|
+
```mermaid
|
|
21
|
+
flowchart TB
|
|
22
|
+
subgraph shell ["Shell — the only layer that does I/O"]
|
|
23
|
+
CLI["CLI"]
|
|
24
|
+
Server["Server::App"]
|
|
25
|
+
RW["Reader · Writer · Folder"]
|
|
26
|
+
CF["Concept::File"]
|
|
27
|
+
end
|
|
28
|
+
subgraph core ["Core — pure: no File / Dir / stdio"]
|
|
29
|
+
Concept["Concept"]
|
|
30
|
+
Bundle["Bundle"]
|
|
31
|
+
Graph["Graph"]
|
|
32
|
+
VL["Validator · Linter"]
|
|
33
|
+
MD["Markdown layer"]
|
|
34
|
+
end
|
|
35
|
+
shell ==>|depends on| core
|
|
36
|
+
core -. "never — boundary_test.rb fails the build" .-> shell
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
# It is enforced, not just intended
|
|
40
|
+
|
|
41
|
+
`test/unit/boundary_test.rb` fails the build if a core file names a shell class or
|
|
42
|
+
reaches for `File` / `Dir` / `FileUtils` / stdio. The dependency rule is executable,
|
|
43
|
+
so the boundary cannot rot silently: **put new I/O in the shell, put new logic in
|
|
44
|
+
the core, pure.**
|
|
45
|
+
|
|
46
|
+
# Why it pays off
|
|
47
|
+
|
|
48
|
+
- **Testable without disk** — every feature runs against an in-memory
|
|
49
|
+
[bundle](../model/bundle.md), so the suite is fast and the 2.4 Docker check is
|
|
50
|
+
cheap.
|
|
51
|
+
- **Embeddable** — the [library API](../capabilities/library-api.md) exposes the
|
|
52
|
+
pure core to host apps that never want the gem's filesystem opinions.
|
|
53
|
+
- **Best-effort reads** — the reader collects unparseable files instead of
|
|
54
|
+
raising, so the pure graph still renders while the shell reports the skips.
|
|
55
|
+
|
|
56
|
+
# Citations
|
|
57
|
+
|
|
58
|
+
[1] [test/unit/boundary_test.rb](https://github.com/serradura/okf-gem/blob/main/test/unit/boundary_test.rb) — the boundary made executable.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Design constraints
|
|
2
|
+
|
|
3
|
+
The enforced boundaries that keep the gem light, portable, and honest. These are
|
|
4
|
+
not style preferences — most are checked by a test or CI, and they explain *why*
|
|
5
|
+
the code looks the way it does.
|
|
6
|
+
|
|
7
|
+
* [Core/shell split](core-shell-split.md) - pure logic must never touch disk, stdio, or the shell layer; a test enforces it.
|
|
8
|
+
* [Ruby 2.4 floor](ruby-floor.md) - runs on the Ruby an OS already ships; newer APIs are banned.
|
|
9
|
+
* [Runtime dependencies](runtime-dependencies.md) - exactly `rack` and `webrick`, no ActiveSupport.
|
|
10
|
+
* [Server trust boundary](server-trust-boundary.md) - the served page renders concept bodies unsanitized.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: Constraint
|
|
3
|
+
title: The Ruby 2.4 floor
|
|
4
|
+
description: The gem runs on every Ruby since 2.4 so it works on the interpreter an OS already ships.
|
|
5
|
+
tags: [ruby, compatibility, portability]
|
|
6
|
+
timestamp: 2026-07-11T12:00:00Z
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Overview
|
|
10
|
+
|
|
11
|
+
`required_ruby_version >= 2.4.0`. The point is to run on the Ruby an operating
|
|
12
|
+
system already ships, without asking anyone to install a newer one — the same
|
|
13
|
+
floor as [rack](runtime-dependencies.md), the gem's core dependency. This is why
|
|
14
|
+
the gem stays deliberately light.
|
|
15
|
+
|
|
16
|
+
# The floor bans APIs RuboCop won't catch
|
|
17
|
+
|
|
18
|
+
RuboCop parses at 2.4 and catches syntax, but **not** newer standard-library
|
|
19
|
+
methods, so those are a manual discipline. A non-exhaustive list of what is off
|
|
20
|
+
limits:
|
|
21
|
+
|
|
22
|
+
- **2.5** — `delete_prefix`/`delete_suffix`, `transform_keys`, `Dir.children`,
|
|
23
|
+
`yield_self`;
|
|
24
|
+
- **2.6** — `to_h { }`, `then`, endless string slices `str[i..]`, `YAML.safe_load`
|
|
25
|
+
keyword args (allowed **only** inside the
|
|
26
|
+
[Frontmatter shim](../format/frontmatter.md));
|
|
27
|
+
- **2.7** — `filter_map`, `tally`, numbered block params;
|
|
28
|
+
- **3.x** — endless methods, hash shorthand.
|
|
29
|
+
|
|
30
|
+
These constraints apply to `test/` too, because the suite runs on 2.4 as well.
|
|
31
|
+
|
|
32
|
+
# The truth test
|
|
33
|
+
|
|
34
|
+
"Works on my Ruby" is not verification here. The floor is checked in CI across
|
|
35
|
+
every supported Ruby, and locally with:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
docker run --rm -v "$PWD":/app -w /app ruby:2.4 \
|
|
39
|
+
bash -c "bundle install && bundle exec rake test"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
# Citations
|
|
43
|
+
|
|
44
|
+
[1] [okf.gemspec](https://github.com/serradura/okf-gem/blob/main/okf.gemspec) — `required_ruby_version = ">= 2.4.0"`.
|
|
45
|
+
[2] [AGENTS.md — Hard constraints](https://github.com/serradura/okf-gem/blob/main/AGENTS.md) — the banned-API list and the Docker truth test.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: Constraint
|
|
3
|
+
title: Exactly two runtime dependencies
|
|
4
|
+
description: The gem depends on rack and webrick only — no ActiveSupport, no build step, no JavaScript toolchain.
|
|
5
|
+
resource: okf.gemspec
|
|
6
|
+
tags: [dependencies, rack, portability]
|
|
7
|
+
timestamp: 2026-07-11T12:00:00Z
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Overview
|
|
11
|
+
|
|
12
|
+
The runtime dependencies are exactly two, and adding a third is a design decision
|
|
13
|
+
to be challenged, not a convenience:
|
|
14
|
+
|
|
15
|
+
| Gem | Why |
|
|
16
|
+
|-----|-----|
|
|
17
|
+
| `rack` (`>= 2.2`) | the [server](../capabilities/graph-server.md) is a mountable Rack app |
|
|
18
|
+
| `webrick` (`>= 1.4`) | the default runner — unbundled from Ruby in 3.0, so it must be declared |
|
|
19
|
+
|
|
20
|
+
# No ActiveSupport, on purpose
|
|
21
|
+
|
|
22
|
+
The gem refuses the usual reach for ActiveSupport. Two small pieces exist
|
|
23
|
+
precisely so it is not needed:
|
|
24
|
+
|
|
25
|
+
- `OKF.blank?` — the emptiness check;
|
|
26
|
+
- `OKF::Markdown::Frontmatter.stringify_keys` — the key coercion, living in
|
|
27
|
+
[the one YAML gateway](../format/frontmatter.md).
|
|
28
|
+
|
|
29
|
+
# What the leanness buys
|
|
30
|
+
|
|
31
|
+
Together with the [Ruby 2.4 floor](ruby-floor.md), a two-dependency footprint is
|
|
32
|
+
what lets the gem run on the interpreter an OS already ships — no build step, no
|
|
33
|
+
bundler for the [served page](server-trust-boundary.md), no JavaScript toolchain.
|
|
34
|
+
Leanness is a feature of this gem, not an accident.
|
|
35
|
+
|
|
36
|
+
# A packaging note
|
|
37
|
+
|
|
38
|
+
`spec.files` comes from `git ls-files` minus `test/`, `bin/`, `.github/`, etc., so
|
|
39
|
+
a new top-level file ships in the gem unless the gemspec rejects it — check
|
|
40
|
+
`gem build` output when adding one.
|
|
41
|
+
|
|
42
|
+
# Citations
|
|
43
|
+
|
|
44
|
+
[1] [okf.gemspec](https://github.com/serradura/okf-gem/blob/main/okf.gemspec) — the two `add_dependency` lines and `spec.files`.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: Constraint
|
|
3
|
+
title: The server trust boundary
|
|
4
|
+
description: The served page renders concept bodies without sanitization, so only serve bundles you trust.
|
|
5
|
+
resource: lib/okf/server/templates/graph.html.erb
|
|
6
|
+
tags: [security, server, xss]
|
|
7
|
+
timestamp: 2026-07-11T12:00:00Z
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Overview
|
|
11
|
+
|
|
12
|
+
The [graph server](../capabilities/graph-server.md) is designed for trusted
|
|
13
|
+
bundles. Its page renders each concept's Markdown body **without sanitization**
|
|
14
|
+
(marked, no sanitizer), so a hostile bundle could carry active content. The rule
|
|
15
|
+
is simple: **only serve bundles you trust.**
|
|
16
|
+
|
|
17
|
+
# Where the boundary sits
|
|
18
|
+
|
|
19
|
+
There are two different data paths into the page, and only one is escaped:
|
|
20
|
+
|
|
21
|
+
| Path | Handling | Safe? |
|
|
22
|
+
|------|----------|-------|
|
|
23
|
+
| Graph data **inlined** into the page | through `json_for_script`, which escapes `<` | yes — it cannot break out of its `<script>` |
|
|
24
|
+
| Concept bodies **fetched** on demand (`/node?id=`) | rendered as Markdown, unsanitized | no — this is the trust boundary |
|
|
25
|
+
|
|
26
|
+
So the XSS boundary is not the inlined data (that is handled); it is the
|
|
27
|
+
on-demand [body](../format/cross-links.md) render. The
|
|
28
|
+
[self-contained page](../capabilities/graph-server.md) keeps external assets down
|
|
29
|
+
to Cytoscape and marked from a CDN, but that does not make the rendered body
|
|
30
|
+
safe.
|
|
31
|
+
|
|
32
|
+
# Citations
|
|
33
|
+
|
|
34
|
+
[1] [README.md — Server trust boundary](https://github.com/serradura/okf-gem/blob/main/README.md) — the unsanitized-render warning.
|
|
35
|
+
[2] [lib/okf/server/templates/graph.html.erb](https://github.com/serradura/okf-gem/blob/main/lib/okf/server/templates/graph.html.erb) — `json_for_script` and the body render.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: Format
|
|
3
|
+
title: Citations (spec §8)
|
|
4
|
+
description: The provenance convention that ties empirical claims in a concept back to their sources.
|
|
5
|
+
resource: lib/okf/markdown/citations.rb
|
|
6
|
+
tags: [format, provenance, citations]
|
|
7
|
+
timestamp: 2026-07-11T12:00:00Z
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Overview
|
|
11
|
+
|
|
12
|
+
A `# Citations` heading holds the external sources backing claims in a concept's
|
|
13
|
+
body. `OKF::Markdown::Citations` extracts them. Provenance is what separates
|
|
14
|
+
trustworthy knowledge from folklore: any external or empirical claim — a latency
|
|
15
|
+
number, an approval, a quota — should trace to a source here.
|
|
16
|
+
|
|
17
|
+
# Why it matters to the tooling
|
|
18
|
+
|
|
19
|
+
Citations are the input to the [linter](../capabilities/linter.md)'s
|
|
20
|
+
**provenance** category, which flags two failure modes:
|
|
21
|
+
|
|
22
|
+
- **uncited external claims** — a concept that asserts an external fact but cites
|
|
23
|
+
nothing;
|
|
24
|
+
- **broken citations** — a citation whose link no longer resolves.
|
|
25
|
+
|
|
26
|
+
Because the [validator](../capabilities/validator.md) is forbidden from rejecting
|
|
27
|
+
a bundle over provenance, these live entirely on the lint side — advisory signal
|
|
28
|
+
that a curator (or an agent) acts on, never a conformance failure.
|
|
29
|
+
|
|
30
|
+
# Citations
|
|
31
|
+
|
|
32
|
+
[1] [lib/okf/markdown/citations.rb](https://github.com/serradura/okf-gem/blob/main/lib/okf/markdown/citations.rb) — citation extraction.
|
|
33
|
+
[2] [SPEC.md §8](https://github.com/serradura/okf-gem/blob/main/lib/okf/skill/reference/SPEC.md) — the citations convention.
|