delivery-friction-analyzer 0.22.0 → 0.22.1

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.
@@ -28,7 +28,7 @@ The command reads local `friction-metrics.v1` JSON and writes deterministic `fri
28
28
  - `source`: optional source-bundle provenance copied into live-generated report
29
29
  artifacts, including `kind` and a human-readable label.
30
30
  - `targetRepository`: analyzed repository identity; live analysis sample size is encoded as `targetRepository.analysisPullRequestLimit` from collection metadata.
31
- - `analysisFilter`: optional metadata for explicit filters applied before metrics computation, including excluded PR classes and before/after PR counts.
31
+ - `analysisFilter`: optional metadata for explicit filters applied before metrics computation, including excluded PR classes and before/after PR counts. Live dry-run completion receipts may also include requested `analysisFilter.excludedPrClasses` with null before/after counts because dry-run coverage probes do not compute filtered metrics or report artifacts.
32
32
  - `configuredWorkflow`: optional user-configured workflow context from the repository profile. It is not observed GitHub evidence and does not change scoring, ranking, CSV exports, or PR class matching.
33
33
  - `contributorSource`: optional sanitized contributor-source metadata from the repository profile and collection path. It records source type, path, coverage status, parsed hint count, and guardrail note. It does not include raw contributor file contents or contributor rankings.
34
34
  - `collectionCoverage`: optional source collection coverage metadata copied into live-generated report JSON artifacts. `collectionCoverage.status` records aggregate coverage, and `collectionCoverage.sourceFamilies` lists source-family entries with `family`, `status`, `attempts`, `source`, `diagnostics`, and `downstreamImpact`. This uses the generic `source-bundle.v1` `coverage.sourceFamilies` name; legacy GitHub `apiFamilies` is not a live `friction-report.v1` field.
@@ -128,7 +128,7 @@ Full live analysis writes `methodology.md` as a hybrid artifact: stable explanat
128
128
 
129
129
  The methodology artifact should stay aligned with this contract and the Markdown methodology summary, but it may be more detailed than the main report.
130
130
 
131
- When PR class filtering is applied, `source-bundle.json` remains the full collected sample, while `normalized.json`, `metrics-summary.json`, `friction-report.json`, `friction-report.md`, `methodology.md`, and CSV exports describe the filtered sample. The methodology and CLI completion output must name the excluded PR classes and show the filtered PR count. If filtering excludes every collected PR, the analysis must fail without writing complete-looking empty reports.
131
+ When PR class filtering is applied to a full analysis, `source-bundle.json` remains the full collected sample, while `normalized.json`, `metrics-summary.json`, `friction-report.json`, `friction-report.md`, `methodology.md`, and CSV exports describe the filtered sample. The methodology and CLI completion output must name the excluded PR classes and show the filtered PR count. If filtering excludes every collected PR, the analysis must fail without writing complete-looking empty reports. Live dry-run receipts record requested PR class exclusions without a filtered count because dry-run does not build normalized metrics.
132
132
 
133
133
  ## CSV Evidence Exports
134
134
 
@@ -143,7 +143,7 @@ Minimum CSV column groups:
143
143
  - `comment-sources.csv`: source name, total comments, bot/scanner classification, human/author classification, and share of all comments.
144
144
  - `collection-coverage.csv`: source family, status, attempts, source label, diagnostics, and downstream impact.
145
145
 
146
- Empty CSV cells mean unavailable or not applicable. Numeric zero should be used only for observed or computed zero counts. Count columns that depend on optional GitHub coverage should keep source or coverage labels nearby so spreadsheet readers can tell unavailable evidence apart from observed zeroes. CSVs must not include raw comment bodies, raw workflow logs, tokens, secret-bearing environment details, or individual contributor/reviewer rankings.
146
+ Empty CSV cells mean unavailable or not applicable. Numeric zero should be used only for observed or computed zero counts. Count columns that depend on optional GitHub or sample-bundle coverage should keep source or coverage labels nearby so spreadsheet readers can tell unavailable evidence apart from observed zeroes. `pr-metrics.csv` `review_threads` is populated when normalized review-thread source evidence is available, including synthetic sample evidence and live GraphQL review-thread evidence. CSVs must not include raw comment bodies, raw workflow logs, tokens, secret-bearing environment details, or individual contributor/reviewer rankings.
147
147
 
148
148
  Contributor-source coverage appears in `collection-coverage.csv` as the `contributor_source` source family when configured. CSVs may include aggregate comment-source counts influenced by contributor hints, but they must not include raw `.all-contributorsrc` contents, contributor names, contributor login lists, or person rankings.
149
149
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "delivery-friction-analyzer",
3
- "version": "0.22.0",
3
+ "version": "0.22.1",
4
4
  "description": "Local GitHub pull request analytics for delivery friction reports.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/release-log.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ### 2026-06-28 — CLI Artifact Truthfulness
6
+
7
+ - What changed: Live dry-run JSON receipts now show requested PR-class exclusions without implying filtered metrics were computed, and `pr-metrics.csv` fills `review_threads` when sample or live review-thread evidence is available.
8
+ - Why it matters: Maintainers and scripts can trust completion receipts and CSV evidence to reflect accepted filters and available review-thread counts.
9
+ - Who is affected: Maintainers and users reading JSON completion receipts, CSV exports, and contract docs from sample or live GitHub analysis.
10
+ - Action needed: None.
11
+ - PR: https://github.com/hannasdev/delivery-friction-analyzer/pull/68
12
+
5
13
  ### 2026-06-28 — CLI Command Help Clarity
6
14
 
7
15
  - What changed: CLI help now separates the bundled sample, live GitHub analysis, live coverage probes, output controls, interactive setup, and reusable live-run presets, with mode-specific help for `--source sample --help` and `--source github --help`.
@@ -1695,6 +1695,15 @@ function summarizeResult({ dryRun, outDir, paths, sourceBundle, metrics, report,
1695
1695
  return summary;
1696
1696
  }
1697
1697
 
1698
+ function requestedAnalysisFilter(excludedPrClasses = []) {
1699
+ if (!excludedPrClasses.length) return null;
1700
+ return {
1701
+ excludedPrClasses,
1702
+ originalPullRequests: null,
1703
+ filteredPullRequests: null,
1704
+ };
1705
+ }
1706
+
1698
1707
  function applyPrClassFilter(normalized, excludedPrClasses = []) {
1699
1708
  if (!excludedPrClasses.length) return normalized;
1700
1709
  const excluded = new Set(excludedPrClasses);
@@ -1824,7 +1833,7 @@ export async function runAnalyzeGithub(options, {
1824
1833
  requestedLimit: options.limit,
1825
1834
  sampledLimit: collectionLimit,
1826
1835
  csv: false,
1827
- analysisFilter: null,
1836
+ analysisFilter: requestedAnalysisFilter(options.excludedPrClasses ?? []),
1828
1837
  savedProfilePath: options.savedProfilePath,
1829
1838
  savedRunPresetPath: options.savedRunPresetPath,
1830
1839
  prClassRulesWritten: options.prClassRulesWritten,
@@ -1988,10 +1997,13 @@ export function formatAnalyzeGithubCompletion(result) {
1988
1997
  }
1989
1998
 
1990
1999
  if (result.analysisFilter?.excludedPrClasses?.length) {
1991
- lines.push(
1992
- `Analysis filter: excluded PR class(es): ${result.analysisFilter.excludedPrClasses.join(", ")}.`,
1993
- `Filtered sample: ${result.analysisFilter.filteredPullRequests} of ${result.analysisFilter.originalPullRequests} collected pull request(s).`,
1994
- );
2000
+ lines.push(`Analysis filter: excluded PR class(es): ${result.analysisFilter.excludedPrClasses.join(", ")}.`);
2001
+ if (Number.isInteger(result.analysisFilter.filteredPullRequests)
2002
+ && Number.isInteger(result.analysisFilter.originalPullRequests)) {
2003
+ lines.push(`Filtered sample: ${result.analysisFilter.filteredPullRequests} of ${result.analysisFilter.originalPullRequests} collected pull request(s).`);
2004
+ } else if (result.dryRun) {
2005
+ lines.push("Filter application: requested only; dry-run coverage probes do not compute filtered metrics or report artifacts.");
2006
+ }
1995
2007
  }
1996
2008
 
1997
2009
  if (result.savedProfilePath) {
@@ -94,9 +94,12 @@ function hasObservedWorkflowRuns(workflowRuns = {}) {
94
94
  return (workflowRuns.coverage ?? workflowRuns.workflowRunCoverage) === "observed";
95
95
  }
96
96
 
97
- function hasObservedReviewThreads(reviewThreads = {}) {
97
+ function hasObservedReviewThreads(reviewThreads = {}, coverage = {}) {
98
98
  const source = reviewThreads.source ?? reviewThreads.reviewThreadSource ?? "unavailable";
99
- return String(source).startsWith("graphql");
99
+ if (String(source).startsWith("graphql")) return true;
100
+ if (String(source).startsWith("not_sampled") || source === "unavailable") return false;
101
+ if (String(source).includes("review_threads")) return true;
102
+ return coverage.status === "available";
100
103
  }
101
104
 
102
105
  function hasObservedReviewDecision(reviewDecision = {}) {
@@ -135,7 +138,7 @@ function prMetricsCsv(metricsSummary, analysisFilter) {
135
138
  ...SCORE_COLUMNS.map(([header]) => header),
136
139
  ];
137
140
  const rows = sortedPullRequests(metricsSummary).map(pr => {
138
- const observedReviewThreads = hasObservedReviewThreads(pr.review?.threads);
141
+ const observedReviewThreads = hasObservedReviewThreads(pr.review?.threads, pr.coverage?.reviewThreads);
139
142
  const observedReviewDecision = hasObservedReviewDecision(pr.review?.decision);
140
143
  const observedWorkflowRuns = hasObservedWorkflowRuns(pr.ci?.workflowRuns);
141
144
  const row = {