pm-ops 2026.8.10 → 2026.8.17
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 +16 -0
- package/README.md +2 -0
- package/dist/assurance.d.ts +52 -0
- package/dist/assurance.d.ts.map +1 -0
- package/dist/assurance.js +493 -0
- package/dist/assurance.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +68 -6
- package/dist/index.js.map +1 -1
- package/manifest.json +4 -2
- package/package.json +9 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2026.8.17 - 2026-08-17
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- A truncated list-all answer corrupts fleet throughput and cycle-time metrics ([ops-x3wc](https://github.com/unbraind/pm-ops/blob/main/.agents/pm/issues/ops-x3wc.toon))
|
|
8
|
+
|
|
9
|
+
## 2026.8.16 - 2026-08-16
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Measure the fleet's coverage and docstring quality as audited assurance bounds via a pm-ops quality measurement provider ([ops-a367](https://github.com/unbraind/pm-ops/blob/main/.agents/pm/features/ops-a367.toon))
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- The pm CLI compatibility floor was declared only in peerDependencies, which only npm enforces, and not in manifest.json pm_min_version, which is the field the CLI enforces ([ops-zl7a](https://github.com/unbraind/pm-ops/blob/main/.agents/pm/issues/ops-zl7a.toon))
|
|
18
|
+
|
|
3
19
|
## 2026.8.10 - 2026-08-10
|
|
4
20
|
|
|
5
21
|
### Fixed
|
package/README.md
CHANGED
|
@@ -218,6 +218,8 @@ pm ops audit --format markdown
|
|
|
218
218
|
|
|
219
219
|
Export pm workspace health as **Prometheus** text-format gauges so a Prometheus/Grafana stack can scrape fleet project-management signals — turning `project management = context management` into a dashboard. Reads each repo's items via the pm CLI (`pm list-all`, `pm list-blocked`) and derives counts, throughput, and cycle-time in-process (the same `closed_at` methodology `pm-brief` momentum uses).
|
|
220
220
|
|
|
221
|
+
Every pm item-list read explicitly uses `--full --output-budget unbounded` and is accepted only when the CLI's pagination, completeness, omission, and row-count receipts all prove that every item was returned. If any signal disagrees, pm-ops marks that repository as unavailable (`pm_workspace_available 0`) instead of publishing plausible but incomplete throughput, cycle-time, status-count, or blocked-item data.
|
|
222
|
+
|
|
221
223
|
```bash
|
|
222
224
|
pm ops metrics # Prometheus exposition for the current repo
|
|
223
225
|
pm ops metrics --repos ~/container/pm-* # fleet-wide, one series set per repo
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Assurance measurement provider that turns the fleet's pinned code-quality
|
|
3
|
+
* thresholds into audited bounds.
|
|
4
|
+
*
|
|
5
|
+
* Every fleet package historically hand-rolled a `scripts/coverage-gate.ts`
|
|
6
|
+
* with thresholds pinned under `coverageGate` in `package.json`. Lowering one
|
|
7
|
+
* was an ordinary diff: nothing in the workflow recorded *why* a number moved,
|
|
8
|
+
* and nothing proved the bound still meant anything. Under pm-cli's assurance
|
|
9
|
+
* surface, weakening a bound requires an `authorization_decision` naming a
|
|
10
|
+
* terminal Decision item the host verifies, and every non-dry gate verdict is
|
|
11
|
+
* appended to the immutable workspace history stream. That only bites if the
|
|
12
|
+
* quality numbers can be *measured* by an assurance provider, which is what
|
|
13
|
+
* this module exports.
|
|
14
|
+
*
|
|
15
|
+
* The provider reads a local lcov coverage report and the canonical docstring
|
|
16
|
+
* analyzer and reduces them to four measurement keys. It owns no quality
|
|
17
|
+
* thresholds itself — those live in assertions and gates the consuming
|
|
18
|
+
* workspace declares — but it refuses to be satisfied by a *stale* coverage
|
|
19
|
+
* report, because a gate that runs against yesterday's `lcov.info` after today's
|
|
20
|
+
* source edit is a gate that measures nothing.
|
|
21
|
+
*
|
|
22
|
+
* @see {@link https://github.com/unbraind/pm-cli/blob/main/docs/ASSURANCE.md ASSURANCE.md}
|
|
23
|
+
* for the measurement / assertion / gate vocabulary this provider plugs into.
|
|
24
|
+
*/
|
|
25
|
+
import type { ExtensionApi } from "@unbrained/pm-cli/sdk/authoring";
|
|
26
|
+
/**
|
|
27
|
+
* Exact contract the host's `api.registerAssuranceMeasurementProvider`
|
|
28
|
+
* expects for one provider registration.
|
|
29
|
+
*
|
|
30
|
+
* `@unbrained/pm-cli` keeps this type on its internal
|
|
31
|
+
* `core/extensions/extension-types` module and does not yet re-export it
|
|
32
|
+
* through any public `sdk/*` barrel (the SDK only exposes it as the parameter
|
|
33
|
+
* of the registration method). Deriving it from that public method gives the
|
|
34
|
+
* identical, exact shape without reaching into `dist/` internals or
|
|
35
|
+
* hand-copying field names — if the SDK ever widens the method signature, this
|
|
36
|
+
* alias widens with it.
|
|
37
|
+
*/
|
|
38
|
+
export type AssuranceMeasurementProviderDefinition = Parameters<ExtensionApi["registerAssuranceMeasurementProvider"]>[0];
|
|
39
|
+
/** Stable provider id used by measurement declarations and gate allow-lists. */
|
|
40
|
+
export declare const QUALITY_PROVIDER_ID = "pm-ops-quality";
|
|
41
|
+
/**
|
|
42
|
+
* The pm-ops quality assurance measurement provider.
|
|
43
|
+
*
|
|
44
|
+
* Registered through the `services` capability, it exposes four keys over a
|
|
45
|
+
* local lcov report and the canonical docstring analyzer. It declares no
|
|
46
|
+
* network use and a `medium` cost class so a trigger policy that caps work at
|
|
47
|
+
* `low` can decline it before invocation. Key ids use the hyphen form the pm-cli
|
|
48
|
+
* host requires for every stable lowercase id (`^[a-z0-9][a-z0-9-]*$`); the
|
|
49
|
+
* hyphen and underscore spellings name the same measurements.
|
|
50
|
+
*/
|
|
51
|
+
export declare const qualityMeasurementProvider: AssuranceMeasurementProviderDefinition;
|
|
52
|
+
//# sourceMappingURL=assurance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assurance.d.ts","sourceRoot":"","sources":["../assurance.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAKpE;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,sCAAsC,GAChD,UAAU,CAAC,YAAY,CAAC,sCAAsC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAEtE,gFAAgF;AAChF,eAAO,MAAM,mBAAmB,mBAAmB,CAAC;AA+epD;;;;;;;;;GASG;AACH,eAAO,MAAM,0BAA0B,EAAE,sCAgFtC,CAAC"}
|
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Assurance measurement provider that turns the fleet's pinned code-quality
|
|
3
|
+
* thresholds into audited bounds.
|
|
4
|
+
*
|
|
5
|
+
* Every fleet package historically hand-rolled a `scripts/coverage-gate.ts`
|
|
6
|
+
* with thresholds pinned under `coverageGate` in `package.json`. Lowering one
|
|
7
|
+
* was an ordinary diff: nothing in the workflow recorded *why* a number moved,
|
|
8
|
+
* and nothing proved the bound still meant anything. Under pm-cli's assurance
|
|
9
|
+
* surface, weakening a bound requires an `authorization_decision` naming a
|
|
10
|
+
* terminal Decision item the host verifies, and every non-dry gate verdict is
|
|
11
|
+
* appended to the immutable workspace history stream. That only bites if the
|
|
12
|
+
* quality numbers can be *measured* by an assurance provider, which is what
|
|
13
|
+
* this module exports.
|
|
14
|
+
*
|
|
15
|
+
* The provider reads a local lcov coverage report and the canonical docstring
|
|
16
|
+
* analyzer and reduces them to four measurement keys. It owns no quality
|
|
17
|
+
* thresholds itself — those live in assertions and gates the consuming
|
|
18
|
+
* workspace declares — but it refuses to be satisfied by a *stale* coverage
|
|
19
|
+
* report, because a gate that runs against yesterday's `lcov.info` after today's
|
|
20
|
+
* source edit is a gate that measures nothing.
|
|
21
|
+
*
|
|
22
|
+
* @see {@link https://github.com/unbraind/pm-cli/blob/main/docs/ASSURANCE.md ASSURANCE.md}
|
|
23
|
+
* for the measurement / assertion / gate vocabulary this provider plugs into.
|
|
24
|
+
*/
|
|
25
|
+
import { readFileSync, statSync } from "node:fs";
|
|
26
|
+
import { isAbsolute, join, relative, sep } from "node:path";
|
|
27
|
+
import { analyzeDocstringCoverage } from "./docstrings.js";
|
|
28
|
+
/** Stable provider id used by measurement declarations and gate allow-lists. */
|
|
29
|
+
export const QUALITY_PROVIDER_ID = "pm-ops-quality";
|
|
30
|
+
/**
|
|
31
|
+
* The four lcov dimensions this provider can reduce to a single percentage.
|
|
32
|
+
*
|
|
33
|
+
* lcov has native `LF`/`LH`, `BRF`/`BRH`, and `FNF`/`FNH` counters for lines,
|
|
34
|
+
* branches, and functions, but no native statement counter: statements are
|
|
35
|
+
* reconstructed from the per-line `DA:` records the reporter already emits.
|
|
36
|
+
*/
|
|
37
|
+
const COVERAGE_DIMENSIONS = [
|
|
38
|
+
"lines",
|
|
39
|
+
"branches",
|
|
40
|
+
"functions",
|
|
41
|
+
"statements",
|
|
42
|
+
];
|
|
43
|
+
/** Default lcov report path when a measurement omits the `report` parameter. */
|
|
44
|
+
const DEFAULT_REPORT = "coverage/lcov.info";
|
|
45
|
+
/** Default source root when a docstring measurement omits the `root` parameter. */
|
|
46
|
+
const DEFAULT_ROOT = ".";
|
|
47
|
+
/**
|
|
48
|
+
* Parse lcov text into one record per covered file.
|
|
49
|
+
*
|
|
50
|
+
* lcov delimits file sections with `SF:` and `end_of_record`. A well-formed
|
|
51
|
+
* report always pairs them, but real reporters occasionally omit the trailing
|
|
52
|
+
* `end_of_record` on the last section, so the parser flushes a pending record
|
|
53
|
+
* on the next `SF:` and once more at end-of-input rather than dropping it.
|
|
54
|
+
* `SF:` paths may be absolute or repo-relative; both are normalised to a
|
|
55
|
+
* repo-relative POSIX label resolved against `base`, matching how the package's
|
|
56
|
+
* own `coverage-gate.ts` reads them back.
|
|
57
|
+
*/
|
|
58
|
+
function parseLcov(text, base) {
|
|
59
|
+
const records = [];
|
|
60
|
+
let file = "";
|
|
61
|
+
let sourcePath = "";
|
|
62
|
+
let linesFound = 0;
|
|
63
|
+
let linesHit = 0;
|
|
64
|
+
let branchesFound = 0;
|
|
65
|
+
let branchesHit = 0;
|
|
66
|
+
let functionsFound = 0;
|
|
67
|
+
let functionsHit = 0;
|
|
68
|
+
let statementsFound = 0;
|
|
69
|
+
let statementsHit = 0;
|
|
70
|
+
let started = false;
|
|
71
|
+
const flush = () => {
|
|
72
|
+
if (!started) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
records.push({
|
|
76
|
+
file,
|
|
77
|
+
sourcePath,
|
|
78
|
+
linesFound,
|
|
79
|
+
linesHit,
|
|
80
|
+
branchesFound,
|
|
81
|
+
branchesHit,
|
|
82
|
+
functionsFound,
|
|
83
|
+
functionsHit,
|
|
84
|
+
statementsFound,
|
|
85
|
+
statementsHit,
|
|
86
|
+
});
|
|
87
|
+
started = false;
|
|
88
|
+
file = "";
|
|
89
|
+
sourcePath = "";
|
|
90
|
+
linesFound = 0;
|
|
91
|
+
linesHit = 0;
|
|
92
|
+
branchesFound = 0;
|
|
93
|
+
branchesHit = 0;
|
|
94
|
+
functionsFound = 0;
|
|
95
|
+
functionsHit = 0;
|
|
96
|
+
statementsFound = 0;
|
|
97
|
+
statementsHit = 0;
|
|
98
|
+
};
|
|
99
|
+
for (const rawLine of text.split("\n")) {
|
|
100
|
+
const line = rawLine.trimEnd();
|
|
101
|
+
if (line === "end_of_record") {
|
|
102
|
+
flush();
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
if (line.startsWith("SF:")) {
|
|
106
|
+
flush();
|
|
107
|
+
const raw = line.slice(3).trim();
|
|
108
|
+
const absolute = isAbsolute(raw) ? raw : join(base, raw);
|
|
109
|
+
file = relative(base, absolute).split(sep).join("/");
|
|
110
|
+
sourcePath = absolute;
|
|
111
|
+
started = true;
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
if (!started) {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
if (line.startsWith("DA:")) {
|
|
118
|
+
// Each `DA:<line>,<count>[,<checksum>]` record is one instrumented
|
|
119
|
+
// statement: count it as found unconditionally and as hit when its
|
|
120
|
+
// execution count (the second field, never the line number) is > 0.
|
|
121
|
+
statementsFound++;
|
|
122
|
+
const firstComma = line.indexOf(",", 3);
|
|
123
|
+
const secondComma = firstComma === -1 ? -1 : line.indexOf(",", firstComma + 1);
|
|
124
|
+
const countEnd = secondComma === -1 ? line.length : secondComma;
|
|
125
|
+
const count = firstComma === -1
|
|
126
|
+
? Number.NaN
|
|
127
|
+
: Number(line.slice(firstComma + 1, countEnd));
|
|
128
|
+
if (Number.isFinite(count) && count > 0) {
|
|
129
|
+
statementsHit++;
|
|
130
|
+
}
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
if (line.startsWith("LF:")) {
|
|
134
|
+
linesFound = countAfter(line, 3);
|
|
135
|
+
}
|
|
136
|
+
else if (line.startsWith("LH:")) {
|
|
137
|
+
linesHit = countAfter(line, 3);
|
|
138
|
+
}
|
|
139
|
+
else if (line.startsWith("BRF:")) {
|
|
140
|
+
branchesFound = countAfter(line, 4);
|
|
141
|
+
}
|
|
142
|
+
else if (line.startsWith("BRH:")) {
|
|
143
|
+
branchesHit = countAfter(line, 4);
|
|
144
|
+
}
|
|
145
|
+
else if (line.startsWith("FNF:")) {
|
|
146
|
+
functionsFound = countAfter(line, 4);
|
|
147
|
+
}
|
|
148
|
+
else if (line.startsWith("FNH:")) {
|
|
149
|
+
functionsHit = countAfter(line, 4);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
flush();
|
|
153
|
+
return records;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Read the integer payload following an lcov count tag (`LF:42`).
|
|
157
|
+
*
|
|
158
|
+
* `tagLength` is the length of the tag including its colon. A malformed payload
|
|
159
|
+
* coerces to `0` rather than `NaN` so one corrupt line cannot poison an
|
|
160
|
+
* otherwise valid report into a non-finite percentage.
|
|
161
|
+
*/
|
|
162
|
+
function countAfter(line, tagLength) {
|
|
163
|
+
return Number(line.slice(tagLength)) || 0;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Resolve a path-shaped provider parameter against the workspace base.
|
|
167
|
+
*
|
|
168
|
+
* Absolute paths are honoured as-is; relative paths resolve against the base so
|
|
169
|
+
* a measurement declared with the default `coverage/lcov.info` finds the report
|
|
170
|
+
* regardless of the process working directory the host happened to launch from.
|
|
171
|
+
* A non-string value falls back to the default rather than throwing, because the
|
|
172
|
+
* host has already validated the parameter against its declared `string` type.
|
|
173
|
+
*/
|
|
174
|
+
function resolveParameterPath(value, fallback, base) {
|
|
175
|
+
const raw = typeof value === "string" ? value : fallback;
|
|
176
|
+
return isAbsolute(raw) ? raw : join(base, raw);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Best on-disk root for resolving relative report and source paths.
|
|
180
|
+
*
|
|
181
|
+
* The host populates `repo_root` and `source_workspace_root` from the Git root
|
|
182
|
+
* and the launch directory; `process.cwd()` is the last resort for direct unit
|
|
183
|
+
* tests that construct a context by hand.
|
|
184
|
+
*/
|
|
185
|
+
function workspaceBase(context) {
|
|
186
|
+
return context.repo_root ?? context.source_workspace_root ?? process.cwd();
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Reduce one record to the found/hit pair for the requested dimension.
|
|
190
|
+
*
|
|
191
|
+
* Statements are the only dimension reconstructed from `DA:` records rather than
|
|
192
|
+
* read from a native lcov counter, so they branch on the parsed field.
|
|
193
|
+
*/
|
|
194
|
+
function dimensionCount(record, dimension) {
|
|
195
|
+
switch (dimension) {
|
|
196
|
+
case "lines":
|
|
197
|
+
return { found: record.linesFound, hit: record.linesHit };
|
|
198
|
+
case "branches":
|
|
199
|
+
return { found: record.branchesFound, hit: record.branchesHit };
|
|
200
|
+
case "functions":
|
|
201
|
+
return { found: record.functionsFound, hit: record.functionsHit };
|
|
202
|
+
case "statements":
|
|
203
|
+
return { found: record.statementsFound, hit: record.statementsHit };
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
/** Round a percentage to two decimals so verdicts carry stable, readable values. */
|
|
207
|
+
function roundPercent(value) {
|
|
208
|
+
return Math.round(value * 100) / 100;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Read and parse the lcov report, then refuse it if it is stale.
|
|
212
|
+
*
|
|
213
|
+
* A missing report fails with an actionable message rather than an opaque
|
|
214
|
+
* filesystem error, because the gate author needs to know the `report`
|
|
215
|
+
* parameter resolved to a path nothing wrote to. After parsing, the staleness
|
|
216
|
+
* refusal compares the report's mtime against every covered source; see
|
|
217
|
+
* {@link assertReportNotStale} for why that comparison is the provider's core
|
|
218
|
+
* security property.
|
|
219
|
+
*/
|
|
220
|
+
function readCoverageReport(reportPath, base) {
|
|
221
|
+
let text;
|
|
222
|
+
try {
|
|
223
|
+
text = readFileSync(reportPath, "utf8");
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
throw new Error(`pm-ops-quality: coverage report not found at ${reportPath}. ` +
|
|
227
|
+
'Provide the "report" parameter or generate the report by running the ' +
|
|
228
|
+
"test suite with an lcov reporter before evaluating the gate.");
|
|
229
|
+
}
|
|
230
|
+
const records = parseLcov(text, base);
|
|
231
|
+
assertReportNotStale(reportPath, records.map((record) => record.sourcePath));
|
|
232
|
+
return records;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Refuse a coverage report older than any source file it covers.
|
|
236
|
+
*
|
|
237
|
+
* This is the security property that makes the provider worth registering: a
|
|
238
|
+
* gate that reads a report file can be satisfied by a *stale* report — run the
|
|
239
|
+
* suite once at full coverage, edit the source, and the gate still passes
|
|
240
|
+
* against yesterday's numbers. Comparing the report's mtime against the mtime of
|
|
241
|
+
* every covered source (and only those, never wall-clock age) makes the verdict
|
|
242
|
+
* depend on *what changed* rather than on *when the gate ran*. If any covered
|
|
243
|
+
* source is newer than the report, resolution throws naming the offending file
|
|
244
|
+
* and both timestamps, so the verdict can never be computed from a stale report
|
|
245
|
+
* and can never silently downgrade to a pass.
|
|
246
|
+
*
|
|
247
|
+
* A source the report names but that no longer exists is skipped here: a missing
|
|
248
|
+
* file cannot be "newer", and the absence is already visible in the verdict's
|
|
249
|
+
* population and contributors. This check answers exactly one question — is the
|
|
250
|
+
* report behind the source tree? — and refuses only when it is.
|
|
251
|
+
*/
|
|
252
|
+
function assertReportNotStale(reportPath, sourcePaths) {
|
|
253
|
+
const reportMtime = statSync(reportPath).mtimeMs;
|
|
254
|
+
const stale = [];
|
|
255
|
+
for (const source of sourcePaths) {
|
|
256
|
+
let info;
|
|
257
|
+
try {
|
|
258
|
+
info = statSync(source);
|
|
259
|
+
}
|
|
260
|
+
catch {
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
if (info.mtimeMs > reportMtime) {
|
|
264
|
+
stale.push({ source, mtime: info.mtimeMs });
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
if (stale.length === 0) {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
const reportIso = new Date(reportMtime).toISOString();
|
|
271
|
+
const offenders = stale
|
|
272
|
+
.map((entry) => ` ${entry.source} (modified ${new Date(entry.mtime).toISOString()})`)
|
|
273
|
+
.join("\n");
|
|
274
|
+
throw new Error(`pm-ops-quality: refusing a stale coverage report.\n` +
|
|
275
|
+
`The report ${reportPath} (modified ${reportIso}) is older than source ` +
|
|
276
|
+
"file(s) it covers, so its numbers no longer describe the current tree. " +
|
|
277
|
+
"Re-run the test suite to regenerate the report before evaluating the gate.\n" +
|
|
278
|
+
`Source files newer than the report:\n${offenders}`);
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Resolve the `coverage_percent` measurement for one dimension.
|
|
282
|
+
*
|
|
283
|
+
* The percentage is the dimension's total hit over its total found across the
|
|
284
|
+
* whole report; the population denominator is that same total found, so a gate
|
|
285
|
+
* reading the receipt can tell a 100% over two hundred lines from a vacuous
|
|
286
|
+
* 100% over zero. Contributors are the repo-relative files still below full
|
|
287
|
+
* coverage on the requested dimension, sorted for deterministic verdicts.
|
|
288
|
+
*/
|
|
289
|
+
function resolveCoveragePercent(context) {
|
|
290
|
+
const dimension = requireCoverageDimension(context.parameters.dimension);
|
|
291
|
+
const base = workspaceBase(context);
|
|
292
|
+
const reportPath = resolveParameterPath(context.parameters.report, DEFAULT_REPORT, base);
|
|
293
|
+
const records = readCoverageReport(reportPath, base);
|
|
294
|
+
let found = 0;
|
|
295
|
+
let hit = 0;
|
|
296
|
+
const contributors = [];
|
|
297
|
+
for (const record of records) {
|
|
298
|
+
const counts = dimensionCount(record, dimension);
|
|
299
|
+
found += counts.found;
|
|
300
|
+
hit += counts.hit;
|
|
301
|
+
if (counts.hit < counts.found) {
|
|
302
|
+
contributors.push(record.file);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
contributors.sort();
|
|
306
|
+
const value = found === 0 ? 100 : roundPercent((hit / found) * 100);
|
|
307
|
+
return {
|
|
308
|
+
value,
|
|
309
|
+
population_size: found,
|
|
310
|
+
cost: records.length,
|
|
311
|
+
contributors,
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Resolve the `uncovered_files` measurement.
|
|
316
|
+
*
|
|
317
|
+
* The value is the repo-relative set of files that are below full coverage on
|
|
318
|
+
* *any* dimension, so a gate can bound how many files still carry coverage debt
|
|
319
|
+
* without caring which dimension slipped. The population is every file in the
|
|
320
|
+
* report, so the receipt reads "3 of 41 files are not fully covered".
|
|
321
|
+
*/
|
|
322
|
+
function resolveUncoveredFiles(context) {
|
|
323
|
+
const base = workspaceBase(context);
|
|
324
|
+
const reportPath = resolveParameterPath(context.parameters.report, DEFAULT_REPORT, base);
|
|
325
|
+
const records = readCoverageReport(reportPath, base);
|
|
326
|
+
// A file is "uncovered" when it falls short of full coverage on any one
|
|
327
|
+
// dimension; a dimension lcov reports as empty (`found` 0) is vacuously
|
|
328
|
+
// satisfied, so a branchless file can still be fully covered.
|
|
329
|
+
const uncovered = records
|
|
330
|
+
.filter((record) => record.linesHit < record.linesFound ||
|
|
331
|
+
record.branchesHit < record.branchesFound ||
|
|
332
|
+
record.functionsHit < record.functionsFound ||
|
|
333
|
+
record.statementsHit < record.statementsFound)
|
|
334
|
+
.map((record) => record.file)
|
|
335
|
+
.sort();
|
|
336
|
+
return {
|
|
337
|
+
value: uncovered,
|
|
338
|
+
population_size: records.length,
|
|
339
|
+
cost: records.length,
|
|
340
|
+
contributors: uncovered,
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Resolve the `docstring_percent` measurement.
|
|
345
|
+
*
|
|
346
|
+
* Delegates to the canonical analyzer at {@link analyzeDocstringCoverage} rather
|
|
347
|
+
* than reimplementing it, so the fleet has exactly one docstring definition.
|
|
348
|
+
* The population is the count of documentable symbols and contributors are the
|
|
349
|
+
* `path:symbol` labels still missing a docstring, matching the analyzer's own
|
|
350
|
+
* violation output.
|
|
351
|
+
*/
|
|
352
|
+
function resolveDocstringPercent(context) {
|
|
353
|
+
const base = workspaceBase(context);
|
|
354
|
+
const root = resolveParameterPath(context.parameters.root, DEFAULT_ROOT, base);
|
|
355
|
+
const report = analyzeDocstringCoverage({ root });
|
|
356
|
+
const total = report.declarations_checked;
|
|
357
|
+
const missing = report.violations.length;
|
|
358
|
+
const value = total === 0 ? 100 : roundPercent(((total - missing) / total) * 100);
|
|
359
|
+
const contributors = docstringContributors(report.violations);
|
|
360
|
+
return { value, population_size: total, cost: total, contributors };
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Resolve the `undocumented_symbols` measurement.
|
|
364
|
+
*
|
|
365
|
+
* The value is the stable `path:symbol` label set for every symbol the analyzer
|
|
366
|
+
* flagged, so a gate can name exactly which declarations carry the debt. The
|
|
367
|
+
* population is the full documentable-symbol count, not just the violators, so
|
|
368
|
+
* the receipt distinguishes "5 of 5 undocumented" from "5 of 200 undocumented".
|
|
369
|
+
*/
|
|
370
|
+
function resolveUndocumentedSymbols(context) {
|
|
371
|
+
const base = workspaceBase(context);
|
|
372
|
+
const root = resolveParameterPath(context.parameters.root, DEFAULT_ROOT, base);
|
|
373
|
+
const report = analyzeDocstringCoverage({ root });
|
|
374
|
+
const labels = docstringContributors(report.violations);
|
|
375
|
+
return {
|
|
376
|
+
value: labels,
|
|
377
|
+
population_size: report.declarations_checked,
|
|
378
|
+
cost: report.declarations_checked,
|
|
379
|
+
contributors: labels,
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Build sorted `path:symbol` contributor labels from analyzer violations.
|
|
384
|
+
*
|
|
385
|
+
* Centralised so both docstring keys derive identical labels from the same
|
|
386
|
+
* violation stream; a future change to the label format changes both at once.
|
|
387
|
+
*/
|
|
388
|
+
function docstringContributors(violations) {
|
|
389
|
+
return violations
|
|
390
|
+
.map((violation) => `${violation.file}:${violation.symbol}`)
|
|
391
|
+
.sort();
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Validate and accept the required `dimension` parameter for `coverage_percent`.
|
|
395
|
+
*
|
|
396
|
+
* The host validates that a required parameter is *present* and *string-typed*,
|
|
397
|
+
* but it cannot validate the value domain, so an unknown dimension like
|
|
398
|
+
* `"loc"` would otherwise flow through to a percentage of nothing. This guard
|
|
399
|
+
* fails fast with the accepted vocabulary instead of returning a misleading 0%.
|
|
400
|
+
*/
|
|
401
|
+
function requireCoverageDimension(value) {
|
|
402
|
+
if (typeof value !== "string") {
|
|
403
|
+
throw new Error('pm-ops-quality: key "coverage_percent" requires a string "dimension" ' +
|
|
404
|
+
`parameter; received ${value === null ? "null" : typeof value}.`);
|
|
405
|
+
}
|
|
406
|
+
if (!COVERAGE_DIMENSIONS.includes(value)) {
|
|
407
|
+
throw new Error(`pm-ops-quality: unknown coverage dimension "${value}". Expected one of: ` +
|
|
408
|
+
`${COVERAGE_DIMENSIONS.join(", ")}.`);
|
|
409
|
+
}
|
|
410
|
+
return value;
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* The pm-ops quality assurance measurement provider.
|
|
414
|
+
*
|
|
415
|
+
* Registered through the `services` capability, it exposes four keys over a
|
|
416
|
+
* local lcov report and the canonical docstring analyzer. It declares no
|
|
417
|
+
* network use and a `medium` cost class so a trigger policy that caps work at
|
|
418
|
+
* `low` can decline it before invocation. Key ids use the hyphen form the pm-cli
|
|
419
|
+
* host requires for every stable lowercase id (`^[a-z0-9][a-z0-9-]*$`); the
|
|
420
|
+
* hyphen and underscore spellings name the same measurements.
|
|
421
|
+
*/
|
|
422
|
+
export const qualityMeasurementProvider = {
|
|
423
|
+
id: QUALITY_PROVIDER_ID,
|
|
424
|
+
cost_class: "medium",
|
|
425
|
+
network: false,
|
|
426
|
+
timeout_ms: 30_000,
|
|
427
|
+
keys: {
|
|
428
|
+
"coverage-percent": {
|
|
429
|
+
value_type: "number",
|
|
430
|
+
description: "Percentage covered for one lcov dimension across the whole report.",
|
|
431
|
+
parameters: {
|
|
432
|
+
dimension: {
|
|
433
|
+
type: "string",
|
|
434
|
+
required: true,
|
|
435
|
+
description: "One of lines, branches, functions, or statements.",
|
|
436
|
+
},
|
|
437
|
+
report: {
|
|
438
|
+
type: "string",
|
|
439
|
+
required: false,
|
|
440
|
+
description: "Path to an lcov report; defaults to coverage/lcov.info.",
|
|
441
|
+
},
|
|
442
|
+
},
|
|
443
|
+
},
|
|
444
|
+
"uncovered-files": {
|
|
445
|
+
value_type: "string_set",
|
|
446
|
+
description: "Repo-relative files that are not at 100% on every dimension.",
|
|
447
|
+
parameters: {
|
|
448
|
+
report: {
|
|
449
|
+
type: "string",
|
|
450
|
+
required: false,
|
|
451
|
+
description: "Path to an lcov report; defaults to coverage/lcov.info.",
|
|
452
|
+
},
|
|
453
|
+
},
|
|
454
|
+
},
|
|
455
|
+
"docstring-percent": {
|
|
456
|
+
value_type: "number",
|
|
457
|
+
description: "Percentage of documentable symbols carrying a docstring.",
|
|
458
|
+
parameters: {
|
|
459
|
+
root: {
|
|
460
|
+
type: "string",
|
|
461
|
+
required: false,
|
|
462
|
+
description: "Source root to scan; defaults to the workspace root.",
|
|
463
|
+
},
|
|
464
|
+
},
|
|
465
|
+
},
|
|
466
|
+
"undocumented-symbols": {
|
|
467
|
+
value_type: "string_set",
|
|
468
|
+
description: "Stable path:symbol labels for symbols missing a docstring.",
|
|
469
|
+
parameters: {
|
|
470
|
+
root: {
|
|
471
|
+
type: "string",
|
|
472
|
+
required: false,
|
|
473
|
+
description: "Source root to scan; defaults to the workspace root.",
|
|
474
|
+
},
|
|
475
|
+
},
|
|
476
|
+
},
|
|
477
|
+
},
|
|
478
|
+
resolve(context) {
|
|
479
|
+
switch (context.key) {
|
|
480
|
+
case "coverage-percent":
|
|
481
|
+
return resolveCoveragePercent(context);
|
|
482
|
+
case "uncovered-files":
|
|
483
|
+
return resolveUncoveredFiles(context);
|
|
484
|
+
case "docstring-percent":
|
|
485
|
+
return resolveDocstringPercent(context);
|
|
486
|
+
case "undocumented-symbols":
|
|
487
|
+
return resolveUndocumentedSymbols(context);
|
|
488
|
+
default:
|
|
489
|
+
throw new Error(`pm-ops-quality: provider does not declare key "${context.key}".`);
|
|
490
|
+
}
|
|
491
|
+
},
|
|
492
|
+
};
|
|
493
|
+
//# sourceMappingURL=assurance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assurance.js","sourceRoot":"","sources":["../assurance.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
import type { ExtensionApi } from "@unbrained/pm-cli/sdk/authoring";
|
|
2
|
+
import { type MergeDecisionReceipt } from "@unbrained/pm-cli/sdk/merge";
|
|
3
|
+
/**
|
|
4
|
+
* Project a receipt's requested preference side for the fleet view.
|
|
5
|
+
*
|
|
6
|
+
* The SDK's receipt reader normalizes every receipt it returns to carry
|
|
7
|
+
* `requested_preference`, folding the legacy schema-v1 `preferred` key and
|
|
8
|
+
* defaulting to `"ours"` when a receipt records neither — the exact chain
|
|
9
|
+
* `summarizeMergeReceipt` applies for committed-history summaries. Centralizing
|
|
10
|
+
* it here keeps the fleet view's field consistent with those summaries and
|
|
11
|
+
* gives the legacy/default arms a directly testable home.
|
|
12
|
+
*/
|
|
13
|
+
export declare function receiptPreferredSide(receipt: Readonly<Pick<MergeDecisionReceipt, "requested_preference" | "preferred">>): "ours" | "theirs";
|
|
2
14
|
interface RepoMetrics {
|
|
3
15
|
path: string;
|
|
4
16
|
repo: string;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAA0C,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAA0C,MAAM,iCAAiC,CAAC;AAY5G,OAAO,EAKL,KAAK,oBAAoB,EAG1B,MAAM,6BAA6B,CAAC;AAqpDrC;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,QAAQ,CAAC,IAAI,CAAC,oBAAoB,EAAE,sBAAsB,GAAG,WAAW,CAAC,CAAC,GAClF,MAAM,GAAG,QAAQ,CAEnB;AAkTD,UAAU,WAAW;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,uBAAuB,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,uBAAuB,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,wEAAwE;IACxE,sBAAsB,EAAE,MAAM,CAAC;IAC/B,sDAAsD;IACtD,yBAAyB,EAAE,MAAM,CAAC;IAClC,4EAA4E;IAC5E,sBAAsB,EAAE,MAAM,CAAC;IAC/B,wFAAwF;IACxF,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AA6KD;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,WAAW,EAAE,GAAG,IAAI,CA0BvE;;;;kBAqNe,YAAY"}
|