okf-kit 0.8.0 → 0.10.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 +291 -0
- package/README.md +85 -11
- package/dist/bundle.d.ts +18 -1
- package/dist/bundle.js +8 -1
- package/dist/bundle.js.map +1 -1
- package/dist/cli.d.ts +21 -0
- package/dist/cli.js +29 -0
- package/dist/cli.js.map +1 -1
- package/dist/git.d.ts +2 -2
- package/dist/git.js +16 -2
- package/dist/git.js.map +1 -1
- package/dist/rules/citations-resolve.d.ts +105 -0
- package/dist/rules/citations-resolve.js +154 -17
- package/dist/rules/citations-resolve.js.map +1 -1
- package/dist/rules/index.d.ts +3 -2
- package/dist/rules/index.js +15 -2
- package/dist/rules/index.js.map +1 -1
- package/dist/rules/prose-line-references.d.ts +2 -0
- package/dist/rules/prose-line-references.js +512 -0
- package/dist/rules/prose-line-references.js.map +1 -0
- package/dist/rules/sources-fresh.d.ts +35 -0
- package/dist/rules/sources-fresh.js +398 -18
- package/dist/rules/sources-fresh.js.map +1 -1
- package/dist/types.d.ts +27 -0
- package/dist/util.d.ts +52 -0
- package/dist/util.js +72 -0
- package/dist/util.js.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,297 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.10.0] - 2026-09-06
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- A new rule, `sources-fresh-future`, complements `sources-fresh`: it flags
|
|
15
|
+
a doc's frontmatter `timestamp` that is later than the doc file's own
|
|
16
|
+
last commit by more than a clock-skew allowance (default 10 minutes,
|
|
17
|
+
`--future-skew-minutes <n>`), catching a local wall-clock time
|
|
18
|
+
mistakenly written with a `Z`/UTC suffix it doesn't actually have.
|
|
19
|
+
Unlike `sources-fresh`, it never looks at `sources` commit times, only
|
|
20
|
+
at the doc file's own git history, and it is assessed over the same doc
|
|
21
|
+
population (a validly-shaped `sources` list and a repo root available).
|
|
22
|
+
An uncommitted doc (no own commit yet) is "unknown, not flagged", the
|
|
23
|
+
same posture `sources-fresh` already takes for an untracked source path.
|
|
24
|
+
`FUTURE-DATED` findings are `warning` severity, same as `STALE`; run
|
|
25
|
+
with the existing `--strict` flag to fail the build on either, rather
|
|
26
|
+
than a second, rule-specific strictness switch. Extends
|
|
27
|
+
`src/rules/sources-fresh.ts` (shares its `getLastCommitEpoch` git
|
|
28
|
+
helper and its doc-population filter) instead of adding a parallel
|
|
29
|
+
mechanism; see the README's "Staleness (sources-fresh)" section,
|
|
30
|
+
"Future-dated timestamps (`sources-fresh-future`)" subsection, for the
|
|
31
|
+
full rule contract and how the two rules relate. Together with the
|
|
32
|
+
`sources-fresh` narrowing below, catches: a source path committed
|
|
33
|
+
after the doc's `timestamp`; a source and the doc co-committed
|
|
34
|
+
together where that commit did not re-stamp the doc; and a local
|
|
35
|
+
wall-clock time hand-written with a `Z`/UTC suffix it doesn't
|
|
36
|
+
actually have. Does NOT catch a doc-only prose edit that leaves
|
|
37
|
+
`sources` untouched and the `timestamp` stale -- neither rule has a
|
|
38
|
+
source-side signal to compare against in that case, so it stays a
|
|
39
|
+
reviewer judgment call.
|
|
40
|
+
- `sources-fresh-future` skips (severity `notice`) a `timestamp` string
|
|
41
|
+
with no `Z`/UTC designator or numeric offset (e.g.
|
|
42
|
+
`2026-01-01T00:00:00`): such a string parses in the machine's own
|
|
43
|
+
local timezone under `Date.parse`, which would swing the check's
|
|
44
|
+
verdict by hours between a UTC+2 laptop and a UTC CI runner against a
|
|
45
|
+
default 10-minute allowance. A numeric offset (`+02:00`, `-0500`) is
|
|
46
|
+
unambiguous and is still assessed normally. `sources-fresh`'s own
|
|
47
|
+
thresholds are days wide, so this ambiguity does not practically
|
|
48
|
+
matter there; the gate applies only to `sources-fresh-future`.
|
|
49
|
+
- CI: the agent-dx `okf-anchor-guard` job (`.github/workflows/ci.yml`)
|
|
50
|
+
gained a strict freshness step that fails the build on any
|
|
51
|
+
`sources-fresh`/`sources-fresh-future` warning for
|
|
52
|
+
`packages/orchestrator-workflow/docs/okf`. **Release dependency:**
|
|
53
|
+
`sources-fresh-future` did not exist in the okf-kit version this job
|
|
54
|
+
installed before this release (a pinned release from npm, kept in sync
|
|
55
|
+
with `package.json`'s own version by
|
|
56
|
+
`orchestrator-workflow/test/docs-consistency.test.ts`), so the step's
|
|
57
|
+
filter matched only `sources-fresh` findings until this release; the
|
|
58
|
+
job's pin moves to 0.10.0 in the same commit as every other
|
|
59
|
+
`okf-kit@<version>` pin (per this file's "Changed" entry below), so it
|
|
60
|
+
now matches both rule ids and no other change to the step is needed,
|
|
61
|
+
since it already filters by rule id rather than a fixed list.
|
|
62
|
+
|
|
63
|
+
### Fixed
|
|
64
|
+
|
|
65
|
+
- `sources-fresh`'s co-commit staleness exception (a source committed
|
|
66
|
+
at/before the doc file's own last commit is treated as fresh) is
|
|
67
|
+
narrowed to a commit that actually re-stamped the doc. A commit that
|
|
68
|
+
co-commits a source change with the doc (a prose edit, a typo fix)
|
|
69
|
+
WITHOUT touching the stamp no longer suppresses staleness --
|
|
70
|
+
previously this unconditional exception let exactly that case (a
|
|
71
|
+
source and the doc's prose committed together with the timestamp left
|
|
72
|
+
stale) pass silently, which was the review class this rule pair
|
|
73
|
+
exists to close. "Re-stamped" is decided by VALUE: the doc's parsed
|
|
74
|
+
frontmatter `timestamp` at that commit is compared against its value
|
|
75
|
+
in the commit's FIRST PARENT, and they must differ (a doc created
|
|
76
|
+
there, having no parent revision at all, counts as re-stamped). The
|
|
77
|
+
lookup follows a rename, so a `git mv` is not read as a creation, and
|
|
78
|
+
it reads trees rather than a patch, so a merge commit -- including the
|
|
79
|
+
`refs/pull/N/merge` ref CI checks out -- is assessed like any other
|
|
80
|
+
commit. Consequently a `timestamp:` line inside a fenced YAML example
|
|
81
|
+
in the doc's BODY is not mistaken for a re-stamp. The check answers
|
|
82
|
+
"did the value change", never "is the new value right": a hand-typed
|
|
83
|
+
or backdated stamp still counts (`sources-fresh-future` is the rule
|
|
84
|
+
that catches an implausible value), and a doc-only prose edit with
|
|
85
|
+
unchanged sources remains outside both rules' reach. When git cannot
|
|
86
|
+
answer the question at all, the doc gets one `staleness not
|
|
87
|
+
assessable` notice rather than a STALE warning or a silent pass. See
|
|
88
|
+
the README's "Staleness (sources-fresh)" section for the full contract
|
|
89
|
+
and its remaining known limitations.
|
|
90
|
+
- `sources-fresh`'s re-stamp lookup no longer misjudges two more shapes
|
|
91
|
+
the value comparison above did not yet cover. (1) In a SHALLOW clone
|
|
92
|
+
(`git clone --depth`, including `actions/checkout`'s default), the
|
|
93
|
+
grafted history boundary commit reports an EMPTY parent list for every
|
|
94
|
+
path touched at or before it -- indistinguishable from a real root
|
|
95
|
+
commit by the parent-list check alone, which previously trusted it and
|
|
96
|
+
assumed "created" (re-stamped) unconditionally. The lookup now checks
|
|
97
|
+
`git rev-parse --is-shallow-repository` (once per `check` run, not per
|
|
98
|
+
doc: only spent at all when some doc's re-stamp lookup actually
|
|
99
|
+
reaches a commit with no parents) and, when the repository is shallow,
|
|
100
|
+
answers `not assessable` there instead -- see the README's "CI usage"
|
|
101
|
+
section for the `fetch-depth: 0` remedy. (2) A NON-ASCII doc path was
|
|
102
|
+
C-quoted by `git diff-tree`'s default `--name-status` output
|
|
103
|
+
(`core.quotePath` defaults to true, e.g. `"bundle/\303\266lt.md"` for
|
|
104
|
+
`bundle/ölt.md`), so the rename/created lookup's plain string match
|
|
105
|
+
against the doc's real (unquoted) path never matched, silently fell
|
|
106
|
+
through to the wrong path, and turned a normal rename or creation into
|
|
107
|
+
a `not assessable` notice instead of the real verdict. The lookup now
|
|
108
|
+
runs with `-z` (NUL-delimited, never quoted regardless of
|
|
109
|
+
`core.quotePath`) instead of the default form. New fixtures also pin:
|
|
110
|
+
an octopus merge (three parents) as a doc's last commit without a
|
|
111
|
+
re-stamp still reports STALE (only the FIRST parent is ever consulted,
|
|
112
|
+
however many there are); and a cosmetic rewrite of the stamp to the
|
|
113
|
+
same instant in another string representation (`...00Z` to
|
|
114
|
+
`...00.000Z`) still counts as a re-stamp, since the comparison is by
|
|
115
|
+
raw parsed VALUE, not by resolved instant -- while adding or removing
|
|
116
|
+
quotes around an otherwise-unchanged value does NOT count, since YAML
|
|
117
|
+
parsing already normalizes those away before the comparison ever sees
|
|
118
|
+
them.
|
|
119
|
+
- `--future-skew-minutes ''` (empty or whitespace-only) is now rejected
|
|
120
|
+
as the same usage error (exit 2) a negative value already gets,
|
|
121
|
+
instead of silently accepting it as `0`.
|
|
122
|
+
- The internal git runner now sets an explicit 16 MiB output cap.
|
|
123
|
+
Node's default for a synchronous child process is 1 MiB, and
|
|
124
|
+
`sources-fresh` reads whole doc blobs to compare frontmatter
|
|
125
|
+
timestamps, so a doc larger than 1 MiB previously resolved to "git
|
|
126
|
+
failed" -- and therefore to a permanent not-assessable notice -- on a
|
|
127
|
+
perfectly healthy repository.
|
|
128
|
+
- CI: the `okf-anchor-guard` job's freshness step (`.github/workflows/ci.yml`)
|
|
129
|
+
now rejects any `error`-severity freshness finding too (was: only
|
|
130
|
+
`warning`), gained a self-test mirroring the neighbouring
|
|
131
|
+
Anchor-citation guard's shape (its `assert_numeric` guard included),
|
|
132
|
+
guards explicitly against a missing or malformed report before its
|
|
133
|
+
first `jq`, and runs with `if: success() || failure()` so its findings
|
|
134
|
+
surface in the same CI round as the Anchor-citation guard's rather
|
|
135
|
+
than being skipped after an anchor failure -- while, unlike
|
|
136
|
+
`always()`, staying out of the way of a cancelled run or an earlier
|
|
137
|
+
setup failure that never produced a report. The self-test step now
|
|
138
|
+
carries the same `if: success() || failure()` (it previously ran
|
|
139
|
+
unconditionally), and both it and the real step now read the SAME
|
|
140
|
+
`FRESHNESS_FILTER` jq expression from the job's `env:` rather than the
|
|
141
|
+
self-test guarding its own hand-kept copy: an edit to the real filter
|
|
142
|
+
is now exercised by the self-test, not silently bypassed by it.
|
|
143
|
+
|
|
144
|
+
### Changed
|
|
145
|
+
|
|
146
|
+
- The release procedure now bumps orchestrator-workflow's okf-kit pins
|
|
147
|
+
(every `npm install -g okf-kit@<version>` or `npx okf-kit@<version>`
|
|
148
|
+
occurrence under `.github/workflows/`) in the same commit as the
|
|
149
|
+
okf-kit version cut, via `scripts/bump-okf-kit-pin.mjs` (repo root,
|
|
150
|
+
supports `--dry-run`). The reason: a release PR that only cuts
|
|
151
|
+
okf-kit's own version leaves OW's docs-consistency parity guard red on
|
|
152
|
+
master, and since `publish-npm.yml` runs the tests at the tag tree, the
|
|
153
|
+
OW tag then cannot publish either. See CONTRIBUTING.md's "Releasing
|
|
154
|
+
okf-kit" section for the full order.
|
|
155
|
+
|
|
156
|
+
## [0.9.0] - 2026-09-01
|
|
157
|
+
|
|
158
|
+
### Added
|
|
159
|
+
|
|
160
|
+
- A new opt-in rule, `prose-line-references` (`--prose-line-references`,
|
|
161
|
+
strict sub-flag `--prose-line-references-strict`), closes a gap
|
|
162
|
+
`citations-resolve` leaves open: a prose-embedded line reference written
|
|
163
|
+
outside its backtick grammar ("lines 496-498",
|
|
164
|
+
"generate-codex-config.ts lines 129-132") is structurally invisible to
|
|
165
|
+
`citations-resolve`'s `CITATION_RE`, which requires a literal `:` between
|
|
166
|
+
the path and the digits. A doc can be re-verified, re-stamped, and pass
|
|
167
|
+
`check` with 0 findings while its prose line numbers are drifted, because
|
|
168
|
+
nothing ever looked at them -- exactly what happened in harness task
|
|
169
|
+
ad66c43f (2026-08-30/31): review round 1 of that OKF sweep found 9 wrong
|
|
170
|
+
prose references behind a fresh `citations-resolve`-clean stamp, the fix
|
|
171
|
+
round's own sweep found more (including one citing the wrong file
|
|
172
|
+
entirely), and the verification round a third residue; both reviewers
|
|
173
|
+
named the missing mechanical guard as the structural cause. Extracts
|
|
174
|
+
`line N`/`lines N-M`/`lines N to M` (deliberately not `L N`, a
|
|
175
|
+
comma-separated list of several numbers, or a second unlabelled range
|
|
176
|
+
chained by "vs"; see the README for the full, deliberately conservative
|
|
177
|
+
grammar), binds each to the nearest named file (same sentence first,
|
|
178
|
+
then same paragraph, reusing `citations-resolve`'s own path-resolution
|
|
179
|
+
rules verbatim rather than a second, drift-prone copy), and reports
|
|
180
|
+
`out-of-bounds`, `blank-start-line`, `unresolvable`, or `ambiguous`.
|
|
181
|
+
`--prose-line-references-strict` additionally flags EVERY extracted
|
|
182
|
+
reference (resolved or not) with the remedy: lift it into a backtick
|
|
183
|
+
anchored citation, or de-precise it to a symbol name. Off by default;
|
|
184
|
+
see "Prose line references (opt-in, `--prose-line-references`)" in the
|
|
185
|
+
README for the full grammar, binding rule, and finding table.
|
|
186
|
+
|
|
187
|
+
- `prose-line-references` review-round fixes, found by a reviewer pass on
|
|
188
|
+
the rule above before it shipped: a start line of `0` no longer indexes
|
|
189
|
+
`lines[-1]` and is now its own `out-of-bounds` finding rather than a
|
|
190
|
+
misclassified `blank-start-line`; `LINE_REF_RE`'s leading `\b` no longer
|
|
191
|
+
matches right after a hyphen, so "in-line 999", "multi-line 999", and
|
|
192
|
+
"command-line 999" are no longer mis-extracted as citations to line 999;
|
|
193
|
+
a reference inside an HTML comment (`` <!-- see line 5 above --> ``) or
|
|
194
|
+
on a line that is itself a Markdown ATX heading (`## Line 3 semantics`)
|
|
195
|
+
is now excluded from extraction, the same way a fenced code block's
|
|
196
|
+
example already was; and a finding now quotes the doc's own matched text
|
|
197
|
+
verbatim (an en-dash range stays an en-dash range) instead of a
|
|
198
|
+
re-rendered, always-hyphenated approximation, with the normalised
|
|
199
|
+
hyphen-ranged form folded into the message body only when it actually
|
|
200
|
+
differs from what the doc wrote. Also closes a coverage gap the
|
|
201
|
+
reviewer found: the binding rule's paragraph-level fallback
|
|
202
|
+
(`nearestPrecedingMentionInParagraph`) and its same-sentence
|
|
203
|
+
"following mention" branch had no test that could tell them apart from
|
|
204
|
+
a hypothetical "always bind to the first mention in the paragraph"
|
|
205
|
+
mutant; see "Verification" below for the fixture and the mutation
|
|
206
|
+
check.
|
|
207
|
+
|
|
208
|
+
- `citations-resolve`: a fifth `--require-anchors` check,
|
|
209
|
+
`anchor-required-continuation` (warning), closes a gap the four checks
|
|
210
|
+
added in 0.8.0 left open: they all fire only for a "full" citation, so a
|
|
211
|
+
continuation (`` `:N` ``/`` `:N-M` ``, `` -`M` ``/`` –`M` ``, `` (`N`) ``)
|
|
212
|
+
or a bound paragraph-bound short-form `:N-M` chained off an
|
|
213
|
+
ALREADY-anchored full citation was invisible to `--require-anchors`
|
|
214
|
+
entirely: it carries no path of its own to hang an anchor on (see
|
|
215
|
+
"Anchored citations" in the README), so it was never itself an in-repo
|
|
216
|
+
full citation `anchor-required` could reach, and the anchor on its
|
|
217
|
+
governing citation does not (and structurally cannot) extend to a later
|
|
218
|
+
continuation of it. A line-shift that lands the continuation on still
|
|
219
|
+
non-blank, in-bounds, unrelated content -- exactly the drift class this
|
|
220
|
+
rule exists to catch -- was previously silent for a continuation the
|
|
221
|
+
same way it was for an unanchored full citation before 0.8.0. Fires once
|
|
222
|
+
the continuation's governing citation resolves in-repo, mirroring
|
|
223
|
+
`anchor-required`'s own exemptions (a reserved citing doc; a
|
|
224
|
+
`requireAnchors.allow` pattern, matched against the GOVERNING citation's
|
|
225
|
+
raw citedPath, since a continuation has no path of its own to match).
|
|
226
|
+
The paragraph-bound short form is included under the identical
|
|
227
|
+
reasoning. See "Anchor strictness (opt-in, `--require-anchors`)" in the
|
|
228
|
+
README for the full rationale.
|
|
229
|
+
|
|
230
|
+
### Verification
|
|
231
|
+
|
|
232
|
+
- `prose-line-references`: default mode (no `--prose-line-references`) is
|
|
233
|
+
byte-identical before and after this change: verified by building the
|
|
234
|
+
CLI at both the pre-change commit and this change, then running `check
|
|
235
|
+
--json` in default mode against the same three real bundles as below
|
|
236
|
+
(each repo's full real tree as `--repo-root`, not a narrow `docs/okf`-
|
|
237
|
+
only extraction, since this rule's own file-mention resolution needs the
|
|
238
|
+
rest of the repo present) and diffing the JSON output byte for byte --
|
|
239
|
+
0 bytes of diff on all three.
|
|
240
|
+
- `prose-line-references` migration backlog, run with
|
|
241
|
+
`--prose-line-references` after this change against the same three
|
|
242
|
+
bundles, full real repo tree as `--repo-root`: agent-dx's own
|
|
243
|
+
orchestrator-workflow bundle 3 findings (2 `out-of-bounds`, 1
|
|
244
|
+
`blank-start-line`), harness 5 (2 `ambiguous`, 1 `blank-start-line`, 2
|
|
245
|
+
`unresolvable`), agent-grounding 0. Spot-checked, not exhaustively
|
|
246
|
+
audited: the harness `ambiguous` pair is a real collision (`intercept.ts`
|
|
247
|
+
resolves to both `src/cli/policy/intercept.ts` and
|
|
248
|
+
`src/runtime/intercept.ts`); the agent-dx `out-of-bounds`/
|
|
249
|
+
`blank-start-line` trio is a real false positive of the binding rule's
|
|
250
|
+
own stated limitation (a "test lines N-M" phrase bound to a file named
|
|
251
|
+
elsewhere in the same sentence for an unrelated reason, not the file the
|
|
252
|
+
line numbers actually belong to -- once before the reference, once
|
|
253
|
+
after) -- see the README's closing paragraph on "Prose line references"
|
|
254
|
+
for that known category. No
|
|
255
|
+
consumer CI currently selects on `[prose-line-references]` findings by
|
|
256
|
+
rule id, so no consumer pin bump is required before this rule starts
|
|
257
|
+
reporting; enabling `--prose-line-references` anywhere is itself the
|
|
258
|
+
opt-in.
|
|
259
|
+
- `prose-line-references` review-round fixes above: default mode is still
|
|
260
|
+
byte-identical, re-verified the same way as above (build both commits,
|
|
261
|
+
`check --json` on the same three real bundles, diff byte for byte -- 0
|
|
262
|
+
bytes of diff on all three). The migration backlog counts are unchanged
|
|
263
|
+
by these fixes: agent-dx 3, harness 5, agent-grounding 0, identical
|
|
264
|
+
reasons to the counts above -- none of the three real bundles contained
|
|
265
|
+
a `line 0`, a hyphen-joined "-line N" word, an ATX heading naming a
|
|
266
|
+
line, or an HTML comment naming one. Four mutation probes verified
|
|
267
|
+
by hand against the new tests: replacing nearest-in-sentence binding
|
|
268
|
+
with first-in-paragraph binding, deleting the en-dash/em-dash/"to"
|
|
269
|
+
alternation from `LINE_REF_RE`, deleting the inverted-range branch in
|
|
270
|
+
`checkTarget`, and disabling the reserved-doc skip each fail a
|
|
271
|
+
dedicated new test, and pass again once reverted.
|
|
272
|
+
- Regression test added for a latent `FILE_MENTION_RE` extension-matching
|
|
273
|
+
bug found while measuring the migration backlog above: the extension
|
|
274
|
+
alternation lists `js` before `json`, and `js` is a strict prefix of
|
|
275
|
+
`json`; without forcing the regex to reject a truncated match, a real
|
|
276
|
+
`config.json` mention resolved (or failed to resolve) as `config.js`
|
|
277
|
+
instead. Fixed with a trailing `(?!\w)` on the match; `citations-resolve`'s
|
|
278
|
+
own `CITATION_RE` does not have this problem, since its mandatory
|
|
279
|
+
trailing `:` already forces the same backtracking.
|
|
280
|
+
- Default mode (no `--require-anchors`) is byte-identical before and
|
|
281
|
+
after this change: verified by building the CLI at both the pre-change
|
|
282
|
+
commit and this change, then running `check --json` in default mode
|
|
283
|
+
against three real bundles (this repo's own
|
|
284
|
+
`packages/orchestrator-workflow/docs/okf`, harness's `docs/okf`, and
|
|
285
|
+
agent-grounding's `docs/okf`, each extracted read-only at a pinned
|
|
286
|
+
commit) and diffing the sorted finding lists -- 0 lines of diff on all
|
|
287
|
+
three, both against a narrow `docs/okf`-only extraction and against the
|
|
288
|
+
full real repo tree as `--repo-root`.
|
|
289
|
+
- Migration backlog, run with `--require-anchors` after this change
|
|
290
|
+
against the same three bundles with their full real repo tree as
|
|
291
|
+
`--repo-root` (a narrow `docs/okf`-only extraction under-counts this,
|
|
292
|
+
since most citations fail to resolve without the rest of the repo
|
|
293
|
+
present): agent-dx's own orchestrator-workflow bundle 70
|
|
294
|
+
`anchor-required-continuation` findings, harness 24, agent-grounding
|
|
295
|
+
24. Consumer CI pins (agent-dx's and agent-grounding's own
|
|
296
|
+
`okf-anchor-guard` jobs, which select findings by the trailing
|
|
297
|
+
`[rule-id]` bracket) must be bumped to this version before
|
|
298
|
+
`anchor-required-continuation` starts gating; that bump is out of scope
|
|
299
|
+
here.
|
|
300
|
+
|
|
10
301
|
## [0.8.0] - 2026-08-27
|
|
11
302
|
|
|
12
303
|
### Added
|
package/README.md
CHANGED
|
@@ -29,8 +29,11 @@ okf-kit check path/to/bundle --repo-root /path/to/repo
|
|
|
29
29
|
# JSON output for tooling
|
|
30
30
|
okf-kit check path/to/bundle --json
|
|
31
31
|
|
|
32
|
-
# fail on warnings too, not just errors (STALE findings are warnings)
|
|
32
|
+
# fail on warnings too, not just errors (STALE and FUTURE-DATED findings are warnings)
|
|
33
33
|
okf-kit check path/to/bundle --strict
|
|
34
|
+
|
|
35
|
+
# narrow sources-fresh-future's default 10-minute clock-skew allowance
|
|
36
|
+
okf-kit check path/to/bundle --future-skew-minutes 2
|
|
34
37
|
```
|
|
35
38
|
|
|
36
39
|
## Scaffold a bundle (`init`)
|
|
@@ -55,7 +58,7 @@ Every template doc except `benchmark-template.md` ships with `sources: [path/to/
|
|
|
55
58
|
|
|
56
59
|
### Authoring guidance
|
|
57
60
|
|
|
58
|
-
- **`timestamp` means "last verified against sources," not "created on."** Bump it, and add a line to `log.md`, every time you re-verify a doc against its sources. Always use the real instant of verification (`new Date().toISOString()` or equivalent); never hand-write an artificial midnight datetime, `sources-fresh` staleness comparisons depend on it being real.
|
|
61
|
+
- **`timestamp` means "last verified against sources," not "created on."** Bump it, and add a line to `log.md`, every time you re-verify a doc against its sources. Always use the real instant of verification (`new Date().toISOString()` or equivalent); never hand-write an artificial midnight datetime, or a local wall-clock time with a `Z` suffix it doesn't actually have -- `sources-fresh` and `sources-fresh-future` staleness comparisons both depend on it being real. See "Future-dated timestamps (`sources-fresh-future`)" below for the measure-after-commit discipline this enforces.
|
|
59
62
|
- **Never list the bundle's own directory in `sources`.** A bundle directory changes on every doc edit inside it, so a self-referential `sources` entry goes permanently stale. This happened to the OKF pilot's own `BENCHMARK.md` (`agent-tasks` `docs/okf/BENCHMARK.md`, `sources: [docs/okf/]`); `benchmark-template.md` here omits `sources` entirely for the same reason, since a benchmark record measures the bundle rather than describing a piece of the codebase.
|
|
60
63
|
- **Keep all links same-directory relative.** Use `name.md`, not `/name.md`; see `no-absolute-links` above for why a leading slash breaks once the bundle is viewed outside its own repository.
|
|
61
64
|
- **Write a sibling short-form citation as a connective-led `:N-M`, not `(N-M)`.** When a paragraph cites several sub-ranges of a source already named by a full `path:N-M` citation earlier in the same paragraph, write each later one as a `:N-M` led by one of the serial connectives the gate accepts -- `,`, `;`, `(`, or a trailing `and`/`or` -- right after the phrase it points at (e.g. `review finding L1 (:1170-1227, ...)`). `citations-resolve` only recognises the colon form; a parenthesized `(N-M)` is never checked, so it can drift silently. This convention is not demonstrated by any scaffolded template; it is a `citations-resolve` authoring rule. See "Citation resolution (citations-resolve)" below.
|
|
@@ -69,8 +72,10 @@ Every template doc except `benchmark-template.md` ships with `sources: [path/to/
|
|
|
69
72
|
| `links-resolve` | error | Markdown links to other `.md` files in the bundle must resolve to a real file. Relative targets resolve against the containing file's directory; targets starting with `/` resolve against the bundle root. A relative target that climbs out of the bundle directory (`../outside.md`) and still resolves on disk is accepted; the rule checks resolution, not containment. |
|
|
70
73
|
| `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. |
|
|
71
74
|
| `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. |
|
|
72
|
-
| `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. |
|
|
73
|
-
| `
|
|
75
|
+
| `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 re-stamping commit (re-stamp decided by comparing frontmatter `timestamp` values across that commit's first-parent boundary). See "Staleness (sources-fresh)" below. |
|
|
76
|
+
| `sources-fresh-future` | warning | For the same docs as `sources-fresh`, flags a `timestamp` later than the doc file's own last commit by more than a clock-skew allowance (default 10 minutes, `--future-skew-minutes`). Catches a local wall-clock time mistakenly written with a `Z`/UTC suffix. See "Staleness (sources-fresh)" below. |
|
|
77
|
+
| `citations-resolve` | warning / notice | For docs with a repo root, flags a `` `path:N`/`path:N-M` `` citation (and its `` `:N` ``/`` -`M` ``/`` (`N`) `` continuations, and bare paragraph-bound short forms `:N-M`/`(N-M)`) 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. A full citation may also carry an optional `#anchor` (e.g. `` `CHANGELOG.md:50-144#0.24.0` ``), checked against the target's own structure/content instead of just its line numbers. A backtick-delimited `` `path:#heading` `` citation (`.md` targets only) resolves to a whole Markdown section instead of a line range, immune to every line-number shift above it; see "Heading-section citations" below. A short-form citation's range into a test file is also checked for a describe/it block boundary. `--require-anchors` opts into five additional checks; see "Anchor strictness (opt-in, `--require-anchors`)" below. See "Citation resolution (citations-resolve)" below. |
|
|
78
|
+
| `prose-line-references` | (opt-in, `--prose-line-references`) warning / notice | Off by default. Flags a prose-embedded line reference outside `citations-resolve`'s own backtick grammar (`line N`, `lines N-M`, `lines N to M`) that is drifted, unresolvable, or ambiguous once bound to the nearest named file. `--prose-line-references-strict` additionally flags every such reference as a formatting policy violation. See "Prose line references (opt-in, `--prose-line-references`)" below. |
|
|
74
79
|
|
|
75
80
|
## repo-root auto-detection
|
|
76
81
|
|
|
@@ -82,12 +87,15 @@ Pass `--repo-root` explicitly to pin a specific root (useful in CI when the bund
|
|
|
82
87
|
|
|
83
88
|
## Staleness (sources-fresh)
|
|
84
89
|
|
|
85
|
-
`sources-fresh` compares each frontmatter `sources` entry's last git commit time against the doc's `timestamp`, and additionally against the doc file's own last commit time: a source committed at or before the doc file's last commit is treated as fresh even when the frontmatter `timestamp` is older. That keeps squash-merge PRs honest: when a doc re-stamp lands in the same commit as its changed sources, the merge gives every source a commit time later than any pre-merge `timestamp`, which used to make such docs stale-on-arrival. The rule never blocks a doc that has no `sources`, and it never invents an error where git can't give a real answer:
|
|
90
|
+
`sources-fresh` compares each frontmatter `sources` entry's last git commit time against the doc's `timestamp`, and additionally against the doc file's own last commit time: a source committed at or before the doc file's last commit is treated as fresh even when the frontmatter `timestamp` is older, PROVIDED that same commit actually re-stamped the doc. "Re-stamped" is decided by VALUE, not by diff text: okf-kit reads the doc's frontmatter at that commit and in the commit's first parent, parses both, and compares the `timestamp` values -- they differ, or the doc did not exist in the parent at all (it was created there), and it counts as re-stamped. The lookup follows a rename, so `git mv`ing a doc is not a creation, and it reads trees rather than a patch, so a merge commit (including the `refs/pull/N/merge` ref CI checks out) is assessed like any other commit. That keeps squash-merge PRs honest: when a doc re-stamp lands in the same commit as its changed sources, the merge gives every source a commit time later than any pre-merge `timestamp`, which used to make such docs stale-on-arrival. It does NOT extend to a commit that merely happens to also touch the doc file -- a typo fix, a co-committed prose edit, a repo-wide formatter run -- without rewriting the stamp: that commit carries no verification claim, so staleness stands. "Created" is trusted only in a genuinely unshallow repository: in a shallow clone (`git clone --depth`), a commit with an empty parent list can simply be the boundary history was grafted onto, not the doc's real first commit, so this case gets the same not-assessable notice as any other unanswerable re-stamp question rather than an assumed pass (see "CI usage" below for the `fetch-depth: 0` remedy). The rule never blocks a doc that has no `sources`, and it never invents an error where git can't give a real answer:
|
|
86
91
|
|
|
87
92
|
| Situation | Severity | Message |
|
|
88
93
|
|-----------|----------|---------|
|
|
89
94
|
| A source path's last commit is newer than the doc's `timestamp` and the doc file's last commit | warning | `STALE: <path> changed <iso> after doc timestamp <iso>` |
|
|
90
|
-
| A source path's last commit is newer than the doc's `timestamp
|
|
95
|
+
| A source path's last commit is newer than the doc's `timestamp`, at/before the doc file's last commit, AND that commit re-stamped the doc | (nothing) | fresh: doc and source landed together with a real re-stamp (or the doc was committed later, or created there) |
|
|
96
|
+
| A source path's last commit is newer than the doc's `timestamp`, at/before the doc file's last commit, but that commit did NOT re-stamp the doc | warning | `STALE: <path> changed <iso> after doc timestamp <iso>` (co-commit with no re-stamp is not an exception) |
|
|
97
|
+
| A source path's last commit is newer than the doc's `timestamp`, at/before the doc file's last commit, and git could not be asked whether that commit re-stamped the doc | notice | `staleness not assessable: git could not read the doc's own last commit to decide whether it re-stamped the doc` (once per doc, never a STALE warning and never a silent pass) |
|
|
98
|
+
| Same, but the repository is a shallow clone and the doc's last commit is the grafted history boundary (an empty parent list that is not really a root commit) | notice | `staleness not assessable: this is a shallow clone ... use fetch-depth: 0 ... to assess it` (once per doc) |
|
|
91
99
|
| A source path exists but has no git history (untracked) | notice | `untracked by git, staleness unknown: <path>` |
|
|
92
100
|
| The doc's `timestamp` is missing or not a parseable date, while `sources` is present | notice | `staleness not assessable: no valid timestamp` |
|
|
93
101
|
| No repo root available (see auto-detection above) | notice | `staleness skipped: not inside a git work tree` |
|
|
@@ -97,10 +105,31 @@ STALE findings are warnings, so they are advisory by default; run with `--strict
|
|
|
97
105
|
|
|
98
106
|
Known limitation: a `git log` call that fails for a reason other than "no history for this path" (for example a corrupt object or a transient git error) is reported the same way as a genuinely untracked path, the `untracked by git, staleness unknown` notice; okf-kit does not currently distinguish a real git failure from "no commits touch this path".
|
|
99
107
|
|
|
100
|
-
Known limitation: the doc-commit comparison suppresses staleness for every source older than the doc file's last commit, not only for sources from the same commit. For a multi-source doc that means
|
|
108
|
+
Known limitation: the doc-commit comparison suppresses staleness for every source older than the doc file's last re-stamping commit, not only for sources from the same commit -- provided that commit is the one that rewrote the stamp. For a multi-source doc that means a re-stamp silences drift on every source changed before it, even ones that specific re-stamp did not itself verify. The frontmatter `timestamp` still governs sources changed after the doc's last commit. Also note: the check only looks at WHETHER the `timestamp` value changed, never at whether the new value is itself correct -- a hand-typed or deliberately backdated stamp still counts as a re-stamp (`sources-fresh-future` is the rule that catches an implausible value). "Changed" means the parsed frontmatter VALUE changed: a rewrite to the same instant in another representation (`...00Z` to `...00.000Z`) still counts as a re-stamp, since the raw string differs even though it names the same instant, while adding or removing quotes around an otherwise-unchanged value does NOT count, since YAML parsing already normalizes those away before the comparison ever sees them. And the comparison is against the FIRST parent only: a merge that takes the doc wholesale from its second parent is judged against the first-parent baseline, which is the baseline the pull request under review is measured against anyway.
|
|
101
109
|
|
|
102
110
|
**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.
|
|
103
111
|
|
|
112
|
+
### Future-dated timestamps (`sources-fresh-future`)
|
|
113
|
+
|
|
114
|
+
`sources-fresh` catches a `timestamp` that is too OLD relative to a source's last commit. `sources-fresh-future` catches the opposite mistake: a `timestamp` that is too NEW relative to the doc file's OWN last commit -- in practice, almost always a local wall-clock time hand-written with a trailing `Z` (or another UTC marker) it does not actually have, rather than a genuine future date. It never looks at `sources` commit times at all, only at the doc file's own git history, so it has nothing to say about whether any source is stale; the two rules are complementary, not overlapping, and are assessed over the same population of docs (a validly-shaped `sources` list and a repo root available, exactly like `sources-fresh` above).
|
|
115
|
+
|
|
116
|
+
| Situation | Severity | Message |
|
|
117
|
+
|-----------|----------|---------|
|
|
118
|
+
| The doc's `timestamp` is later than the doc file's own last commit by more than the skew allowance | warning | `FUTURE-DATED: doc timestamp <iso> is after the doc's own last commit <iso> (skew allowance <n>s)` |
|
|
119
|
+
| The doc's `timestamp` is at or within the skew allowance of the doc file's own last commit | (nothing) | fresh: an ordinary write-then-commit gap, not a mistake |
|
|
120
|
+
| The doc's `timestamp` string has no `Z`/UTC designator or numeric offset (e.g. `2026-01-01T00:00:00`, no offset) | notice | future-dated check skipped: timestamp has no UTC designator (`Z`) or numeric offset, can't be compared reliably across timezones |
|
|
121
|
+
| The doc has no git history yet (uncommitted) | (nothing) | unknown, not flagged: there is no real commit time to compare against |
|
|
122
|
+
| The doc's `timestamp` is missing or not a parseable date | (nothing) | left to `sources-fresh`'s own notice, not duplicated here |
|
|
123
|
+
| No repo root available (see auto-detection above) | (nothing) | left to `sources-fresh`'s single bundle-level notice, not duplicated here |
|
|
124
|
+
|
|
125
|
+
The skew allowance defaults to 10 minutes (600 seconds), absorbing the ordinary gap between writing a timestamp and the commit that carries it landing; override it with `--future-skew-minutes <n>`. Like `STALE` findings, `FUTURE-DATED` findings are warnings, advisory by default; run with `--strict` to fail the build on either.
|
|
126
|
+
|
|
127
|
+
**The measure-after-commit discipline both rules enforce:** re-verify the doc against its sources, THEN bump `timestamp` to the real instant of that verification (`new Date().toISOString()` or equivalent, never a hand-written value), THEN commit -- committing without re-stamping is what `sources-fresh` catches, and hand-writing a local time with a `Z` suffix it doesn't have is what `sources-fresh-future` catches.
|
|
128
|
+
|
|
129
|
+
**Why the UTC-designator gate exists:** a bare local datetime string (no `Z`, no numeric offset) parses under `Date.parse` in the machine's OWN timezone, so the same frontmatter value would compare differently on a UTC+2 laptop than on a UTC CI runner -- unusable against a default allowance measured in minutes. A numeric offset (`+02:00`, `-0500`) is unambiguous and IS assessed normally (`Date.parse` already normalizes it to a real UTC instant); only a fully bare datetime is skipped. `sources-fresh`'s own thresholds are days wide, so this ambiguity doesn't practically matter there and the gate applies only to `sources-fresh-future`.
|
|
130
|
+
|
|
131
|
+
**What these two rules catch, precisely, and what they don't:** a source path committed after the doc's `timestamp` (`sources-fresh`'s base case); a source and the doc co-committed together where that commit did NOT re-stamp the doc (`sources-fresh`'s narrowed co-commit exception, see "Staleness (sources-fresh)" above); and a local wall-clock time hand-written with a `Z`/UTC suffix it doesn't actually have (`sources-fresh-future`). What they do NOT catch: a doc-only prose edit that leaves `sources` untouched and the `timestamp` stale -- neither rule has a source-side signal to compare against in that case, so it is not mechanically assessable and stays a reviewer judgment call, the same as before this pair of rules existed.
|
|
132
|
+
|
|
104
133
|
## Citation resolution (citations-resolve)
|
|
105
134
|
|
|
106
135
|
`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.
|
|
@@ -134,13 +163,14 @@ Known limitation: the doc-commit comparison suppresses staleness for every sourc
|
|
|
134
163
|
| `anchor-not-on-last-line` | (opt-in, `--require-anchors`) A string-anchored citation's anchor text was found in the cited range, but not on the range's own last CONTENT line (see below). **Warning**. |
|
|
135
164
|
| `anchor-not-unique-in-range` | (opt-in, `--require-anchors`) A string-anchored citation's anchor text occurs on more than one line of the cited range. **Warning**. |
|
|
136
165
|
| `test-range-straddles-block` | (opt-in, `--require-anchors`) A full citation's own range into a `.test.`/`.spec.` target (`.ts`, `.js`, `.mjs`) contains a `describe`/`it`/`test` block-head line, at the same or a shallower indent than the range's own first line, on any line other than that first line. **Warning**. |
|
|
166
|
+
| `anchor-required-continuation` | (opt-in, `--require-anchors`) A continuation (any of `` `:N` ``/`` -`M` ``/`` (`N`) ``) or a bound paragraph-bound short-form `:N-M` citation whose governing citation resolves in-repo -- it cannot carry a `#anchor` of its own. **Warning**. |
|
|
137
167
|
|
|
138
168
|
**Anchored citations.** A full citation may carry an anchor directly after its range: `` `path:N-M#anchor` ``, e.g. `` `CHANGELOG.md:50-144#0.24.0` ``. This closes a gap the checks above cannot: a CHANGELOG.md that grows by inserting each new release at the top shifts every later entry's absolute line numbers, so a citation that lands 15 lines off in the *wrong* release section is exactly as green as before the shift -- none of `missing-file`/`inverted-range`/`range-exceeds-file`/`blank-start-line`/`closing-brace-start-line` can tell the difference. An anchor pins the citation to a piece of the target's own structure or content that a pure line-shift does not preserve. Two forms, told apart by the anchor text itself:
|
|
139
169
|
|
|
140
170
|
- **Heading form** (bare/unquoted, e.g. `#0.24.0` or `#[0.24.0]`): the citation's nearest *enclosing* Markdown heading (level 1 or 2 only -- see below) must contain the anchor text, and the range must not run past that heading's own section (no heading of the same or shallower level may start before the range's end line). Capped at heading level 2 deliberately: a Keep-a-Changelog `CHANGELOG.md` nests `## [x.y.z]` release headings around identically-named `### Added`/`### Changed`/`### Fixed` subsections repeated in every release, so matching "the nearest heading of any level" would make this check nearly useless there (it would match the wrong release's own `### Changed` just as readily as the right one's); deeper subsection headings are transparent to the search instead. The anchor text itself may contain word characters, `.`, and `-`, but never starts or ends on a `.` or `-`, so a trailing sentence period or a following `,`/`)` never becomes part of it (e.g. `` `path:7-8#0.24.0.` `` at the end of a sentence captures `0.24.0`, not `0.24.0.`); a hyphenated token like `0.24.0-rc1` is captured whole. A fenced code block inside the *target* (e.g. a `` ```bash `` example containing a `#`-led comment) is excluded from the heading search the same way a citing doc's own fences are already excluded from short-form matching (see below): a comment line inside an example is never mistaken for a real heading, on either end of the enclosure check.
|
|
141
171
|
- **String form** (double-quoted, e.g. `#"reproduction requirement"`): the anchor text must occur, verbatim, on at least one line of the cited range itself -- "occurs inside it" rather than "encloses it", so this form also works against a non-Markdown target (`.ts`/`.js`/...) where "enclosing heading" has no meaning. The quoted text cannot cross a line break or a backtick: an unterminated opening quote (a typo) fails to parse as an anchor at all (the citation is then checked as if no anchor had been written, with no diagnostic) rather than greedily consuming the rest of the document up to some unrelated later quote character, which would otherwise silently hide every citation in between from this rule entirely.
|
|
142
172
|
|
|
143
|
-
Anchors are checked only once the base checks above (blank-start-line, closing-brace-start-line, inverted-range, range-exceeds-file) already came back clean for that citation. Anchors are full-citation-only: a continuation or a short-form citation never carries its own path, so there is nowhere natural to hang one on. The `#anchor` suffix is entirely optional and strictly additive: an existing anchorless citation matches and is checked exactly as it was before this feature existed.
|
|
173
|
+
Anchors are checked only once the base checks above (blank-start-line, closing-brace-start-line, inverted-range, range-exceeds-file) already came back clean for that citation. Anchors are full-citation-only: a continuation or a short-form citation never carries its own path, so there is nowhere natural to hang one on -- under `--require-anchors`, such a continuation or bound short-form is instead flagged `anchor-required-continuation` (see below) rather than silently exempted. The `#anchor` suffix is entirely optional and strictly additive: an existing anchorless citation matches and is checked exactly as it was before this feature existed.
|
|
144
174
|
|
|
145
175
|
**Anchor syntax note.** Any `#` immediately following a citation's range is read as the start of an anchor, with no other signal required -- there is no way to write a bare range immediately followed by an unrelated `#` and have it ignored. This means a Markdown link like `` [note](path:1-2#x) `` or an editor-style fragment such as `path:1-2#L7` is interpreted as an anchored citation (`x` and `L7` respectively), whether that was intended or not. The recommended, unambiguous form is the one used throughout this doc: the whole citation, range and anchor together, inside a single pair of backticks, e.g. `` `path:1-2#L7` ``.
|
|
146
176
|
|
|
@@ -170,15 +200,51 @@ Like `sources-fresh`, this rule requires a repo root (explicit `--repo-root` or
|
|
|
170
200
|
|
|
171
201
|
### Anchor strictness (opt-in, `--require-anchors`)
|
|
172
202
|
|
|
173
|
-
Everything above is on by default. `--require-anchors` opts into
|
|
203
|
+
Everything above is on by default. `--require-anchors` opts into five additional, stricter checks against `citations-resolve`, all `warning`-severity, off unless the flag is passed -- an existing bundle's findings under a plain `check` are byte-for-byte unaffected by this section existing. `--require-anchors` (and its sibling `--require-anchors-allow`) is a CLI flag only; okf-kit has no bundle-level config file today (no `.okf.yml`, no `okf-kit.config`, no frontmatter field), so there is no other way to turn this on. All five checks are also exempt for a citing doc that is a reserved file (`index.md`, `log.md`), the same carve-out `citations-resolve` already gives reserved docs for short-form matching elsewhere in this doc -- a reserved doc's citations still get the unconditional base checks (`missing-file`, `inverted-range`, `anchor-not-found-in-range`, and so on), just none of the five opt-in ones:
|
|
174
204
|
|
|
175
205
|
- **`anchor-required`.** An in-repo full citation (resolved to a real target -- an unresolved one already gets its own `missing-file`/`unresolved-ambiguous` finding, not this one) carrying no `#anchor` at all is flagged. Also exempt (beyond the reserved-doc carve-out above): a citedPath matching one of `--require-anchors-allow`'s patterns (repeatable/space-separated, exact string or a `*`/`?` glob against the citation's raw, as-written path text), for a doc category not meant to be anchor-checked, e.g. `--require-anchors-allow README.md INSTALL-AGENT.md`. A target cited under several different spellings in the same bundle (`init.ts` in one paragraph, `src/init.ts` in another) needs one allow pattern per spelling, or a `*basename` glob that covers all of them -- the match is against the citation's own raw text, not the path it resolves to. `--require-anchors-allow` is commander-variadic: its list stops at the next `-`-prefixed token, so flags placed after it parse normally, but it swallows the positional bundle path if placed before it (`check --require-anchors-allow README.md <bundleDir>` fails with a missing-argument error); pass it after the bundle path.
|
|
176
206
|
- **`anchor-not-on-last-line`.** A string-anchored citation (`` `path:N-M#"text"` ``) whose anchor text was found in the cited range, but not on the range's own last CONTENT line. A content line is any line whose trimmed text is not empty and not composed only of closing brackets/braces/parens plus an optional trailing `,`/`;` (`}`, `});`, `]);`, `}),`, and similar); a range ending on one or more such boilerplate lines (the common shape of a `});` that closes a whole cited block) resolves to the real content line before them instead. An anchor sitting on the range's first line survives a small insertion above the range (the shifted window still contains the anchor's original content, just at a different offset); anchoring on the last content line closes that, since the original last content line falls out of the shifted window on any insertion at all.
|
|
177
207
|
- **`anchor-not-unique-in-range`.** A string-anchored citation whose anchor text occurs on more than one line of the cited range -- ambiguous evidence for which occurrence is the one actually pinning the citation. A count of zero is unaffected (already `anchor-not-found-in-range`, unconditionally).
|
|
178
208
|
- **`test-range-straddles-block`.** A FULL citation's own range into a `.test.`/`.spec.` target (`.ts`, `.js`, `.mjs`) must not run into a sibling or outer `describe`/`it`/`test` block (including `.only`/`.skip`/`.each` variants): a block-head line at the same or a shallower indent than the range's own start line, found on any line of the range OTHER than that start line, means the citation straddled out into a sibling or outer block. A block-head line indented STRICTLY DEEPER than the start line is a nested block (e.g. every `it(` inside a `describe(` the range cites in full) and is not a straddle -- citing a whole block is expected to contain every head line nested inside it. The start line itself is never checked here -- it is either a legitimate block head (the range correctly starts a block) or legitimately inside a block's body (a deliberate partial citation), and both are fine. A range that leaves its block without a later block-head line inside it (ending on an outer block's closing line) is not detected by this line-based check. Deliberately scoped to test-file targets only, and to this same opt-in: a full citation into a Markdown target legitimately cites a couple of arbitrary lines all the time (the same reasoning that keeps the Markdown half of `test-range-start-not-head`/`test-range-end-not-closing` scoped to short-form citations only, see above), and turning this on unconditionally for every existing full citation would silently regress an already-green bundle for every consumer, not just one that opted in.
|
|
209
|
+
- **`anchor-required-continuation`.** A continuation (`` `:N` ``/`` `:N-M` ``, `` -`M` ``/`` –`M` ``, `` (`N`) ``) or a bound paragraph-bound short-form `:N-M` citation whose governing citation (the full citation it is chained to, or the paragraph's last-named full range for a short form) resolves in-repo is flagged: it structurally cannot carry a `#anchor` of its own (see "Anchored citations" above), so it is invisible to every other check in this section, including when its governing full citation is itself already anchored -- an anchor on the full citation does not cover a later continuation of it. Same exemptions as `anchor-required`: a reserved citing doc, and `requireAnchors.allow`, matched against the GOVERNING citation's raw citedPath (a continuation carries no path of its own to match). The remedy is always the same: lift the continuation into its own full `path:N-M#anchor` citation. This closes a gap the other four checks leave open: they all fire only for a "full" citation, so a continuation chained off an ALREADY-anchored full citation was previously invisible to `--require-anchors` -- a line-shift landing it on still non-blank, in-bounds content is exactly the drift class `anchor-required` exists to catch for a full citation, just unreachable through it for a continuation.
|
|
179
210
|
|
|
180
211
|
`--require-anchors` is off by default and, once on, surfaces a real backlog the first time it is run against an existing, unaudited bundle -- the same posture `blank-start-line` already documents for `citations-resolve` itself. `anchor-required` in particular is only useful once a bundle's citations have actually been anchored; running it against a bundle that predates anchoring will flag most of that bundle's full citations.
|
|
181
212
|
|
|
213
|
+
## Prose line references (opt-in, `--prose-line-references`)
|
|
214
|
+
|
|
215
|
+
`citations-resolve` only sees a citation written in its own backtick grammar (`` `path:N` ``). Prose habitually names a line number a different way instead -- "lines 496-498", "generate-codex-config.ts lines 129-132" -- and those shapes are structurally invisible to `citations-resolve`'s `CITATION_RE`, which requires a literal `:` between the path and the digits. A doc can be re-verified, re-stamped, and pass `check` with 0 findings while its prose line numbers are drifted, because nothing ever looked at them. `--prose-line-references` closes that gap. Off by default; every finding below is `prose-line-references`, off unless the flag is passed, so an existing bundle's `check` output is byte-for-byte unaffected by this section existing.
|
|
216
|
+
|
|
217
|
+
**Extraction grammar** (conservative -- deliberately narrower than everything actually seen in the wild): `line N`, `lines N-M` (hyphen, en-dash, or em-dash), and `lines N to M`. Deliberately NOT matched:
|
|
218
|
+
|
|
219
|
+
- `L N` / `L1` -- not observed in the corpus this rule was measured against, and more ambiguous than `line N` (`L1` already means "review finding 1" in this package's own short-form-citation authoring convention, see "Authoring guidance" above).
|
|
220
|
+
- `<file>:N` outside backticks -- already matched by `citations-resolve`'s own `CITATION_RE`, which has no backtick requirement (only its heading-section form does). Matching it again here would double-report the same drift under two rule ids.
|
|
221
|
+
- a comma-separated list of several numbers/ranges after one `lines` keyword (e.g. "lines 178, 234, 265-268"), or a second, unlabelled range chained by "vs"/"and" onto an already-extracted one (e.g. "lines 676-698 vs 564-591" only extracts `676-698`) -- only the first number/range is extracted; the rest are silently under-extracted, never mis-parsed. Under-extraction, never mis-binding, matches this rule's "never guess" posture throughout.
|
|
222
|
+
|
|
223
|
+
A reference inside a fenced code block, an indented code block, an inline code span, or a Markdown table row is excluded from extraction, same as `citations-resolve`'s own short-form matching. So is a reference falling inside a real `citations-resolve` full citation's own span (mostly relevant to a citation's quoted string anchor, e.g. `` `path.md:10-20#"see line 5 above"` ``), a reference inside an HTML comment (e.g. `` <!-- see line 5 above for the earlier draft --> ``, which narrates a past state rather than citing current content), and a reference on a line that is itself a Markdown ATX heading (e.g. `## Line 3 semantics`, which names a section about a line rather than citing it). A URL with a port (`host:8080`), an ISO timestamp, and a version string (`1.2.3`) need no special-casing at all: the grammar above requires the literal word `line`/`lines` immediately before the digits, which none of those three shapes contain. A hyphen-joined compound word ("in-line 999", "multi-line 999", "command-line 999") is also excluded: the leading boundary check rejects a match starting right after a `-`.
|
|
224
|
+
|
|
225
|
+
**Binding rule** (conservative -- never guessed across a paragraph boundary): a reference is bound to the nearest "file mention" -- a path-like token with one of `citations-resolve`'s own recognised extensions (`.ts`, `.js`, `.mjs`, `.md`, `.yml`, `.yaml`, `.json`) that resolves to a real file under the bundle's repo root, using `citations-resolve`'s own path-resolution rules verbatim (frontmatter `sources` match, ancestor climb for a bare filename, repo-root-relative, doc-relative, "last full path mentioned", repo-wide basename search):
|
|
226
|
+
|
|
227
|
+
1. the nearest file mention in the same sentence, preceding first, then following (a rough sentence-boundary heuristic: a `.`/`!`/`?` followed by whitespace and then a capital letter, digit, backtick, quote, or open bracket, or the end of the paragraph);
|
|
228
|
+
2. otherwise the nearest PRECEDING file mention in the same paragraph;
|
|
229
|
+
3. otherwise `unresolvable` -- never guessed, never silently bound to the wrong file.
|
|
230
|
+
|
|
231
|
+
A candidate file-mention token that itself fails to resolve, or resolves to more than one real file (two files sharing a basename), is not skipped in favor of a farther candidate: "nearest wins" is taken literally, so the outcome is `unresolvable`/`ambiguous` rather than quietly falling through to a second-nearest mention the prose did not actually name. `ambiguous` covers only that one case, a single mention whose own basename collides across files; two DISTINCT mentions that each resolve cleanly (e.g. "`` `src/a.ts` `` sets it up; `` `src/b.ts` `` line 5 reads it") are resolved by nearest-wins as usual and are deliberately never reported as `ambiguous` -- the binding rule picked one of them on purpose, it did not fail to pick. A bare backtick-wrapped filename (`` `src/cli.ts` ``) counts as a file mention exactly the way a bare filename in running prose does -- unlike the base extraction grammar above, mention detection deliberately does NOT exclude inline code, since a backtick-wrapped filename is this package's own normal, encouraged way to name a file in prose (see "Authoring guidance" above). `index.md`/`log.md` (reserved citing docs) are skipped entirely, the same carve-out `citations-resolve` already gives them: an append-only narrative journal routinely narrates historical line-number deltas as prose about the past, not live citations against current content.
|
|
232
|
+
|
|
233
|
+
**Findings:**
|
|
234
|
+
|
|
235
|
+
| Reason | Meaning | Severity |
|
|
236
|
+
|--------|---------|----------|
|
|
237
|
+
| `out-of-bounds` | The bound reference's range is inverted, or its start (or end) line exceeds the bound file's line count. | Warning |
|
|
238
|
+
| `blank-start-line` | The bound reference's start line is blank. | Warning |
|
|
239
|
+
| `unresolvable` | No file mention could be bound (no candidate in the sentence or paragraph, the nearest candidate does not resolve to a real file, or the resolved target could not be read). | Notice |
|
|
240
|
+
| `ambiguous` | The nearest file mention's basename resolves to more than one real file; not evaluated. The finding names both (or all) candidates. | Notice |
|
|
241
|
+
|
|
242
|
+
`unresolvable` and `ambiguous` are notice-severity rather than warning: "line" occurs constantly in ordinary English with no adjacent file nearby ("in line with", "product line"), so "no file mention nearby" is weaker evidence of actual drift than a reference that resolved cleanly and then turned out wrong. `out-of-bounds` and `blank-start-line` are warning-severity, mirroring `citations-resolve`'s own "a wrong start/target is strong drift evidence" posture once a reference DOES resolve to a single real file.
|
|
243
|
+
|
|
244
|
+
**Strict mode (`--prose-line-references-strict`, ignored unless `--prose-line-references` is also passed):** flags EVERY extracted reference, resolved or not, with its own warning-severity `prose-line-reference-not-anchored` finding and a fixed remedy: lift it into a backtick `path:N-M` citation (so `citations-resolve` can verify it going forward), or de-precise it to a symbol name instead of a line number. This is additive, not a replacement for the base checks above -- a drifted reference under strict mode gets both its `out-of-bounds`/etc. finding AND the policy finding, since both facts are independently true of it.
|
|
245
|
+
|
|
246
|
+
`--prose-line-references` is off by default and, once on, surfaces a real backlog the first time it is run against an existing, unaudited bundle -- the same posture `--require-anchors` already documents above. Known false-positive category: the binding rule conflates "the file this sentence is about" with "the file these line numbers belong to" when they differ, e.g. "`06-handoff.md`'s `## Accepted Waivers` heading ... (test lines 132-135)" binds `test lines 132-135` to `06-handoff.md` even though the line numbers actually belong to an unnamed test file discussed elsewhere in the same sentence -- a real limitation of a conservative, non-semantic heuristic, not a bug; expect some of this class of noise on a first run against an existing corpus.
|
|
247
|
+
|
|
182
248
|
## Exit codes
|
|
183
249
|
|
|
184
250
|
| Code | Meaning |
|
|
@@ -189,15 +255,23 @@ Everything above is on by default. `--require-anchors` opts into four additional
|
|
|
189
255
|
|
|
190
256
|
## CI usage
|
|
191
257
|
|
|
192
|
-
This is advisory: don't fail the build on warnings unless you pass `--strict`. Use a normal (non-shallow) checkout of the repo that owns the bundle: repo-root detection runs `git rev-parse --show-toplevel` from the `path/to/bundle` argument itself, not from the shell's working directory, and `sources-fresh` reads `git log`,
|
|
258
|
+
This is advisory: don't fail the build on warnings unless you pass `--strict`. Use a normal (non-shallow) checkout of the repo that owns the bundle: repo-root detection runs `git rev-parse --show-toplevel` from the `path/to/bundle` argument itself, not from the shell's working directory, and `sources-fresh` reads `git log`. A shallow clone (`actions/checkout`'s default `fetch-depth: 1`, or any `git clone --depth`) does NOT report paths as untracked -- every path is still tracked at the boundary commit's own commit time. What it DOES cost: `sources-fresh`'s re-stamp check (see "Staleness (sources-fresh)" above) can no longer tell a doc's real root commit from the grafted boundary commit history was cut off at, so a doc whose own last commit lands there gets a `staleness not assessable` notice instead of a real STALE/pass verdict. Pass `fetch-depth: 0` (a full checkout) to get a real verdict there too.
|
|
193
259
|
|
|
194
260
|
```yaml
|
|
261
|
+
- uses: actions/checkout@v5
|
|
262
|
+
with:
|
|
263
|
+
fetch-depth: 0
|
|
195
264
|
- name: OKF bundle check
|
|
196
|
-
run: npx okf-kit@0.
|
|
265
|
+
run: npx okf-kit@0.10.0 check path/to/bundle
|
|
197
266
|
```
|
|
198
267
|
|
|
199
268
|
Pin the version: an unpinned `npx okf-kit` picks up new rules on their release day, which turns an unrelated PR red.
|
|
200
269
|
|
|
270
|
+
Releasing a new okf-kit version to npm must also bump the `npm install -g
|
|
271
|
+
okf-kit@<version>` pins this repo's own `orchestrator-workflow` package
|
|
272
|
+
carries in `.github/workflows/`, in the same release commit; see
|
|
273
|
+
`CONTRIBUTING.md`'s "Releasing okf-kit" section for the order.
|
|
274
|
+
|
|
201
275
|
## Where this fits
|
|
202
276
|
|
|
203
277
|
okf-kit is the producer-side check: it validates a bundle you are authoring or maintaining. Consuming an OKF bundle at query time (loading, indexing, ranking passages for an agent) lives in [codebase-oracle](https://github.com/LanNguyenSi/codebase-oracle), a separate tool.
|
package/dist/bundle.d.ts
CHANGED
|
@@ -1,2 +1,19 @@
|
|
|
1
|
-
import type { BundleContext, RunGit } from "./types.js";
|
|
1
|
+
import type { BundleContext, FrontmatterInfo, RunGit } from "./types.js";
|
|
2
2
|
export declare function loadBundle(bundleDir: string, repoRoot?: string, runGit?: RunGit): BundleContext;
|
|
3
|
+
/**
|
|
4
|
+
* A frontmatter block is the first line being exactly `---` up to the next
|
|
5
|
+
* line that is exactly `---`. Anything else (no opening delimiter, or an
|
|
6
|
+
* opening delimiter with no matching close) counts as no frontmatter block
|
|
7
|
+
* at all, per the OKF v0.1 shape rule.
|
|
8
|
+
*
|
|
9
|
+
* Exported so `sources-fresh` can apply the IDENTICAL parse to a historical
|
|
10
|
+
* blob (`git show <sha>:<path>`) that `loadBundle` applies to the working-tree
|
|
11
|
+
* file: the rule's re-stamp test compares the parsed frontmatter `timestamp`
|
|
12
|
+
* VALUE across a commit boundary, and a second, subtly different parser there
|
|
13
|
+
* would decide freshness by a different notion of "frontmatter" than the rest
|
|
14
|
+
* of the tool.
|
|
15
|
+
*/
|
|
16
|
+
export declare function parseFrontmatter(raw: string): {
|
|
17
|
+
frontmatter: FrontmatterInfo;
|
|
18
|
+
body: string;
|
|
19
|
+
};
|
package/dist/bundle.js
CHANGED
|
@@ -39,8 +39,15 @@ function walkMarkdownFiles(dir) {
|
|
|
39
39
|
* line that is exactly `---`. Anything else (no opening delimiter, or an
|
|
40
40
|
* opening delimiter with no matching close) counts as no frontmatter block
|
|
41
41
|
* at all, per the OKF v0.1 shape rule.
|
|
42
|
+
*
|
|
43
|
+
* Exported so `sources-fresh` can apply the IDENTICAL parse to a historical
|
|
44
|
+
* blob (`git show <sha>:<path>`) that `loadBundle` applies to the working-tree
|
|
45
|
+
* file: the rule's re-stamp test compares the parsed frontmatter `timestamp`
|
|
46
|
+
* VALUE across a commit boundary, and a second, subtly different parser there
|
|
47
|
+
* would decide freshness by a different notion of "frontmatter" than the rest
|
|
48
|
+
* of the tool.
|
|
42
49
|
*/
|
|
43
|
-
function parseFrontmatter(raw) {
|
|
50
|
+
export function parseFrontmatter(raw) {
|
|
44
51
|
const lines = raw.split(/\r?\n/);
|
|
45
52
|
if (lines[0] !== "---") {
|
|
46
53
|
return { frontmatter: { present: false }, body: raw };
|
package/dist/bundle.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bundle.js","sourceRoot":"","sources":["../src/bundle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,IAAI,MAAM,MAAM,CAAC;AAQxB,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC;AAE3D,MAAM,UAAU,UAAU,CACxB,SAAiB,EACjB,QAAiB,EACjB,MAAe;IAEf,MAAM,KAAK,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC3C,MAAM,IAAI,GAAgB,KAAK,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;QAC9C,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC5E,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACxC,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC7C,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACpD,OAAO;YACL,OAAO;YACP,QAAQ;YACR,UAAU,EAAE,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC;YAC5C,GAAG;YACH,WAAW;YACX,IAAI;SACL,CAAC;IACJ,CAAC,CAAC,CAAC;IACH,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IACxD,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AAC/C,CAAC;AAED,SAAS,iBAAiB,CAAC,GAAW;IACpC,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,MAAM,KAAK,IAAI,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QACjE,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACxB,GAAG,CAAC,IAAI,CAAC,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC;QACvC,CAAC;aAAM,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YACxD,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjB,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"bundle.js","sourceRoot":"","sources":["../src/bundle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,IAAI,MAAM,MAAM,CAAC;AAQxB,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC;AAE3D,MAAM,UAAU,UAAU,CACxB,SAAiB,EACjB,QAAiB,EACjB,MAAe;IAEf,MAAM,KAAK,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC3C,MAAM,IAAI,GAAgB,KAAK,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;QAC9C,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC5E,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACxC,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC7C,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACpD,OAAO;YACL,OAAO;YACP,QAAQ;YACR,UAAU,EAAE,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC;YAC5C,GAAG;YACH,WAAW;YACX,IAAI;SACL,CAAC;IACJ,CAAC,CAAC,CAAC;IACH,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IACxD,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AAC/C,CAAC;AAED,SAAS,iBAAiB,CAAC,GAAW;IACpC,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,MAAM,KAAK,IAAI,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QACjE,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACxB,GAAG,CAAC,IAAI,CAAC,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC;QACvC,CAAC;aAAM,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YACxD,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjB,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAI1C,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACjC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,KAAK,EAAE,CAAC;QACvB,OAAO,EAAE,WAAW,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;IACxD,CAAC;IACD,IAAI,YAAY,GAAG,CAAC,CAAC,CAAC;IACtB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,KAAK,EAAE,CAAC;YACvB,YAAY,GAAG,CAAC,CAAC;YACjB,MAAM;QACR,CAAC;IACH,CAAC;IACD,IAAI,YAAY,KAAK,CAAC,CAAC,EAAE,CAAC;QACxB,OAAO,EAAE,WAAW,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;IACxD,CAAC;IACD,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzD,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,YAAY,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACpC,OAAO,EAAE,WAAW,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC;IAC1D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,UAAU,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACpE,OAAO,EAAE,WAAW,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,CAAC;IAC9D,CAAC;AACH,CAAC"}
|
package/dist/cli.d.ts
CHANGED
|
@@ -19,6 +19,27 @@ export interface CheckOptions {
|
|
|
19
19
|
* Ignored when `requireAnchors` is not set.
|
|
20
20
|
*/
|
|
21
21
|
requireAnchorsAllow?: string[];
|
|
22
|
+
/**
|
|
23
|
+
* Opt-in for `prose-line-references`: flag a prose-embedded line
|
|
24
|
+
* reference outside `citations-resolve`'s own backtick grammar (e.g.
|
|
25
|
+
* "lines 129-132") that is drifted, unresolvable, or ambiguous. See the
|
|
26
|
+
* doc block in `src/rules/prose-line-references.ts`.
|
|
27
|
+
*/
|
|
28
|
+
proseLineReferences?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Also flag EVERY prose line reference `prose-line-references` extracts,
|
|
31
|
+
* not only a drifted one, with the remedy to lift it into a backtick
|
|
32
|
+
* citation or a symbol name. Ignored when `proseLineReferences` is not
|
|
33
|
+
* set.
|
|
34
|
+
*/
|
|
35
|
+
proseLineReferencesStrict?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* `sources-fresh-future`'s clock-skew allowance, in minutes (default 10
|
|
38
|
+
* when omitted; see `DEFAULT_FUTURE_SKEW_SECONDS` in
|
|
39
|
+
* `src/rules/sources-fresh.ts`). Converted to seconds and stored on
|
|
40
|
+
* `ctx.freshnessFutureSkewSeconds`.
|
|
41
|
+
*/
|
|
42
|
+
futureSkewMinutes?: number;
|
|
22
43
|
/** Test-only override for git access; production code shells out to the real `git` binary. */
|
|
23
44
|
runGit?: RunGit;
|
|
24
45
|
}
|