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.
Files changed (65) hide show
  1. checksums.yaml +7 -0
  2. data/.okf/capabilities/agent-skill.md +46 -0
  3. data/.okf/capabilities/graph-server.md +60 -0
  4. data/.okf/capabilities/index.md +20 -0
  5. data/.okf/capabilities/library-api.md +67 -0
  6. data/.okf/capabilities/linter.md +49 -0
  7. data/.okf/capabilities/read-views.md +84 -0
  8. data/.okf/capabilities/validator.md +40 -0
  9. data/.okf/cli.md +52 -0
  10. data/.okf/design/core-shell-split.md +58 -0
  11. data/.okf/design/index.md +10 -0
  12. data/.okf/design/ruby-floor.md +45 -0
  13. data/.okf/design/runtime-dependencies.md +44 -0
  14. data/.okf/design/server-trust-boundary.md +35 -0
  15. data/.okf/format/citations.md +33 -0
  16. data/.okf/format/cross-links.md +52 -0
  17. data/.okf/format/frontmatter.md +38 -0
  18. data/.okf/format/index.md +9 -0
  19. data/.okf/format/okf-format.md +43 -0
  20. data/.okf/index.md +18 -0
  21. data/.okf/log.md +9 -0
  22. data/.okf/model/bundle.md +38 -0
  23. data/.okf/model/concept.md +44 -0
  24. data/.okf/model/graph.md +44 -0
  25. data/.okf/model/index.md +8 -0
  26. data/.okf/overview.md +66 -0
  27. data/CHANGELOG.md +54 -0
  28. data/CODE_OF_CONDUCT.md +10 -0
  29. data/LICENSE.txt +201 -0
  30. data/NOTICE +10 -0
  31. data/README.md +276 -0
  32. data/exe/okf +6 -0
  33. data/lib/okf/bundle/folder.rb +94 -0
  34. data/lib/okf/bundle/graph.rb +118 -0
  35. data/lib/okf/bundle/linter/report.rb +56 -0
  36. data/lib/okf/bundle/linter.rb +416 -0
  37. data/lib/okf/bundle/reader.rb +60 -0
  38. data/lib/okf/bundle/validator/result.rb +35 -0
  39. data/lib/okf/bundle/validator.rb +131 -0
  40. data/lib/okf/bundle/writer.rb +137 -0
  41. data/lib/okf/bundle.rb +216 -0
  42. data/lib/okf/cli.rb +910 -0
  43. data/lib/okf/concept/file.rb +63 -0
  44. data/lib/okf/concept.rb +101 -0
  45. data/lib/okf/markdown/citations.rb +49 -0
  46. data/lib/okf/markdown/frontmatter.rb +55 -0
  47. data/lib/okf/markdown/links.rb +98 -0
  48. data/lib/okf/path.rb +34 -0
  49. data/lib/okf/server/app.rb +120 -0
  50. data/lib/okf/server/graph.rb +112 -0
  51. data/lib/okf/server/runner.rb +78 -0
  52. data/lib/okf/server/templates/graph.html.erb +803 -0
  53. data/lib/okf/skill/SKILL.md +133 -0
  54. data/lib/okf/skill/reference/APACHE-2.0.txt +202 -0
  55. data/lib/okf/skill/reference/SPEC.md +460 -0
  56. data/lib/okf/skill/reference/authoring.md +218 -0
  57. data/lib/okf/skill/reference/cli.md +196 -0
  58. data/lib/okf/skill/templates/concept.md +24 -0
  59. data/lib/okf/skill/templates/index.md +8 -0
  60. data/lib/okf/skill/templates/log.md +6 -0
  61. data/lib/okf/skill/templates/root-index.md +12 -0
  62. data/lib/okf/skill.rb +82 -0
  63. data/lib/okf/version.rb +5 -0
  64. data/lib/okf.rb +55 -0
  65. 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.