delivery-friction-analyzer 0.17.0 → 0.18.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.
@@ -41,6 +41,12 @@ This keeps validation-target details in profile data rather than hardcoded produ
41
41
 
42
42
  Each file rule must include a unique lowercase `id`, a non-empty `match` object, a supported `category`, and a supported `role`. Optional `functionalSurface` values use the same lowercase identifier shape as rule IDs. Optional `generated` values must be booleans. Unsupported rule or matcher keys fail validation, and invalid JavaScript regexes fail before collection with the rule ID and matcher field in the error.
43
43
 
44
+ File rules may include an optional `notes` string. Notes are human-readable
45
+ profile context for maintainers and reviewers. They can explain why a path rule
46
+ exists, what product surface it represents, or why a generated or low-signal
47
+ path is intentionally classified. Notes do not affect matching order,
48
+ classification output, scoring, collection, reports, or CSV exports.
49
+
44
50
  ## Pull Request Classes
45
51
 
46
52
  `prClasses` is optional. Rules are evaluated in order and the first matching rule wins. The current profile contract supports title-only matchers:
@@ -64,6 +70,12 @@ Class identifiers are validated as lower-kebab-case or lower_snake_case strings.
64
70
 
65
71
  PR class evidence is interpretive and profile-driven. It helps reports show class distributions, dominance notes, and explicit `--exclude-pr-class` filtering when you request filtering, but configured PR class rules do not change default scoring, ranking formulas, collection, or CSV export shape by themselves.
66
72
 
73
+ PR class rules may include an optional `notes` string. Use notes to document the
74
+ repository convention behind a class rule, such as Conventional Commit-style
75
+ title prefixes or release-train naming. Notes are valid JSON profile data for
76
+ humans only; they do not change title matching, PR class assignment, filtering,
77
+ scoring, collection, reports, or CSV exports.
78
+
67
79
  Interactive setup can add a release PR class rule from a confirmed title convention using the current title-only matcher shape. Branch strategy answers stay in `workflow` context only; they do not create branch-based PR class matching. Branch-based class matching is deferred until a future matcher contract supports branch fields explicitly.
68
80
 
69
81
  ### Copyable PR Class Examples
@@ -0,0 +1,110 @@
1
+ {
2
+ "schemaVersion": "repository-profile.v1",
3
+ "repository": {
4
+ "owner": "example-org",
5
+ "name": "delivery-dashboard"
6
+ },
7
+ "rules": [
8
+ {
9
+ "id": "frontend-ui-paths",
10
+ "match": { "prefix": "apps/web/" },
11
+ "category": "code",
12
+ "role": "product_ui",
13
+ "functionalSurface": "dashboard_ui",
14
+ "generated": false,
15
+ "notes": "Maps the customer-facing dashboard app to the UI surface so broad UI changes are not treated as unknown code."
16
+ },
17
+ {
18
+ "id": "api-runtime-paths",
19
+ "match": { "prefix": "services/api/" },
20
+ "category": "code",
21
+ "role": "core_product_code",
22
+ "functionalSurface": "delivery_api",
23
+ "generated": false,
24
+ "notes": "Represents backend delivery workflow behavior that usually needs API-focused review."
25
+ },
26
+ {
27
+ "id": "worker-runtime-paths",
28
+ "match": { "prefix": "workers/" },
29
+ "category": "code",
30
+ "role": "core_product_code",
31
+ "functionalSurface": "background_jobs",
32
+ "generated": false
33
+ },
34
+ {
35
+ "id": "integration-tests",
36
+ "match": { "prefix": "tests/integration/" },
37
+ "category": "tests",
38
+ "role": "tests",
39
+ "functionalSurface": "test_suite",
40
+ "generated": false
41
+ },
42
+ {
43
+ "id": "workflow-config",
44
+ "match": { "prefix": ".github/workflows/" },
45
+ "category": "infrastructure",
46
+ "role": "infrastructure",
47
+ "functionalSurface": "ci_workflows",
48
+ "generated": false,
49
+ "notes": "Classifies CI workflow edits separately from product runtime changes."
50
+ },
51
+ {
52
+ "id": "planning-notes",
53
+ "match": { "prefix": "docs/initiatives/" },
54
+ "category": "docs",
55
+ "role": "planning_docs",
56
+ "functionalSurface": "planning",
57
+ "generated": false
58
+ },
59
+ {
60
+ "id": "release-notes",
61
+ "match": { "exact": "CHANGELOG.md" },
62
+ "category": "docs",
63
+ "role": "release_notes",
64
+ "functionalSurface": "release_docs",
65
+ "generated": false
66
+ },
67
+ {
68
+ "id": "generated-api-client",
69
+ "match": { "prefix": "packages/generated-client/" },
70
+ "category": "generated",
71
+ "role": "generated_or_vendored",
72
+ "functionalSurface": "generated_client",
73
+ "generated": true,
74
+ "notes": "Shows how generated code can stay visible while receiving low-signal weighting."
75
+ }
76
+ ],
77
+ "prClasses": [
78
+ {
79
+ "id": "feature-title",
80
+ "class": "feature",
81
+ "match": { "titleRegex": "^feat(?:\\([^)]+\\))?:" },
82
+ "notes": "Use when feature PR titles follow a Conventional Commit-style prefix."
83
+ },
84
+ {
85
+ "id": "fix-title",
86
+ "class": "fix",
87
+ "match": { "titleRegex": "^fix(?:\\([^)]+\\))?:" },
88
+ "notes": "Separates corrective work from feature delivery in the class distribution."
89
+ },
90
+ {
91
+ "id": "test-title",
92
+ "class": "test",
93
+ "match": { "titleRegex": "^test(?:\\([^)]+\\))?:" }
94
+ },
95
+ {
96
+ "id": "maintenance-title",
97
+ "class": "maintenance",
98
+ "match": { "titleRegex": "^(?:chore|ci|build|refactor)(?:\\([^)]+\\))?:" }
99
+ }
100
+ ],
101
+ "workflow": {
102
+ "primaryMergeMethod": "squash_merge",
103
+ "releaseStrategy": "release_prs",
104
+ "branchStrategy": "trunk_based"
105
+ },
106
+ "contributors": {
107
+ "sourceType": "all_contributors",
108
+ "path": ".all-contributorsrc"
109
+ }
110
+ }
@@ -0,0 +1,7 @@
1
+ # Tutorial Sample Report Excerpt
2
+
3
+ Source: Bundled synthetic sample, not live GitHub data.
4
+
5
+ The generated sample report opens by pointing readers to Change scope, Review churn, and Repo guidance gap. It also shows why the first findings should not be overgeneralized: PR #104 is intentionally broad and outlier-sensitive, PR-open diff coverage is partial, and one PR has unavailable workflow-run history.
6
+
7
+ Use the excerpt in public docs to set expectations: the sample is synthetic tutorial evidence, not observed GitHub data, and its value is showing how caveats, profile classifications, PR classes, workflow context, and CSV evidence appear in a local report bundle.
@@ -0,0 +1,388 @@
1
+ {
2
+ "schemaVersion": "source-bundle.v1",
3
+ "collectedAt": "2026-06-15T12:00:00Z",
4
+ "source": {
5
+ "kind": "sample",
6
+ "label": "Bundled synthetic sample, not live GitHub data",
7
+ "metadata": {
8
+ "tutorial": true,
9
+ "publicSafety": "fully synthetic placeholder data"
10
+ }
11
+ },
12
+ "collector": {
13
+ "name": "tutorial-sample-collector",
14
+ "provider": "bundled-sample"
15
+ },
16
+ "targetRepository": {
17
+ "owner": "example-org",
18
+ "name": "delivery-dashboard",
19
+ "defaultBranch": "main",
20
+ "visibility": "public",
21
+ "analysisPullRequestLimit": 4,
22
+ "isValidationTarget": false
23
+ },
24
+ "repositoryMetadata": {
25
+ "id": "sample-repository-001",
26
+ "name": "delivery-dashboard",
27
+ "owner": "example-org",
28
+ "fullName": "example-org/delivery-dashboard",
29
+ "defaultBranch": "main",
30
+ "visibility": "public",
31
+ "isPrivate": false,
32
+ "htmlUrl": "https://example.com/example-org/delivery-dashboard"
33
+ },
34
+ "selection": {
35
+ "strategy": "bundled_tutorial_pull_requests",
36
+ "requestedLimit": 4,
37
+ "collectedCount": 4,
38
+ "source": "bundled tutorial sample"
39
+ },
40
+ "coverage": {
41
+ "status": "partial",
42
+ "sourceFamilies": [
43
+ {
44
+ "family": "repository_metadata",
45
+ "source": "bundled tutorial sample",
46
+ "status": "available",
47
+ "attempts": 1,
48
+ "diagnostics": [],
49
+ "downstreamImpact": "Fictional repository identity is available for report labeling."
50
+ },
51
+ {
52
+ "family": "pull_request_inventory",
53
+ "source": "bundled tutorial sample",
54
+ "status": "available",
55
+ "attempts": 1,
56
+ "diagnostics": [],
57
+ "downstreamImpact": "Four fictional merged pull requests are available for normalization and metrics."
58
+ },
59
+ {
60
+ "family": "pull_request_details",
61
+ "source": "bundled tutorial sample",
62
+ "status": "available",
63
+ "attempts": 1,
64
+ "diagnostics": [],
65
+ "downstreamImpact": "Synthetic lifecycle, files, reviews, checks, and workflow summaries are populated."
66
+ },
67
+ {
68
+ "family": "review_threads",
69
+ "source": "bundled tutorial sample",
70
+ "status": "available",
71
+ "attempts": 1,
72
+ "diagnostics": [],
73
+ "downstreamImpact": "Review-thread counts and comment sources are populated without comment text."
74
+ },
75
+ {
76
+ "family": "workflow_runs",
77
+ "source": "bundled tutorial sample",
78
+ "status": "partial",
79
+ "attempts": 1,
80
+ "diagnostics": ["One PR intentionally has unavailable workflow-run history to demonstrate coverage caveats."],
81
+ "downstreamImpact": "Validation-gap metrics use observed runs where present and preserve unavailable coverage where missing."
82
+ },
83
+ {
84
+ "family": "pr_open_diff",
85
+ "source": "bundled tutorial sample",
86
+ "status": "partial",
87
+ "attempts": 1,
88
+ "diagnostics": ["One PR intentionally lacks PR-open diff data to demonstrate partial diff-growth coverage."],
89
+ "downstreamImpact": "Diff-growth ratios are computed only for PRs with open-time snapshots."
90
+ },
91
+ {
92
+ "family": "languages",
93
+ "source": "bundled tutorial sample",
94
+ "status": "available",
95
+ "attempts": 1,
96
+ "diagnostics": [],
97
+ "downstreamImpact": "Language distribution is illustrative context only."
98
+ },
99
+ {
100
+ "family": "contributor_source",
101
+ "source": "bundled tutorial sample",
102
+ "status": "partial",
103
+ "attempts": 1,
104
+ "diagnostics": ["Synthetic contributor hints omit one reviewer to demonstrate partial profile context."],
105
+ "downstreamImpact": "Contributor-aware comment-source hints are available; scoring and person-level outputs are unchanged."
106
+ }
107
+ ]
108
+ },
109
+ "languageDistribution": {
110
+ "source": "bundled tutorial sample",
111
+ "bytesByLanguage": {
112
+ "TypeScript": 420000,
113
+ "JavaScript": 180000,
114
+ "Markdown": 52000,
115
+ "YAML": 18000
116
+ },
117
+ "coverage": {
118
+ "family": "languages",
119
+ "source": "bundled tutorial sample",
120
+ "status": "available",
121
+ "attempts": 1,
122
+ "diagnostics": [],
123
+ "downstreamImpact": "Language distribution is illustrative context only."
124
+ }
125
+ },
126
+ "contributorSource": {
127
+ "sourceType": "all_contributors",
128
+ "path": ".all-contributorsrc",
129
+ "coverage": {
130
+ "family": "contributor_source",
131
+ "source": "bundled tutorial sample",
132
+ "status": "partial",
133
+ "attempts": 1,
134
+ "diagnostics": ["Synthetic contributor hints omit one reviewer to demonstrate partial profile context."],
135
+ "downstreamImpact": "Contributor-aware comment-source hints are available; scoring and person-level outputs are unchanged."
136
+ },
137
+ "hintCount": 3
138
+ },
139
+ "pullRequests": [
140
+ {
141
+ "number": 101,
142
+ "title": "feat: add route health dashboard",
143
+ "author": { "login": "sample-author-ui", "type": "User" },
144
+ "url": "https://example.com/pull/101",
145
+ "state": "MERGED",
146
+ "createdAt": "2026-05-04T09:00:00Z",
147
+ "mergedAt": "2026-05-05T16:30:00Z",
148
+ "updatedAt": "2026-05-05T16:45:00Z",
149
+ "baseRefName": "main",
150
+ "headRefName": "sample/route-health-dashboard",
151
+ "headRefOid": "sample101merge",
152
+ "additions": 410,
153
+ "deletions": 96,
154
+ "changedFiles": 5,
155
+ "prOpenDiff": { "source": "direct", "confidence": "high", "additions": 190, "deletions": 44, "changedFiles": 5 },
156
+ "commits": [
157
+ { "oid": "sample101a", "authoredDate": "2026-05-04T08:40:00Z", "committedDate": "2026-05-04T08:42:00Z", "messageHeadline": "feat: add route health dashboard" },
158
+ { "oid": "sample101b", "authoredDate": "2026-05-05T12:15:00Z", "committedDate": "2026-05-05T12:16:00Z", "messageHeadline": "test: cover delayed route states" }
159
+ ],
160
+ "files": [
161
+ { "path": "apps/web/src/routes/RouteHealthPanel.tsx", "additions": 140, "deletions": 22, "changeType": "ADDED" },
162
+ { "path": "apps/web/src/routes/RouteHealthChart.tsx", "additions": 94, "deletions": 16, "changeType": "ADDED" },
163
+ { "path": "services/api/src/routes/health-summary.ts", "additions": 78, "deletions": 18, "changeType": "MODIFIED" },
164
+ { "path": "tests/integration/route-health.test.ts", "additions": 82, "deletions": 20, "changeType": "ADDED" },
165
+ { "path": "CHANGELOG.md", "additions": 16, "deletions": 20, "changeType": "MODIFIED" }
166
+ ],
167
+ "reviews": [
168
+ { "id": "sample-review-101-a", "author": { "login": "sample-reviewer-a", "type": "User" }, "submittedAt": "2026-05-04T14:20:00Z", "state": "CHANGES_REQUESTED", "commitOid": "sample101a", "generatedCommentCount": 0, "failedAttempt": true },
169
+ { "id": "sample-review-101-b", "author": { "login": "sample-reviewer-a", "type": "User" }, "submittedAt": "2026-05-05T15:20:00Z", "state": "APPROVED", "commitOid": "sample101b", "generatedCommentCount": 0, "failedAttempt": false }
170
+ ],
171
+ "reviewThreads": {
172
+ "source": "bundled tutorial sample:review_threads",
173
+ "totalCount": 2,
174
+ "nodes": [
175
+ { "id": "sample-thread-101-a", "isResolved": true, "isOutdated": false, "path": "apps/web/src/routes/RouteHealthPanel.tsx", "line": 88, "comments": [
176
+ { "databaseId": "sample-comment-101-a", "author": { "login": "sample-reviewer-a", "type": "User" }, "path": "apps/web/src/routes/RouteHealthPanel.tsx", "line": 88, "originalLine": 88, "createdAt": "2026-05-04T14:21:00Z", "updatedAt": "2026-05-04T14:21:00Z", "url": "https://example.com/pull/101#discussion-sample-101-a" },
177
+ { "databaseId": "sample-comment-101-b", "author": { "login": "sample-author-ui", "type": "User" }, "path": "apps/web/src/routes/RouteHealthPanel.tsx", "line": 88, "originalLine": 88, "createdAt": "2026-05-05T12:18:00Z", "updatedAt": "2026-05-05T12:18:00Z", "url": "https://example.com/pull/101#discussion-sample-101-b" }
178
+ ] },
179
+ { "id": "sample-thread-101-b", "isResolved": true, "isOutdated": true, "path": "services/api/src/routes/health-summary.ts", "line": 41, "comments": [
180
+ { "databaseId": "sample-comment-101-c", "author": { "login": "sample-reviewer-b", "type": "User" }, "path": "services/api/src/routes/health-summary.ts", "line": 41, "originalLine": 41, "createdAt": "2026-05-04T15:02:00Z", "updatedAt": "2026-05-04T15:02:00Z", "url": "https://example.com/pull/101#discussion-sample-101-c" }
181
+ ] }
182
+ ]
183
+ },
184
+ "statusCheckRollup": [
185
+ { "__typename": "CheckRun", "name": "unit", "context": null, "workflowName": "CI", "status": "COMPLETED", "conclusion": "SUCCESS", "startedAt": "2026-05-04T09:10:00Z", "completedAt": "2026-05-04T09:16:00Z" },
186
+ { "__typename": "CheckRun", "name": "integration", "context": null, "workflowName": "CI", "status": "COMPLETED", "conclusion": "FAILURE", "startedAt": "2026-05-04T09:12:00Z", "completedAt": "2026-05-04T09:24:00Z" },
187
+ { "__typename": "CheckRun", "name": "integration", "context": null, "workflowName": "CI", "status": "COMPLETED", "conclusion": "SUCCESS", "startedAt": "2026-05-05T12:20:00Z", "completedAt": "2026-05-05T12:31:00Z" }
188
+ ],
189
+ "workflowRuns": {
190
+ "source": "bundled tutorial sample:workflow_runs",
191
+ "totalCount": 2,
192
+ "conclusions": { "success": 1, "failure": 1 },
193
+ "runs": [
194
+ { "id": "sample-run-101-a", "name": "CI", "workflowName": "CI", "headSha": "sample101a", "headBranch": "sample/route-health-dashboard", "event": "pull_request", "status": "completed", "conclusion": "failure", "createdAt": "2026-05-04T09:10:00Z", "updatedAt": "2026-05-04T09:24:00Z", "runStartedAt": "2026-05-04T09:12:00Z", "htmlUrl": "https://example.com/actions/runs/sample-run-101-a" },
195
+ { "id": "sample-run-101-b", "name": "CI", "workflowName": "CI", "headSha": "sample101b", "headBranch": "sample/route-health-dashboard", "event": "pull_request", "status": "completed", "conclusion": "success", "createdAt": "2026-05-05T12:20:00Z", "updatedAt": "2026-05-05T12:31:00Z", "runStartedAt": "2026-05-05T12:21:00Z", "htmlUrl": "https://example.com/actions/runs/sample-run-101-b" }
196
+ ]
197
+ },
198
+ "coverage": {
199
+ "prOpenDiff": { "family": "pr_open_diff", "source": "bundled tutorial sample", "status": "available", "attempts": 1, "diagnostics": [], "downstreamImpact": "Diff-growth metrics can compare open and merge sizes." },
200
+ "reviewThreads": { "family": "review_threads", "source": "bundled tutorial sample", "status": "available", "attempts": 1, "diagnostics": [], "downstreamImpact": "Review-thread evidence is available." },
201
+ "workflowRuns": { "family": "workflow_runs", "source": "bundled tutorial sample", "status": "available", "attempts": 1, "diagnostics": [], "downstreamImpact": "Workflow-run evidence is available." }
202
+ }
203
+ },
204
+ {
205
+ "number": 102,
206
+ "title": "fix: retry stalled dispatch jobs",
207
+ "author": { "login": "sample-author-api", "type": "User" },
208
+ "url": "https://example.com/pull/102",
209
+ "state": "MERGED",
210
+ "createdAt": "2026-05-07T10:00:00Z",
211
+ "mergedAt": "2026-05-07T18:10:00Z",
212
+ "updatedAt": "2026-05-07T18:20:00Z",
213
+ "baseRefName": "main",
214
+ "headRefName": "sample/retry-dispatch-jobs",
215
+ "headRefOid": "sample102merge",
216
+ "additions": 126,
217
+ "deletions": 34,
218
+ "changedFiles": 4,
219
+ "prOpenDiff": { "source": "direct", "confidence": "high", "additions": 120, "deletions": 30, "changedFiles": 4 },
220
+ "commits": [
221
+ { "oid": "sample102a", "authoredDate": "2026-05-07T09:45:00Z", "committedDate": "2026-05-07T09:46:00Z", "messageHeadline": "fix: retry stalled dispatch jobs" },
222
+ { "oid": "sample102b", "authoredDate": "2026-05-07T15:10:00Z", "committedDate": "2026-05-07T15:12:00Z", "messageHeadline": "test: cover retry backoff" }
223
+ ],
224
+ "files": [
225
+ { "path": "workers/dispatch/retry.ts", "additions": 58, "deletions": 18, "changeType": "MODIFIED" },
226
+ { "path": "services/api/src/jobs/dispatch.ts", "additions": 24, "deletions": 8, "changeType": "MODIFIED" },
227
+ { "path": "tests/integration/dispatch-retry.test.ts", "additions": 42, "deletions": 8, "changeType": "ADDED" },
228
+ { "path": "docs/initiatives/retry-jobs/checklist.md", "additions": 2, "deletions": 0, "changeType": "ADDED" }
229
+ ],
230
+ "reviews": [
231
+ { "id": "sample-review-102-a", "author": { "login": "sample-reviewer-b", "type": "User" }, "submittedAt": "2026-05-07T13:00:00Z", "state": "COMMENTED", "commitOid": "sample102a", "generatedCommentCount": 0, "failedAttempt": false },
232
+ { "id": "sample-review-102-b", "author": { "login": "sample-reviewer-b", "type": "User" }, "submittedAt": "2026-05-07T17:40:00Z", "state": "APPROVED", "commitOid": "sample102b", "generatedCommentCount": 0, "failedAttempt": false }
233
+ ],
234
+ "reviewThreads": {
235
+ "source": "bundled tutorial sample:review_threads",
236
+ "totalCount": 1,
237
+ "nodes": [
238
+ { "id": "sample-thread-102-a", "isResolved": true, "isOutdated": false, "path": "workers/dispatch/retry.ts", "line": 30, "comments": [
239
+ { "databaseId": "sample-comment-102-a", "author": { "login": "sample-reviewer-b", "type": "User" }, "path": "workers/dispatch/retry.ts", "line": 30, "originalLine": 30, "createdAt": "2026-05-07T13:01:00Z", "updatedAt": "2026-05-07T13:01:00Z", "url": "https://example.com/pull/102#discussion-sample-102-a" },
240
+ { "databaseId": "sample-comment-102-b", "author": { "login": "sample-author-api", "type": "User" }, "path": "workers/dispatch/retry.ts", "line": 30, "originalLine": 30, "createdAt": "2026-05-07T15:15:00Z", "updatedAt": "2026-05-07T15:15:00Z", "url": "https://example.com/pull/102#discussion-sample-102-b" }
241
+ ] }
242
+ ]
243
+ },
244
+ "statusCheckRollup": [
245
+ { "__typename": "CheckRun", "name": "unit", "context": null, "workflowName": "CI", "status": "COMPLETED", "conclusion": "SUCCESS", "startedAt": "2026-05-07T10:10:00Z", "completedAt": "2026-05-07T10:16:00Z" }
246
+ ],
247
+ "workflowRuns": {
248
+ "source": "bundled tutorial sample:workflow_runs",
249
+ "totalCount": 1,
250
+ "conclusions": { "success": 1 },
251
+ "runs": [
252
+ { "id": "sample-run-102-a", "name": "CI", "workflowName": "CI", "headSha": "sample102b", "headBranch": "sample/retry-dispatch-jobs", "event": "pull_request", "status": "completed", "conclusion": "success", "createdAt": "2026-05-07T15:20:00Z", "updatedAt": "2026-05-07T15:29:00Z", "runStartedAt": "2026-05-07T15:21:00Z", "htmlUrl": "https://example.com/actions/runs/sample-run-102-a" }
253
+ ]
254
+ },
255
+ "coverage": {
256
+ "prOpenDiff": { "family": "pr_open_diff", "source": "bundled tutorial sample", "status": "available", "attempts": 1, "diagnostics": [], "downstreamImpact": "Diff-growth metrics can compare open and merge sizes." },
257
+ "reviewThreads": { "family": "review_threads", "source": "bundled tutorial sample", "status": "available", "attempts": 1, "diagnostics": [], "downstreamImpact": "Review-thread evidence is available." },
258
+ "workflowRuns": { "family": "workflow_runs", "source": "bundled tutorial sample", "status": "available", "attempts": 1, "diagnostics": [], "downstreamImpact": "Workflow-run evidence is available." }
259
+ }
260
+ },
261
+ {
262
+ "number": 103,
263
+ "title": "test: add billing webhook contract coverage",
264
+ "author": { "login": "sample-author-test", "type": "User" },
265
+ "url": "https://example.com/pull/103",
266
+ "state": "MERGED",
267
+ "createdAt": "2026-05-10T11:00:00Z",
268
+ "mergedAt": "2026-05-10T14:40:00Z",
269
+ "updatedAt": "2026-05-10T14:45:00Z",
270
+ "baseRefName": "main",
271
+ "headRefName": "sample/billing-webhook-tests",
272
+ "headRefOid": "sample103merge",
273
+ "additions": 88,
274
+ "deletions": 10,
275
+ "changedFiles": 3,
276
+ "prOpenDiff": { "source": "unavailable", "confidence": "unavailable", "reason": "Open-time snapshot intentionally omitted from the bundled sample." },
277
+ "commits": [
278
+ { "oid": "sample103a", "authoredDate": "2026-05-10T10:40:00Z", "committedDate": "2026-05-10T10:41:00Z", "messageHeadline": "test: add billing webhook contract coverage" }
279
+ ],
280
+ "files": [
281
+ { "path": "tests/integration/billing-webhooks.test.ts", "additions": 72, "deletions": 4, "changeType": "ADDED" },
282
+ { "path": "services/api/src/billing/webhook-fixtures.ts", "additions": 10, "deletions": 4, "changeType": "MODIFIED" },
283
+ { "path": "packages/generated-client/billing.ts", "additions": 6, "deletions": 2, "changeType": "MODIFIED" }
284
+ ],
285
+ "reviews": [
286
+ { "id": "sample-review-103-a", "author": { "login": "sample-reviewer-a", "type": "User" }, "submittedAt": "2026-05-10T13:30:00Z", "state": "APPROVED", "commitOid": "sample103a", "generatedCommentCount": 0, "failedAttempt": false }
287
+ ],
288
+ "reviewThreads": { "source": "bundled tutorial sample:review_threads", "totalCount": 0, "nodes": [] },
289
+ "statusCheckRollup": [
290
+ { "__typename": "CheckRun", "name": "unit", "context": null, "workflowName": "CI", "status": "COMPLETED", "conclusion": "SUCCESS", "startedAt": "2026-05-10T11:10:00Z", "completedAt": "2026-05-10T11:15:00Z" }
291
+ ],
292
+ "workflowRuns": {
293
+ "source": "unavailable",
294
+ "totalCount": null,
295
+ "conclusions": {},
296
+ "runs": []
297
+ },
298
+ "coverage": {
299
+ "prOpenDiff": { "family": "pr_open_diff", "source": "bundled tutorial sample", "status": "unavailable", "attempts": 1, "diagnostics": ["Open-time snapshot intentionally omitted from the bundled sample."], "downstreamImpact": "Diff-growth metrics are unavailable for this PR." },
300
+ "reviewThreads": { "family": "review_threads", "source": "bundled tutorial sample", "status": "available", "attempts": 1, "diagnostics": [], "downstreamImpact": "Review-thread evidence is available and shows zero threads." },
301
+ "workflowRuns": { "family": "workflow_runs", "source": "bundled tutorial sample", "status": "unavailable", "attempts": 1, "diagnostics": ["Workflow-run history intentionally omitted for this PR."], "downstreamImpact": "Validation-gap metrics preserve unavailable workflow coverage." }
302
+ }
303
+ },
304
+ {
305
+ "number": 104,
306
+ "title": "feat: consolidate dispatch settings",
307
+ "author": { "login": "sample-author-platform", "type": "User" },
308
+ "url": "https://example.com/pull/104",
309
+ "state": "MERGED",
310
+ "createdAt": "2026-05-14T08:00:00Z",
311
+ "mergedAt": "2026-05-17T19:00:00Z",
312
+ "updatedAt": "2026-05-17T19:15:00Z",
313
+ "baseRefName": "main",
314
+ "headRefName": "sample/consolidate-dispatch-settings",
315
+ "headRefOid": "sample104merge",
316
+ "additions": 980,
317
+ "deletions": 420,
318
+ "changedFiles": 8,
319
+ "prOpenDiff": { "source": "direct", "confidence": "high", "additions": 260, "deletions": 90, "changedFiles": 6 },
320
+ "commits": [
321
+ { "oid": "sample104a", "authoredDate": "2026-05-14T07:45:00Z", "committedDate": "2026-05-14T07:46:00Z", "messageHeadline": "feat: consolidate dispatch settings" },
322
+ { "oid": "sample104b", "authoredDate": "2026-05-15T10:20:00Z", "committedDate": "2026-05-15T10:22:00Z", "messageHeadline": "fix: split overloaded settings model" },
323
+ { "oid": "sample104c", "authoredDate": "2026-05-16T16:30:00Z", "committedDate": "2026-05-16T16:31:00Z", "messageHeadline": "test: cover settings migration" },
324
+ { "oid": "sample104d", "authoredDate": "2026-05-17T13:00:00Z", "committedDate": "2026-05-17T13:02:00Z", "messageHeadline": "ci: repair migration fixture" }
325
+ ],
326
+ "files": [
327
+ { "path": "apps/web/src/settings/DispatchSettingsPage.tsx", "additions": 210, "deletions": 90, "changeType": "MODIFIED" },
328
+ { "path": "apps/web/src/settings/DispatchSettingsForm.tsx", "additions": 180, "deletions": 70, "changeType": "ADDED" },
329
+ { "path": "services/api/src/settings/dispatch-settings.ts", "additions": 190, "deletions": 88, "changeType": "MODIFIED" },
330
+ { "path": "workers/dispatch/settings-sync.ts", "additions": 150, "deletions": 64, "changeType": "ADDED" },
331
+ { "path": "tests/integration/dispatch-settings.test.ts", "additions": 120, "deletions": 30, "changeType": "ADDED" },
332
+ { "path": ".github/workflows/dispatch-settings.yml", "additions": 62, "deletions": 20, "changeType": "ADDED" },
333
+ { "path": "docs/initiatives/dispatch-settings/prd.md", "additions": 48, "deletions": 22, "changeType": "MODIFIED" },
334
+ { "path": "packages/generated-client/settings.ts", "additions": 20, "deletions": 36, "changeType": "MODIFIED" }
335
+ ],
336
+ "reviews": [
337
+ { "id": "sample-review-104-a", "author": { "login": "sample-reviewer-a", "type": "User" }, "submittedAt": "2026-05-14T16:00:00Z", "state": "CHANGES_REQUESTED", "commitOid": "sample104a", "generatedCommentCount": 0, "failedAttempt": true },
338
+ { "id": "sample-review-104-b", "author": { "login": "sample-reviewer-b", "type": "User" }, "submittedAt": "2026-05-15T14:00:00Z", "state": "CHANGES_REQUESTED", "commitOid": "sample104b", "generatedCommentCount": 0, "failedAttempt": true },
339
+ { "id": "sample-review-104-c", "author": { "login": "sample-reviewer-a", "type": "User" }, "submittedAt": "2026-05-16T18:30:00Z", "state": "COMMENTED", "commitOid": "sample104c", "generatedCommentCount": 0, "failedAttempt": false },
340
+ { "id": "sample-review-104-d", "author": { "login": "sample-reviewer-b", "type": "User" }, "submittedAt": "2026-05-17T17:45:00Z", "state": "APPROVED", "commitOid": "sample104d", "generatedCommentCount": 0, "failedAttempt": false }
341
+ ],
342
+ "reviewThreads": {
343
+ "source": "bundled tutorial sample:review_threads",
344
+ "totalCount": 3,
345
+ "nodes": [
346
+ { "id": "sample-thread-104-a", "isResolved": true, "isOutdated": true, "path": "services/api/src/settings/dispatch-settings.ts", "line": 65, "comments": [
347
+ { "databaseId": "sample-comment-104-a", "author": { "login": "sample-reviewer-a", "type": "User" }, "path": "services/api/src/settings/dispatch-settings.ts", "line": 65, "originalLine": 65, "createdAt": "2026-05-14T16:02:00Z", "updatedAt": "2026-05-14T16:02:00Z", "url": "https://example.com/pull/104#discussion-sample-104-a" },
348
+ { "databaseId": "sample-comment-104-b", "author": { "login": "sample-author-platform", "type": "User" }, "path": "services/api/src/settings/dispatch-settings.ts", "line": 65, "originalLine": 65, "createdAt": "2026-05-15T10:24:00Z", "updatedAt": "2026-05-15T10:24:00Z", "url": "https://example.com/pull/104#discussion-sample-104-b" }
349
+ ] },
350
+ { "id": "sample-thread-104-b", "isResolved": true, "isOutdated": false, "path": "apps/web/src/settings/DispatchSettingsForm.tsx", "line": 112, "comments": [
351
+ { "databaseId": "sample-comment-104-c", "author": { "login": "sample-reviewer-b", "type": "User" }, "path": "apps/web/src/settings/DispatchSettingsForm.tsx", "line": 112, "originalLine": 112, "createdAt": "2026-05-15T14:05:00Z", "updatedAt": "2026-05-15T14:05:00Z", "url": "https://example.com/pull/104#discussion-sample-104-c" },
352
+ { "databaseId": "sample-comment-104-d", "author": { "login": "sample-author-platform", "type": "User" }, "path": "apps/web/src/settings/DispatchSettingsForm.tsx", "line": 112, "originalLine": 112, "createdAt": "2026-05-16T16:40:00Z", "updatedAt": "2026-05-16T16:40:00Z", "url": "https://example.com/pull/104#discussion-sample-104-d" }
353
+ ] },
354
+ { "id": "sample-thread-104-c", "isResolved": false, "isOutdated": false, "path": ".github/workflows/dispatch-settings.yml", "line": 18, "comments": [
355
+ { "databaseId": "sample-comment-104-e", "author": { "login": "sample-ci-bot", "type": "Bot" }, "path": ".github/workflows/dispatch-settings.yml", "line": 18, "originalLine": 18, "createdAt": "2026-05-17T13:10:00Z", "updatedAt": "2026-05-17T13:10:00Z", "url": "https://example.com/pull/104#discussion-sample-104-e" }
356
+ ] }
357
+ ]
358
+ },
359
+ "statusCheckRollup": [
360
+ { "__typename": "CheckRun", "name": "unit", "context": null, "workflowName": "CI", "status": "COMPLETED", "conclusion": "FAILURE", "startedAt": "2026-05-14T08:10:00Z", "completedAt": "2026-05-14T08:16:00Z" },
361
+ { "__typename": "CheckRun", "name": "integration", "context": null, "workflowName": "CI", "status": "COMPLETED", "conclusion": "FAILURE", "startedAt": "2026-05-14T08:12:00Z", "completedAt": "2026-05-14T08:28:00Z" },
362
+ { "__typename": "CheckRun", "name": "migration", "context": null, "workflowName": "Migration checks", "status": "COMPLETED", "conclusion": "SUCCESS", "startedAt": "2026-05-17T13:10:00Z", "completedAt": "2026-05-17T13:24:00Z" }
363
+ ],
364
+ "workflowRuns": {
365
+ "source": "bundled tutorial sample:workflow_runs",
366
+ "totalCount": 4,
367
+ "conclusions": { "success": 1, "failure": 2, "cancelled": 1 },
368
+ "runs": [
369
+ { "id": "sample-run-104-a", "name": "CI", "workflowName": "CI", "headSha": "sample104a", "headBranch": "sample/consolidate-dispatch-settings", "event": "pull_request", "status": "completed", "conclusion": "failure", "createdAt": "2026-05-14T08:10:00Z", "updatedAt": "2026-05-14T08:28:00Z", "runStartedAt": "2026-05-14T08:12:00Z", "htmlUrl": "https://example.com/actions/runs/sample-run-104-a" },
370
+ { "id": "sample-run-104-b", "name": "CI", "workflowName": "CI", "headSha": "sample104b", "headBranch": "sample/consolidate-dispatch-settings", "event": "pull_request", "status": "completed", "conclusion": "failure", "createdAt": "2026-05-15T10:30:00Z", "updatedAt": "2026-05-15T10:48:00Z", "runStartedAt": "2026-05-15T10:31:00Z", "htmlUrl": "https://example.com/actions/runs/sample-run-104-b" },
371
+ { "id": "sample-run-104-c", "name": "CI", "workflowName": "CI", "headSha": "sample104c", "headBranch": "sample/consolidate-dispatch-settings", "event": "pull_request", "status": "completed", "conclusion": "cancelled", "createdAt": "2026-05-16T16:36:00Z", "updatedAt": "2026-05-16T16:39:00Z", "runStartedAt": "2026-05-16T16:37:00Z", "htmlUrl": "https://example.com/actions/runs/sample-run-104-c" },
372
+ { "id": "sample-run-104-d", "name": "Migration checks", "workflowName": "Migration checks", "headSha": "sample104d", "headBranch": "sample/consolidate-dispatch-settings", "event": "pull_request", "status": "completed", "conclusion": "success", "createdAt": "2026-05-17T13:10:00Z", "updatedAt": "2026-05-17T13:24:00Z", "runStartedAt": "2026-05-17T13:11:00Z", "htmlUrl": "https://example.com/actions/runs/sample-run-104-d" }
373
+ ]
374
+ },
375
+ "coverage": {
376
+ "prOpenDiff": { "family": "pr_open_diff", "source": "bundled tutorial sample", "status": "available", "attempts": 1, "diagnostics": [], "downstreamImpact": "Diff-growth metrics can compare open and merge sizes." },
377
+ "reviewThreads": { "family": "review_threads", "source": "bundled tutorial sample", "status": "available", "attempts": 1, "diagnostics": [], "downstreamImpact": "Review-thread evidence is available." },
378
+ "workflowRuns": { "family": "workflow_runs", "source": "bundled tutorial sample", "status": "partial", "attempts": 1, "diagnostics": ["One cancelled run is included to demonstrate validation caveats."], "downstreamImpact": "Validation-gap metrics include observed failed and cancelled workflow runs." }
379
+ }
380
+ }
381
+ ],
382
+ "raw": {
383
+ "sample": {
384
+ "description": "Fictional tutorial data for Delivery Friction Analyzer.",
385
+ "containsPrivateData": false
386
+ }
387
+ }
388
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "delivery-friction-analyzer",
3
- "version": "0.17.0",
3
+ "version": "0.18.0",
4
4
  "description": "Local GitHub pull request analytics for delivery friction reports.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -10,6 +10,7 @@
10
10
  "files": [
11
11
  "src",
12
12
  "schemas",
13
+ "examples/tutorial",
13
14
  "docs/contracts",
14
15
  "docs/reference",
15
16
  "fixtures/github/mcp-writing/profile.json",
package/release-log.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ### 2026-06-27 — Tutorial Sample Bundle
6
+
7
+ - What changed: Added the packaged tutorial sample source bundle, educational sample profile, and generated-aligned report excerpt for a fully synthetic repository.
8
+ - Why it matters: Maintainers can review and ship a public-safe sample report experience without relying on private repository access or live GitHub data.
9
+ - Who is affected: Maintainers preparing the tutorial sample experience and future users who inspect the bundled sample artifacts after sample CLI wiring lands.
10
+ - Action needed: None.
11
+ - PR: https://github.com/hannasdev/delivery-friction-analyzer/pull/61
12
+
5
13
  ### 2026-06-26 — Generic Source Bundle Report Wording
6
14
 
7
15
  - What changed: Report Markdown, methodology-adjacent contract docs, and CSV coverage expectations now use generic source-bundle and source-family wording for `source-bundle.v1`, including `collectionCoverage.status` and `collectionCoverage.sourceFamilies`.