@nielspeter/eess-md 0.1.2 → 0.3.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.
@@ -25,12 +25,31 @@ export interface LinkResolveOptions {
25
25
  * resolution is still tried first, so plain repo-absolute links keep working.
26
26
  */
27
27
  readonly rootDir?: string;
28
+ /**
29
+ * Resolve a link naming a real directory (`./fixed/` or `./fixed`, with or
30
+ * without the trailing slash — both forms occur in this repo's own corpus)
31
+ * even when it has no index file. Correct for a repo-hosted corpus (GitHub,
32
+ * GitLab render a directory link as its listing); wrong for a static-site
33
+ * corpus where a bare directory is not itself a page — there, reach for
34
+ * {@link tryIndex} instead. Off by default: this widens what "resolves"
35
+ * means, and a link resolving that shouldn't is a false green, not a
36
+ * convenience. Default `false`.
37
+ *
38
+ * A link naming a real directory always wins over an unrelated typo — if
39
+ * `./foo` was meant as a deleted `foo.md` but a directory `foo/` happens to
40
+ * exist at that path, it resolves as the directory. Not a new failure mode:
41
+ * GitHub renders that link as the directory listing too, so this only
42
+ * closes the gap between the gate and what the link actually does when
43
+ * followed, it doesn't introduce a new way to be surprised by it.
44
+ */
45
+ readonly resolveDirectories?: boolean;
28
46
  }
29
47
  /**
30
48
  * Condition: every (internal) link resolves to a file in the corpus's repo tree.
31
49
  * Closes over the `Corpus` for the file index. External links and pure anchors
32
50
  * are skipped. `options` adds extensionless-link resolution (`tryExtensions`,
33
- * `tryIndex`) for static-site corpora.
51
+ * `tryIndex`) for static-site corpora, and directory resolution
52
+ * (`resolveDirectories`) for repo-hosted ones.
34
53
  */
35
54
  export declare function linkResolves(corpus: Corpus, options?: LinkResolveOptions): Condition<MdLink>;
36
55
  //# sourceMappingURL=resolve.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/conditions/resolve.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAW,MAAM,kBAAkB,CAAA;AAC1D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAG/C;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC1C;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAC1B;AAmCD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,kBAAuB,GAAG,SAAS,CAAC,MAAM,CAAC,CAgChG"}
1
+ {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/conditions/resolve.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAW,MAAM,kBAAkB,CAAA;AAC1D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAG/C;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC1C;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IACzB;;;;;;;;;;;;;;;;OAgBG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAA;CACtC;AA8DD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,kBAAuB,GAAG,SAAS,CAAC,MAAM,CAAC,CAyEhG"}
@@ -32,11 +32,38 @@ function candidates(target, options) {
32
32
  }
33
33
  return out;
34
34
  }
35
+ /**
36
+ * Every known directory reachable from `fileIndex`'s files, derived from it
37
+ * rather than new I/O: each indexed file's ancestors are known directories.
38
+ * `fileIndex` itself spans the whole physical repo walk from `cwd`, not just
39
+ * the corpus's configured `roots` (`roots` only filters which files become
40
+ * *documents* — see `corpus()`), so a directory link can resolve against
41
+ * something outside any configured root. Built only when `resolveDirectories`
42
+ * is on — the cost of a directory-shaped link resolving is opt-in, so is the
43
+ * cost of computing it.
44
+ *
45
+ * The repo root itself (`.`) is deliberately excluded — `[root](/)` still
46
+ * won't resolve even with the option on. Nobody links "the whole repo," and
47
+ * `resolveTargets` never had a resolvable shape for it before this option
48
+ * existed either.
49
+ */
50
+ function directoryIndex(corpus) {
51
+ const dirs = new Set();
52
+ for (const rel of corpus.fileIndex) {
53
+ let dir = posix.dirname(rel);
54
+ while (dir !== '.' && dir !== '/' && !dirs.has(dir)) {
55
+ dirs.add(dir);
56
+ dir = posix.dirname(dir);
57
+ }
58
+ }
59
+ return dirs;
60
+ }
35
61
  /**
36
62
  * Condition: every (internal) link resolves to a file in the corpus's repo tree.
37
63
  * Closes over the `Corpus` for the file index. External links and pure anchors
38
64
  * are skipped. `options` adds extensionless-link resolution (`tryExtensions`,
39
- * `tryIndex`) for static-site corpora.
65
+ * `tryIndex`) for static-site corpora, and directory resolution
66
+ * (`resolveDirectories`) for repo-hosted ones.
40
67
  */
41
68
  export function linkResolves(corpus, options = {}) {
42
69
  // Basename → repo files, for finding a uniquely-moved target of a broken link.
@@ -49,6 +76,33 @@ export function linkResolves(corpus, options = {}) {
49
76
  else
50
77
  byBasename.set(base, [rel]);
51
78
  }
79
+ const dirIndex = options.resolveDirectories === true ? directoryIndex(corpus) : undefined;
80
+ // Bug 0137: a link that names a real directory and one that names nothing
81
+ // at all both reported the identical "does not resolve" message — no hint
82
+ // that `resolveDirectories` exists, or that this specific target would
83
+ // resolve with it on. Built lazily (at most once, memoized) rather than
84
+ // unconditionally alongside `dirIndex`, so the happy path (no violations)
85
+ // never pays for it, and only when `resolveDirectories` is already off —
86
+ // when it's on, a real directory already resolved above and can't reach
87
+ // this branch at all.
88
+ //
89
+ // A site-absolute link with `rootDir` set yields two candidates
90
+ // (repo-root, then content-root — see `resolveTargets`), and either can
91
+ // independently be a real directory. Reporting whichever comes first
92
+ // unlabelled can name the *wrong* one: a repo-root directory that happens
93
+ // to share a name with the intended content-root page sends the author to
94
+ // toggle `resolveDirectories`, which would resolve the unrelated
95
+ // repo-root directory, not fix their actual (still-missing) page. Labelled
96
+ // only when there are two candidates to disambiguate between — the common,
97
+ // single-target case (no `rootDir`) is unaffected.
98
+ let diagnosticDirIndex;
99
+ const directoryHint = (target, label) => {
100
+ diagnosticDirIndex ??= directoryIndex(corpus);
101
+ const bare = target.replace(/\/+$/, '');
102
+ return diagnosticDirIndex.has(bare)
103
+ ? ` — "${bare}"${label} is a real directory; this check runs with resolveDirectories off`
104
+ : '';
105
+ };
52
106
  return {
53
107
  description: 'resolve to an existing file',
54
108
  evaluate: (links, ctx) => links.flatMap((link) => {
@@ -59,12 +113,19 @@ export function linkResolves(corpus, options = {}) {
59
113
  return [];
60
114
  if (targets.some((t) => candidates(t, options).some((c) => corpus.fileIndex.has(c))))
61
115
  return [];
116
+ if (dirIndex !== undefined && targets.some((t) => dirIndex.has(t.replace(/\/+$/, ''))))
117
+ return [];
118
+ const hint = options.resolveDirectories === true
119
+ ? ''
120
+ : (targets
121
+ .map((t, i) => directoryHint(t, targets.length > 1 ? (i === 0 ? ' (repo-root)' : ' (content-root)') : ''))
122
+ .find((h) => h) ?? '');
62
123
  return [
63
124
  mdViolation({
64
125
  element: `${link.doc.relPath} → ${link.url}`,
65
126
  file: link.doc.file,
66
127
  line: link.line,
67
- message: `broken link: "${link.url}" does not resolve to a file in the repo`,
128
+ message: `broken link: "${link.url}" does not resolve to a file in the repo${hint}`,
68
129
  sourceText: link.doc.text,
69
130
  fix: movedLinkFix(link, byBasename),
70
131
  context: ctx,
@@ -1 +1 @@
1
- {"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/conditions/resolve.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/conditions/resolve.ts"],"names":[],"mappings":"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"}
@@ -34,6 +34,19 @@ export interface HonestyAtCloseOptions extends PresetReportOptions {
34
34
  * check is disabled. Default `false`.
35
35
  */
36
36
  readonly closeInPlace?: boolean;
37
+ /**
38
+ * The full `State:` vocabulary this corpus uses. A token outside it is
39
+ * **reported** (`ledger/unknown-state`), not ignored. Default: the plan lane's
40
+ * enum. A bug-shaped lane passes its own, e.g.
41
+ * `['Draft', 'Ready', 'Fixed', 'Rejected', 'Parked']`.
42
+ */
43
+ readonly states?: readonly string[];
44
+ /**
45
+ * Which of {@link states} mean *closed*. Default `['Done', "Won't-do"]`; a
46
+ * bug lane passes `['Fixed', 'Rejected']`. Members are treated as known states
47
+ * whether or not they also appear in {@link states}.
48
+ */
49
+ readonly terminalStates?: readonly string[];
37
50
  }
38
51
  /**
39
52
  * Run the honesty-at-close gate over a corpus. Throws `ArchRuleError` on any
@@ -41,4 +54,29 @@ export interface HonestyAtCloseOptions extends PresetReportOptions {
41
54
  * done-items (the inverse of the frozen-folder exemption).
42
55
  */
43
56
  export declare function honestyAtClose(corpus: Corpus, options?: HonestyAtCloseOptions): ArchViolation[];
57
+ /** What a {@link honestyAtClose} run actually examined. */
58
+ export interface LedgerStats {
59
+ /** Documents considered (board files excluded). */
60
+ readonly scanned: number;
61
+ /** …of which carry a `State:` line the declared vocabulary can read. */
62
+ readonly withReadableState: number;
63
+ /** …of which carry a `State:` line whose value is not in the vocabulary. */
64
+ readonly unreadableState: number;
65
+ /** …of which are *done* — by folder or by terminal state — and so ledger-checked. */
66
+ readonly doneItems: number;
67
+ }
68
+ /**
69
+ * The denominator, computed by the preset itself.
70
+ *
71
+ * `withReadableState` is the number nobody could see when bug 0119 happened: the
72
+ * placement check had never examined a document, and every summary line in the
73
+ * repo reported a healthy done-count derived purely from folder membership. A
74
+ * caller that prints this cannot be blind and green at the same time — if the
75
+ * region assumption drifts from the corpus again, this drops and says so.
76
+ *
77
+ * Callers must not re-derive it. `check-ledger.mjs` did, with a copy of the very
78
+ * expression 0119 removed, and its copy disagreed with the preset on 56 of 56
79
+ * records while heading a section captioned "so a green is provably non-vacuous".
80
+ */
81
+ export declare function ledgerStats(corpus: Corpus, options?: HonestyAtCloseOptions): LedgerStats;
44
82
  //# sourceMappingURL=ledger.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ledger.d.ts","sourceRoot":"","sources":["../../src/rules/ledger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AACzE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAE1C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AAG1D;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,qBAAsB,SAAQ,mBAAmB;IAChE;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACxC,sEAAsE;IACtE,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACvC;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAA;CAChC;AA6ID;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,qBAA0B,GAClC,aAAa,EAAE,CAkBjB"}
1
+ {"version":3,"file":"ledger.d.ts","sourceRoot":"","sources":["../../src/rules/ledger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AACzE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAE1C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AAG1D;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,qBAAsB,SAAQ,mBAAmB;IAChE;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACxC,sEAAsE;IACtE,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACvC;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAA;IAC/B;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACnC;;;;OAIG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAC5C;AAgPD;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,qBAA0B,GAClC,aAAa,EAAE,CAoBjB;AAED,2DAA2D;AAC3D,MAAM,WAAW,WAAW;IAC1B,mDAAmD;IACnD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,wEAAwE;IACxE,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAA;IAClC,4EAA4E;IAC5E,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;IAChC,qFAAqF;IACrF,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,qBAA0B,GAAG,WAAW,CAqB5F"}
@@ -2,11 +2,59 @@ import { finishPreset } from '@nielspeter/eess';
2
2
  import { collectTaskItems } from '../model/task-items.js';
3
3
  const DEFAULT_DONE_FOLDERS = ['/completed/', '/fixed/', '/wont-do/', '/delivered/', '/archived/'];
4
4
  const DEFAULT_BOARD_FILES = ['ROADMAP.md', 'BUGS.md', 'REFINEMENT.md', 'SUPPORT.md', 'README.md'];
5
- // A terminal `State:` token in the header — `**State:** Done` / `Won't-do`.
6
- const DONE_STATE_RE = /^\s*(?:[-*]\s+)?(?:\*\*)?State:?(?:\*\*)?\s*(Done|Won't-do)\b/im;
7
- // The full neutral State enum on one line (for state↔folder coherence).
8
- const STATE_TOKEN_LINE_RE = /^\s*(?:[-*]\s+)?(?:\*\*)?State:?(?:\*\*)?\s*(Draft|Ready|Open|Done|Won't-do)\b/i;
9
- const TERMINAL_STATES = new Set(['Done', "Won't-do"]);
5
+ const DEFAULT_STATES = ['Draft', 'Ready', 'Open', 'Done', "Won't-do"];
6
+ const DEFAULT_TERMINAL_STATES = ['Done', "Won't-do"];
7
+ // The `State:` label. A colon is **required** in every form: making it optional
8
+ // turned any line beginning with the word "State" into a state declaration, so
9
+ // `Stateless rendering is the default` reported its state as `less` and
10
+ // `State machine transitions are documented` reported `machine`. Both were
11
+ // silent before and would have been build failures.
12
+ const LABEL = String.raw `^\s*(?:[-*+]\s+)?(?:\*\*State:\*\*|\*\*State\*\*\s*:|__State__\s*:|State\s*:)\s*`;
13
+ // An emphasis wrapper or a leading symbol on the value — `**Done**`, `` `Done` ``,
14
+ // `✅ Done`. Markdown, not vocabulary.
15
+ const WRAP = String.raw `(?:\*\*|__|\x60|_)?(?:[^\w\s'’-]+\s*)?`;
16
+ const escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
17
+ /**
18
+ * A matcher built **from the declared vocabulary**, not a blind token grab.
19
+ *
20
+ * Capturing `(\S+)` and comparing it to the enum looked equivalent and was not:
21
+ * it regressed shapes the old enum regex read correctly — `**State: Done**`
22
+ * captured `Done**`, `- **State:** Done.` captured `Done.` — and told the author
23
+ * their corpus does not declare a state it plainly declares. It also made a
24
+ * multi-word state (`In progress`) unrepresentable, so `states` could not
25
+ * express the vocabularies it exists to express.
26
+ *
27
+ * Alternatives are sorted longest-first so `In progress` wins over `In`, the
28
+ * apostrophe accepts either glyph (an editor smart-quoting `Won't-do` should not
29
+ * break a build over two near-identical characters), and matching is
30
+ * case-insensitive with the declared spelling returned as canonical.
31
+ *
32
+ * An empty `states` must never match — a lane with no known/terminal
33
+ * vocabulary (`terminalStates: []`, a legitimate config: bug 0121's
34
+ * `proposals` lane, where nothing is ledger-closed by design) is a real,
35
+ * supported input, not a caller error. Naively joining zero alternatives
36
+ * makes the capture group `()`, a zero-width match that fires at almost any
37
+ * position — `findState` would then read every `State:`-shaped line as
38
+ * "readable, value ''" instead of falling through to the `UNREADABLE_RE`
39
+ * fallback, silently miscounting `withReadableState`/`unreadableState` and
40
+ * (for `isDoneItem`, which calls this with `terminalStates` directly) relying
41
+ * on `[].includes('')` being `false` by accident rather than by design.
42
+ */
43
+ function stateMatcher(states) {
44
+ if (states.length === 0)
45
+ return /(?!)/; // never matches: forces the UNREADABLE_RE fallback
46
+ const alts = [...states]
47
+ .sort((a, b) => b.length - a.length)
48
+ .map((s) => escapeRe(s).replace(/['’]/g, "['’]"))
49
+ .join('|');
50
+ return new RegExp(`${LABEL}${WRAP}(${alts})(?![\\w'’-])`, 'i');
51
+ }
52
+ // Fallback: the line IS a state declaration, but its value is not in the
53
+ // vocabulary. Used only to name the offending value in the message — so it
54
+ // captures up to the house `—` separator rather than one whitespace-delimited
55
+ // token, or a multi-word state would be reported by its first word alone.
56
+ const UNREADABLE_RE = new RegExp(`${LABEL}(\\S[^—–]*?)\\s*(?:[—–].*)?$`, 'i');
57
+ const ELIDE = 32;
10
58
  // Any one of these on a box's line marks it *disposed* (not silent). Tokens are
11
59
  // the canonical hyphenated enum, word-bounded — so prose like "…the connection
12
60
  // dropped in prod" does not falsely exempt a silent box. `deferred` must name a
@@ -21,13 +69,45 @@ const FENCE_RE = /(```|~~~)[\s\S]*?\1/g;
21
69
  function stripFencedCode(s) {
22
70
  return s.replace(FENCE_RE, (m) => '\n'.repeat((m.match(/\n/g) ?? []).length));
23
71
  }
24
- function headerRegion(text) {
25
- return stripFencedCode(text).split(/^##\s/m)[0] ?? '';
72
+ /**
73
+ * The `State:` token in the header region, with its line — one scan, shared by
74
+ * the done-item test and the placement check so the two cannot disagree about
75
+ * what a document says it is.
76
+ */
77
+ function findState(text, vocabulary) {
78
+ const known = stateMatcher(vocabulary);
79
+ const canonical = (m) => vocabulary.find((s) => s.toLowerCase().replace(/’/g, "'") === m.toLowerCase().replace(/’/g, "'")) ?? m;
80
+ const lines = stripFencedCode(text).split('\n');
81
+ // The preamble **and the first section**. Stopping at the first `##` — as this
82
+ // did — meant the check never ran on a real document in the corpus it was
83
+ // written for: the house template is `# Title` / `## Status` / `- **State:** X`,
84
+ // so every single `State:` line sat one heading past where it looked, and the
85
+ // whole placement half reported green having examined nothing (bug 0119).
86
+ let seenHeading = 0;
87
+ for (let i = 0; i < lines.length; i++) {
88
+ if (/^##\s/.test(lines[i] ?? '')) {
89
+ seenHeading += 1;
90
+ if (seenHeading > 1)
91
+ break;
92
+ continue;
93
+ }
94
+ const line = lines[i] ?? '';
95
+ const hit = known.exec(line);
96
+ if (hit?.[1] !== undefined)
97
+ return { state: canonical(hit[1]), raw: hit[1], line: i + 1 };
98
+ const unreadable = UNREADABLE_RE.exec(line);
99
+ if (unreadable?.[1] !== undefined) {
100
+ const raw = unreadable[1];
101
+ return { raw: raw.length > ELIDE ? `${raw.slice(0, ELIDE)}…` : raw, line: i + 1 };
102
+ }
103
+ }
104
+ return null;
26
105
  }
27
- function isDoneItem(doc, doneFolders) {
106
+ function isDoneItem(doc, doneFolders, terminalStates) {
28
107
  if (doneFolders.some((seg) => `/${doc.relPath}`.includes(seg)))
29
108
  return true;
30
- return DONE_STATE_RE.test(headerRegion(doc.text));
109
+ const found = findState(doc.text, terminalStates);
110
+ return found?.state !== undefined && terminalStates.includes(found.state);
31
111
  }
32
112
  const v = (rule, doc, line, message, because) => ({
33
113
  rule,
@@ -37,29 +117,28 @@ const v = (rule, doc, line, message, because) => ({
37
117
  message,
38
118
  because,
39
119
  });
40
- /** State↔folder coherence — the two mechanically-knowable placement defects. */
41
- function placementViolation(doc, inDoneFolder, closeInPlace) {
42
- const lines = stripFencedCode(doc.text).split('\n');
43
- let stateLine = 0;
44
- let state = '';
45
- for (let i = 0; i < lines.length; i++) {
46
- if (/^##\s/.test(lines[i] ?? ''))
47
- break; // header region only
48
- const m = STATE_TOKEN_LINE_RE.exec(lines[i] ?? '');
49
- if (m) {
50
- stateLine = i + 1;
51
- state = m[1] ?? '';
52
- break;
53
- }
120
+ /**
121
+ * What the header's `State:` line establishes — an unreadable token first, then
122
+ * state↔folder coherence. The order matters: a token the vocabulary does not
123
+ * contain cannot be classified as terminal or not, so reporting it is the only
124
+ * honest answer. Returning `null` there is what made this check silently
125
+ * self-disabling (bug 0118).
126
+ */
127
+ function headerStateViolation(doc, inDoneFolder, closeInPlace, states, terminalStates) {
128
+ const known = [...new Set([...states, ...terminalStates])];
129
+ const found = findState(doc.text, known);
130
+ if (!found)
131
+ return null; // no State line at all → this document is not an item
132
+ if (found.state === undefined) {
133
+ return v('ledger/unknown-state', doc, found.line, `State: ${found.raw} is not a state this corpus declares — expected one of ${known.join(', ')}.` +
134
+ ` An unreadable state cannot be checked against its folder, so it is reported rather than skipped.`, 'a state nobody can read is a check that silently stops running');
54
135
  }
55
- if (!stateLine)
56
- return null; // no neutral State token → nothing to check
57
- const terminal = TERMINAL_STATES.has(state);
136
+ const terminal = terminalStates.includes(found.state);
58
137
  if (inDoneFolder && !terminal) {
59
- return v('ledger/state-folder-mismatch', doc, stateLine, `State: ${state} but filed in a done-folder — move it back to the active lane or close it out.`, 'a done-folder item marked as not-done is a silent placement corruption');
138
+ return v('ledger/state-folder-mismatch', doc, found.line, `State: ${found.state} but filed in a done-folder — move it back to the active lane or close it out.`, 'a done-folder item marked as not-done is a silent placement corruption');
60
139
  }
61
140
  if (!inDoneFolder && terminal && !closeInPlace) {
62
- return v('ledger/state-folder-mismatch', doc, stateLine, `State: ${state} but not in a done-folder — the move-to-done was never made (orphaned close).`, 'a done item left in an active lane is an orphaned post-merge move');
141
+ return v('ledger/state-folder-mismatch', doc, found.line, `State: ${found.state} but not in a done-folder — the move-to-done was never made (orphaned close).`, 'a done item left in an active lane is an orphaned post-merge move');
63
142
  }
64
143
  return null;
65
144
  }
@@ -102,18 +181,58 @@ export function honestyAtClose(corpus, options = {}) {
102
181
  const doneFolders = options.doneFolders ?? DEFAULT_DONE_FOLDERS;
103
182
  const boardFiles = new Set(options.boardFiles ?? DEFAULT_BOARD_FILES);
104
183
  const closeInPlace = options.closeInPlace ?? false;
184
+ const states = options.states ?? DEFAULT_STATES;
185
+ const terminalStates = options.terminalStates ?? DEFAULT_TERMINAL_STATES;
105
186
  const violations = [];
106
187
  for (const doc of corpus.documents()) {
107
188
  const base = doc.relPath.split('/').pop() ?? doc.relPath;
108
189
  if (boardFiles.has(base))
109
190
  continue;
110
191
  const inDoneFolder = doneFolders.some((seg) => `/${doc.relPath}`.includes(seg));
111
- const placement = placementViolation(doc, inDoneFolder, closeInPlace);
112
- if (placement)
113
- violations.push(placement);
114
- if (isDoneItem(doc, doneFolders))
192
+ const header = headerStateViolation(doc, inDoneFolder, closeInPlace, states, terminalStates);
193
+ if (header)
194
+ violations.push(header);
195
+ if (isDoneItem(doc, doneFolders, terminalStates))
115
196
  violations.push(...ledgerViolations(doc));
116
197
  }
117
198
  return finishPreset(violations, options);
118
199
  }
200
+ /**
201
+ * The denominator, computed by the preset itself.
202
+ *
203
+ * `withReadableState` is the number nobody could see when bug 0119 happened: the
204
+ * placement check had never examined a document, and every summary line in the
205
+ * repo reported a healthy done-count derived purely from folder membership. A
206
+ * caller that prints this cannot be blind and green at the same time — if the
207
+ * region assumption drifts from the corpus again, this drops and says so.
208
+ *
209
+ * Callers must not re-derive it. `check-ledger.mjs` did, with a copy of the very
210
+ * expression 0119 removed, and its copy disagreed with the preset on 56 of 56
211
+ * records while heading a section captioned "so a green is provably non-vacuous".
212
+ */
213
+ export function ledgerStats(corpus, options = {}) {
214
+ const doneFolders = options.doneFolders ?? DEFAULT_DONE_FOLDERS;
215
+ const boardFiles = new Set(options.boardFiles ?? DEFAULT_BOARD_FILES);
216
+ const states = options.states ?? DEFAULT_STATES;
217
+ const terminalStates = options.terminalStates ?? DEFAULT_TERMINAL_STATES;
218
+ const known = [...new Set([...states, ...terminalStates])];
219
+ let scanned = 0;
220
+ let withReadableState = 0;
221
+ let unreadableState = 0;
222
+ let doneItems = 0;
223
+ for (const doc of corpus.documents()) {
224
+ const base = doc.relPath.split('/').pop() ?? doc.relPath;
225
+ if (boardFiles.has(base))
226
+ continue;
227
+ scanned += 1;
228
+ const found = findState(doc.text, known);
229
+ if (found?.state !== undefined)
230
+ withReadableState += 1;
231
+ else if (found !== null)
232
+ unreadableState += 1;
233
+ if (isDoneItem(doc, doneFolders, terminalStates))
234
+ doneItems += 1;
235
+ }
236
+ return { scanned, withReadableState, unreadableState, doneItems };
237
+ }
119
238
  //# sourceMappingURL=ledger.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ledger.js","sourceRoot":"","sources":["../../src/rules/ledger.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ledger.js","sourceRoot":"","sources":["../../src/rules/ledger.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nielspeter/eess-md",
3
- "version": "0.1.2",
3
+ "version": "0.3.0",
4
4
  "description": "Architecture testing for a Markdown corpus — the Markdown dialect of the eess family. Links resolve, code pointers ground, ADRs declare enforcement.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -48,7 +48,7 @@
48
48
  "eess"
49
49
  ],
50
50
  "dependencies": {
51
- "@nielspeter/eess": "^0.2.0",
51
+ "@nielspeter/eess": "^0.2.2",
52
52
  "mdast-util-from-markdown": "^2.0.3",
53
53
  "mdast-util-gfm": "^3.1.0",
54
54
  "micromark-extension-gfm": "^3.0.0",