pm-ops 2026.7.31 → 2026.8.3

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
@@ -1,5 +1,28 @@
1
1
  # Changelog
2
2
 
3
+ ## 2026.8.1 - 2026-08-01
4
+
5
+ ### Added
6
+
7
+ - Docstring coverage gate: a lexer-backed, fail-closed documentation gate the fleet can share ([ops-073p](https://github.com/unbraind/pm-ops/blob/main/.agents/pm/features/ops-073p.toon))
8
+
9
+ ### Fixed
10
+
11
+ - Brace-ending initializers consume following ASI declarations and members ([ops-cqyv](https://github.com/unbraind/pm-ops/blob/main/.agents/pm/issues/ops-cqyv.toon))
12
+ - Prevent symlink cycles during docstring source traversal ([ops-hkxb](https://github.com/unbraind/pm-ops/blob/main/.agents/pm/issues/ops-hkxb.toon))
13
+ - Semicolon-free exported variables consume following declarations ([ops-aun4](https://github.com/unbraind/pm-ops/blob/main/.agents/pm/issues/ops-aun4.toon))
14
+ - Sanitize Node test-runner context before release verification ([ops-hq5u](https://github.com/unbraind/pm-ops/blob/main/.agents/pm/issues/ops-hq5u.toon))
15
+ - The docstrings command writes JSON on the failure path under the default toon format ([ops-k6wq](https://github.com/unbraind/pm-ops/blob/main/.agents/pm/issues/ops-k6wq.toon))
16
+ - Semicolon-free class fields bypass docstring evaluation ([ops-6wen](https://github.com/unbraind/pm-ops/blob/main/.agents/pm/issues/ops-6wen.toon))
17
+ - The docstring gate is fail-open for exported bindings it cannot decompose ([ops-f25k](https://github.com/unbraind/pm-ops/blob/main/.agents/pm/issues/ops-f25k.toon))
18
+
19
+ ## 2026.7.31 - 2026-07-31
20
+
21
+ ### Other
22
+
23
+ - Expose the analyzer as a pm ops subcommand and a package subpath export ([ops-qvbu](https://github.com/unbraind/pm-ops/blob/main/.agents/pm/tasks/ops-qvbu.toon))
24
+ - Build the lexer-backed docstring analyzer and its negative controls ([ops-w8n4](https://github.com/unbraind/pm-ops/blob/main/.agents/pm/tasks/ops-w8n4.toon))
25
+
3
26
  ## 2026.7.29 - 2026-07-29
4
27
 
5
28
  ### Added
@@ -0,0 +1,148 @@
1
+ /**
2
+ * @file Lexer-backed docstring coverage analyzer.
3
+ *
4
+ * The fleet mandate requires every source declaration to carry a real
5
+ * docstring, enforced by a gate that cannot be cheated. A naive implementation
6
+ * is worse than none: a green check that proves nothing is active cover for
7
+ * missing documentation. This module is the replacement for a prior regex-based
8
+ * attempt, rebuilt on the one source of truth that distinguishes real syntax
9
+ * from text that merely looks syntactic: the TypeScript token stream.
10
+ *
11
+ * ## Why a lexer, not a regex or the compiler
12
+ *
13
+ * TypeScript 7 removed the stable compiler API (`createSourceFile`,
14
+ * `forEachChild`, …), so there is no AST to walk: `import ts from "typescript"`
15
+ * yields only `{ version }` at run time. What the `typescript/unstable/ast`
16
+ * surface still exposes is the lexer — `createScanner` — which emits exactly
17
+ * the punctuation and keywords that exist in the file. A brace inside a string,
18
+ * template literal, regex, or comment is never a `OpenBraceToken`; a declaration
19
+ * written inside a comment produces no tokens at all. That is precisely the
20
+ * property a brace-counting regex lacks, and it is what makes the rules below
21
+ * trustworthy.
22
+ *
23
+ * The scanner needs two corrections the unstable surface does not apply
24
+ * automatically, both handled in {@link tokenize}:
25
+ *
26
+ * 1. A template substitution `${ … }` emits a `CloseBraceToken` for the `}`
27
+ * without a matching `OpenBraceToken` (the `${` is folded into the
28
+ * `TemplateHead`). Plain brace counting is corrupted by it, exactly as the
29
+ * regex version was corrupted by braces in strings. The fix is to track a
30
+ * frame stack and, only when a `}` closes a substitution frame, call
31
+ * `reScanTemplateToken` so it becomes a `TemplateMiddle` / `TemplateTail`
32
+ * and never reaches the brace counter.
33
+ *
34
+ * 2. The scanner does not decide regex versus division on its own: a `/` is
35
+ * always a `SlashToken`, and `reScanSlashToken` scans a regex
36
+ * unconditionally — which swallows a division operator and everything after
37
+ * it as one unterminated regular expression. The fix is to rescan `/` as a
38
+ * regex only when the previous significant token cannot end an expression,
39
+ * matching the heuristic every JavaScript lexer uses.
40
+ *
41
+ * After that, the token array contains only real, balanced braces, and group
42
+ * matching over it is exact.
43
+ *
44
+ * ## Docstring recovery
45
+ *
46
+ * `getLeadingCommentRanges(text, tokenFullStart)` returns only the comments in
47
+ * the contiguous trivia immediately preceding a token, so a JSDoc block that
48
+ * lives inside a string literal, a template, or a commented-out line is
49
+ * invisible to it — the four classic cheats. JSDoc is a block comment (`kind`
50
+ * `3`) beginning with `/**`; a `//` line comment is `kind` `2` and does not
51
+ * count.
52
+ *
53
+ * ## Rules
54
+ *
55
+ * The documented surface — every symbol {@link judge} evaluates — is:
56
+ *
57
+ * - every exported declaration (`function`, `class`, `interface`, `type`,
58
+ * `const` / `let` / `var`, including `export default function|class`);
59
+ * - every non-`private`, non-`protected`, non-`#` member of an exported class
60
+ * (methods, properties, and `get` / `set` accessors);
61
+ * - every non-exported `function` whose body spans more than
62
+ * {@link INTERNAL_BODY_LINES} lines, implemented (unlike the prior attempt's
63
+ * declared-but-never-read constant).
64
+ *
65
+ * A passing docstring must carry at least {@link MIN_DOC_WORDS} meaningful words
66
+ * after filler removal and contribute at least {@link MIN_NOVEL_WORDS} terms the
67
+ * identifier did not already carry, so a `Gets the name.` JSDoc on `getName`
68
+ * fails.
69
+ *
70
+ * ## Fail-closed
71
+ *
72
+ * A declaration keyword the finder cannot classify is a violation with reason
73
+ * `unrecognized declaration form`, never a silent skip. The silent skip is the
74
+ * cheat this gate exists to prevent.
75
+ *
76
+ * ## Out of scope (structural, not configurable)
77
+ *
78
+ * `.d.ts` ambient declarations, `test/`, `dist/`, and `node_modules` are skipped
79
+ * by hard-coded directory rules; imports and re-export statements (`export { … }
80
+ * from`, `export * from`) declare nothing; `export default <expression>`,
81
+ * overload signatures, constructors, index signatures, computed-name members,
82
+ * `private` / `protected` / `#` members, and anonymous function/arrow
83
+ * expressions carry no stable identifier and are therefore not in the documented
84
+ * surface. There is deliberately no ignore list: an exemption must be a
85
+ * documented rule with a reason, not a configuration entry.
86
+ */
87
+ /**
88
+ * Minimum number of meaningful words a docstring must contain after filler
89
+ * removal. Below this the comment is too thin to document anything.
90
+ */
91
+ export declare const MIN_DOC_WORDS = 4;
92
+ /**
93
+ * Minimum number of meaningful words a docstring must contribute that the
94
+ * identifier itself does not already carry. This is what rejects a comment that
95
+ * merely restates the name.
96
+ */
97
+ export declare const MIN_NOVEL_WORDS = 2;
98
+ /**
99
+ * Number of source lines a non-exported function body must span before it joins
100
+ * the documented surface. Short private helpers stay undocumented; substantial
101
+ * ones must explain themselves.
102
+ */
103
+ export declare const INTERNAL_BODY_LINES = 4;
104
+ /** A single documentation violation found in one source file. */
105
+ export interface DocstringViolation {
106
+ /** Path of the file, relative to the scanned root. */
107
+ readonly file: string;
108
+ /** 1-based line of the declaration that failed. */
109
+ readonly line: number;
110
+ /** Symbol that failed, e.g. `parseFoo` or `Widget.render`. */
111
+ readonly symbol: string;
112
+ /** Actionable reason naming the rule that failed. */
113
+ readonly reason: string;
114
+ }
115
+ /** Aggregate result of scanning one tree. */
116
+ export interface DocstringReport {
117
+ /** Number of `.ts` files analyzed. */
118
+ readonly files_scanned: number;
119
+ /** Number of declarations evaluated against the rules. */
120
+ readonly declarations_checked: number;
121
+ /** Every violation, in source order. */
122
+ readonly violations: readonly DocstringViolation[];
123
+ }
124
+ /**
125
+ * Result of analyzing a single source text via {@link analyzeSource}: the
126
+ * violations it produced and how many declarations were evaluated.
127
+ */
128
+ export interface SourceAnalysis {
129
+ /** Violations found in this source. */
130
+ readonly violations: readonly DocstringViolation[];
131
+ /** Declarations evaluated against the rules. */
132
+ readonly declarations: number;
133
+ }
134
+ /**
135
+ * Analyze one already-read TypeScript source text and report the docstring
136
+ * violations it contains plus how many declarations were evaluated.
137
+ */
138
+ export declare function analyzeSource(text: string, file: string): SourceAnalysis;
139
+ /**
140
+ * Walk a directory tree and analyze every authored `.ts` source beneath it,
141
+ * skipping `.d.ts` files and the structural non-source directories. Scanning
142
+ * zero files fails by throwing rather than passing vacuously.
143
+ */
144
+ export declare function analyzeDocstringCoverage(options: {
145
+ root: string;
146
+ sourceDirs?: readonly string[];
147
+ }): DocstringReport;
148
+ //# sourceMappingURL=docstrings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docstrings.d.ts","sourceRoot":"","sources":["../docstrings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqFG;AAOH;;;GAGG;AACH,eAAO,MAAM,aAAa,IAAI,CAAC;AAE/B;;;;GAIG;AACH,eAAO,MAAM,eAAe,IAAI,CAAC;AAEjC;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC,iEAAiE;AACjE,MAAM,WAAW,kBAAkB;IACjC,sDAAsD;IACtD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,mDAAmD;IACnD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,8DAA8D;IAC9D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,qDAAqD;IACrD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,6CAA6C;AAC7C,MAAM,WAAW,eAAe;IAC9B,sCAAsC;IACtC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,0DAA0D;IAC1D,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC,wCAAwC;IACxC,QAAQ,CAAC,UAAU,EAAE,SAAS,kBAAkB,EAAE,CAAC;CACpD;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,uCAAuC;IACvC,QAAQ,CAAC,UAAU,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACnD,gDAAgD;IAChD,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AA2lCD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,cAAc,CAKxE;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GAAG,eAAe,CAoBnH"}