okf-kit 0.4.0 → 0.5.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.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,39 @@ All notable changes to `okf-kit` are documented here.
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
+ ## [0.5.0] - 2026-08-22
9
+
10
+ ### Added
11
+
12
+ - New rule `citations-resolve`: for docs with a repo root, flags a
13
+ `` `path:N`/`path:N-M` `` citation (and its `` `:N` ``/`` -`M` ``/`` (`N`) ``
14
+ continuations) whose target file is missing, whose range is inverted or
15
+ exceeds the file, or whose start line is blank or (for a non-markdown
16
+ target) only a closing brace. Ported from agent-grounding's
17
+ `scripts/okf-citations-resolve.mjs` (agent-grounding PR #185), a
18
+ repo-local spike that closed a gap `sources-fresh` cannot: `sources-fresh`
19
+ only compares a doc's `sources` list against file mtimes, so it is
20
+ structurally blind to an edit that shifts line numbers inside a still-fresh
21
+ source file. `citations-resolve` gives every repo consuming okf-kit this
22
+ check without copying the script. See "Citation resolution
23
+ (citations-resolve)" in the README. Not ported: the original's
24
+ `docs/testing`-file expansion (an agent-grounding-specific convention, not
25
+ part of the OKF spec, and not exercised by the ported tests) -- this rule
26
+ only scans the docs already loaded for the bundle being checked.
27
+ `citations-resolve` findings are warn-level: an existing `okf-kit check
28
+ --strict` run that was previously green can now fail once this version is
29
+ picked up, purely from this rule's new warnings, with no other change to
30
+ the bundle. Two path-resolution refinements differ from the
31
+ agent-grounding original: (1) a bare filename with no `/` (e.g.
32
+ `README.md`) now tries the citing doc's own directory and each ancestor
33
+ directory up to the repo root, nearest first, before falling back to a
34
+ plain repo-root-relative lookup, so a package-level file is not shadowed
35
+ by a same-named file that happens to also sit at the repo root; (2) a
36
+ `` `path:N` `` match that starts at column 0 of its line, right after a
37
+ previous line ending in `-`/`–`, is treated as the phantom tail of a
38
+ filename hard-wrapped across the line break and skipped, instead of being
39
+ checked (and typically flagged `missing-file`) as its own citation.
40
+
8
41
  ## [0.4.0] - 2026-08-13
9
42
 
10
43
  ### Changed
package/README.md CHANGED
@@ -69,6 +69,7 @@ Every template doc except `benchmark-template.md` ships with `sources: [path/to/
69
69
  | `no-absolute-links` | warning | Link targets should not start with `/`. GitHub resolves a leading slash against the repository root, not the bundle root, so an absolute link 404s once the bundle is viewed outside its own repository. Use a same-directory relative link instead. |
70
70
  | `sources-shape` | error | Frontmatter `sources`, when present, must be a non-empty array of non-empty strings. With a repo root (explicit or auto-detected), each listed path (file or directory) must also exist under it. |
71
71
  | `sources-fresh` | warning / notice | For docs with a `sources` list and a repo root, flags a source path whose last git commit is newer than both the doc's `timestamp` and the doc file's own last commit. See "Staleness (sources-fresh)" below. |
72
+ | `citations-resolve` | warning / notice | For docs with a repo root, flags a `` `path:N`/`path:N-M` `` citation (and its `` `:N` ``/`` -`M` ``/`` (`N`) `` continuations) whose target file is missing, whose range is inverted or exceeds the file, or whose start line is blank or (for a non-markdown target) only a closing brace. See "Citation resolution (citations-resolve)" below. |
72
73
 
73
74
  ## repo-root auto-detection
74
75
 
@@ -99,6 +100,29 @@ Known limitation: the doc-commit comparison suppresses staleness for every sourc
99
100
 
100
101
  **Authoring guidance:** when you re-verify a doc against its sources, bump its frontmatter `timestamp` (and add a line to the bundle's `log.md`) so `sources-fresh` reflects that the doc is current again.
101
102
 
103
+ ## Citation resolution (citations-resolve)
104
+
105
+ `sources-fresh` catches a source file changing after a doc's `timestamp`, but it is structurally blind to an edit that shifts *line numbers* inside a still-fresh file: a doc citing `path:42` keeps citing line 42 even after an edit moves the referenced content to line 50. `citations-resolve` finds every `` `path:N` ``/`` `path:N-M` `` citation in a doc, resolves `path` to a real file, and flags a citation that clearly cannot be pointing at real content any more. It is mechanical only (no symbol/AST resolution): it does not verify the cited line is *semantically* the right one, only that the target exists, the range is sound, and the start line is not blank or (for a non-markdown target) a lone closing brace/bracket.
106
+
107
+ | Rule id | Meaning |
108
+ |---------|---------|
109
+ | `missing-file` | The cited path could not be resolved to a real file (see path resolution below). |
110
+ | `path-traversal-rejected` | The cited path contains a `..` segment; rejected without ever being resolved. |
111
+ | `inverted-range` | A range's end line is before its start line. |
112
+ | `range-exceeds-file` | The cited line (or the end of a range) is past the end of the resolved file. |
113
+ | `blank-start-line` | The start line resolves to a blank line. A citation whose start line is blank is flagged; cite the first content line instead. Consumers enabling this rule on an existing bundle should expect to fix citations like this once, the first time they turn it on. |
114
+ | `closing-brace-start-line` | For a non-markdown target, the start line is only a closing brace/bracket/paren (`}`, `)`, `]`, optionally with a trailing `,`/`;`) -- a common signature of a cited block having moved. |
115
+ | `unresolved-ambiguous` | More than one file in the repo matches the cited path; reported as a `notice` (never counted toward `--strict`), not guessed at. |
116
+ | `unreadable-target` | The resolved target file exists but could not be read (e.g. permission denied); reported as a `notice` (never counted toward `--strict`) with the OS error code in `detail`, since an unreadable file is not evidence the citation itself is wrong. |
117
+
118
+ **Continuation citations.** Once a sentence states a full `path:N` citation, prose commonly repeats just the line (or range) for a later reference in the same sentence: `` `:N` ``/`` `:N-M` `` (bare colon-prefixed), `` -`M` ``/`` –`M` `` (hyphen- or en-dash-led, the tail of a split range like `` `path:N`-`M` ``), or `` (`N`) `` (parenthesized). Each resolves against the nearest preceding citation in the same doc that resolved to a real file; a continuation right after an unresolved, ambiguous, or out-of-scope citation is silently skipped, not misattributed to an earlier, unrelated file.
119
+
120
+ **Hard-wrapped prose.** A doc that hard-wraps prose at a fixed column can split a hyphenated filename across a line break right after its trailing `-` (e.g. `run-state-lifecycle-and-markers.md` wrapping to `run-state-lifecycle-and-\nmarkers.md`). A `` `path:N` `` match is skipped entirely (not checked, not counted as a citation) when it starts at column 0 of its line -- optionally after only whitespace or a list/quote marker (`-`, `*`, `>`, digits, `.`) -- and the previous line ends with `-`/`–`: the signature of a wrapped continuation rather than a genuine citation to a short bare filename.
121
+
122
+ **Path resolution**, tried in order: (1) the doc's own frontmatter `sources` list, matched by exact suffix, only when exactly one source matches; (2) for a citedPath with no `/` only, doc-relative and then each ancestor directory of the doc up to the repo root, nearest first (so a bare filename like `README.md` prefers the README *for the package the doc lives in* over a same-named file that merely happens to also sit at the repo root); (3) repo-root-relative; (4) doc-relative; (5) the nearest earlier citation in the same doc whose path contains a `/` and ends with the same suffix (the "full path was mentioned earlier" convention); (6) a repo-wide search by basename. A cited path starting with `/` is treated as an absolute or placeholder path (e.g. inside a fabricated example stack trace) and skipped without a finding.
123
+
124
+ Like `sources-fresh`, this rule requires a repo root (explicit `--repo-root` or auto-detected): without one, it emits a single bundle-level notice (`citation resolution skipped: not inside a git work tree`) rather than silently reporting nothing.
125
+
102
126
  ## Exit codes
103
127
 
104
128
  | Code | Meaning |
@@ -113,7 +137,7 @@ This is advisory: don't fail the build on warnings unless you pass `--strict`. U
113
137
 
114
138
  ```yaml
115
139
  - name: OKF bundle check
116
- run: npx okf-kit@0.4.0 check path/to/bundle
140
+ run: npx okf-kit@0.5.0 check path/to/bundle
117
141
  ```
118
142
 
119
143
  Pin the version: an unpinned `npx okf-kit` picks up new rules on their release day, which turns an unrelated PR red.
@@ -0,0 +1,2 @@
1
+ import type { Rule } from "../types.js";
2
+ export declare const citationsResolveRule: Rule;
@@ -0,0 +1,629 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { getValidSources } from "../util.js";
4
+ const RULE_ID = "citations-resolve";
5
+ /**
6
+ * Warn-only checker for `path:N[-M]` citations inside a bundle's docs (and
7
+ * their continuation forms, see below).
8
+ *
9
+ * Ported from agent-grounding's `scripts/okf-citations-resolve.mjs`
10
+ * (PR #185), which was a repo-local, no-dependency spike at closing a gap
11
+ * `sources-fresh` cannot: `sources-fresh` only compares a doc's `sources`
12
+ * list against file mtimes, so it is structurally blind to an edit that
13
+ * shifts line numbers inside a still-fresh source file. This rule finds
14
+ * every `path:N` / `path:N-M` citation in a doc, resolves `path` to a real
15
+ * file, and warns when the citation clearly cannot be pointing at real
16
+ * content any more.
17
+ *
18
+ * What it checks (mechanical only, no symbol/AST resolution):
19
+ * - the target file does not exist (after resolution, see below)
20
+ * - a range's end line is before its start line (an inverted range)
21
+ * - the cited line (or the end of a range) is past the end of the file
22
+ * - the start line is blank
23
+ * - for non-markdown targets only, the start line is only a closing
24
+ * brace/bracket/paren (`}`, `)`, `]`, optionally with a trailing `,`/`;`)
25
+ * -- a common signature of "the cited block moved and this now points at
26
+ * the line right after it used to end"
27
+ *
28
+ * What it does NOT check: whether the cited line is semantically the right
29
+ * one (that needs the actual symbol; out of scope for a warn-only mechanical
30
+ * rule), nor citations without a file extension this rule recognises.
31
+ *
32
+ * Hard-wrapped prose. Some docs hard-wrap prose at a fixed column, which can
33
+ * split a hyphenated filename like `run-state-lifecycle-and-markers.md`
34
+ * across a line break right after the trailing `-`. `CITATION_RE` cannot
35
+ * (and should not) span the newline to reassemble the real citedPath, but
36
+ * without a guard it instead matches the phantom tail on its own
37
+ * (`markers.md:172`), which usually does not exist as a file and produces a
38
+ * false `missing-file`. A `CITATION_RE` match is skipped entirely (neither
39
+ * checked nor counted as a real citation) when it starts at column 0 of its
40
+ * line -- optionally after only whitespace or a list/quote marker
41
+ * (`-`, `*`, `>`, digits, `.`) -- and the previous line ends with `-` or
42
+ * `–`: the signature of a wrapped continuation, not a new citation.
43
+ *
44
+ * Unreadable targets. A resolved target file that exists but cannot be read
45
+ * (permission denied, and similar OS-level failures) is reported as a
46
+ * bundle-level `notice` tagged `unreadable-target` with the OS error code in
47
+ * `detail`, rather than throwing and aborting the whole check: a citation
48
+ * pointing at a file the checker itself cannot read is not evidence the
49
+ * citation is wrong.
50
+ *
51
+ * Porting decision (scope cut from the original): the original script also
52
+ * scanned any `docs/testing/*.md` file that an okf doc's frontmatter
53
+ * `sources` cited, an agent-grounding-specific convention (docs/testing is
54
+ * not part of the OKF spec) that is not exercised by any of the ported
55
+ * tests. This rule instead only scans the docs `loadBundle` already loaded
56
+ * for `ctx.bundleDir` -- a citing doc outside the bundle is out of scope.
57
+ *
58
+ * Path resolution. A bundle's prose citations are frequently written
59
+ * *relative to context* rather than as a full repo-root path, e.g. a doc's
60
+ * frontmatter `sources` list carries `packages/foo/src/cli.ts`, but the
61
+ * prose later just says `cli.ts:42` or `src/cli.ts:42` once the reader
62
+ * "knows" which package the section is about -- and source basenames can be
63
+ * reused across sibling packages, so a resolver that only tried
64
+ * repo-root-relative and doc-relative paths would either flag real,
65
+ * non-drifted citations as "missing file" or silently check the wrong
66
+ * same-named file. Resolution here instead tries, in order:
67
+ * 1. the citing doc's own frontmatter `sources` list, matched by exact
68
+ * suffix (`source === citedPath || source.endsWith('/' + citedPath)`),
69
+ * only when exactly one source matches -- the doc's author already
70
+ * disambiguated which physical file this citation means
71
+ * 2. for a citedPath with no `/` only: doc-relative, then each ancestor
72
+ * directory of the doc up to (and including) repoRoot, nearest first
73
+ * -- a bare filename like `README.md` almost always means "the README
74
+ * *for the package this doc lives in*", and a repo-root-relative
75
+ * lookup done first would instead silently resolve to an unrelated,
76
+ * differently-sized file of the same name that happens to sit at the
77
+ * repo root ("shadowing"). A citedPath containing a `/` skips straight
78
+ * to step 3: the caller already qualified enough of the path that
79
+ * "closest ancestor" guessing isn't needed.
80
+ * 3. repo-root-relative (`<repoRoot>/<citedPath>`)
81
+ * 4. doc-relative (`<dirname(doc)>/<citedPath>`) -- redundant with step 2
82
+ * for a no-`/` citedPath (already tried there), the effective first
83
+ * resolution attempt for a `/`-containing one
84
+ * 5. the nearest earlier citation *in the same doc* whose path contains a
85
+ * `/` and ends with the same suffix as citedPath (the "last full path
86
+ * mentioned" convention)
87
+ * 6. a repo-wide search for a file whose path ends with citedPath (or,
88
+ * for a bare filename, whose basename equals it)
89
+ * A citedPath starting with `/` is treated as out of scope (an absolute or
90
+ * placeholder path, e.g. inside a fabricated example stack trace) and
91
+ * skipped without a finding. When step 6 finds more than one candidate the
92
+ * citation is reported as a `notice`-severity "ambiguous target" finding
93
+ * rather than guessed at or false-flagged as missing (never counted toward
94
+ * `--strict`). A citation resolved by none of the above, with zero
95
+ * candidates at step 6, is reported as `missing-file`. A citedPath
96
+ * containing a `..` segment is rejected outright (`path-traversal-rejected`)
97
+ * without ever being resolved, so a malformed or hostile citation cannot
98
+ * walk resolution outside the repo.
99
+ *
100
+ * Continuation citations. Once a sentence has stated a full `path:N`
101
+ * citation, prose habitually repeats just the line (or range) for a later
102
+ * reference in the same sentence rather than retyping the path, in three
103
+ * forms:
104
+ * - `` `:N` `` or `` `:N-M` `` -- a bare colon-prefixed line/range
105
+ * - `` -`M` `` / `` –`M` `` -- a hyphen- or en-dash-led bare line, the
106
+ * tail half of a `` `path:N`-`M` `` split range
107
+ * - `` (`N`) `` -- a parenthesized bare line
108
+ * Each of these resolves against `governing`: the nearest *preceding*
109
+ * citation (full or itself a continuation) that resolved to a real file,
110
+ * scanned in document order. `governing` resets to none whenever the
111
+ * citation immediately before it failed to resolve, was ambiguous, or was
112
+ * out of scope (leading `/`) -- a continuation never silently inherits a
113
+ * stale or unrelated path from further up the doc. A continuation with no
114
+ * governing citation at all (e.g. very start of a doc) is skipped, not
115
+ * flagged: there is nothing to validate it against.
116
+ *
117
+ * A continuation is further split into two roles (see
118
+ * collectContinuationAtoms): "fresh" (a genuinely new start line, checked
119
+ * the same five ways as a full citation's start) versus "extension" (only
120
+ * ever the tail `M` of a split range whose start was already checked) --
121
+ * an extension gets *only* the range-bound checks (inverted-range,
122
+ * range-exceeds-file), never blank/closing-brace: that start line was
123
+ * already checked when it was first cited, so re-running it here would
124
+ * double-report the same drift, and a range legitimately ending on a
125
+ * closing brace is normal, not drift.
126
+ *
127
+ * Requires `ctx.repoRoot` (explicit `--repo-root` or auto-detected): target
128
+ * files usually live outside the bundle itself, so without a repo root
129
+ * there is no filesystem tree to resolve a citation against. Without one,
130
+ * this rule emits a single bundle-level notice, matching `sources-fresh`'s
131
+ * "not inside a git work tree" posture.
132
+ */
133
+ const CITATION_RE = /([\w./-]+\.(?:ts|js|mjs|md|yml|yaml|json)):(\d+)(?:-(\d+))?/g;
134
+ // Continuation citation forms (see the "Continuation citations" doc block
135
+ // above). Each requires the backtick delimiter as part of the match so it
136
+ // can never overlap a CITATION_RE match: a full citation's regex match
137
+ // never includes the surrounding backticks, and none of these three
138
+ // require a `path.ext` prefix before the digits.
139
+ const CONT_COLON_RE = /`:(\d+)(?:-(\d+))?`/g;
140
+ const CONT_DASH_RE = /[-–]`(\d+)`/g;
141
+ const CONT_PAREN_RE = /\(`(\d+)`\)/g;
142
+ const CLOSING_ONLY_EXTS = new Set(["ts", "js", "mjs", "yml", "yaml", "json"]);
143
+ const CLOSING_BRACE_RE = /^[)\]}][;,]?$/;
144
+ const EXCLUDED_DIRS = new Set([
145
+ "node_modules",
146
+ ".git",
147
+ "dist",
148
+ "build",
149
+ "coverage",
150
+ ".next",
151
+ ".turbo",
152
+ // Ported 1:1 from the original script's own disposable test-fixtures
153
+ // exclusion, kept so the ported EXCLUDED_DIRS regression test carries
154
+ // over verbatim; harmless for any consuming repo that has no directory
155
+ // with this name.
156
+ "okf-citations-resolve-fixtures",
157
+ ]);
158
+ function isFile(p) {
159
+ try {
160
+ return fs.statSync(p).isFile();
161
+ }
162
+ catch {
163
+ return false;
164
+ }
165
+ }
166
+ /** True when citedPath has a literal `..` path segment. */
167
+ function hasParentSegment(citedPath) {
168
+ return citedPath.split("/").includes("..");
169
+ }
170
+ /**
171
+ * Repo-wide search for files with an exact basename, memoized per root
172
+ * within `cache`. `cache` is built lazily and scoped to a single
173
+ * `citationsResolveRule.run(ctx)` invocation (see the rule's `run`), not
174
+ * held at module scope, so an in-process caller running the check
175
+ * repeatedly (e.g. in a long-lived process, or a test suite that edits
176
+ * fixtures between runs) never sees a stale index from an earlier run.
177
+ */
178
+ function findByBasename(cache, root, basename) {
179
+ let index = cache.get(root);
180
+ if (!index) {
181
+ index = new Map();
182
+ const found = index;
183
+ const walk = (dir) => {
184
+ let entries;
185
+ try {
186
+ entries = fs.readdirSync(dir, { withFileTypes: true });
187
+ }
188
+ catch {
189
+ return;
190
+ }
191
+ for (const entry of entries) {
192
+ if (entry.name.startsWith(".") && entry.name !== ".github")
193
+ continue;
194
+ if (EXCLUDED_DIRS.has(entry.name))
195
+ continue;
196
+ const full = path.join(dir, entry.name);
197
+ if (entry.isDirectory()) {
198
+ walk(full);
199
+ }
200
+ else if (entry.isFile()) {
201
+ const list = found.get(entry.name) ?? [];
202
+ list.push(full);
203
+ found.set(entry.name, list);
204
+ }
205
+ }
206
+ };
207
+ walk(root);
208
+ cache.set(root, index);
209
+ }
210
+ return index.get(basename) ?? [];
211
+ }
212
+ /**
213
+ * Finds the nearest citation earlier in the same doc whose cited path
214
+ * contains a `/` and ends with the same suffix as `citedPath`, the "full
215
+ * path was mentioned earlier in this section/doc" convention.
216
+ */
217
+ function findPriorQualifiedCitation(content, beforeIndex, citedPath) {
218
+ const suffix = "/" + citedPath;
219
+ let best = null;
220
+ let bestIndex = -1;
221
+ const re = new RegExp(CITATION_RE.source, "g");
222
+ let m;
223
+ while ((m = re.exec(content)) !== null) {
224
+ if (m.index >= beforeIndex)
225
+ break;
226
+ const candidate = m[1];
227
+ if (candidate === citedPath)
228
+ continue; // not more qualified than itself
229
+ if (candidate.includes("/") &&
230
+ (candidate === citedPath || candidate.endsWith(suffix))) {
231
+ if (m.index > bestIndex) {
232
+ bestIndex = m.index;
233
+ best = candidate;
234
+ }
235
+ }
236
+ }
237
+ return best;
238
+ }
239
+ /**
240
+ * For a bare (no `/`) citedPath, tries doc-relative first, then each
241
+ * ancestor directory of the doc, nearest first, up to and including `root`.
242
+ * Returns the first real file found, or `null`. See the "Path resolution"
243
+ * doc block above (step 2) for why this runs before the plain
244
+ * repo-root-relative lookup: a same-named file closer to the citing doc
245
+ * (e.g. a package's own `README.md`) should win over one that merely
246
+ * happens to also exist at the repo root.
247
+ */
248
+ function resolveViaAncestorClimb(root, docAbsPath, citedPath) {
249
+ const resolvedRoot = path.resolve(root);
250
+ let dir = path.dirname(docAbsPath);
251
+ for (;;) {
252
+ const candidate = path.resolve(dir, citedPath);
253
+ if (isFile(candidate))
254
+ return candidate;
255
+ if (path.resolve(dir) === resolvedRoot)
256
+ return null;
257
+ const parent = path.dirname(dir);
258
+ if (parent === dir)
259
+ return null; // reached the filesystem root
260
+ dir = parent;
261
+ }
262
+ }
263
+ /**
264
+ * Resolves a citation's path to a single real file. Returns `{ skip: true }`
265
+ * for a citedPath out of scope (leading `/`), `{ path }` on a definitive
266
+ * single resolution, `{ ambiguous: true, candidates }` when more than one
267
+ * plausible target exists, or `null` when nothing matches. Callers must
268
+ * reject a citedPath with a `..` segment (see hasParentSegment) before
269
+ * calling this; it is not re-checked here.
270
+ */
271
+ function resolveCitation(cache, root, docAbsPath, docContent, docSources, citedPath, matchIndex) {
272
+ if (citedPath.startsWith("/")) {
273
+ return { skip: true };
274
+ }
275
+ const sourceMatches = docSources.filter((s) => s === citedPath || s.endsWith("/" + citedPath));
276
+ if (sourceMatches.length === 1) {
277
+ const candidate = path.resolve(root, sourceMatches[0]);
278
+ if (isFile(candidate))
279
+ return { path: candidate };
280
+ }
281
+ if (!citedPath.includes("/")) {
282
+ const viaAncestor = resolveViaAncestorClimb(root, docAbsPath, citedPath);
283
+ if (viaAncestor)
284
+ return { path: viaAncestor };
285
+ }
286
+ const rootRelative = path.resolve(root, citedPath);
287
+ if (isFile(rootRelative))
288
+ return { path: rootRelative };
289
+ const docRelative = path.resolve(path.dirname(docAbsPath), citedPath);
290
+ if (isFile(docRelative))
291
+ return { path: docRelative };
292
+ const prior = findPriorQualifiedCitation(docContent, matchIndex, citedPath);
293
+ if (prior) {
294
+ const candidate = path.resolve(root, prior);
295
+ if (isFile(candidate))
296
+ return { path: candidate };
297
+ }
298
+ const base = citedPath.includes("/")
299
+ ? citedPath.split("/").pop()
300
+ : citedPath;
301
+ const bySuffix = findByBasename(cache, root, base).filter((m) => {
302
+ const normalized = m.split(path.sep).join("/");
303
+ return (normalized === citedPath ||
304
+ normalized.endsWith("/" + citedPath) ||
305
+ !citedPath.includes("/"));
306
+ });
307
+ if (bySuffix.length === 1)
308
+ return { path: bySuffix[0] };
309
+ if (bySuffix.length > 1) {
310
+ return {
311
+ ambiguous: true,
312
+ candidates: bySuffix.map((m) => path.relative(root, m)),
313
+ };
314
+ }
315
+ return null;
316
+ }
317
+ function splitLines(content) {
318
+ const lines = content.split("\n");
319
+ if (lines.length > 0 &&
320
+ lines[lines.length - 1] === "" &&
321
+ content.endsWith("\n")) {
322
+ lines.pop();
323
+ }
324
+ return lines;
325
+ }
326
+ // Range-bound checks shared by a full citation's own range (via
327
+ // checkTarget below) and a cont-ext atom's extension (via
328
+ // checkRangeBoundOnly): a citation's end before its start, or either bound
329
+ // past the end of the file. Both are pure "does this range make sense"
330
+ // checks, independent of what the start line's content actually is.
331
+ function checkRangeBound(startLine, endLine, lineCount) {
332
+ if (endLine !== null && endLine < startLine) {
333
+ return {
334
+ rule: "inverted-range",
335
+ message: `range end (${endLine}) is before its start (${startLine})`,
336
+ };
337
+ }
338
+ const last = endLine ?? startLine;
339
+ if (startLine > lineCount || last > lineCount) {
340
+ return {
341
+ rule: "range-exceeds-file",
342
+ message: `citation exceeds file length (${lineCount} line(s))`,
343
+ };
344
+ }
345
+ return null;
346
+ }
347
+ /**
348
+ * Reads a resolved target's content, or reports why it couldn't be read
349
+ * (permission denied, and similar OS-level failures short of the file not
350
+ * existing at all -- `resolveCitation` already confirmed the target exists
351
+ * via `isFile`/`fs.statSync`, which does not require read permission).
352
+ * Returned as a `Problem` with `rule: "unreadable-target"` and `code` set to
353
+ * the OS error code, so callers can route it to a `notice`-severity finding
354
+ * (see pushUnreadable) instead of the usual `warning`-severity drift finding
355
+ * -- an unreadable file is not evidence the citation itself is wrong.
356
+ */
357
+ function readTarget(resolvedPath) {
358
+ try {
359
+ return { content: fs.readFileSync(resolvedPath, "utf8") };
360
+ }
361
+ catch (err) {
362
+ const code = err.code ?? "UNKNOWN";
363
+ return {
364
+ rule: "unreadable-target",
365
+ message: "target file exists but could not be read",
366
+ code,
367
+ };
368
+ }
369
+ }
370
+ function checkTarget(citedPath, startLine, endLine, resolvedPath) {
371
+ const read = readTarget(resolvedPath);
372
+ if ("rule" in read)
373
+ return read;
374
+ const content = read.content;
375
+ const lines = splitLines(content);
376
+ const lineCount = lines.length;
377
+ const bound = checkRangeBound(startLine, endLine, lineCount);
378
+ if (bound)
379
+ return bound;
380
+ const startText = lines[startLine - 1] ?? "";
381
+ const trimmed = startText.trim();
382
+ if (trimmed === "") {
383
+ return { rule: "blank-start-line", message: "start line is blank" };
384
+ }
385
+ const ext = (citedPath.split(".").pop() ?? "").toLowerCase();
386
+ if (CLOSING_ONLY_EXTS.has(ext) && CLOSING_BRACE_RE.test(trimmed)) {
387
+ return {
388
+ rule: "closing-brace-start-line",
389
+ message: `start line is only a closing brace/bracket ("${trimmed}")`,
390
+ };
391
+ }
392
+ return null;
393
+ }
394
+ // A cont-ext atom only ever extends the *end* of a range whose start line
395
+ // was already fully checked (blank / closing-brace) when it was cited as
396
+ // its own full citation or cont-fresh atom -- re-running checkTarget here
397
+ // would re-derive that same start-line check against the identical line
398
+ // and, on a real drift, double-report it as a second finding. This checks
399
+ // only whether the (possibly inverted, possibly out-of-file) range itself
400
+ // is sound.
401
+ function checkRangeBoundOnly(startLine, endLine, resolvedPath) {
402
+ const read = readTarget(resolvedPath);
403
+ if ("rule" in read)
404
+ return read;
405
+ const lineCount = splitLines(read.content).length;
406
+ return checkRangeBound(startLine, endLine, lineCount);
407
+ }
408
+ /**
409
+ * Collects every continuation-citation atom (see the "Continuation
410
+ * citations" doc block above) in `content`, sorted by document position.
411
+ *
412
+ * Each atom is tagged with a role:
413
+ * - "cont-fresh": establishes a new start line (optionally with its own
414
+ * embedded end, e.g. `` `:75-78` ``), checked the same way as a full
415
+ * citation's start (blank / closing-brace / range-exceeds).
416
+ * - "cont-ext": purely extends the *end* of whatever start line came
417
+ * immediately before it (a `` -`M` `` / `` –`M` `` tail, or a
418
+ * `` `:M` `` directly preceded by a `-`/`–`). Ending a range on a
419
+ * closing brace is completely normal, so an extension is checked ONLY
420
+ * for the range-bound checks (inverted-range, range-exceeds-file),
421
+ * never blank/closing-brace.
422
+ * A colon-form match (`` `:N` ``) is "cont-ext" exactly when the nearest
423
+ * non-whitespace character before its opening backtick is `-` or `–`;
424
+ * otherwise it is "cont-fresh". Dash-form (`` -`M` ``/`` –`M` ``) is
425
+ * always "cont-ext" by construction. Paren-form (`` (`N`) ``) is always
426
+ * "cont-fresh".
427
+ */
428
+ function collectContinuationAtoms(content) {
429
+ const atoms = [];
430
+ const colonRe = new RegExp(CONT_COLON_RE.source, "g");
431
+ let m;
432
+ while ((m = colonRe.exec(content)) !== null) {
433
+ const before = content.slice(0, m.index).trimEnd();
434
+ if (/[-–]$/.test(before)) {
435
+ atoms.push({
436
+ kind: "cont-ext",
437
+ index: m.index,
438
+ value: m[2] ? Number(m[2]) : Number(m[1]),
439
+ });
440
+ }
441
+ else {
442
+ atoms.push({
443
+ kind: "cont-fresh",
444
+ index: m.index,
445
+ startLine: Number(m[1]),
446
+ endLine: m[2] ? Number(m[2]) : null,
447
+ });
448
+ }
449
+ }
450
+ const dashRe = new RegExp(CONT_DASH_RE.source, "g");
451
+ while ((m = dashRe.exec(content)) !== null) {
452
+ atoms.push({ kind: "cont-ext", index: m.index, value: Number(m[1]) });
453
+ }
454
+ const parenRe = new RegExp(CONT_PAREN_RE.source, "g");
455
+ while ((m = parenRe.exec(content)) !== null) {
456
+ atoms.push({
457
+ kind: "cont-fresh",
458
+ index: m.index,
459
+ startLine: Number(m[1]),
460
+ endLine: null,
461
+ });
462
+ }
463
+ return atoms;
464
+ }
465
+ function pushDrift(findings, file, citation, rule, message, resolvedTo) {
466
+ findings.push({
467
+ ruleId: RULE_ID,
468
+ severity: "warning",
469
+ file,
470
+ message: `\`${citation}\`: ${message} [${rule}]`,
471
+ ...(resolvedTo ? { detail: `resolvedTo: ${resolvedTo}` } : {}),
472
+ });
473
+ }
474
+ function pushAmbiguous(findings, file, citation, candidates) {
475
+ findings.push({
476
+ ruleId: RULE_ID,
477
+ severity: "notice",
478
+ file,
479
+ message: `\`${citation}\`: ambiguous target, not evaluated [unresolved-ambiguous]`,
480
+ detail: `candidates: ${candidates.join(", ")}`,
481
+ });
482
+ }
483
+ function pushUnreadable(findings, file, citation, resolvedTo, code) {
484
+ findings.push({
485
+ ruleId: RULE_ID,
486
+ severity: "notice",
487
+ file,
488
+ message: `\`${citation}\`: target file exists but could not be read [unreadable-target]`,
489
+ detail: `resolvedTo: ${resolvedTo}, errorCode: ${code}`,
490
+ });
491
+ }
492
+ /**
493
+ * True when a `CITATION_RE` match at `matchIndex` is the phantom tail of a
494
+ * filename hard-wrapped across a line break (see the "Hard-wrapped prose"
495
+ * doc block above): the match starts at column 0 of its line, optionally
496
+ * after only whitespace or a list/quote marker, and the previous line ends
497
+ * with `-` or `–`.
498
+ */
499
+ function isWrappedPathContinuation(content, matchIndex) {
500
+ const lineStart = content.lastIndexOf("\n", matchIndex - 1) + 1;
501
+ if (lineStart === 0)
502
+ return false; // no previous line to have wrapped from
503
+ const prefix = content.slice(lineStart, matchIndex);
504
+ if (!/^[\s>*.\d-]*$/.test(prefix))
505
+ return false;
506
+ const prevLineEnd = lineStart - 1; // index of the newline just before lineStart
507
+ const prevLineStart = content.lastIndexOf("\n", prevLineEnd - 1) + 1;
508
+ const prevLine = content.slice(prevLineStart, prevLineEnd);
509
+ return /[-–]$/.test(prevLine);
510
+ }
511
+ function scanDoc(cache, root, bundleDir, doc) {
512
+ const findings = [];
513
+ const content = doc.raw;
514
+ const sources = getValidSources(doc.frontmatter.parsed) ?? [];
515
+ const docAbsPath = path.join(bundleDir, doc.relPath);
516
+ const fullAtoms = [];
517
+ const re = new RegExp(CITATION_RE.source, "g");
518
+ let m;
519
+ while ((m = re.exec(content)) !== null) {
520
+ if (isWrappedPathContinuation(content, m.index))
521
+ continue;
522
+ fullAtoms.push({
523
+ kind: "full",
524
+ index: m.index,
525
+ citedPath: m[1],
526
+ startLine: Number(m[2]),
527
+ endLine: m[3] ? Number(m[3]) : null,
528
+ });
529
+ }
530
+ const atoms = [...fullAtoms, ...collectContinuationAtoms(content)].sort((a, b) => a.index - b.index);
531
+ // `governing`: nearest preceding citation (full or continuation) that
532
+ // resolved to a real file; see the "Continuation citations" doc block
533
+ // above for the reset rules. `lastStartLine`: the start line a "cont-ext"
534
+ // atom extends into a range; tracks the most recent full or cont-fresh
535
+ // atom's own startLine, scoped together with `governing`.
536
+ let governing = null;
537
+ let lastStartLine = null;
538
+ for (const atom of atoms) {
539
+ if (atom.kind === "cont-ext") {
540
+ if (!governing || lastStartLine === null)
541
+ continue; // nothing to extend
542
+ const citation = `${governing.citedPath}:${lastStartLine}-${atom.value} (continuation)`;
543
+ const problem = checkRangeBoundOnly(lastStartLine, atom.value, governing.resolvedPath);
544
+ if (problem?.rule === "unreadable-target") {
545
+ pushUnreadable(findings, doc.relPath, citation, path.relative(root, governing.resolvedPath), problem.code ?? "UNKNOWN");
546
+ }
547
+ else if (problem) {
548
+ pushDrift(findings, doc.relPath, citation, problem.rule, problem.message, path.relative(root, governing.resolvedPath));
549
+ }
550
+ continue; // governing and lastStartLine both carry over unchanged
551
+ }
552
+ if (atom.kind === "cont-fresh") {
553
+ if (!governing)
554
+ continue; // nothing to validate a bare continuation against
555
+ const { startLine, endLine } = atom;
556
+ const citation = `${governing.citedPath}:${startLine}${endLine ? "-" + endLine : ""} (continuation)`;
557
+ const problem = checkTarget(governing.citedPath, startLine, endLine, governing.resolvedPath);
558
+ if (problem?.rule === "unreadable-target") {
559
+ pushUnreadable(findings, doc.relPath, citation, path.relative(root, governing.resolvedPath), problem.code ?? "UNKNOWN");
560
+ }
561
+ else if (problem) {
562
+ pushDrift(findings, doc.relPath, citation, problem.rule, problem.message, path.relative(root, governing.resolvedPath));
563
+ }
564
+ lastStartLine = startLine;
565
+ continue; // governing (same file) carries over unchanged
566
+ }
567
+ const { citedPath, startLine, endLine } = atom;
568
+ const citation = `${citedPath}:${startLine}${endLine ? "-" + endLine : ""}`;
569
+ if (hasParentSegment(citedPath)) {
570
+ pushDrift(findings, doc.relPath, citation, "path-traversal-rejected", `citedPath contains a ".." segment and was rejected without resolving: ${citedPath}`);
571
+ governing = null;
572
+ lastStartLine = null;
573
+ continue;
574
+ }
575
+ const resolution = resolveCitation(cache, root, docAbsPath, content, sources, citedPath, atom.index);
576
+ if (!resolution) {
577
+ pushDrift(findings, doc.relPath, citation, "missing-file", `could not resolve ${citedPath}: tried doc sources, ancestor climb (bare filenames only), repo-root, doc-relative, nearest prior qualified mention, repo-wide search; no candidate file exists`);
578
+ governing = null;
579
+ lastStartLine = null;
580
+ continue;
581
+ }
582
+ if ("skip" in resolution) {
583
+ governing = null;
584
+ lastStartLine = null;
585
+ continue;
586
+ }
587
+ if ("ambiguous" in resolution) {
588
+ pushAmbiguous(findings, doc.relPath, citation, resolution.candidates);
589
+ governing = null;
590
+ lastStartLine = null;
591
+ continue;
592
+ }
593
+ const problem = checkTarget(citedPath, startLine, endLine, resolution.path);
594
+ if (problem?.rule === "unreadable-target") {
595
+ pushUnreadable(findings, doc.relPath, citation, path.relative(root, resolution.path), problem.code ?? "UNKNOWN");
596
+ }
597
+ else if (problem) {
598
+ pushDrift(findings, doc.relPath, citation, problem.rule, problem.message, path.relative(root, resolution.path));
599
+ }
600
+ governing = { citedPath, resolvedPath: resolution.path };
601
+ lastStartLine = startLine;
602
+ }
603
+ return findings;
604
+ }
605
+ export const citationsResolveRule = {
606
+ id: RULE_ID,
607
+ description: "`path:N`/`path:N-M` citations (and their `:N`, -`M`/–`M`, (`N`) continuations) must resolve to a real target file and land on real, non-blank content. Mechanical only: does not verify the cited line is semantically correct.",
608
+ run(ctx) {
609
+ if (!ctx.repoRoot) {
610
+ // Never silently skip: mirrors sources-fresh's posture so a "clean"
611
+ // run is never a fake pass when there is no filesystem tree to
612
+ // resolve citation targets against.
613
+ return [
614
+ {
615
+ ruleId: RULE_ID,
616
+ severity: "notice",
617
+ file: "",
618
+ message: "citation resolution skipped: not inside a git work tree",
619
+ },
620
+ ];
621
+ }
622
+ const root = ctx.repoRoot;
623
+ // Fresh per invocation: see findByBasename's doc comment for why this
624
+ // is not held at module scope.
625
+ const cache = new Map();
626
+ return ctx.docs.flatMap((doc) => scanDoc(cache, root, ctx.bundleDir, doc));
627
+ },
628
+ };
629
+ //# sourceMappingURL=citations-resolve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"citations-resolve.js","sourceRoot":"","sources":["../../src/rules/citations-resolve.ts"],"names":[],"mappings":"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"}
@@ -5,5 +5,6 @@ import { linksResolveRule } from "./links-resolve.js";
5
5
  import { noAbsoluteLinksRule } from "./no-absolute-links.js";
6
6
  import { sourcesShapeRule } from "./sources-shape.js";
7
7
  import { sourcesFreshRule } from "./sources-fresh.js";
8
+ import { citationsResolveRule } from "./citations-resolve.js";
8
9
  export declare const allRules: Rule[];
9
- export { frontmatterRequiredRule, reservedFilesBareRule, linksResolveRule, noAbsoluteLinksRule, sourcesShapeRule, sourcesFreshRule, };
10
+ export { frontmatterRequiredRule, reservedFilesBareRule, linksResolveRule, noAbsoluteLinksRule, sourcesShapeRule, sourcesFreshRule, citationsResolveRule, };
@@ -4,6 +4,7 @@ import { linksResolveRule } from "./links-resolve.js";
4
4
  import { noAbsoluteLinksRule } from "./no-absolute-links.js";
5
5
  import { sourcesShapeRule } from "./sources-shape.js";
6
6
  import { sourcesFreshRule } from "./sources-fresh.js";
7
+ import { citationsResolveRule } from "./citations-resolve.js";
7
8
  export const allRules = [
8
9
  frontmatterRequiredRule,
9
10
  reservedFilesBareRule,
@@ -11,6 +12,7 @@ export const allRules = [
11
12
  noAbsoluteLinksRule,
12
13
  sourcesShapeRule,
13
14
  sourcesFreshRule,
15
+ citationsResolveRule,
14
16
  ];
15
- export { frontmatterRequiredRule, reservedFilesBareRule, linksResolveRule, noAbsoluteLinksRule, sourcesShapeRule, sourcesFreshRule, };
17
+ export { frontmatterRequiredRule, reservedFilesBareRule, linksResolveRule, noAbsoluteLinksRule, sourcesShapeRule, sourcesFreshRule, citationsResolveRule, };
16
18
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,MAAM,CAAC,MAAM,QAAQ,GAAW;IAC9B,uBAAuB;IACvB,qBAAqB;IACrB,gBAAgB;IAChB,mBAAmB;IACnB,gBAAgB;IAChB,gBAAgB;CACjB,CAAC;AAEF,OAAO,EACL,uBAAuB,EACvB,qBAAqB,EACrB,gBAAgB,EAChB,mBAAmB,EACnB,gBAAgB,EAChB,gBAAgB,GACjB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAE9D,MAAM,CAAC,MAAM,QAAQ,GAAW;IAC9B,uBAAuB;IACvB,qBAAqB;IACrB,gBAAgB;IAChB,mBAAmB;IACnB,gBAAgB;IAChB,gBAAgB;IAChB,oBAAoB;CACrB,CAAC;AAEF,OAAO,EACL,uBAAuB,EACvB,qBAAqB,EACrB,gBAAgB,EAChB,mBAAmB,EACnB,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,GACrB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "okf-kit",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "CLI that validates OKF v0.1 knowledge bundles for structural correctness",
5
5
  "type": "module",
6
6
  "bin": {