okf-kit 0.9.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 CHANGED
@@ -7,6 +7,152 @@ 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
+
10
156
  ## [0.9.0] - 2026-09-01
11
157
 
12
158
  ### 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,7 +72,8 @@ 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. |
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. |
73
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. |
74
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. |
75
79
 
@@ -83,12 +87,15 @@ Pass `--repo-root` explicitly to pin a specific root (useful in CI when the bund
83
87
 
84
88
  ## Staleness (sources-fresh)
85
89
 
86
- `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:
87
91
 
88
92
  | Situation | Severity | Message |
89
93
  |-----------|----------|---------|
90
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>` |
91
- | A source path's last commit is newer than the doc's `timestamp` but at/before the doc file's last commit | (nothing) | fresh: doc and source landed together (or the doc was committed later) |
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) |
92
99
  | A source path exists but has no git history (untracked) | notice | `untracked by git, staleness unknown: <path>` |
93
100
  | The doc's `timestamp` is missing or not a parseable date, while `sources` is present | notice | `staleness not assessable: no valid timestamp` |
94
101
  | No repo root available (see auto-detection above) | notice | `staleness skipped: not inside a git work tree` |
@@ -98,10 +105,31 @@ STALE findings are warnings, so they are advisory by default; run with `--strict
98
105
 
99
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".
100
107
 
101
- 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 any commit touching the doc (a typo fix, a repo-wide formatter run, a rename, which resets the doc's last-commit time because `git log` runs without `--follow`) silences drift on all sources changed before it, even ones nobody re-verified. The frontmatter `timestamp` still governs sources changed after the doc's last commit.
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.
102
109
 
103
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.
104
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
+
105
133
  ## Citation resolution (citations-resolve)
106
134
 
107
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.
@@ -227,15 +255,23 @@ A candidate file-mention token that itself fails to resolve, or resolves to more
227
255
 
228
256
  ## CI usage
229
257
 
230
- 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`, so a shallow clone reports paths as untracked.
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.
231
259
 
232
260
  ```yaml
261
+ - uses: actions/checkout@v5
262
+ with:
263
+ fetch-depth: 0
233
264
  - name: OKF bundle check
234
- run: npx okf-kit@0.9.0 check path/to/bundle
265
+ run: npx okf-kit@0.10.0 check path/to/bundle
235
266
  ```
236
267
 
237
268
  Pin the version: an unpinned `npx okf-kit` picks up new rules on their release day, which turns an unrelated PR red.
238
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
+
239
275
  ## Where this fits
240
276
 
241
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 };
@@ -1 +1 @@
1
- {"version":3,"file":"bundle.js","sourceRoot":"","sources":["../src/bundle.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"bundle.js","sourceRoot":"","sources":["../src/bundle.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts CHANGED
@@ -33,6 +33,13 @@ export interface CheckOptions {
33
33
  * set.
34
34
  */
35
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;
36
43
  /** Test-only override for git access; production code shells out to the real `git` binary. */
37
44
  runGit?: RunGit;
38
45
  }
package/dist/cli.js CHANGED
@@ -33,6 +33,9 @@ export function runCheck(bundleDir, options = {}) {
33
33
  strict: Boolean(options.proseLineReferencesStrict),
34
34
  };
35
35
  }
36
+ if (options.futureSkewMinutes !== undefined) {
37
+ ctx.freshnessFutureSkewSeconds = Math.round(options.futureSkewMinutes * 60);
38
+ }
36
39
  const findings = allRules.flatMap((rule) => rule.run(ctx));
37
40
  const summary = summarize(findings);
38
41
  const exitCode = summary.errors > 0 || (Boolean(options.strict) && summary.warnings > 0)
@@ -67,9 +70,23 @@ program
67
70
  'reference outside citations-resolve\'s own backtick grammar, e.g. "lines 129-132" (opt-in, see README)')
68
71
  .option("--prose-line-references-strict", "prose-line-references: also flag every prose line reference, not only a drifted one, with " +
69
72
  "the remedy to lift it into a backtick citation or a symbol name (ignored unless --prose-line-references is also passed)")
73
+ .option("--future-skew-minutes <n>", "sources-fresh-future: clock-skew allowance in minutes before a doc `timestamp` later than " +
74
+ "the doc's own last commit is flagged as future-dated (default 10)")
70
75
  .exitOverride()
71
76
  .action((bundleDir, opts) => {
72
77
  try {
78
+ let futureSkewMinutes;
79
+ if (opts.futureSkewMinutes !== undefined) {
80
+ const trimmed = opts.futureSkewMinutes.trim();
81
+ // `Number("")` and `Number(" ")` both resolve to 0, which would
82
+ // otherwise silently accept an empty/whitespace-only value as
83
+ // "0 minutes" instead of rejecting it as the usage error it is.
84
+ const n = trimmed === "" ? Number.NaN : Number(trimmed);
85
+ if (!Number.isFinite(n) || n < 0) {
86
+ throw new UsageError(`--future-skew-minutes must be a non-negative number, got \`${opts.futureSkewMinutes}\``);
87
+ }
88
+ futureSkewMinutes = n;
89
+ }
73
90
  const result = runCheck(bundleDir, {
74
91
  repoRoot: opts.repoRoot,
75
92
  strict: opts.strict,
@@ -77,6 +94,7 @@ program
77
94
  requireAnchorsAllow: opts.requireAnchorsAllow,
78
95
  proseLineReferences: opts.proseLineReferences,
79
96
  proseLineReferencesStrict: opts.proseLineReferencesStrict,
97
+ futureSkewMinutes,
80
98
  });
81
99
  const output = opts.json
82
100
  ? renderJson(result.bundleDir, result.findings)
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
package/dist/git.d.ts CHANGED
@@ -3,8 +3,8 @@ import type { RunGit } from "./types.js";
3
3
  * Default RunGit implementation: shells out to the real `git` binary.
4
4
  * stderr is discarded (git's own "fatal: not a git repository" etc. text is
5
5
  * an expected, silent signal here, not something to surface), and any
6
- * failure (non-zero exit, git missing) resolves to null instead of
7
- * throwing.
6
+ * failure (non-zero exit, git missing, output past MAX_GIT_OUTPUT_BYTES)
7
+ * resolves to null instead of throwing.
8
8
  */
9
9
  export declare const runGit: RunGit;
10
10
  /**
package/dist/git.js CHANGED
@@ -1,10 +1,23 @@
1
1
  import { execFileSync } from "node:child_process";
2
+ /**
3
+ * Output cap (bytes) for a single git invocation. Node's own default for
4
+ * `execFileSync` is 1 MiB, and exceeding it does not throw a distinguishable
5
+ * error here -- it lands in the catch below and resolves to null, i.e. the
6
+ * caller sees "git failed" for a perfectly healthy repository. `sources-fresh`
7
+ * reads whole doc blobs (`git show <sha>:<path>`) to compare frontmatter
8
+ * timestamps, and an OKF doc larger than 1 MiB is unusual but entirely legal,
9
+ * so the cap is raised well past any plausible doc size. It is deliberately
10
+ * still a cap and not `Infinity`: a runaway git invocation should fail loudly
11
+ * (as null, which every caller treats as "not assessable") rather than grow
12
+ * the process heap without bound.
13
+ */
14
+ const MAX_GIT_OUTPUT_BYTES = 16 * 1024 * 1024;
2
15
  /**
3
16
  * Default RunGit implementation: shells out to the real `git` binary.
4
17
  * stderr is discarded (git's own "fatal: not a git repository" etc. text is
5
18
  * an expected, silent signal here, not something to surface), and any
6
- * failure (non-zero exit, git missing) resolves to null instead of
7
- * throwing.
19
+ * failure (non-zero exit, git missing, output past MAX_GIT_OUTPUT_BYTES)
20
+ * resolves to null instead of throwing.
8
21
  */
9
22
  export const runGit = (args, cwd) => {
10
23
  try {
@@ -12,6 +25,7 @@ export const runGit = (args, cwd) => {
12
25
  cwd,
13
26
  encoding: "utf8",
14
27
  stdio: ["ignore", "pipe", "ignore"],
28
+ maxBuffer: MAX_GIT_OUTPUT_BYTES,
15
29
  }).trim();
16
30
  }
17
31
  catch {
package/dist/git.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"git.js","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGlD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,MAAM,GAAW,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;IAC1C,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,KAAK,EAAE,IAAI,EAAE;YAC/B,GAAG;YACH,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC;SACpC,CAAC,CAAC,IAAI,EAAE,CAAC;IACZ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAC5B,QAAgB,EAChB,MAAc,MAAM;IAEpB,MAAM,MAAM,GAAG,GAAG,CAAC,CAAC,WAAW,EAAE,iBAAiB,CAAC,EAAE,QAAQ,CAAC,CAAC;IAC/D,OAAO,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;AACrC,CAAC"}
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGlD;;;;;;;;;;;GAWG;AACH,MAAM,oBAAoB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAE9C;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,MAAM,GAAW,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;IAC1C,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,KAAK,EAAE,IAAI,EAAE;YAC/B,GAAG;YACH,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC;YACnC,SAAS,EAAE,oBAAoB;SAChC,CAAC,CAAC,IAAI,EAAE,CAAC;IACZ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAC5B,QAAgB,EAChB,MAAc,MAAM;IAEpB,MAAM,MAAM,GAAG,GAAG,CAAC,CAAC,WAAW,EAAE,iBAAiB,CAAC,EAAE,QAAQ,CAAC,CAAC;IAC/D,OAAO,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;AACrC,CAAC"}
@@ -4,8 +4,8 @@ import { reservedFilesBareRule } from "./reserved-files-bare.js";
4
4
  import { linksResolveRule } from "./links-resolve.js";
5
5
  import { noAbsoluteLinksRule } from "./no-absolute-links.js";
6
6
  import { sourcesShapeRule } from "./sources-shape.js";
7
- import { sourcesFreshRule } from "./sources-fresh.js";
7
+ import { sourcesFreshRule, sourcesFreshFutureRule } from "./sources-fresh.js";
8
8
  import { citationsResolveRule } from "./citations-resolve.js";
9
9
  import { proseLineReferencesRule } from "./prose-line-references.js";
10
10
  export declare const allRules: Rule[];
11
- export { frontmatterRequiredRule, reservedFilesBareRule, linksResolveRule, noAbsoluteLinksRule, sourcesShapeRule, sourcesFreshRule, citationsResolveRule, proseLineReferencesRule, };
11
+ export { frontmatterRequiredRule, reservedFilesBareRule, linksResolveRule, noAbsoluteLinksRule, sourcesShapeRule, sourcesFreshRule, sourcesFreshFutureRule, citationsResolveRule, proseLineReferencesRule, };
@@ -3,7 +3,7 @@ import { reservedFilesBareRule } from "./reserved-files-bare.js";
3
3
  import { linksResolveRule } from "./links-resolve.js";
4
4
  import { noAbsoluteLinksRule } from "./no-absolute-links.js";
5
5
  import { sourcesShapeRule } from "./sources-shape.js";
6
- import { sourcesFreshRule } from "./sources-fresh.js";
6
+ import { sourcesFreshRule, sourcesFreshFutureRule } from "./sources-fresh.js";
7
7
  import { citationsResolveRule } from "./citations-resolve.js";
8
8
  import { proseLineReferencesRule } from "./prose-line-references.js";
9
9
  // proseLineReferencesRule is always registered here, same as
@@ -11,6 +11,11 @@ import { proseLineReferencesRule } from "./prose-line-references.js";
11
11
  // ctx.proseLineReferences is set (see src/rules/prose-line-references.ts),
12
12
  // so a consumer that never passes --prose-line-references sees
13
13
  // byte-identical `check` output to before this rule existed.
14
+ //
15
+ // sourcesFreshFutureRule, unlike those two, is always active (no opt-in
16
+ // flag gates it, only `--future-skew-minutes` tunes its threshold): it
17
+ // assesses the same doc population as sourcesFreshRule, just the opposite
18
+ // time direction, so it is always registered alongside it.
14
19
  export const allRules = [
15
20
  frontmatterRequiredRule,
16
21
  reservedFilesBareRule,
@@ -18,8 +23,9 @@ export const allRules = [
18
23
  noAbsoluteLinksRule,
19
24
  sourcesShapeRule,
20
25
  sourcesFreshRule,
26
+ sourcesFreshFutureRule,
21
27
  citationsResolveRule,
22
28
  proseLineReferencesRule,
23
29
  ];
24
- export { frontmatterRequiredRule, reservedFilesBareRule, linksResolveRule, noAbsoluteLinksRule, sourcesShapeRule, sourcesFreshRule, citationsResolveRule, proseLineReferencesRule, };
30
+ export { frontmatterRequiredRule, reservedFilesBareRule, linksResolveRule, noAbsoluteLinksRule, sourcesShapeRule, sourcesFreshRule, sourcesFreshFutureRule, citationsResolveRule, proseLineReferencesRule, };
25
31
  //# 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;AACtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAErE,6DAA6D;AAC7D,sEAAsE;AACtE,2EAA2E;AAC3E,+DAA+D;AAC/D,6DAA6D;AAC7D,MAAM,CAAC,MAAM,QAAQ,GAAW;IAC9B,uBAAuB;IACvB,qBAAqB;IACrB,gBAAgB;IAChB,mBAAmB;IACnB,gBAAgB;IAChB,gBAAgB;IAChB,oBAAoB;IACpB,uBAAuB;CACxB,CAAC;AAEF,OAAO,EACL,uBAAuB,EACvB,qBAAqB,EACrB,gBAAgB,EAChB,mBAAmB,EACnB,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,EACpB,uBAAuB,GACxB,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,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAC9E,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAErE,6DAA6D;AAC7D,sEAAsE;AACtE,2EAA2E;AAC3E,+DAA+D;AAC/D,6DAA6D;AAC7D,EAAE;AACF,wEAAwE;AACxE,uEAAuE;AACvE,0EAA0E;AAC1E,2DAA2D;AAC3D,MAAM,CAAC,MAAM,QAAQ,GAAW;IAC9B,uBAAuB;IACvB,qBAAqB;IACrB,gBAAgB;IAChB,mBAAmB;IACnB,gBAAgB;IAChB,gBAAgB;IAChB,sBAAsB;IACtB,oBAAoB;IACpB,uBAAuB;CACxB,CAAC;AAEF,OAAO,EACL,uBAAuB,EACvB,qBAAqB,EACrB,gBAAgB,EAChB,mBAAmB,EACnB,gBAAgB,EAChB,gBAAgB,EAChB,sBAAsB,EACtB,oBAAoB,EACpB,uBAAuB,GACxB,CAAC"}
@@ -1,2 +1,37 @@
1
1
  import type { Rule } from "../types.js";
2
+ /**
3
+ * Default clock-skew allowance (seconds) for `sources-fresh-future`: a doc
4
+ * timestamp up to this far after the doc's own last commit is still treated
5
+ * as fresh, absorbing the ordinary gap between "author wrote the timestamp"
6
+ * and "the commit that carries it landed". Override via
7
+ * `ctx.freshnessFutureSkewSeconds` (CLI: `--future-skew-minutes`).
8
+ */
9
+ export declare const DEFAULT_FUTURE_SKEW_SECONDS = 600;
2
10
  export declare const sourcesFreshRule: Rule;
11
+ /**
12
+ * Complements `sources-fresh`'s "too old" check with the opposite direction:
13
+ * a doc `timestamp` that is later than the doc file's OWN last commit (past
14
+ * a small clock-skew allowance) is almost always a mistake, not a real
15
+ * future date -- typically a local wall-clock time hand-written with a
16
+ * trailing `Z`/UTC suffix it does not actually have. Unlike `sources-fresh`,
17
+ * this check never looks at `sources` commit times at all: it only compares
18
+ * the doc's own `timestamp` against the doc file's own git history, so it
19
+ * has nothing to say about whether any source is stale.
20
+ *
21
+ * Deliberately assessed for the SAME population as `sources-fresh` (docs
22
+ * with a validly-shaped `sources` list and a repo root available): a
23
+ * `timestamp` only has "last verified against sources" semantics for a doc
24
+ * that declares `sources` (see the package README's authoring guidance), so
25
+ * a sourceless doc is out of scope for both freshness rules, not just this
26
+ * one. It shares `sources-fresh`'s "staleness unknown" posture for the two
27
+ * cases that make a real answer impossible: no repo root (silently defers
28
+ * to the single bundle-level notice `sources-fresh` already emits above,
29
+ * rather than duplicating it) and no valid `timestamp` (`sources-fresh`
30
+ * already reports that per-doc notice, so this rule silently skips such a
31
+ * doc rather than reporting it twice). An uncommitted doc (no own commit
32
+ * yet) is likewise "unknown, not flagged": there is no real commit time to
33
+ * compare the timestamp against, and flagging every hand-authored,
34
+ * not-yet-committed doc as "future-dated" would be a false positive on
35
+ * every fresh draft.
36
+ */
37
+ export declare const sourcesFreshFutureRule: Rule;