Skip to content

Commit a2603c5

Browse files
fix(risk-map): scope billing globs to src/ and classify apps/website (#15415)
## Summary The `billing` R3 class matches filename characters, not concepts, and `apps/website/**` has no rule at all — so a PNG snapshot named `pricing-tiers-*.png` grades "flag + ephemeral env + Sheriff sign-off" while the production launch page next to it grades R0. This scopes the billing globs to the trees where billing code lives and gives the marketing site an explicit R2 class. ## Defects fixed (measured over 212 labelled PRs) Impact is measured by replaying the **real** changed-path lists of the 212 labelled PRs from the pilot window through `grade-pr-risk.sh --stdin`, live map vs. this map. Counts are PRs whose path floor moves, not estimates. | # | Defect | Kind | Real PRs | Evidence | |---|---|---|---|---| | D1 | `**/pricing/**` reaches into `apps/website/` — a coins-icon swap and a redundant-CSS-class removal grade R3 | over-grade | **16** | #15156, #15262, #14545 all match the `pricing/` **directory**, not the token globs | | D2 | bare filename tokens (`**/*credit*`, `**/*price*`, `**/*Subscription*`, …) match e2e specs, **PNG snapshots**, `.stories.ts`, `.test.ts`, `locales/*/pricing.json`, `docs/pricing-faq.md`, and `useSubscriptionCleanup.ts` (an *event* subscription) | over-grade | **11** | #14570 is `browser_tests/tests/priceBadge.spec.ts` + a binary screenshot baseline → R3 | | D4 | `apps/website/**` has no rule, and the map is **inverted** over it: pages grade R0 while a `pricing-*.png` beside them grades R3 | under-grade | **37 touch it, 25 touch nothing else** | #15201, a 19-file production launch page, contributes nothing to the floor | D1 and D4 are **coupled**. The path floor is a MAX over every matched rule, so adding an `apps/website/**` rule alone cannot fix D1 — the website rule only works paired with scoping billing off the website tree. ## Before / after — path-floor distribution over the same 212 PRs | path floor | current | this PR | |---|---|---| | R3 | 118 (55.7%) | **110 (51.9%)** | | R2 | 26 (12.3%) | **55 (25.9%)** | | R0 | 68 (32.1%) | **47 (22.2%)** | 31 PRs move: **23 R0 → R2** (coverage hole closed), **6 R3 → R2** and **2 R3 → R0** (over-grading relieved). 181 unchanged. **No PR moves up into R3.** Attribution also improves: PRs whose *only* R3 class is `billing` drop from **30 to 22**, and the `billing` class stops matching 10 PRs that never touched a money path. ## Backed by a synthetic path-glob suite The path-floor axis is a pure function of `(changed-path list, risk map)` — no network, no clock, no CI state, no author — so it is testable exhaustively at zero labelling cost. The suite pins provenance (`human`) and reversibility (`SUCCESS`) constant and asserts on `.risk.axes.path_floor.tier`, never the overall tier, so axis 1 is isolated by construction. Against 44 invariant cases + 28 contested cases: - **44/44 invariant cases still pass** (0 fail, 0 skip). These are the ones whose expected tier is not in dispute — positive coverage for every class, precedence, glob anchoring, composition, origin paths. - **13 of the 28 contested cases resolve, all in the argued direction** — 11 token-proxy over-grades and 2 coverage holes. 9 land exactly on the argued tier; 4 move toward it without reaching it (the two `apps/website` presentation cases land R2 rather than R0, which is the website rule doing its job). - No contested case moves the wrong way. Three glob-matcher behaviours the suite measured directly, worth knowing before editing this file: 1. `*` does **not** cross `/`. 2. A pattern with no leading `**/` is **root-anchored**. 3. **Character classes are not supported** — `[Bb]illing` matches a file literally named `[Bb]illing.ts`, not `Billing.ts`. Every case must be spelled out, which is why both casings appear explicitly. ## Review Focus **Three additions beyond the directly-measured proposal**, each closing a hole this diff would otherwise open. Drop any of them freely: - `src/**/credit/**` — singular, symmetric with the existing plural `credits/`; keeps `src/components/dialog/content/credit/**` at R3. - `src/config/*<token>*` — symmetric with the `src/stores/` and `src/services/` scoping; keeps `src/config/subscriptionPricesConfig.ts` at R3. - `src/**/*TopUp*.vue` — keeps `TopUpCreditsDialogContentLegacy.vue`, the legacy purchase dialog, at R3. The `.vue` suffix is deliberate: the unsuffixed form re-catches its `.test.ts` sibling, which is exactly D2. **Files that deliberately lose R3.** Seven shipping files under `src/` drop out of the billing class, all display-only: `UserCredit.vue`, `CreditsPanel.vue`, `CreditBadge.vue`, `NodePricingBadge.vue`, `CreditSlider.vue`, `useNodePricing.ts`, `usePriceBadge.ts`. They render numbers the backend already computed. They now land at `default_tier` because `src/components/**` and `src/composables/**` are unmapped — a separate coverage defect, not this one. If you want them at R2, the fix is a `src/components/**` rule, not a billing token glob. **`apps/website/**` at R2, not R0.** The site is production, but it is copy and layout — it cannot corrupt a saved workflow or move money. I did not add the decorative `website-content` R0 sub-rule for images and snapshots: under max semantics an R0 rule cannot lower anything, so it would annotate without changing a single grade. ## Deliberately out of scope These are real defects the same suite measured, and **a path map cannot fix any of them** — the discriminator is diff *content*, which is a closed question (rejected on data). They belong on the runbook / map-granularity track, not here: - **`.github/workflows/**` is R3 for everything** — 22 PRs are R3 with `ci` as their only R3 class, and a 2-line SHA **pin** bump (#15310, the *safe* operation) grades byte-identically to a credential-bearing workflow rewrite. Splitting privileged from routine workflows is a separate, contested change and needs its own conversation. - **`deps` cannot see a `package.json` version-field bump** vs. a real dependency addition — 13 PRs are R3 on `deps` alone; #15344 and 4 siblings were one-line version bumps. The lockfile delta is the available discriminator and the map does not use it. - **Comment-only diffs in R3 paths** grade R3. Also unfixed and recorded honestly: `src/platform/registry/accreditationBadge.ts` still grades R3. Prefix-scoping fixes *where* the token globs apply, not the substring match itself — "accreditation" contains "credit". Only deleting the bare-token globs entirely fixes that class of collision. ## Changes - **What**: `.github/risk.json` only — `billing` path globs scoped, new `website` R2 class. No code, no behaviour change outside the advisory risk label. - **Breaking**: none. Labels are advisory and do not gate merges.
1 parent 7494f71 commit a2603c5

1 file changed

Lines changed: 41 additions & 15 deletions

File tree

.github/risk.json

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -83,22 +83,42 @@
8383
{
8484
"class": "billing",
8585
"tier": "R3",
86-
"why": "billing / credits — money moves",
86+
"why": "billing / credits — money moves. Scoped to the application source tree: an unscoped **/ token glob matched the marketing site, e2e specs, PNG snapshots, stories, locale files and docs on filename characters alone",
8787
"paths": [
88-
"**/billing/**",
89-
"**/pricing/**",
90-
"**/credits/**",
91-
"**/subscription/**",
92-
"**/*billing*",
93-
"**/*Billing*",
94-
"**/*subscription*",
95-
"**/*Subscription*",
96-
"**/*credit*",
97-
"**/*Credit*",
98-
"**/*price*",
99-
"**/*Price*",
100-
"**/*pricing*",
101-
"**/*Pricing*"
88+
"src/**/billing/**",
89+
"src/**/pricing/**",
90+
"src/**/credit/**",
91+
"src/**/credits/**",
92+
"src/**/subscription/**",
93+
"src/**/*TopUp*.vue",
94+
"src/platform/**/*billing*",
95+
"src/platform/**/*Billing*",
96+
"src/platform/**/*subscription*",
97+
"src/platform/**/*Subscription*",
98+
"src/platform/**/*credit*",
99+
"src/platform/**/*Credit*",
100+
"src/platform/**/*price*",
101+
"src/platform/**/*Price*",
102+
"src/platform/**/*pricing*",
103+
"src/platform/**/*Pricing*",
104+
"src/stores/*billing*",
105+
"src/stores/*Billing*",
106+
"src/stores/*credit*",
107+
"src/stores/*Credit*",
108+
"src/services/*billing*",
109+
"src/services/*Billing*",
110+
"src/services/*credit*",
111+
"src/services/*Credit*",
112+
"src/config/*billing*",
113+
"src/config/*Billing*",
114+
"src/config/*credit*",
115+
"src/config/*Credit*",
116+
"src/config/*price*",
117+
"src/config/*Price*",
118+
"src/config/*pricing*",
119+
"src/config/*Pricing*",
120+
"src/config/*subscription*",
121+
"src/config/*Subscription*"
102122
]
103123
},
104124
{
@@ -198,6 +218,12 @@
198218
"packages/**"
199219
]
200220
},
221+
{
222+
"class": "website",
223+
"tier": "R2",
224+
"why": "the marketing site is production and had no rule at all — but it is copy and layout, so it cannot corrupt a saved workflow or move money",
225+
"paths": ["apps/website/**"]
226+
},
201227

202228
{
203229
"class": "docs",

0 commit comments

Comments
 (0)