memento-mori-jester 0.1.77 → 0.1.79

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 CHANGED
@@ -4,6 +4,18 @@ All notable changes to Memento Mori Jester are tracked here.
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## 0.1.79
8
+
9
+ - Added `npm run promo:check` to verify the current repo-local promo video, stills, docs, and fixture evidence numbers stay aligned.
10
+ - Wired promo freshness validation into `npm test` and the production-readiness guard.
11
+ - Updated release, readiness, roadmap, and promo docs with the new maintainer check.
12
+
13
+ ## 0.1.78
14
+
15
+ - Added a refreshed HyperFrames X demo render under `promo/x-demo-v0.1.78` with current version and fixture-evidence numbers.
16
+ - Updated the promo share-kit stills from the fresh render so public images show `v0.1.78`, 216 fixtures, and 6 quiet-pass examples.
17
+ - Updated promo docs, demo transcript, roadmap, and release notes while keeping `promo/` outside the npm package.
18
+
7
19
  ## 0.1.77
8
20
 
9
21
  - Added a repo-local promo/share kit with X post copy, a 30-second demo script, posting checklist, and asset guidance.
package/README.md CHANGED
@@ -503,6 +503,7 @@ Use the false-positive template for noisy cautions or blocks. Include `jester su
503
503
  Maintainers can use [docs/MAINTAINER_TRIAGE.md](docs/MAINTAINER_TRIAGE.md) to turn useful false-positive reports into redacted fixtures.
504
504
  Run `npm run fixtures:check` before merging fixture changes; it catches duplicate IDs, missing rule metadata, weak descriptions, unsafe-looking content, and duplicate content.
505
505
  Run `npm run fixtures:report` to see fixture coverage by rule, rule family, preset slice, kind, verdict, quiet-pass boundaries, feasible pass-case gaps, and curation-next guidance before choosing the next fixture. Use `npm run fixtures:report -- --markdown` when you want a paste-ready summary for release notes or GitHub issues.
506
+ Run `npm run promo:check` after editing promo assets; it checks the current demo video, stills, docs, and fixture evidence numbers stay in sync.
506
507
 
507
508
  For vulnerabilities, private code exposure, or credential-handling concerns, follow [SECURITY.md](SECURITY.md) instead of opening a public issue with sensitive details.
508
509
 
@@ -512,6 +513,7 @@ Release checklist:
512
513
 
513
514
  ```powershell
514
515
  npm.cmd test
516
+ npm.cmd run promo:check
515
517
  npm.cmd run production:check
516
518
  npm.cmd run pack:dry
517
519
  git tag -a v0.1.x -m "Memento Mori Jester v0.1.x"
package/ROADMAP.md CHANGED
@@ -6,6 +6,8 @@ Memento Mori Jester is usable today as a CLI, MCP server, GitHub Action, and git
6
6
 
7
7
  ## Recently Shipped
8
8
 
9
+ - Promo freshness check in v0.1.79, verifying the current demo video, share-kit stills, docs, and fixture evidence numbers before public posting.
10
+ - Fresh demo render in v0.1.78, updating the repo-local X video and share-kit stills to current version and fixture totals.
9
11
  - Promo/share kit in v0.1.77, adding X post copy, a short demo script, a posting checklist, and still images from the existing demo video.
10
12
  - Real-world preset quiet-pass curation in v0.1.76, adding eight safe examples across python, security, web, and AI workflows while keeping fixture coverage gaps clean.
11
13
  - Markdown fixture report export in v0.1.75 for paste-ready coverage snapshots in release notes, GitHub issues, and maintainer updates.
@@ -68,7 +70,7 @@ Memento Mori Jester is usable today as a CLI, MCP server, GitHub Action, and git
68
70
 
69
71
  - Collect real-world reports for the next lowest-count preset slices now highlighted by `fixtures:report`.
70
72
  - Add more framework-specific false-positive examples from real reports so tuning guidance keeps getting sharper.
71
- - Add a fresh demo render that reflects the latest release number and fixture totals.
73
+ - Add a lightweight social preview card or landing-page still for GitHub and X link previews.
72
74
 
73
75
  ## Quality And Safety
74
76
 
package/docs/DEMO.md CHANGED
@@ -189,20 +189,37 @@ Source: built-in
189
189
  Kinds: plan, command, diff, final
190
190
  Project config: none loaded
191
191
 
192
+ Why it exists:
193
+ Auth, billing, production, migrations, and similar domains have outsized user or business impact.
194
+
195
+ When it may be noisy:
196
+ It can be noisy in docs, release notes, or rule text that merely mentions a sensitive word.
197
+
198
+ Safer move:
199
+ Add targeted tests, a manual verification note, or a rollback path for the sensitive area.
200
+
201
+ Recommendation:
202
+ If repeated hits are harmless for this repo, disable the rule and validate the config.
203
+
204
+ Before muting:
205
+ - Confirm the latest hit is harmless, documentation-only, example-only, or already covered by another guard.
206
+ - Prefer fixing the risky change or adding verification when the rule found real risk.
207
+ - Prefer muting only after repeated false positives in this repo.
208
+
192
209
  Fixture tuning evidence:
193
210
  Support: limited
194
211
  Confidence: medium
195
- Total fixtures checked: 208
196
- Weighted fixtures checked: 399.2
212
+ Total fixtures checked: 216
213
+ Weighted fixtures checked: 412.5
197
214
  Matching fixtures: 11
198
215
  Weighted matches: 23
199
216
  Expected-match weight: 18
200
217
  Unexpected-match weight: 5
201
218
  Edge-case matches: 0
202
- Quiet-pass fixtures: 5
203
- Quiet-pass weight: 3.6
219
+ Quiet-pass fixtures: 6
220
+ Quiet-pass weight: 4.25
204
221
  By kind: command 0, plan 5, diff 5, final 1
205
- Fixture coverage: 11/208 (5.8% weighted)
222
+ Fixture coverage: 11/216 (5.6% weighted)
206
223
  By verdict: pass 0, caution 3, block 8
207
224
  Matched fixture samples:
208
225
  infra-public-ingress-block: Public ingress should block in low-risk-tolerance infra repos.
@@ -217,9 +234,6 @@ Quiet-pass fixture samples:
217
234
  universal-risky-domain-docs-pass: Documentation-only sensitive-domain vocabulary should stay quiet when no code behavior changes.
218
235
  web-docs-only-browser-storage-pass: Docs-only web guidance should not warn just because it mentions browser storage or redirects.
219
236
 
220
- When it may be noisy:
221
- It can be noisy in docs, release notes, or rule text that merely mentions a sensitive word.
222
-
223
237
  Commands:
224
238
  jester rule risky-domain
225
239
  jester config disable-rule risky-domain
@@ -9,6 +9,7 @@ This checklist defines what "production grade" means for Memento Mori Jester rig
9
9
  - GitHub Releases and npm publishing are automated from annotated `v*` tags through GitHub Actions trusted publishing.
10
10
  - CI runs tests and a package dry run on every push to `main` and pull request.
11
11
  - The local playground, GitHub Action, MCP setup snippets, preset examples, fixtures, and release notes ship in the npm package.
12
+ - Repo-local promo assets stay outside the npm package, but `npm run promo:check` keeps the current demo video, stills, docs, and fixture evidence numbers aligned.
12
13
 
13
14
  ## npm Package
14
15
 
@@ -54,6 +55,7 @@ This checklist defines what "production grade" means for Memento Mori Jester rig
54
55
  - `docs/MAINTAINER_TRIAGE.md` explains how to turn useful false-positive reports into fixture coverage before changing rule logic.
55
56
  - `npm run fixtures:check` validates fixture IDs, metadata, unsafe-looking content, duplicate content, and explicit expected/absent rule intent.
56
57
  - `npm run fixtures:report` shows fixture coverage by rule, rule family, preset slice, kind, verdict, quiet-pass rule boundaries, and feasible pass-case gaps so maintainers can pick the next fixture target; `npm run fixtures:report -- --markdown` produces a paste-ready maintainer snapshot.
58
+ - `npm run promo:check` verifies current repo-local promo assets against the current fixture evidence before maintainers post or refresh the demo.
57
59
  - npm publish has a manual workflow fallback, but the normal release path is tag-driven trusted publishing.
58
60
 
59
61
  ## Static Guard
@@ -69,6 +71,7 @@ This checklist defines what "production grade" means for Memento Mori Jester rig
69
71
  - maintainer triage docs exist and link noisy-rule reports back to fixture coverage.
70
72
  - fixture authoring checks are wired into `npm test`.
71
73
  - fixture coverage reports are wired into `npm test`.
74
+ - promo freshness checks are wired into `npm test`.
72
75
 
73
76
  `npm test` runs this check after the TypeScript build and unit tests.
74
77
 
package/docs/RELEASE.md CHANGED
@@ -12,6 +12,7 @@ npm.cmd run fixtures:check
12
12
  npm.cmd run fixtures:report
13
13
  npm.cmd run fixtures:report -- --json
14
14
  npm.cmd run fixtures:report -- --markdown
15
+ npm.cmd run promo:check
15
16
  npm.cmd run pack:dry
16
17
  git diff --check
17
18
  ```
@@ -0,0 +1,49 @@
1
+ # Memento Mori Jester v0.1.78
2
+
3
+ ## Summary
4
+
5
+ This release refreshes the repo-local X demo video and share-kit stills so public promo assets show the current release number and fixture evidence.
6
+
7
+ ## What Changed
8
+
9
+ - Added `promo/x-demo-v0.1.78` as the current editable HyperFrames demo source.
10
+ - Rendered `promo/x-demo-v0.1.78/renders/memento-mori-jester-x-demo-v0.1.78.mp4`.
11
+ - Updated the share-kit stills from the fresh render.
12
+ - Updated promo docs, demo transcript, roadmap, changelog, and release notes for the refreshed demo asset.
13
+
14
+ ## Public Interface
15
+
16
+ - No CLI command changes.
17
+ - No MCP tool changes.
18
+ - No config schema changes.
19
+ - No review rule, scoring, or verdict behavior changes.
20
+ - No GitHub Action behavior changes.
21
+ - `promo/` remains outside the npm package `files` list.
22
+
23
+ ## Release Validation
24
+
25
+ ```powershell
26
+ npm.cmd test
27
+ npm.cmd run demo:svg:check
28
+ Push-Location promo\x-demo-v0.1.78
29
+ npm.cmd run check
30
+ Pop-Location
31
+ npm.cmd run pack:dry
32
+ git diff --check
33
+ git diff | node .\dist\cli.js diff --fail-on block --subject "v0.1.78 fresh demo render"
34
+ ```
35
+
36
+ Additional media checks:
37
+
38
+ ```powershell
39
+ $ffprobe = Resolve-Path promo\x-demo-v0.1.78\node_modules\ffprobe-static\bin\win32\x64\ffprobe.exe
40
+ & $ffprobe -v error -select_streams v:0 -show_entries stream=width,height,r_frame_rate,duration -of default=noprint_wrappers=1 promo\x-demo-v0.1.78\renders\memento-mori-jester-x-demo-v0.1.78.mp4
41
+ ```
42
+
43
+ Expected:
44
+
45
+ - video is 1080x1920, 42 seconds, 30fps,
46
+ - share-kit stills render clearly with `v0.1.78`, 216 fixtures, and 6 quiet-pass examples,
47
+ - promo files are tracked in Git,
48
+ - promo files are not included in the npm tarball,
49
+ - GitHub Release and npm Publish complete from the `v0.1.78` tag.
@@ -0,0 +1,47 @@
1
+ # Memento Mori Jester v0.1.79
2
+
3
+ ## Summary
4
+
5
+ This release adds a repo-local promo freshness check so maintainers can verify the current demo video, stills, docs, and fixture evidence numbers before posting or refreshing public assets.
6
+
7
+ ## What Changed
8
+
9
+ - Added `scripts/check-promo-freshness.mjs`.
10
+ - Added `npm run promo:check`.
11
+ - Wired `promo:check` into `npm test`.
12
+ - Updated production-readiness checks so the promo freshness guard cannot silently disappear.
13
+ - Updated README, release docs, production-readiness docs, promo docs, roadmap, changelog, and release notes.
14
+
15
+ ## Public Interface
16
+
17
+ - No CLI command changes.
18
+ - No MCP tool changes.
19
+ - No config schema changes.
20
+ - No review rule, scoring, or verdict behavior changes.
21
+ - No GitHub Action behavior changes.
22
+ - `promo/` remains outside the npm package `files` list.
23
+
24
+ ## Release Validation
25
+
26
+ ```powershell
27
+ npm.cmd test
28
+ npm.cmd run demo:svg:check
29
+ npm.cmd run promo:check
30
+ npm.cmd run pack:dry
31
+ git diff --check
32
+ git diff | node .\dist\cli.js diff --fail-on block --subject "v0.1.79 promo freshness check"
33
+ ```
34
+
35
+ For a future same-version promo refresh, maintainers can run:
36
+
37
+ ```powershell
38
+ npm.cmd run promo:check -- --require-package-version
39
+ ```
40
+
41
+ This release does not require that strict mode because the current public demo snapshot remains `x-demo-v0.1.78`.
42
+
43
+ Expected:
44
+
45
+ - default `promo:check` passes for the current published demo snapshot,
46
+ - `--require-package-version` is available for intentional same-version promo refreshes,
47
+ - GitHub Release and npm Publish complete from the `v0.1.79` tag.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memento-mori-jester",
3
- "version": "0.1.77",
3
+ "version": "0.1.79",
4
4
  "description": "A local court-jester sidecar for AI coding agents: review plans, commands, diffs, and final claims before they get too pleased with themselves.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -40,12 +40,13 @@
40
40
  "build": "tsc -p tsconfig.json",
41
41
  "start": "node dist/server.js",
42
42
  "start:mcp": "node dist/server.js",
43
- "test": "npm run build && node scripts/run-tests.mjs && npm run fixtures:check && npm run fixtures:report && npm run production:check",
43
+ "test": "npm run build && node scripts/run-tests.mjs && npm run fixtures:check && npm run fixtures:report && npm run promo:check && npm run production:check",
44
44
  "doctor": "node dist/cli.js doctor",
45
45
  "demo:svg": "node scripts/render-demo-svg.mjs",
46
46
  "demo:svg:check": "node scripts/render-demo-svg.mjs --check",
47
47
  "fixtures:check": "node scripts/check-fixtures.mjs",
48
48
  "fixtures:report": "node scripts/report-fixtures.mjs",
49
+ "promo:check": "node scripts/check-promo-freshness.mjs",
49
50
  "production:check": "node scripts/check-production-readiness.mjs",
50
51
  "pack:dry": "npm pack --dry-run",
51
52
  "prepare": "npm run build",
@@ -64,6 +64,7 @@ for (const path of [
64
64
  "docs/MAINTAINER_TRIAGE.md",
65
65
  `docs/RELEASE_NOTES_${tag}.md`,
66
66
  "action.yml",
67
+ "scripts/check-promo-freshness.mjs",
67
68
  "scripts/check-fixtures.mjs",
68
69
  "scripts/report-fixtures.mjs",
69
70
  ".github/ISSUE_TEMPLATE/bug_report.yml",
@@ -132,8 +133,11 @@ requireText("scripts/report-fixtures.mjs", /--markdown/, "Markdown fixture repor
132
133
  forbidText("scripts/report-fixtures.mjs", /src\/config\.ts|src\/types\.ts/, "source-only fixture report dependencies");
133
134
  requireText("package.json", /"fixtures:check": "node scripts\/check-fixtures\.mjs"/, "fixture authoring check script");
134
135
  requireText("package.json", /"fixtures:report": "node scripts\/report-fixtures\.mjs"/, "fixture coverage report script");
136
+ requireText("package.json", /"promo:check": "node scripts\/check-promo-freshness\.mjs"/, "promo freshness check script");
135
137
  requireText("package.json", /npm run fixtures:check/, "fixture authoring check in npm test");
136
138
  requireText("package.json", /npm run fixtures:report/, "fixture coverage report in npm test");
139
+ requireText("package.json", /npm run promo:check/, "promo freshness check in npm test");
140
+ requireText("scripts/check-promo-freshness.mjs", /--require-package-version/, "optional strict package-version promo check");
137
141
  requireText("SECURITY.md", /doctor --json/, "doctor JSON redaction guidance");
138
142
  requireText("SECURITY.md", /security\/advisories\/new/, "private vulnerability report link");
139
143
  requireText(".github/ISSUE_TEMPLATE/bug_report.yml", /doctor --json/, "doctor JSON support prompt");
@@ -0,0 +1,201 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync, readFileSync, statSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { spawnSync } from "node:child_process";
5
+
6
+ const root = process.cwd();
7
+ const failures = [];
8
+ const args = new Set(process.argv.slice(2));
9
+ const requirePackageVersion = args.has("--require-package-version");
10
+
11
+ for (const arg of args) {
12
+ if (arg !== "--require-package-version") {
13
+ failures.push(`Unknown option: ${arg}`);
14
+ }
15
+ }
16
+
17
+ function read(path) {
18
+ return readFileSync(join(root, path), "utf8");
19
+ }
20
+
21
+ function readJson(path) {
22
+ return JSON.parse(read(path));
23
+ }
24
+
25
+ function escapeRegExp(value) {
26
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
27
+ }
28
+
29
+ function requireFile(path, description, minBytes = 1) {
30
+ const fullPath = join(root, path);
31
+ if (!existsSync(fullPath)) {
32
+ failures.push(`${description} is missing: ${path}`);
33
+ return;
34
+ }
35
+ const size = statSync(fullPath).size;
36
+ if (size < minBytes) {
37
+ failures.push(`${description} looks too small: ${path} (${size} bytes).`);
38
+ }
39
+ }
40
+
41
+ function requireText(path, pattern, description) {
42
+ const content = read(path);
43
+ if (!pattern.test(content)) {
44
+ failures.push(`${path} should include ${description}.`);
45
+ }
46
+ }
47
+
48
+ function failJson(path, error) {
49
+ failures.push(`${path} could not be parsed: ${error instanceof Error ? error.message : String(error)}.`);
50
+ }
51
+
52
+ function loadRiskyDomainEvidence() {
53
+ const cliPath = join(root, "dist", "cli.js");
54
+ if (!existsSync(cliPath)) {
55
+ failures.push("dist/cli.js is missing; run `npm run build` before `npm run promo:check`.");
56
+ return null;
57
+ }
58
+
59
+ const result = spawnSync(
60
+ process.execPath,
61
+ [cliPath, "tune", "risky-domain", "--json", "--no-config"],
62
+ { cwd: root, encoding: "utf8" }
63
+ );
64
+
65
+ if (result.status !== 0) {
66
+ failures.push(`Could not load risky-domain fixture evidence: ${result.stderr || result.stdout}`.trim());
67
+ return null;
68
+ }
69
+
70
+ try {
71
+ const parsed = JSON.parse(result.stdout);
72
+ return parsed.fixtureEvidence ?? null;
73
+ } catch (error) {
74
+ failures.push(`Could not parse risky-domain fixture evidence JSON: ${error instanceof Error ? error.message : String(error)}.`);
75
+ return null;
76
+ }
77
+ }
78
+
79
+ let packageJson;
80
+ try {
81
+ packageJson = readJson("package.json");
82
+ } catch (error) {
83
+ failJson("package.json", error);
84
+ packageJson = {};
85
+ }
86
+
87
+ let fixtures;
88
+ try {
89
+ fixtures = readJson("examples/fixtures/preset-review-cases.json");
90
+ } catch (error) {
91
+ failJson("examples/fixtures/preset-review-cases.json", error);
92
+ fixtures = [];
93
+ }
94
+
95
+ const promoReadmePath = "promo/README.md";
96
+ const promoReadme = read(promoReadmePath);
97
+ const currentVideo = promoReadme.match(/Final vertical demo video:\s*\[([^\]]+)\]\(([^)]+)\)/);
98
+
99
+ if (!currentVideo) {
100
+ failures.push(`${promoReadmePath} should link the final vertical demo video.`);
101
+ }
102
+
103
+ const linkedLabel = currentVideo?.[1] ?? "";
104
+ const linkedTarget = currentVideo?.[2] ?? "";
105
+ if (linkedLabel && linkedTarget && linkedLabel !== linkedTarget) {
106
+ failures.push(`${promoReadmePath} video link label and target should match.`);
107
+ }
108
+
109
+ const videoMatch = linkedTarget.match(/^x-demo-v(\d+\.\d+\.\d+)\/renders\/memento-mori-jester-x-demo-v\1\.mp4$/);
110
+ if (!videoMatch) {
111
+ failures.push(`${promoReadmePath} video should point at x-demo-vX.Y.Z/renders/memento-mori-jester-x-demo-vX.Y.Z.mp4.`);
112
+ }
113
+
114
+ const promoVersion = videoMatch?.[1] ?? "unknown";
115
+ const demoId = `x-demo-v${promoVersion}`;
116
+ const demoDir = `promo/${demoId}`;
117
+ const demoVideoPath = linkedTarget ? `promo/${linkedTarget}` : "";
118
+ const fixtureTotal = Array.isArray(fixtures) ? fixtures.length : 0;
119
+ const riskyEvidence = loadRiskyDomainEvidence();
120
+
121
+ if (requirePackageVersion && packageJson.version !== promoVersion) {
122
+ failures.push(`Current promo version v${promoVersion} should match package.json ${packageJson.version} when --require-package-version is used.`);
123
+ }
124
+
125
+ if (promoVersion !== "unknown") {
126
+ requireFile(`${demoDir}/index.html`, "current promo demo HTML");
127
+ requireFile(`${demoDir}/README.md`, "current promo demo README");
128
+ requireFile(`${demoDir}/package.json`, "current promo demo package.json");
129
+ requireFile(`${demoDir}/package-lock.json`, "current promo demo package-lock.json");
130
+ requireFile(`${demoDir}/meta.json`, "current promo demo metadata");
131
+ requireFile(demoVideoPath, "current promo demo video", 100_000);
132
+
133
+ for (const still of ["01-opener.jpg", "02-command-block.jpg", "03-tuning-evidence.jpg", "04-try-it.jpg"]) {
134
+ requireFile(`promo/share-kit/stills/${still}`, `share-kit still ${still}`, 50_000);
135
+ }
136
+
137
+ try {
138
+ const demoPackage = readJson(`${demoDir}/package.json`);
139
+ if (demoPackage.name !== demoId) {
140
+ failures.push(`${demoDir}/package.json name should be ${demoId}.`);
141
+ }
142
+ } catch (error) {
143
+ failJson(`${demoDir}/package.json`, error);
144
+ }
145
+
146
+ try {
147
+ const demoLock = readJson(`${demoDir}/package-lock.json`);
148
+ if (demoLock.name !== demoId || demoLock.packages?.[""]?.name !== demoId) {
149
+ failures.push(`${demoDir}/package-lock.json root name should be ${demoId}.`);
150
+ }
151
+ } catch (error) {
152
+ failJson(`${demoDir}/package-lock.json`, error);
153
+ }
154
+
155
+ try {
156
+ const demoMeta = readJson(`${demoDir}/meta.json`);
157
+ if (demoMeta.id !== demoId || demoMeta.name !== demoId) {
158
+ failures.push(`${demoDir}/meta.json id and name should be ${demoId}.`);
159
+ }
160
+ } catch (error) {
161
+ failJson(`${demoDir}/meta.json`, error);
162
+ }
163
+
164
+ const escapedVersion = escapeRegExp(promoVersion);
165
+ requireText(`${demoDir}/README.md`, new RegExp(`# Memento Mori Jester X Demo v${escapedVersion}`), `demo title v${promoVersion}`);
166
+ requireText(`${demoDir}/README.md`, new RegExp(`renders/memento-mori-jester-x-demo-v${escapedVersion}\\.mp4`), "current render path");
167
+ requireText(`${demoDir}/index.html`, new RegExp(`<span>v${escapedVersion}</span>`), `visible version v${promoVersion}`);
168
+ requireText(`${demoDir}/index.html`, new RegExp(`PASS package-version: ${escapedVersion}`), `doctor package version ${promoVersion}`);
169
+ requireText(`${demoDir}/index.html`, new RegExp(`<strong>${fixtureTotal}</strong>\\s*<span>fixtures checked</span>`), `${fixtureTotal} fixture count`);
170
+ requireText("promo/share-kit/README.md", new RegExp(escapeRegExp(`../${linkedTarget}`)), "current promo video path");
171
+
172
+ if (riskyEvidence) {
173
+ requireText(
174
+ `${demoDir}/index.html`,
175
+ new RegExp(`<strong>${riskyEvidence.matchCount}</strong>\\s*<span>risky-domain matches</span>`),
176
+ `${riskyEvidence.matchCount} risky-domain match count`
177
+ );
178
+ requireText(
179
+ `${demoDir}/index.html`,
180
+ new RegExp(`<strong>${riskyEvidence.quietPassCount}</strong>\\s*<span>quiet-pass examples</span>`),
181
+ `${riskyEvidence.quietPassCount} quiet-pass count`
182
+ );
183
+ requireText("docs/DEMO.md", new RegExp(`Total fixtures checked: ${riskyEvidence.totalFixtures}`), "current tune fixture total");
184
+ requireText("docs/DEMO.md", new RegExp(`Matching fixtures: ${riskyEvidence.matchCount}`), "current tune match count");
185
+ requireText("docs/DEMO.md", new RegExp(`Quiet-pass fixtures: ${riskyEvidence.quietPassCount}`), "current tune quiet-pass count");
186
+ }
187
+ }
188
+
189
+ if (failures.length > 0) {
190
+ process.stderr.write("Promo freshness check failed:\n");
191
+ for (const failure of failures) {
192
+ process.stderr.write(`- ${failure}\n`);
193
+ }
194
+ process.exit(1);
195
+ }
196
+
197
+ process.stdout.write(
198
+ `Promo freshness check passed for ${demoId}: ${fixtureTotal} fixtures, ` +
199
+ `${riskyEvidence?.matchCount ?? "unknown"} risky-domain matches, ` +
200
+ `${riskyEvidence?.quietPassCount ?? "unknown"} quiet-pass examples.\n`
201
+ );