okf 1.12.0 → 2.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 +4 -4
- data/CHANGELOG.md +296 -0
- data/README.md +94 -466
- data/lib/okf/bundle/folder.rb +48 -3
- data/lib/okf/bundle/graph.rb +12 -3
- data/lib/okf/bundle/linter.rb +470 -47
- data/lib/okf/bundle/reader.rb +47 -18
- data/lib/okf/bundle/references.rb +111 -0
- data/lib/okf/bundle/row_filter.rb +53 -0
- data/lib/okf/bundle/search.rb +20 -2
- data/lib/okf/bundle/validator/result.rb +6 -3
- data/lib/okf/bundle/validator.rb +267 -26
- data/lib/okf/bundle/writer.rb +1 -1
- data/lib/okf/bundle.rb +124 -8
- data/lib/okf/cli/catalog.rb +2 -2
- data/lib/okf/cli/command.rb +93 -19
- data/lib/okf/cli/dirs.rb +1 -1
- data/lib/okf/cli/files.rb +2 -2
- data/lib/okf/cli/index.rb +3 -3
- data/lib/okf/cli/lint.rb +70 -12
- data/lib/okf/cli/references.rb +97 -0
- data/lib/okf/cli/search.rb +23 -8
- data/lib/okf/cli/stats.rb +3 -39
- data/lib/okf/cli/tags.rb +6 -43
- data/lib/okf/cli/types.rb +1 -1
- data/lib/okf/cli/validate.rb +3 -3
- data/lib/okf/cli.rb +4 -1
- data/lib/okf/concept/file.rb +17 -2
- data/lib/okf/concept.rb +362 -10
- data/lib/okf/markdown/citations.rb +41 -4
- data/lib/okf/markdown/frontmatter.rb +1 -1
- data/lib/okf/markdown/links.rb +67 -7
- data/lib/okf/path.rb +17 -3
- data/lib/okf/render/graph/template.html.erb +173 -41
- data/lib/okf/render/graph.rb +11 -3
- data/lib/okf/safe_read.rb +50 -0
- data/lib/okf/server/app.rb +47 -15
- data/lib/okf/server/hub.rb +1 -1
- data/lib/okf/skill/SKILL.md +14 -12
- data/lib/okf/skill/playbooks/curate.md +8 -3
- data/lib/okf/skill/playbooks/doctor.md +3 -1
- data/lib/okf/skill/playbooks/maintain.md +7 -6
- data/lib/okf/skill/playbooks/menu.md +5 -4
- data/lib/okf/skill/playbooks/migrate.md +31 -8
- data/lib/okf/skill/playbooks/produce.md +16 -9
- data/lib/okf/skill/playbooks/search.md +2 -2
- data/lib/okf/skill/reference/SPEC.md +739 -187
- data/lib/okf/skill/reference/authoring.md +154 -35
- data/lib/okf/skill/reference/cli.md +160 -44
- data/lib/okf/skill/templates/attested-computation.md +41 -0
- data/lib/okf/skill/templates/concept.md +13 -6
- data/lib/okf/skill/templates/root-index.md +1 -1
- data/lib/okf/version.rb +1 -1
- data/lib/okf.rb +23 -2
- metadata +7 -3
- data/CODE_OF_CONDUCT.md +0 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 43476118366ed4610628f4692f1c1432e4babe4a9d5749f06eb69fc41738c4e5
|
|
4
|
+
data.tar.gz: 218144bd22483995d933cf9d89f44e9602c2c95255d9e1c95626700a4f5b2f2e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 40ccd9ca9a9fe8580856825a972753b0f58ff2158fec6262db84d0b9d504e24c071a78e5b05939c0c5648ac7da67083621085417547ae194a0343ae1de34694b
|
|
7
|
+
data.tar.gz: 506f9d1feb6c6428c9db8845b4880207e904901636b35411b8b85f37dc909abb08b96d82246f06e410c97f6991ccfd947a559eed1d9c5b8d8e429b4790bd5929
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,299 @@ 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
|
+
## [2.0.0] - 2026-08-14
|
|
9
|
+
|
|
10
|
+
> Major, not minor. The Breaking entries below change public shapes a shipped
|
|
11
|
+
> consumer already reads: two lint check ids are renamed, the `stale` finding's
|
|
12
|
+
> JSON metric moves from `timestamp` to `generated_at`, and the `timestamp`
|
|
13
|
+
> catalog column is gone. A v0.1 *bundle* still validates with zero warnings —
|
|
14
|
+
> §13.1's fallbacks hold — but spec compatibility and API compatibility are
|
|
15
|
+
> different promises, and semver tracks the second. okf-mcp's open-ended
|
|
16
|
+
> `okf >= 1.13` is closed to `>= 2.0, < 3` in the same release.
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- **`okf references` — the §6.3 inventory.** Lists every file under
|
|
21
|
+
`references/`, the non-markdown ones included (the concept model carries
|
|
22
|
+
only markdown, so no other verb can see a `.py` attester or a `.sql`
|
|
23
|
+
computation), with which concepts cite each file through the §6.2
|
|
24
|
+
path-valued fields — plus every pointer into `references/` that resolves to
|
|
25
|
+
nothing. A bare `references/…` written from a subdirectory resolves relative
|
|
26
|
+
to the concept (the §6.2 trap the SPEC's own examples invite), and when the
|
|
27
|
+
leading-slash spelling would have hit, the dangling entry says so. Advisory
|
|
28
|
+
(exit 0); `--json` with `--fields`/`--except` like the other read views.
|
|
29
|
+
Backed by `OKF::Bundle::References` (pure) and
|
|
30
|
+
`OKF::Bundle::Folder#references` (the disk manifest) in the library, and by
|
|
31
|
+
`Markdown::Links.resolve_path` — `#resolve`'s path arithmetic without its
|
|
32
|
+
`.md` gate, since §6.2 fields accept any file.
|
|
33
|
+
- **The §5/§10 families across every surface.** The validator warns on their
|
|
34
|
+
shapes (raw keys only, warnings only — §11's three hard conditions are
|
|
35
|
+
untouched) and its warnings become machine-readable: each carries `check:`
|
|
36
|
+
(a stable id) and `source:` (`spec` | `convention`), with errors keeping
|
|
37
|
+
their exact two-key shape. A root `index.md`'s `okf_version` is actually
|
|
38
|
+
read now — an unknown version warns under §12 and the bundle is consumed
|
|
39
|
+
anyway; unquoted `okf_version: 0.2` (a Psych Float) is accepted.
|
|
40
|
+
- **Eight lint categories with pinned severities** (`Linter::SEVERITIES`, a
|
|
41
|
+
tested constant): Freshness gains `expired` (info, clock-gated — the CLI
|
|
42
|
+
passes today, `--today YYYY-MM-DD` pins it for reproducible CI reports, and
|
|
43
|
+
the pure library runs no clock check unless handed `today:`, confessing via
|
|
44
|
+
`stats[:skipped_checks]`); Provenance gains `broken_source`,
|
|
45
|
+
`unattributed_claim` (warn — only once a concept adopts keyed attribution,
|
|
46
|
+
with the label↔id join case-folded the way GFM resolves footnotes),
|
|
47
|
+
`unused_source`, and `unprefixed_actor` (info — a `verified[].by` outside
|
|
48
|
+
§7's three forms reads as machine-confirmed); a missing `generated.by` is
|
|
49
|
+
the validator's warning alone, never double-reported by lint. Attestation
|
|
50
|
+
gains `incomplete_computation` (warn, on
|
|
51
|
+
neither-or-both computation shapes) and `broken_attestation_ref` (warn — a
|
|
52
|
+
`computation`, `executor.resource` or `attester.resource` on an
|
|
53
|
+
`Attested Computation` naming an in-bundle `.md` that is not there; a contract whose parts are named but
|
|
54
|
+
absent is one no consumer can follow, so it warns rather than informs, with
|
|
55
|
+
the same URL/non-`.md` exemption `broken_source` carries); Migration's `legacy_timestamp` and
|
|
56
|
+
`legacy_citations` (info) name what a v0.1 bundle would change without ever
|
|
57
|
+
failing it. Reports gain `trust` and `status` distributions.
|
|
58
|
+
- **`--fail-on info`** joins `never | warn` — gateability without a severity
|
|
59
|
+
promotion; a migration campaign is
|
|
60
|
+
`okf lint <dir> --only legacy_timestamp,legacy_citations --fail-on info`.
|
|
61
|
+
- **`Linter.call(only:/except:)` refuses an unknown check id** with an
|
|
62
|
+
ArgumentError naming it, instead of silently intersecting to an empty run
|
|
63
|
+
that reports healthy — the library-side twin of the CLI's exit 2, and what
|
|
64
|
+
keeps a caller pinned to a renamed id from reading "checked and fine" over
|
|
65
|
+
a run that ran nothing.
|
|
66
|
+
- **`--status` and `--trust` filters** on catalog, files, search, tags and
|
|
67
|
+
types — `--status` matches the effective value (absent reads `stable`),
|
|
68
|
+
`--trust` folds either tier spelling.
|
|
69
|
+
- **Sources are searchable text** in both Ruby engines (weight 1 — the weight
|
|
70
|
+
the body text carried in v0.1) and snippet-eligible, so a migrated bundle
|
|
71
|
+
keeps its recall and a source-only hit keeps a snippet; the static page
|
|
72
|
+
bakes and indexes the same source text.
|
|
73
|
+
- **Trust as the graph page's third channel**: tier chips, status badges
|
|
74
|
+
(declared non-default only), a generated line, a client-clock stale marker,
|
|
75
|
+
and status/trust filter groups counted off the catalog.
|
|
76
|
+
- The skill teaches v0.2: the vendored SPEC is the published v0.2, a new
|
|
77
|
+
attested-computation template carries §10.3's MUST NOT, and the migrate
|
|
78
|
+
playbook walks the v0.1→v0.2 rewrite.
|
|
79
|
+
- **`Concept#shows_trust?` and `Bundle::RowFilter.shows_trust?` — §5.3's
|
|
80
|
+
display half, in one place.** The tier is always derivable; whether a surface
|
|
81
|
+
should *claim* it is a different question, and the answer is no for a concept
|
|
82
|
+
that declared no §5 family, which is every concept of every v0.1 bundle.
|
|
83
|
+
Claiming it there would paint a provenance verdict onto documents that never
|
|
84
|
+
made one — the false claim the trust system exists to prevent. The rule was
|
|
85
|
+
spelled twice, inline in `/node/meta` and in the page's `showsTrust`, and a
|
|
86
|
+
third consumer (okf-tui, which needs it for a chip, a facet gate, the facet
|
|
87
|
+
counts and the narrowing) is what a hand-copied predicate does not survive:
|
|
88
|
+
a gate disagreeing with the counts beside it reads "unverified 3" over two
|
|
89
|
+
chipped cards. One class method takes the two wire values so a Concept and a
|
|
90
|
+
catalog row ask the same rule; the server now routes through it, and the
|
|
91
|
+
page's client-side twin — unavoidable, since it runs over baked rows with no
|
|
92
|
+
Ruby to call — is pinned equal to it by `parity_test.rb`, truth table and
|
|
93
|
+
source literal both.
|
|
94
|
+
- **`Bundle#okf_version` — the spec version the root index declares (§12)**,
|
|
95
|
+
as the producer wrote it, or nil when it declares none, which §12 permits.
|
|
96
|
+
Stringified and stripped for the reason the validator compares that way: an
|
|
97
|
+
unquoted `okf_version: 0.2` is a Psych Float, and a consumer switching on it
|
|
98
|
+
must not be handed 0.2 the number. Public because the alternative is a
|
|
99
|
+
literal — a consumer naming the version on screen had no way to ask, which is
|
|
100
|
+
how a reader gets told "v0.1" about a bundle declaring 0.2. Unparseable
|
|
101
|
+
frontmatter stays the validator's error to report rather than this reader's
|
|
102
|
+
to raise.
|
|
103
|
+
- **`log_order` [info], the first log-side lint check.** §9 describes the log
|
|
104
|
+
as date-grouped entries, newest first — prose, not an RFC keyword, so
|
|
105
|
+
disorder is curation slack rather than a conformance error: exactly lint's
|
|
106
|
+
side of the split. Only shape-valid headings are compared; a malformed date
|
|
107
|
+
stays the validator's error, reported once.
|
|
108
|
+
- **`unprefixed_actor` now covers `generated.by` too.** §7 gives the actor
|
|
109
|
+
convention to both identity fields; only `verified[].by` was checked, on the
|
|
110
|
+
argument that §5.3 derives trust from nothing else — true, but a
|
|
111
|
+
`generated.by` no form can classify leaves a provenance reader unable to
|
|
112
|
+
tell a person from a process, so it earns the same info finding with its own
|
|
113
|
+
consequence. A *missing* `generated.by` stays the validator's warning.
|
|
114
|
+
|
|
115
|
+
- **`Bundle#stats` and `Bundle#tag_groups` join the library API** —
|
|
116
|
+
extracted from the CLI's `stats` and `tags --by` so the MCP shell reads
|
|
117
|
+
the same rollups instead of hand-copying them: the `by_dir` zero-keeping
|
|
118
|
+
subtlety and the within-group-beside-total tag counting each have one
|
|
119
|
+
home now (`Folder` delegates both). The CLI verbs are pure consumers of
|
|
120
|
+
the extraction; their output is unchanged.
|
|
121
|
+
- **Two long-standing behaviors are documented contract now, each with a
|
|
122
|
+
pinning test.** Hidden files are outside the bundle: the reader excludes
|
|
123
|
+
dot-prefixed files and everything under a dot-prefixed directory — the
|
|
124
|
+
Unix convention, kept so reading a project root cannot pull an installed
|
|
125
|
+
skill or `.github/` templates in as concepts. And a frontmatter `id:`
|
|
126
|
+
renames the concept, not its home — an extension beyond §2's path-derived
|
|
127
|
+
identity, with the recorded split stated where authors read it: the
|
|
128
|
+
identity views (catalog, hubs, `--dir`, search) follow the id, the
|
|
129
|
+
physical views (`index`, `dirs`, stats' `by_dir`) keep the file where it
|
|
130
|
+
lives. Both in the skill's authoring guide.
|
|
131
|
+
|
|
132
|
+
### Changed
|
|
133
|
+
|
|
134
|
+
- **`validate` warns on a declared-but-blank `status`.** `status: ""` is a
|
|
135
|
+
producer typo, not an absence — §5.4's default belongs to a concept that
|
|
136
|
+
never declared the key — and reading the blank through the default turned it
|
|
137
|
+
into `stable` before the vocabulary check ran, so the one value §5.4 names
|
|
138
|
+
nowhere was the one that never warned.
|
|
139
|
+
- **`lint --stale-after` refuses the ISO spellings it used to reinterpret.**
|
|
140
|
+
It took whatever `Date.iso8601` parsed, so the basic (`20260101`) and week
|
|
141
|
+
(`2026-W01-1`) forms silently became a cutoff nobody asked for — `20260101`
|
|
142
|
+
read as the year 2026 day 01 of month 01 only by luck of the parser, and
|
|
143
|
+
`2026-W01-1` as 2025-12-29. Both now exit 2 naming the accepted shapes. A
|
|
144
|
+
`YYYY-MM-DD` date and a full `2026-01-01T09:00:00Z` timestamp (what a
|
|
145
|
+
concept's own `generated.at` looks like) are both still accepted, the
|
|
146
|
+
timestamp reduced to its date as before. The new `--today` takes the
|
|
147
|
+
narrower grammar — it names a calendar day, not a moment.
|
|
148
|
+
- **The gem targets OKF v0.2** and keeps reading v0.1 under §13.1's two
|
|
149
|
+
sanctioned fallbacks: a legacy `timestamp` reads as `generated.at` (per-key,
|
|
150
|
+
so a half-migrated `generated: { by: … }` beside a `timestamp` keeps its
|
|
151
|
+
date, and no actor is ever invented), and a body `# Citations` list reads as
|
|
152
|
+
`sources` whenever the native key yields zero mappings. One `OKF::Concept`
|
|
153
|
+
class, no version hierarchy; a pure v0.1 bundle still validates with zero
|
|
154
|
+
warnings.
|
|
155
|
+
- **Breaking: lint check ids renamed** — `missing_timestamp` is
|
|
156
|
+
`missing_generated`, `broken_citation` is `broken_source` — so `--only` /
|
|
157
|
+
`--except` lists naming the old ids exit 2. `uncited_external` is redefined
|
|
158
|
+
over `#sources` (a v0.1 `# Citations` still silences it — a prose-only
|
|
159
|
+
section included; so does a migrated `sources:` block). The `stale`
|
|
160
|
+
finding's JSON metric renames `timestamp:` to `generated_at:` (and its
|
|
161
|
+
message says "last updated <generated_at>") — a consumer reading
|
|
162
|
+
`finding.metric.timestamp` gets `null` and must move with it.
|
|
163
|
+
- **Breaking: the `timestamp` catalog column is removed** — `--fields
|
|
164
|
+
timestamp` exits 2 loudly, naming the valid fields. The `concepts` row gains
|
|
165
|
+
`generated_at`, `generated_by`, `generated` (the raw declared-key boolean
|
|
166
|
+
that tells hand-written apart from v0.1-with-timestamp), `trust` (the
|
|
167
|
+
hyphenated wire literals `unverified` | `machine-confirmed` |
|
|
168
|
+
`human-reviewed`), `stale_after`, and a `sources` count. `status` stays the
|
|
169
|
+
raw declared value, `null` when absent. Temporal values render ISO 8601.
|
|
170
|
+
- **Breaking: `Concept#citations` and `Markdown::Citations.targets` are
|
|
171
|
+
removed**, both subsumed by `Concept#sources` / `Citations.entries`.
|
|
172
|
+
- **The staleness boundary is `today >= stale_after`** — a concept is stale
|
|
173
|
+
**on** the day itself, per §5.5. A consumer replacing its own `<`-based
|
|
174
|
+
check shifts by one day and should know it.
|
|
175
|
+
- `GET /node/meta` returns JSON (`{ description, trust: { tier, generated_by,
|
|
176
|
+
generated_at, status, stale_after } }`, null-stripped) instead of an escaped
|
|
177
|
+
HTML fragment; the graph page composes the trust line client-side for served
|
|
178
|
+
and baked pages alike, and computes expiry against the viewer's own clock.
|
|
179
|
+
- The demo Open Graph card URL the graph template points at moved to
|
|
180
|
+
`og-demo-v5.png` (the site's card-art version bump); the old URL keeps
|
|
181
|
+
serving the current art, so nothing breaks in between.
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
### Fixed
|
|
185
|
+
|
|
186
|
+
- **A calendar-invalid log heading is now a §11 error.** §9's MUST is ISO
|
|
187
|
+
8601, and `## 2026-02-30` matched the digit shape while naming a day that
|
|
188
|
+
never existed — the validator now asks `Date.iso8601`, so a log.md is
|
|
189
|
+
conformant only around real dates.
|
|
190
|
+
- **`MAILTO:` is excluded case-insensitively**, like every other scheme (RFC
|
|
191
|
+
3986). The guard sat inline and case-sensitive beside the case-insensitive
|
|
192
|
+
`SCHEME` regex in three places, so an uppercase mailto whose address ends in
|
|
193
|
+
`.md` resolved as a relative path — a link outside the bundle reported as a
|
|
194
|
+
file inside it. One `Links::MAILTO` now, beside `SCHEME`, for the same
|
|
195
|
+
reason `SCHEME` moved there.
|
|
196
|
+
- **`incomplete_computation` requires the fence, not the heading.** §10.3's
|
|
197
|
+
inline form is a fenced code block under `# Computation`; a heading over
|
|
198
|
+
prose used to count as provided, and a contract with nothing an executor
|
|
199
|
+
could run lint'd clean.
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
## [1.13.0] - 2026-08-07
|
|
203
|
+
|
|
204
|
+
### Added
|
|
205
|
+
|
|
206
|
+
- **`Bundle#directories`** — the bundle's own answer to "does this bundle have
|
|
207
|
+
directory X?", public because every surface now asks it (the CLI's concept
|
|
208
|
+
views, `okf-mcp`'s `dir` refusal) and the API this version's minor bump
|
|
209
|
+
exists for. Introduced by, and argued in, the `root` fix under Fixed below.
|
|
210
|
+
|
|
211
|
+
### Changed
|
|
212
|
+
|
|
213
|
+
- **The repository became a monorepo, and the gem moved into `okf/`.** Nothing
|
|
214
|
+
about the published gem changes in kind — same name, same library, same `v*` tag
|
|
215
|
+
series, same image, byte-identical in size to a pre-move build — but a checkout is laid out differently: one
|
|
216
|
+
directory per gem, named for the gem it ships, so `okf-mcp/` and the rest can
|
|
217
|
+
land beside the baseline without reshaping anything. `plugin/`,
|
|
218
|
+
`.claude-plugin/`, `.okf/` and the `Dockerfile` stay at the root; the last of
|
|
219
|
+
those because its build context must be the repo root, where the `.git` the
|
|
220
|
+
gemspec's `git ls-files` needs lives. From a checkout it is `cd okf` for
|
|
221
|
+
everything about the gem, and plain `rake` at the root to run every gem's suite
|
|
222
|
+
or to validate this repo's own bundle.
|
|
223
|
+
- **`CODE_OF_CONDUCT.md` no longer ships in the gem** (70 files, from 71). It is
|
|
224
|
+
a repository document and was packaged only because nothing rejected it. The
|
|
225
|
+
gem now carries **its own README** instead of the project's — which also fixes
|
|
226
|
+
a long-standing wart, since the project README opens with images under
|
|
227
|
+
`.github/` that the gemspec has never packaged, so the page on rubygems.org has
|
|
228
|
+
been rendering a broken hero.
|
|
229
|
+
- `changelog_uri` points at `okf/CHANGELOG.md`, where this file now lives.
|
|
230
|
+
|
|
231
|
+
### Fixed
|
|
232
|
+
|
|
233
|
+
- **A directory really named `root` is nameable again.** `--dir` and `--area`
|
|
234
|
+
accept `root` as a spelling of the bundle root, which is worth a shell quote
|
|
235
|
+
right up until a bundle actually has a `root/` directory — and then the alias
|
|
236
|
+
made it unaddressable, answering for the bundle root instead: the wrong
|
|
237
|
+
concepts, exit 0, nothing said. The alias now yields to a real directory,
|
|
238
|
+
since a convenience is worth less than a directory being reachable at all;
|
|
239
|
+
where no such directory exists nothing changes, which is every bundle that
|
|
240
|
+
has ever relied on the spelling. `okf dirs` carried the same fold into its
|
|
241
|
+
`subtree` column, where it computed the `root` row's subtree against the
|
|
242
|
+
bundle root — so the number on the row disagreed with what `--dir` on that
|
|
243
|
+
row returns, the one thing it promises never to do.
|
|
244
|
+
|
|
245
|
+
"Does this bundle have a `root` directory?" is now asked of the bundle
|
|
246
|
+
(`Bundle#directories`, under Added above) rather than of whichever list a view
|
|
247
|
+
had to hand. The concept views were reading it off the *catalog*, which knows
|
|
248
|
+
only directories holding concepts, so a `root/` carrying nothing but an
|
|
249
|
+
`index.md` stayed folded in `catalog`/`files`/`tags`/`types`/`search` while
|
|
250
|
+
`dirs` and `index` named it correctly — two answers to one question about one
|
|
251
|
+
bundle. And a multi-bundle `okf search @a @b --dir root` resolved per bundle
|
|
252
|
+
inside the loop, so one flag meant the `root/` subtree in the bundle that has
|
|
253
|
+
one and the bundle root in the bundle that does not, merged into a single
|
|
254
|
+
ranking with nothing in the output saying so; the served set resolves it once.
|
|
255
|
+
|
|
256
|
+
`Bundle#directories` counts every file kind that makes a directory real —
|
|
257
|
+
concepts, an `index.md`, and now a scoped `log.md`, which the first cut left
|
|
258
|
+
out: a `root/` holding only its history was invisible to the set, so the
|
|
259
|
+
alias won again for exactly the shape the fix was named after, one file kind
|
|
260
|
+
over. The same seed feeds `directory_index`, so `dirs`, `index` and `stats`
|
|
261
|
+
count a log-only directory too, and every surface answers "does this bundle
|
|
262
|
+
have directory X?" from one list.
|
|
263
|
+
- **`filter_entries` regained its 1.12.0 arity.** The dirs-source fix above
|
|
264
|
+
added a required third parameter to a helper on `CLI::Command` — the base
|
|
265
|
+
every verb inherits, plugin gems' verbs included — so an out-of-tree verb
|
|
266
|
+
calling the released two-argument shape raised `ArgumentError` at runtime.
|
|
267
|
+
The parameter defaults now, and the default is 1.12.0's resolution — the
|
|
268
|
+
helper is handed rows, not the folder, so it has nothing to resolve a real
|
|
269
|
+
`root/` against, and the `root` alias folds exactly as it did; the plugin
|
|
270
|
+
suite drives a 1.12.0-era verb through the two-argument shape to pin it.
|
|
271
|
+
Its sibling `filter_ids` makes the opposite choice on purpose: it *is*
|
|
272
|
+
handed the folder, and its two-argument shape resolves through `dir_scope`,
|
|
273
|
+
because the unconditional fold there was the bug this entry fixes, not a
|
|
274
|
+
contract — an old caller gets the corrected answer rather than the familiar
|
|
275
|
+
wrong one. In-tree callers pass
|
|
276
|
+
the set through `dir_scope`, which also derives it only when a `--dir` or
|
|
277
|
+
`--area` flag will actually consult it — a plain listing no longer pays a
|
|
278
|
+
per-path ancestor walk for a set nothing reads (and `Bundle#directories` is
|
|
279
|
+
memoized, so the verbs that ask twice pay once).
|
|
280
|
+
- **Coverage stopped measuring the plugin's curation hook** for one commit, and
|
|
281
|
+
said so by going *up*: SimpleCov's root defaults to the working directory, so
|
|
282
|
+
moving the gem down a level dropped ~100 tested lines out of the report and
|
|
283
|
+
line coverage read 98.63% against 98.47%. Its root is the repository now, with
|
|
284
|
+
the report still written inside the gem.
|
|
285
|
+
|
|
286
|
+
### Security
|
|
287
|
+
|
|
288
|
+
- **A symlinked file in a bundle can no longer read past the bundle root.** The
|
|
289
|
+
traversal guard `Path.join_under!` was lexical — it expands the path string,
|
|
290
|
+
and `File.expand_path` does not resolve a symlink — so a link whose name sat
|
|
291
|
+
inside the root but whose target did not passed the check, and the read
|
|
292
|
+
followed it. Every read now realpath-resolves and re-checks against the real
|
|
293
|
+
root: `Path.under?` is the shared pure predicate, `Bundle::Reader` quarantines
|
|
294
|
+
an escaping file into the unparseable bucket (a planted symlink is one bad
|
|
295
|
+
file, not grounds to fail the whole bundle read, which would hand any writer of
|
|
296
|
+
a served directory a denial of service), and `Concept::File` guards its own
|
|
297
|
+
read. A symlink that stays inside the bundle still resolves as before. The gap
|
|
298
|
+
reached every reader; serving a bundle's contents to an agent — as `okf-mcp`
|
|
299
|
+
does — is what surfaced it.
|
|
300
|
+
|
|
8
301
|
## [1.12.0] - 2026-07-24
|
|
9
302
|
|
|
10
303
|
### Added
|
|
@@ -1223,6 +1516,9 @@ Initial release.
|
|
|
1223
1516
|
|
|
1224
1517
|
- Runs on Ruby >= 2.4 with two runtime dependencies: rack and webrick.
|
|
1225
1518
|
|
|
1519
|
+
[2.0.0]: https://github.com/serradura/okf-gem/compare/v1.13.0...v2.0.0
|
|
1520
|
+
[1.13.0]: https://github.com/serradura/okf-gem/compare/v1.12.0...v1.13.0
|
|
1521
|
+
[1.12.0]: https://github.com/serradura/okf-gem/compare/v1.11.0...v1.12.0
|
|
1226
1522
|
[1.11.0]: https://github.com/serradura/okf-gem/compare/v1.10.0...v1.11.0
|
|
1227
1523
|
[1.10.0]: https://github.com/serradura/okf-gem/compare/v1.9.0...v1.10.0
|
|
1228
1524
|
[1.9.0]: https://github.com/serradura/okf-gem/compare/v1.8.0...v1.9.0
|