mjolnir-qa 1.0.2 → 1.0.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/dist/cli.mjs CHANGED
@@ -10077,6 +10077,237 @@ function createScanCache(root) {
10077
10077
  };
10078
10078
  }
10079
10079
  //#endregion
10080
+ //#region src/brand/tokens.ts
10081
+ /**
10082
+ * The single source of brand truth.
10083
+ *
10084
+ * Every colour, typeface and motion constant Mjölnir shows a human —
10085
+ * terminal, README SVGs, demo video, website, docs, badges — resolves to
10086
+ * a value in this file. Nothing else may define one.
10087
+ *
10088
+ * WHY THIS EXISTS. Before it, the palette existed in six independent
10089
+ * copies: `site/.vitepress/theme/styles/vars.css`, `NORSE` in
10090
+ * `src/reporter/theme.ts`, `scripts/readme-svg.ts`,
10091
+ * `scripts/video/terminal-page.ts`, `scripts/generate-readme-architecture.ts`
10092
+ * and the table in `assets/brand/README.md`. Exactly one pair of those
10093
+ * was guarded (site-doctor Check 8, doc ↔ vars.css). The unguarded edges
10094
+ * are where the shipped surfaces drifted apart: the terminal and the site
10095
+ * disagreed on six semantic roles, the architecture diagram invented its
10096
+ * own neutral ramp, and the README badges still carried a palette retired
10097
+ * two releases earlier. `scripts/brand-doctor.mjs` now checks every edge
10098
+ * against this file.
10099
+ *
10100
+ * PURITY. Pure data. No I/O, no rendering, no environment access, no
10101
+ * imports, no logic. Consumers convert (hex → ANSI triplet, hex → CSS)
10102
+ * themselves. Same reason `score-state.ts` is pure: it makes the whole
10103
+ * thing golden-testable and safe to ship inside the npm package, where
10104
+ * it costs a few hundred bytes and replaces values the package already
10105
+ * carried anyway.
10106
+ *
10107
+ * DERIVATION. The palette is the one derived from the logo in PR #20
10108
+ * (brushed steel and forge gold under an aurora, over midnight iron).
10109
+ * Where the terminal disagreed with it, the terminal converges — see
10110
+ * `PENDING_TERMINAL` below. Full rationale: `assets/brand/README.md`.
10111
+ *
10112
+ * ACCESSIBILITY. Every foreground token in `BRAND` meets WCAG AA
10113
+ * (≥ 4.5:1) against every surface token it is allowed to sit on. That is
10114
+ * not a claim, it is `brand-doctor` rule 8, which computes the ratios.
10115
+ * The weakest legal pairing is `steelDim` on `ink800` at 5.00:1.
10116
+ */
10117
+ /**
10118
+ * The two brand hues plus the neutral they sit on.
10119
+ *
10120
+ * GOLD IS SCARCE. It means forged / certified / earned / decisive — the
10121
+ * primary mark, the FORGED state, one call to action. It is not a paint
10122
+ * bucket: gold as default text, default border or default heading is a
10123
+ * brand-doctor finding, not a style choice.
10124
+ *
10125
+ * AURORA is verification energy — the secondary, and the hue that marks
10126
+ * the runtime half of the trust ladder.
10127
+ */
10128
+ const BRAND = {
10129
+ gold: "#C19A34",
10130
+ goldBright: "#E6BD57",
10131
+ goldHot: "#F4DC9C",
10132
+ /** Pressed / deepest gold — the only step dark enough to carry white. */
10133
+ goldDeep: "#A5811C",
10134
+ aurora: "#37ABBD",
10135
+ auroraBright: "#45C1D4",
10136
+ auroraCyan: "#5CBDE0",
10137
+ steel: "#C8CBCF",
10138
+ steelDim: "#8B939D"
10139
+ };
10140
+ /**
10141
+ * Midnight iron. One ramp, four steps, darkest first.
10142
+ *
10143
+ * `terminal` and `terminalBar` share one tone deliberately: the window's
10144
+ * only seam is a hairline ring and an inset shadow, never a second fill.
10145
+ * `chromeDot` is the three window dots — see the note on
10146
+ * `PENDING_TERMINAL.chromeDots` for why they are no longer red/amber/green.
10147
+ */
10148
+ const SURFACE = {
10149
+ ink950: "#0A1119",
10150
+ ink900: "#0C1420",
10151
+ ink850: "#111A29",
10152
+ ink800: "#18243A",
10153
+ /** Raised panel (cards, elevated surfaces). */
10154
+ panel: "#141F33",
10155
+ /** Soft fill (inline code, quiet chips). */
10156
+ soft: "#1A2740",
10157
+ /** Terminal body — the deepest tone, so a terminal reads as recessed. */
10158
+ terminal: "#0A1119",
10159
+ /** Terminal title bar — the same tone; the seam is shadow, not colour. */
10160
+ terminalBar: "#0A1119",
10161
+ /** The three window dots. One neutral, not a traffic light. */
10162
+ chromeDot: "#18243A"
10163
+ };
10164
+ const TEXT = {
10165
+ primary: "#EAEEF5",
10166
+ secondary: "#ABB6C6",
10167
+ muted: "#8B939D",
10168
+ /** Ink for text set ON gold (buttons, the FORGED chip). 7.17:1 on `gold`. */
10169
+ onGold: "#0A1119"
10170
+ };
10171
+ /**
10172
+ * Non-score status. `ok` is the one green in the system and it is NOT a
10173
+ * score colour — it survives only for contexts with no worthiness
10174
+ * meaning ("autofix applied", "analysis complete"). A green score would
10175
+ * say "your software is fine", which is the exact claim this product
10176
+ * refuses to make.
10177
+ */
10178
+ const STATUS = {
10179
+ ok: "#4FB477",
10180
+ info: "#5CC4E0",
10181
+ warning: "#E6BD57",
10182
+ error: "#EC6B66"
10183
+ };
10184
+ /**
10185
+ * The four ScoreState bands plus the unmeasured state. Band thresholds
10186
+ * and runes live in `src/reporter/score-state.ts`, which stays free of
10187
+ * colour — it emits a palette KEY and each surface resolves it here.
10188
+ *
10189
+ * `unmeasured` is steel-dim on purpose. UNKNOWN is a legitimate answer,
10190
+ * not a failure: colouring it red would make "we did not measure this"
10191
+ * look like "this is broken", which is precisely the dishonesty the
10192
+ * north-star law exists to prevent.
10193
+ */
10194
+ const SCORE = {
10195
+ critical: "#EC6B66",
10196
+ warning: "#E6BD57",
10197
+ trusted: "#5CC4E0",
10198
+ forged: "#F4DC9C",
10199
+ unmeasured: "#8B939D"
10200
+ };
10201
+ /**
10202
+ * E0 → E1 → E2 is a certainty ramp, and it is deliberately HUE-FREE.
10203
+ *
10204
+ * Evidence level says how sure we are, not whether the news is good. A
10205
+ * deterministic proof (E2) is a defect we are certain about — painting
10206
+ * it gold or green would read as an achievement. So certainty is carried
10207
+ * by brightness alone, and the *shape* does the real work:
10208
+ *
10209
+ * E0 open ring observation, no weight
10210
+ * E1 half-filled pattern evidence, half weight
10211
+ * E2 sealed deterministic proof, full weight
10212
+ *
10213
+ * Colour never carries this alone (R11): the geometry is the signal and
10214
+ * survives `--ascii`, `NO_COLOR` and monochrome print.
10215
+ */
10216
+ const EVIDENCE = {
10217
+ e0: "#8B939D",
10218
+ e1: "#ABB6C6",
10219
+ e2: "#EAEEF5"
10220
+ };
10221
+ /**
10222
+ * L0–L5, and the most important boundary in the product.
10223
+ *
10224
+ * L0–L2 are STATIC: the neutral steel ramp, brightening to the static
10225
+ * ceiling at L2. L3–L5 require a real run, and the hue changes to aurora
10226
+ * exactly there. The boundary is a hue break, not a gradient step,
10227
+ * because it is a change of kind and not of degree — a static-only
10228
+ * finding can never climb past L2, however confident it is.
10229
+ *
10230
+ * Every surface that draws the ladder must draw that break.
10231
+ */
10232
+ const TRUST = {
10233
+ l0: "#8B939D",
10234
+ l1: "#ABB6C6",
10235
+ l2: "#C8CBCF",
10236
+ l3: "#37ABBD",
10237
+ l4: "#45C1D4",
10238
+ l5: "#5CC4E0"
10239
+ };
10240
+ const TINT = {
10241
+ gold: {
10242
+ fill: "#F6EBCC",
10243
+ stroke: "#7A5F16",
10244
+ text: "#4A3A0E"
10245
+ },
10246
+ aurora: {
10247
+ fill: "#D9F0F4",
10248
+ stroke: "#1F6F7C",
10249
+ text: "#10353C"
10250
+ },
10251
+ error: {
10252
+ fill: "#FADEDD",
10253
+ stroke: "#A83A35",
10254
+ text: "#4E1B19"
10255
+ },
10256
+ /** The unmeasured / unknown state. Neutral, never the error tint. */
10257
+ neutral: {
10258
+ fill: "#E4E7EB",
10259
+ stroke: "#5C646E",
10260
+ text: "#262B31"
10261
+ },
10262
+ ok: {
10263
+ fill: "#DCF0E4",
10264
+ stroke: "#276B45",
10265
+ text: "#163A26"
10266
+ }
10267
+ };
10268
+ /**
10269
+ * The score bands as the badge Mjölnir itself generates renders them.
10270
+ *
10271
+ * These are DEEPER than the score tokens on purpose, and it is not a
10272
+ * style preference: shields.io sets the message text in white and gives
10273
+ * you no say in it. `score.forged` (#F4DC9C) under white text measures
10274
+ * 1.35:1 — an unreadable badge, shipped to look on-brand. The brand's
10275
+ * own deep steps put every band between 4.9 and 6.3:1.
10276
+ *
10277
+ * What they replace was worse than off-brand, it was wrong:
10278
+ *
10279
+ * band was rendered as white-on now white-on
10280
+ * ────────────────────────────────────────────────────────────────────────────
10281
+ * 0-49 `red` #dd4343 4.24 #A83A35 6.32
10282
+ * 50-79 `yellow` #d8b800 1.95 #7A5F16 6.04
10283
+ * 80-99 `important` #ea7233 ORANGE 3.02 #1F6F7C 5.80
10284
+ * 100 `success` #44bb00 GREEN 2.51 #8A6D1E 4.90
10285
+ * unmeasured `lightgrey` #939393 3.07 #5C646E 5.99
10286
+ *
10287
+ * Two of those were defects, not preferences. `success` is green, and
10288
+ * green is not a score colour here — a 100 badge said "your software is
10289
+ * fine", which is the one claim this product refuses to make. And
10290
+ * `important` is ORANGE, not the blue-family colour the code's own
10291
+ * comment claimed for eight releases: every WORTHY badge ever rendered
10292
+ * showed the trusted band in a warning colour. Nobody had resolved a
10293
+ * shields name to a value and looked.
10294
+ *
10295
+ * Values are hex without `#`, the form shields.io's endpoint takes.
10296
+ * `A83A35`, `7A5F16`, `1F6F7C` and `5C646E` are the `TINT` strokes —
10297
+ * the same deep steps the mermaid diagrams use, for the same reason.
10298
+ * `8A6D1E` is the gold the brand document already named as the light
10299
+ * FORGED gradient's start.
10300
+ */
10301
+ const BADGE_BAND = {
10302
+ critical: "A83A35",
10303
+ warning: "7A5F16",
10304
+ trusted: "1F6F7C",
10305
+ forged: "8A6D1E",
10306
+ unmeasured: "5C646E"
10307
+ };
10308
+ SURFACE.ink950, SURFACE.ink900, SURFACE.ink850, SURFACE.ink800, BRAND.steel, BRAND.steelDim, BRAND.gold, BRAND.goldBright, BRAND.goldHot, BRAND.aurora, BRAND.auroraBright, BRAND.auroraCyan;
10309
+ SCORE.trusted, SCORE.forged, SCORE.warning, SCORE.critical, STATUS.info, TEXT.onGold, STATUS.ok, EVIDENCE.e0, EVIDENCE.e1, EVIDENCE.e2, TRUST.l0, TRUST.l1, TRUST.l2, TRUST.l3, TRUST.l4, TRUST.l5;
10310
+ //#endregion
10080
10311
  //#region src/reporter/score-state.ts
10081
10312
  const HEADLINES = {
10082
10313
  critical: "The hammer is cracked — {n} findings break its edge.",
@@ -10153,11 +10384,11 @@ function headlineFor(state, findings) {
10153
10384
  * Respects NO_COLOR and non-TTY via `palette(isTTY)` — every renderer
10154
10385
  * receives a palette and never touches process.env directly.
10155
10386
  *
10156
- * Palette: a cold northern set — frost-steel, aurora teal, Yggdrasil
10157
- * green — with amber for warnings (Mjölnir's lightning) and a
10158
- * rune-red for errors. No magenta/pink. Emitted as 24-bit truecolor
10159
- * SGR (`38;2;r;g;b`), which every modern terminal renders and which
10160
- * `shouldColorize` already gates behind TTY + !NO_COLOR.
10387
+ * Palette: resolved from `src/brand/tokens.ts`, the single source of
10388
+ * brand truth — this file defines no colour of its own. Emitted as
10389
+ * 24-bit truecolor SGR (`38;2;r;g;b`), which every modern terminal
10390
+ * renders and which `shouldColorize` already gates behind
10391
+ * TTY + !NO_COLOR.
10161
10392
  *
10162
10393
  * Symbols always accompany color (color-blind safe, R11).
10163
10394
  *
@@ -10170,53 +10401,42 @@ function headlineFor(state, findings) {
10170
10401
  * flag so output degrades to plain characters on cmd.exe/legacy
10171
10402
  * consoles that mangle box-drawing glyphs and emoji.
10172
10403
  */
10173
- /** Norse-forge palette, 24-bit truecolor. */
10404
+ /**
10405
+ * `"#RRGGBB"` → the `[r, g, b]` triplet the SGR truecolor emitter needs.
10406
+ * Lives here rather than in `src/brand/tokens.ts`, which is pure data:
10407
+ * each surface converts the canonical hex into its own colour space.
10408
+ */
10409
+ function fromHex(hex) {
10410
+ const n = Number.parseInt(hex.slice(1), 16);
10411
+ return [
10412
+ n >> 16 & 255,
10413
+ n >> 8 & 255,
10414
+ n & 255
10415
+ ];
10416
+ }
10417
+ /**
10418
+ * The terminal palette, 24-bit truecolor, resolved from
10419
+ * `src/brand/tokens.ts` — the single source of brand truth. Nothing in
10420
+ * this file may name a hex value of its own, and `brand-doctor` rule 2
10421
+ * fails if it tries.
10422
+ *
10423
+ * Every role is now the canonical token. Six of them used to be the
10424
+ * terminal's own: a frost-steel blue for headers, a teal for info, an
10425
+ * amber for warnings, a rune-red for errors, a bone white for bold and a
10426
+ * weathered stone for dim — a second palette for one product. The
10427
+ * rune-red also failed WCAG AA at 4.36:1 on this terminal's own
10428
+ * background; `STATUS.error` on the canonical ground is 6.20:1.
10429
+ */
10174
10430
  const NORSE = {
10175
- ok: [
10176
- 79,
10177
- 180,
10178
- 119
10179
- ],
10180
- info: [
10181
- 63,
10182
- 176,
10183
- 160
10184
- ],
10185
- accent: [
10186
- 138,
10187
- 180,
10188
- 216
10189
- ],
10190
- warning: [
10191
- 224,
10192
- 165,
10193
- 38
10194
- ],
10195
- error: [
10196
- 208,
10197
- 69,
10198
- 59
10199
- ],
10200
- trusted: [
10201
- 92,
10202
- 196,
10203
- 224
10204
- ],
10205
- forged: [
10206
- 244,
10207
- 220,
10208
- 156
10209
- ],
10210
- bold: [
10211
- 237,
10212
- 230,
10213
- 214
10214
- ],
10215
- dim: [
10216
- 124,
10217
- 133,
10218
- 144
10219
- ]
10431
+ ok: fromHex(STATUS.ok),
10432
+ info: fromHex(BRAND.aurora),
10433
+ accent: fromHex(BRAND.steel),
10434
+ warning: fromHex(STATUS.warning),
10435
+ error: fromHex(STATUS.error),
10436
+ trusted: fromHex(SCORE.trusted),
10437
+ forged: fromHex(SCORE.forged),
10438
+ bold: fromHex(TEXT.primary),
10439
+ dim: fromHex(TEXT.muted)
10220
10440
  };
10221
10441
  const on = {
10222
10442
  ok: rgb(NORSE.ok),
@@ -12858,16 +13078,47 @@ function countBySeverity(result) {
12858
13078
  }
12859
13079
  return counts;
12860
13080
  }
13081
+ EVIDENCE.e0, EVIDENCE.e1, EVIDENCE.e2;
13082
+ const RUNG_MEANINGS = [
13083
+ "observation only",
13084
+ "heuristic static",
13085
+ "deterministic static",
13086
+ "the finding's file executed",
13087
+ "the finding's test executed",
13088
+ "the run verdict corroborates"
13089
+ ];
13090
+ const RUNG_COLORS = [
13091
+ TRUST.l0,
13092
+ TRUST.l1,
13093
+ TRUST.l2,
13094
+ TRUST.l3,
13095
+ TRUST.l4,
13096
+ TRUST.l5
13097
+ ];
13098
+ const TRUST_RUNGS = RUNG_MEANINGS.map((meaning, i) => ({
13099
+ level: `L${i}`,
13100
+ meaning,
13101
+ runtime: i >= 3,
13102
+ color: RUNG_COLORS[i]
13103
+ }));
12861
13104
  //#endregion
12862
13105
  //#region src/reporter/trust-report.ts
12863
- const TRUST_LABELS = {
12864
- L0: "L0 · observation only",
12865
- L1: "L1 · heuristic static",
12866
- L2: "L2 · deterministic static",
12867
- L3: "L3 · file executed",
12868
- L4: "L4 · test executed",
12869
- L5: "L5 · run corroborates defect"
12870
- };
13106
+ /**
13107
+ * The rung labels, built from `src/brand/symbols.ts` rather than typed
13108
+ * again here.
13109
+ *
13110
+ * They had drifted the moment there were two copies: this file said
13111
+ * "file executed" and "run corroborates defect" where the symbol module,
13112
+ * the architecture diagram and the website's ladder all said "the
13113
+ * finding's file executed" and "the run verdict corroborates". Small
13114
+ * enough that nobody would notice, and exactly the kind of divergence
13115
+ * that makes a reader wonder whether two surfaces mean the same thing.
13116
+ *
13117
+ * The runtime marker is not decoration either: L3 and above cannot be
13118
+ * reached without a real run report, and the label says so wherever the
13119
+ * ladder is not drawn to show it.
13120
+ */
13121
+ const TRUST_LABELS = Object.fromEntries(TRUST_RUNGS.map((r) => [r.level, `${r.level} · ${r.meaning}${r.runtime ? " · runtime" : ""}`]));
12871
13122
  function pct$1(v) {
12872
13123
  return `${Math.round(v * 100)}%`;
12873
13124
  }
@@ -13003,7 +13254,7 @@ function renderSarif(result, repoRootUri) {
13003
13254
  tool: { driver: {
13004
13255
  name: "Mjölnir",
13005
13256
  informationUri: "https://github.com/Sergey-Bar/Mjolnir",
13006
- version: "1.0.2",
13257
+ version: "1.0.3",
13007
13258
  rules: [...rules.values()].map((r) => {
13008
13259
  const meta = RULES.find((x) => x.id === r.id);
13009
13260
  return {
@@ -13132,6 +13383,22 @@ function renderCodeQuality(result) {
13132
13383
  }
13133
13384
  //#endregion
13134
13385
  //#region src/reporter/mermaid.ts
13386
+ /**
13387
+ * `--format mermaid` — test-architecture diagram (Sprint 9 Task 38,
13388
+ * Master-Stabilization-Plan.md). Ranked first among the delight
13389
+ * features because it is genuinely useful to QA leads presenting scan
13390
+ * results to stakeholders, not merely decorative — a flowchart of
13391
+ * detected frameworks → rule categories → severity buckets, so gaps
13392
+ * are visible at a glance in a format that pastes directly into a
13393
+ * GitHub/GitLab markdown comment or a slide (Mermaid renders natively
13394
+ * in both).
13395
+ *
13396
+ * Score-neutral (Sprint 9's own DoD line): this is a pure alternate
13397
+ * rendering of the exact same ScanResult every other format uses — it
13398
+ * changes no scoring, no exit code, no JSON contract field. Output is
13399
+ * fully deterministic: every collection is sorted before rendering, so
13400
+ * the same ScanResult always produces byte-identical Mermaid source.
13401
+ */
13135
13402
  function sanitizeId(raw) {
13136
13403
  return raw.replace(/[^a-z0-9]/gi, "_");
13137
13404
  }
@@ -13139,6 +13406,14 @@ function sanitizeId(raw) {
13139
13406
  function escapeLabel(text) {
13140
13407
  return text.replaceAll("\"", """).replaceAll("\n", " ");
13141
13408
  }
13409
+ /**
13410
+ * One Mermaid `classDef` line from one brand tint. Every colour this
13411
+ * renderer emits comes from `src/brand/tokens.ts`; it names none of its
13412
+ * own, and `brand-doctor` rule 6 fails if it starts to.
13413
+ */
13414
+ function classDef(name, tint) {
13415
+ return ` classDef ${name} fill:${tint.fill},stroke:${tint.stroke},color:${tint.text};`;
13416
+ }
13142
13417
  function dimensionStyleClass(dim) {
13143
13418
  if (dim.score >= 80) return "healthy";
13144
13419
  if (dim.score >= 50) return "warn";
@@ -13157,8 +13432,8 @@ function renderMermaid(result) {
13157
13432
  if (result.score === null) {
13158
13433
  lines.push(` ${rootId} --> NOTESTS["No test files detected"]`);
13159
13434
  lines.push("");
13160
- lines.push(" classDef critical fill:#fee2e2,stroke:#b91c1c,color:#7f1d1d;");
13161
- lines.push(" class NOTESTS critical;");
13435
+ lines.push(classDef("unknown", TINT.neutral));
13436
+ lines.push(" class NOTESTS unknown;");
13162
13437
  return lines.join("\n");
13163
13438
  }
13164
13439
  const frameworks = [...result.frameworks].sort((a, b) => a.localeCompare(b));
@@ -13194,10 +13469,11 @@ function renderMermaid(result) {
13194
13469
  }
13195
13470
  }
13196
13471
  lines.push("");
13197
- lines.push(" classDef healthy fill:#dcfce7,stroke:#15803d,color:#14532d;");
13198
- lines.push(" classDef warn fill:#fef9c3,stroke:#a16207,color:#713f12;");
13199
- lines.push(" classDef critical fill:#fee2e2,stroke:#b91c1c,color:#7f1d1d;");
13200
- lines.push(" classDef info fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a;");
13472
+ lines.push(classDef("healthy", TINT.ok));
13473
+ lines.push(classDef("warn", TINT.gold));
13474
+ lines.push(classDef("critical", TINT.error));
13475
+ lines.push(classDef("info", TINT.aurora));
13476
+ lines.push(classDef("unknown", TINT.neutral));
13201
13477
  for (const { id, cls } of styleAssignments) lines.push(` class ${id} ${cls};`);
13202
13478
  return lines.join("\n");
13203
13479
  }
@@ -16469,17 +16745,19 @@ function renderMutationSummary(report) {
16469
16745
  * retarget fixes the historical threshold drift (the badge used
16470
16746
  * ≥90/≥75/≥50 with four bands while the reporter used ≥80/≥50).
16471
16747
  *
16472
- * Shields.io has no cyan or white-gold, so the mapping is documented
16473
- * here: trusted → `important` (blue-family, closest to aurora-cyan),
16474
- * forged → `success` (the strongest positive signal shields offers).
16475
- * The badge is a peripheral surface; ScoreState remains the truth.
16748
+ * They are brand values now, from `BADGE_BAND`, not shields.io's named
16749
+ * colors. That mapping was documented as "trusted → `important`
16750
+ * (blue-family, closest to aurora-cyan)" and was simply untrue:
16751
+ * `important` resolves to #ea7233, which is orange. Every WORTHY badge
16752
+ * rendered the trusted band in a warning colour, and `success` — the
16753
+ * 100 state — rendered green, which is not a score colour here.
16754
+ *
16755
+ * ScoreState remains the truth; the badge is still a peripheral
16756
+ * surface. But peripheral is not the same as unchecked.
16476
16757
  */
16477
16758
  function colorFor(score) {
16478
16759
  const band = deriveScoreState(score).band;
16479
- if (band === "unmeasured") return "lightgrey";
16480
- if (band === "forged") return "success";
16481
- if (band === "trusted") return "important";
16482
- return band === "warning" ? "yellow" : "red";
16760
+ return BADGE_BAND[band];
16483
16761
  }
16484
16762
  /** Build the shields.io endpoint payload from a scan result. */
16485
16763
  function buildBadge(result, commit) {
@@ -16490,7 +16768,6 @@ function buildBadge(result, commit) {
16490
16768
  label: "MJÖLNIR",
16491
16769
  message: score === null ? "no tests found" : score === 100 && errors === 0 ? "100/100 · forged" : `${score}/100 · ${errors} error${errors === 1 ? "" : "s"}`,
16492
16770
  color: colorFor(score),
16493
- namedLogo: "vitest",
16494
16771
  ...commit !== void 0 ? { commit } : {}
16495
16772
  };
16496
16773
  }
@@ -19434,7 +19711,7 @@ const { runScan, buildUniversalRules, fallbackWorkspace, pathMatchesGlob, isVali
19434
19711
  * `scripts/sync-sarif-version.cjs` on release and guarded by
19435
19712
  * `tests/version-consistency.spec.ts` locally.
19436
19713
  */
19437
- const CLI_VERSION = "1.0.2";
19714
+ const CLI_VERSION = "1.0.3";
19438
19715
  function parseArgs(argv, onError) {
19439
19716
  const args = {
19440
19717
  target: ".",