@supportpages.io/wtfm 0.0.0-stage → 0.3.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.
Files changed (275) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +23 -0
  3. package/README.md +392 -2
  4. package/dist/actions.d.ts +98 -0
  5. package/dist/actions.js +86 -0
  6. package/dist/actions.js.map +1 -0
  7. package/dist/agent-settings.d.ts +22 -0
  8. package/dist/agent-settings.js +35 -0
  9. package/dist/agent-settings.js.map +1 -0
  10. package/dist/api.d.ts +14 -0
  11. package/dist/api.js +159 -0
  12. package/dist/api.js.map +1 -0
  13. package/dist/article-link.d.ts +16 -0
  14. package/dist/article-link.js +25 -0
  15. package/dist/article-link.js.map +1 -0
  16. package/dist/artifacts.d.ts +45 -0
  17. package/dist/artifacts.js +144 -0
  18. package/dist/artifacts.js.map +1 -0
  19. package/dist/brand.d.ts +4 -0
  20. package/dist/brand.js +9 -0
  21. package/dist/brand.js.map +1 -0
  22. package/dist/bridge.d.ts +2964 -0
  23. package/dist/bridge.js +1036 -0
  24. package/dist/bridge.js.map +1 -0
  25. package/dist/capacity.d.ts +5 -0
  26. package/dist/capacity.js +11 -0
  27. package/dist/capacity.js.map +1 -0
  28. package/dist/credentials.d.ts +13 -0
  29. package/dist/credentials.js +88 -0
  30. package/dist/credentials.js.map +1 -0
  31. package/dist/development-tls.d.ts +8 -0
  32. package/dist/development-tls.js +38 -0
  33. package/dist/development-tls.js.map +1 -0
  34. package/dist/errors.d.ts +15 -0
  35. package/dist/errors.js +18 -0
  36. package/dist/errors.js.map +1 -0
  37. package/dist/export.d.ts +11 -0
  38. package/dist/export.js +91 -0
  39. package/dist/export.js.map +1 -0
  40. package/dist/hosted-operations.d.ts +108 -0
  41. package/dist/hosted-operations.js +120 -0
  42. package/dist/hosted-operations.js.map +1 -0
  43. package/dist/hosting-benefits.d.ts +66 -0
  44. package/dist/hosting-benefits.js +67 -0
  45. package/dist/hosting-benefits.js.map +1 -0
  46. package/dist/index.d.ts +2 -0
  47. package/dist/index.js +49 -0
  48. package/dist/index.js.map +1 -0
  49. package/dist/local-inventory.d.ts +22 -0
  50. package/dist/local-inventory.js +59 -0
  51. package/dist/local-inventory.js.map +1 -0
  52. package/dist/local-setup.d.ts +208 -0
  53. package/dist/local-setup.js +140 -0
  54. package/dist/local-setup.js.map +1 -0
  55. package/dist/pairing.d.ts +71 -0
  56. package/dist/pairing.js +235 -0
  57. package/dist/pairing.js.map +1 -0
  58. package/dist/preferences.d.ts +18 -0
  59. package/dist/preferences.js +44 -0
  60. package/dist/preferences.js.map +1 -0
  61. package/dist/progress.d.ts +205 -0
  62. package/dist/progress.js +224 -0
  63. package/dist/progress.js.map +1 -0
  64. package/dist/reminders.d.ts +31 -0
  65. package/dist/reminders.js +73 -0
  66. package/dist/reminders.js.map +1 -0
  67. package/dist/replace-connection.d.ts +6 -0
  68. package/dist/replace-connection.js +77 -0
  69. package/dist/replace-connection.js.map +1 -0
  70. package/dist/repository-actions.d.ts +18 -0
  71. package/dist/repository-actions.js +9 -0
  72. package/dist/repository-actions.js.map +1 -0
  73. package/dist/repository-benefits.d.ts +96 -0
  74. package/dist/repository-benefits.js +64 -0
  75. package/dist/repository-benefits.js.map +1 -0
  76. package/dist/run-update.d.ts +17 -0
  77. package/dist/run-update.js +27 -0
  78. package/dist/run-update.js.map +1 -0
  79. package/dist/runs.d.ts +541 -0
  80. package/dist/runs.js +146 -0
  81. package/dist/runs.js.map +1 -0
  82. package/dist/runtime.d.ts +12 -0
  83. package/dist/runtime.js +58 -0
  84. package/dist/runtime.js.map +1 -0
  85. package/dist/schema.d.ts +277 -0
  86. package/dist/schema.js +66 -0
  87. package/dist/schema.js.map +1 -0
  88. package/dist/server.d.ts +4 -0
  89. package/dist/server.js +298 -0
  90. package/dist/server.js.map +1 -0
  91. package/dist/session.d.ts +1714 -0
  92. package/dist/session.js +619 -0
  93. package/dist/session.js.map +1 -0
  94. package/dist/settings.d.ts +9 -0
  95. package/dist/settings.js +21 -0
  96. package/dist/settings.js.map +1 -0
  97. package/dist/sync.d.ts +495 -0
  98. package/dist/sync.js +191 -0
  99. package/dist/sync.js.map +1 -0
  100. package/dist/telemetry-scrub.d.ts +13 -0
  101. package/dist/telemetry-scrub.js +51 -0
  102. package/dist/telemetry-scrub.js.map +1 -0
  103. package/dist/telemetry.d.ts +73 -0
  104. package/dist/telemetry.js +173 -0
  105. package/dist/telemetry.js.map +1 -0
  106. package/dist/walkthroughs.d.ts +224 -0
  107. package/dist/walkthroughs.js +109 -0
  108. package/dist/walkthroughs.js.map +1 -0
  109. package/dist/workspace.d.ts +14 -0
  110. package/dist/workspace.js +127 -0
  111. package/dist/workspace.js.map +1 -0
  112. package/dist/writer-agent.d.ts +21 -0
  113. package/dist/writer-agent.js +27 -0
  114. package/dist/writer-agent.js.map +1 -0
  115. package/dist/writer-entry.d.ts +171 -0
  116. package/dist/writer-entry.js +233 -0
  117. package/dist/writer-entry.js.map +1 -0
  118. package/dist/writing-style.d.ts +7 -0
  119. package/dist/writing-style.js +52 -0
  120. package/dist/writing-style.js.map +1 -0
  121. package/engine/SYNC.json +4 -0
  122. package/engine/VERSION +1 -0
  123. package/engine/detect-project/README.md +141 -0
  124. package/engine/detect-project/SKILL.md +1421 -0
  125. package/engine/detect-project/assets/desktop/desktop-frame.css +428 -0
  126. package/engine/detect-project/assets/game/game-frame.css +132 -0
  127. package/engine/detect-project/assets/macosui/LICENSE-puppertino.txt +21 -0
  128. package/engine/detect-project/assets/macosui/VERSIONS.txt +1 -0
  129. package/engine/detect-project/assets/macosui/fonts.css +15 -0
  130. package/engine/detect-project/assets/macosui/macos-frame.css +481 -0
  131. package/engine/detect-project/assets/macosui/puppertino.css +2153 -0
  132. package/engine/detect-project/assets/mobileui/LICENSE-fonts.txt +13 -0
  133. package/engine/detect-project/assets/mobileui/LICENSE-framework7.txt +52 -0
  134. package/engine/detect-project/assets/mobileui/VERSIONS.txt +6 -0
  135. package/engine/detect-project/assets/mobileui/device-frame.css +316 -0
  136. package/engine/detect-project/assets/mobileui/f7-color-theme.mjs +1345 -0
  137. package/engine/detect-project/assets/mobileui/f7-icons-names.json +1254 -0
  138. package/engine/detect-project/assets/mobileui/fonts.css +16 -0
  139. package/engine/detect-project/assets/mobileui/framework7-components.css +39 -0
  140. package/engine/detect-project/assets/mobileui/framework7-core.css +5245 -0
  141. package/engine/detect-project/assets/mobileui/icons.css +31 -0
  142. package/engine/detect-project/assets/mobileui/md3-defaults.css +89 -0
  143. package/engine/detect-project/assets/mobileui/platforms.json +46 -0
  144. package/engine/detect-project/assets/tailwind-fallback.css +1729 -0
  145. package/engine/detect-project/assets/webtui/LICENSE-webtui.txt +28 -0
  146. package/engine/detect-project/assets/webtui/VERSIONS.txt +7 -0
  147. package/engine/detect-project/assets/webtui/terminal-frame.css +195 -0
  148. package/engine/detect-project/assets/webtui/theme-catppuccin.css +1 -0
  149. package/engine/detect-project/assets/webtui/theme-everforest.css +1 -0
  150. package/engine/detect-project/assets/webtui/theme-gruvbox.css +1 -0
  151. package/engine/detect-project/assets/webtui/theme-nord.css +1 -0
  152. package/engine/detect-project/assets/webtui/theme-vitesse.css +1 -0
  153. package/engine/detect-project/assets/webtui/themes.json +37 -0
  154. package/engine/detect-project/assets/webtui/webtui-core.css +1 -0
  155. package/engine/detect-project/assets/win32ui/7css.css +2 -0
  156. package/engine/detect-project/assets/win32ui/LICENSE-7css.txt +21 -0
  157. package/engine/detect-project/assets/win32ui/VERSIONS.txt +1 -0
  158. package/engine/detect-project/assets/win32ui/win32-frame.css +278 -0
  159. package/engine/detect-project/package-lock.json +12 -0
  160. package/engine/detect-project/package.json +10 -0
  161. package/engine/detect-project/scripts/apply_runtime_profiles.js +313 -0
  162. package/engine/detect-project/scripts/check_css_health.js +412 -0
  163. package/engine/detect-project/scripts/check_project_map.js +150 -0
  164. package/engine/detect-project/scripts/check_runtime_coverage.js +311 -0
  165. package/engine/detect-project/scripts/check_runtime_recipe_quality.js +184 -0
  166. package/engine/detect-project/scripts/classify_app_type.sh +246 -0
  167. package/engine/detect-project/scripts/classify_surface.sh +95 -0
  168. package/engine/detect-project/scripts/classify_workspace.js +39 -0
  169. package/engine/detect-project/scripts/compile_css.sh +447 -0
  170. package/engine/detect-project/scripts/detect_static.js +645 -0
  171. package/engine/detect-project/scripts/detect_structure.js +187 -0
  172. package/engine/detect-project/scripts/include_census.js +451 -0
  173. package/engine/detect-project/scripts/json_get.js +142 -0
  174. package/engine/detect-project/scripts/merge_json.js +52 -0
  175. package/engine/detect-project/scripts/recommend_model_tier.js +252 -0
  176. package/engine/detect-project/scripts/resolve_route_chains.js +135 -0
  177. package/engine/detect-project/scripts/run_css_build.sh +40 -0
  178. package/engine/detect-project/scripts/sanitize_css.js +83 -0
  179. package/engine/detect-project/scripts/test_classify_surface.js +108 -0
  180. package/engine/detect-project/scripts/test_node_helpers.js +142 -0
  181. package/engine/detect-project/scripts/test_recommend_model_tier.js +119 -0
  182. package/engine/detect-project/scripts/test_resolve_route_chains.js +182 -0
  183. package/engine/detect-project/scripts/test_runtime_coverage.js +323 -0
  184. package/engine/detect-project/scripts/test_runtime_recipe_quality.js +187 -0
  185. package/engine/detect-project/scripts/theme_overrides.js +169 -0
  186. package/engine/detect-project/scripts/write_branding.js +129 -0
  187. package/engine/generate-illustrated-article/SKILL.md +461 -0
  188. package/engine/generate-illustrated-article/contracts/desktop.md +90 -0
  189. package/engine/generate-illustrated-article/contracts/game.md +38 -0
  190. package/engine/generate-illustrated-article/contracts/label-evidence.md +38 -0
  191. package/engine/generate-illustrated-article/contracts/macos.md +79 -0
  192. package/engine/generate-illustrated-article/contracts/mobile.md +40 -0
  193. package/engine/generate-illustrated-article/contracts/terminal.md +29 -0
  194. package/engine/generate-illustrated-article/contracts/win32.md +43 -0
  195. package/engine/generate-illustrated-article/package-lock.json +366 -0
  196. package/engine/generate-illustrated-article/package.json +15 -0
  197. package/engine/generate-illustrated-article/scripts/article_blocks.js +34 -0
  198. package/engine/generate-illustrated-article/scripts/check_article_json.js +105 -0
  199. package/engine/generate-illustrated-article/scripts/emit_walkthrough_signals.js +100 -0
  200. package/engine/generate-illustrated-article/scripts/extract_images.js +187 -0
  201. package/engine/generate-illustrated-article/scripts/generate_content_images.js +376 -0
  202. package/engine/generate-illustrated-article/scripts/include_census.js +451 -0
  203. package/engine/generate-illustrated-article/scripts/inject_assets.js +1691 -0
  204. package/engine/generate-illustrated-article/scripts/jit_mockup_css.js +191 -0
  205. package/engine/generate-illustrated-article/scripts/label_evidence.js +87 -0
  206. package/engine/generate-illustrated-article/scripts/lint_article_copy.js +252 -0
  207. package/engine/generate-illustrated-article/scripts/lint_mockup_fidelity.js +2403 -0
  208. package/engine/generate-illustrated-article/scripts/polish_tickets.js +1148 -0
  209. package/engine/generate-illustrated-article/scripts/related_repos.sh +52 -0
  210. package/engine/generate-illustrated-article/scripts/render_all.js +177 -0
  211. package/engine/generate-illustrated-article/scripts/render_mockup.js +665 -0
  212. package/engine/generate-illustrated-article/scripts/render_ready.js +164 -0
  213. package/engine/generate-illustrated-article/scripts/resolve_workspace.sh +86 -0
  214. package/engine/generate-illustrated-article/scripts/runtime_region_geometry.js +55 -0
  215. package/engine/generate-illustrated-article/scripts/source_paths.js +49 -0
  216. package/engine/generate-illustrated-article/scripts/test_control_visibility.js +30 -0
  217. package/engine/generate-illustrated-article/scripts/test_emit_walkthrough_signals.js +157 -0
  218. package/engine/generate-illustrated-article/scripts/test_generate_content_images.js +168 -0
  219. package/engine/generate-illustrated-article/scripts/test_include_census.js +126 -0
  220. package/engine/generate-illustrated-article/scripts/test_label_evidence.js +49 -0
  221. package/engine/generate-illustrated-article/scripts/test_lint_article_copy.js +138 -0
  222. package/engine/generate-illustrated-article/scripts/test_lint_mockup_fidelity.js +653 -0
  223. package/engine/generate-illustrated-article/scripts/test_node_ports.js +152 -0
  224. package/engine/generate-illustrated-article/scripts/test_polish_tickets.js +481 -0
  225. package/engine/generate-illustrated-article/scripts/test_related_repos.js +64 -0
  226. package/engine/generate-illustrated-article/scripts/test_render_ready.js +85 -0
  227. package/engine/generate-illustrated-article/scripts/test_source_paths.js +68 -0
  228. package/engine/generate-illustrated-article/scripts/trace_hook.js +79 -0
  229. package/engine/generate-illustrated-article/scripts/trace_hook.sh +4 -0
  230. package/engine/generate-illustrated-article/scripts/validate_html.js +114 -0
  231. package/engine/generate-illustrated-article/scripts/watermark.js +69 -0
  232. package/install.sh +14 -0
  233. package/package.json +57 -4
  234. package/scripts/auth.mjs +33 -0
  235. package/scripts/auto-update.mjs +7 -0
  236. package/scripts/build-plugin.mjs +58 -0
  237. package/scripts/build-release.mjs +94 -0
  238. package/scripts/check-release.mjs +40 -0
  239. package/scripts/check-runtime.mjs +10 -0
  240. package/scripts/cli.mjs +8 -0
  241. package/scripts/install.mjs +64 -0
  242. package/scripts/lib/agent-runner.mjs +181 -0
  243. package/scripts/lib/agent-settings.mjs +215 -0
  244. package/scripts/lib/article-skills.mjs +82 -0
  245. package/scripts/lib/auto-update.mjs +50 -0
  246. package/scripts/lib/brand.mjs +9 -0
  247. package/scripts/lib/browser.mjs +12 -0
  248. package/scripts/lib/claude-connection.mjs +33 -0
  249. package/scripts/lib/claude-permissions.mjs +47 -0
  250. package/scripts/lib/claude-plugin.mjs +15 -0
  251. package/scripts/lib/claude-writer.mjs +10 -0
  252. package/scripts/lib/cli-main.mjs +85 -0
  253. package/scripts/lib/cli.mjs +888 -0
  254. package/scripts/lib/codex-config.mjs +51 -0
  255. package/scripts/lib/codex-integration.mjs +61 -0
  256. package/scripts/lib/codex-skill.mjs +34 -0
  257. package/scripts/lib/harness-models.mjs +109 -0
  258. package/scripts/lib/install.mjs +269 -0
  259. package/scripts/lib/managed-writer.mjs +38 -0
  260. package/scripts/lib/planning.mjs +263 -0
  261. package/scripts/lib/prepare-update.mjs +85 -0
  262. package/scripts/lib/refresh-writers.mjs +12 -0
  263. package/scripts/lib/remove.mjs +167 -0
  264. package/scripts/lib/renderer.mjs +34 -0
  265. package/scripts/lib/terminal.mjs +252 -0
  266. package/scripts/lib/uninit.mjs +72 -0
  267. package/scripts/lib/update.mjs +55 -0
  268. package/scripts/lib/writer-recovery.mjs +35 -0
  269. package/scripts/lib/yolo.mjs +362 -0
  270. package/scripts/plugin-session.mjs +65 -0
  271. package/scripts/prepare-update.mjs +12 -0
  272. package/scripts/publish-release.mjs +92 -0
  273. package/scripts/skills/supportpages/SKILL.md +120 -0
  274. package/scripts/smoke-release.mjs +101 -0
  275. package/server.json +27 -0
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env bash
2
+ # List the sibling repositories of a multi-repo product.
3
+ #
4
+ # RTFM checks every repository of a multi-repo project out side by side under
5
+ # $RTFM_REPOS_ROOT and runs the skill from one of them (the anchor,
6
+ # $RTFM_WORKSPACE, default cwd). The siblings are extra source to read, never a
7
+ # place to write. Prints one line per sibling, relative to the anchor:
8
+ #
9
+ # ../<dir> (or) ../<dir> <role> <surface>
10
+ #
11
+ # The role and surface come from $RTFM_REPO_RELATIONSHIPS
12
+ # (repository_relationships.json, `repositories[].directory` / `.role` /
13
+ # `.surface`) when that file exists; either may be empty. surface is the
14
+ # repository's interface (web, terminal, mobile, … or none for an API/library). Prints nothing
15
+ # when RTFM_REPOS_ROOT is unset or holds no other checkout — single-repo runs
16
+ # are unchanged. Always exits 0. bash 3.2 / macOS + Linux.
17
+
18
+ root="${RTFM_REPOS_ROOT:-}"
19
+ [ -n "$root" ] && [ -d "$root" ] || exit 0
20
+
21
+ anchor=$(cd "${RTFM_WORKSPACE:-.}" 2>/dev/null && pwd -P) || exit 0
22
+ root=$(cd "$root" && pwd -P) || exit 0
23
+ rels="${RTFM_REPO_RELATIONSHIPS:-}"
24
+
25
+ meta_for() {
26
+ [ -n "$rels" ] && [ -f "$rels" ] || return 0
27
+ command -v node >/dev/null 2>&1 || return 0
28
+ node -e '
29
+ try {
30
+ const data = JSON.parse(require("fs").readFileSync(process.argv[1], "utf8"));
31
+ const repo = (data.repositories || []).find(r => r && r.directory === process.argv[2]);
32
+ if (!repo) process.exit(0);
33
+ const clean = v => (v == null ? "" : String(v).replace(/\s+/g, " ").trim());
34
+ if (repo.role || repo.surface) process.stdout.write(clean(repo.role) + "\t" + clean(repo.surface));
35
+ } catch (e) {}
36
+ ' "$rels" "$1" 2>/dev/null
37
+ }
38
+
39
+ for dir in "$root"/*/; do
40
+ dir="${dir%/}"
41
+ [ -d "$dir/.git" ] || continue
42
+ real=$(cd "$dir" && pwd -P) || continue
43
+ [ "$real" = "$anchor" ] && continue
44
+ name=$(basename "$real")
45
+ meta=$(meta_for "$name")
46
+ if [ -n "$meta" ]; then
47
+ printf '../%s\t%s\n' "$name" "$meta"
48
+ else
49
+ printf '../%s\n' "$name"
50
+ fi
51
+ done
52
+ exit 0
@@ -0,0 +1,177 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * render_all.js — render every step_*.html in a directory to PNG using ONE
4
+ * warm Puppeteer browser with concurrent tabs, instead of a browser launch
5
+ * per screenshot (which is what the old per-file render_mockup.js loop cost).
6
+ *
7
+ * Also logs per-step and total durations so render time is measurable — it's
8
+ * the last tool call in a run, so trace-gap timing can't see it.
9
+ *
10
+ * Usage: render_all.js <dir> [concurrency]
11
+ * <dir> directory containing step_*.html; PNGs land beside them
12
+ * [concurrency] max simultaneous tabs (default 1 — concurrent tabs rendering
13
+ * CSS-heavy mockups stall Chromium's renderer until the ~180s
14
+ * protocol timeout in containers; sequential renders on the
15
+ * warm browser take well under a second each)
16
+ *
17
+ * Exit code 0 even if individual steps fail (matches the old loop's
18
+ * `|| echo failed` behaviour); failures are reported per step.
19
+ */
20
+
21
+ const fs = require('fs');
22
+ const path = require('path');
23
+ const { puppeteer, launchOptions, renderOnBrowser, probeStringsFor } = require('./render_mockup.js');
24
+
25
+ async function main() {
26
+ const dir = process.argv[2];
27
+ const concurrency = Math.max(1, parseInt(process.argv[3], 10) || 1);
28
+ if (!dir || !fs.statSync(dir).isDirectory()) {
29
+ console.error('Usage: render_all.js <dir> [concurrency]');
30
+ process.exit(1);
31
+ }
32
+
33
+ const htmlFiles = fs.readdirSync(dir)
34
+ .filter(f => /^(?:step_\d+|block_[a-z0-9]+(?:-[a-z0-9]+)*)\.html$/.test(f))
35
+ .sort()
36
+ .map(f => path.join(dir, f));
37
+
38
+ if (htmlFiles.length === 0) {
39
+ console.log('No article mockup HTML files found — nothing to render.');
40
+ return;
41
+ }
42
+
43
+ // Per-step verbatim evidence from view_sources.json (when present): the
44
+ // renderer verifies each string actually PAINTS inside the cropped frame.
45
+ // The lint's text-level checks cannot see computed layout — a step whose
46
+ // subject is clipped/collapsed passes every input-side gate and ships a
47
+ // blank frame. Entries are strings (web/desktop) or {string} objects
48
+ // (mobile l10n evidence); both are handled.
49
+ const evidenceByStep = {};
50
+ let viewSources = null;
51
+ try {
52
+ const vs = JSON.parse(fs.readFileSync(path.join(dir, 'view_sources.json'), 'utf8'));
53
+ viewSources = vs;
54
+ for (const step of vs.blocks || vs.steps || []) {
55
+ const ev = (step.verbatim_evidence || [])
56
+ .map(e => (typeof e === 'string' ? e : (e && e.string) || ''))
57
+ .filter(Boolean);
58
+ if (ev.length) evidenceByStep[step.block_id || step.index] = ev;
59
+ }
60
+ } catch { /* no view_sources.json — render without the evidence gate */ }
61
+ // Probe strings come from the project map's app_shell. The article dir is
62
+ // <root>/output/articles/<slug>, and the skill runs from <root>; try both.
63
+ const projectRoot = [path.resolve(dir, '..', '..', '..'), process.cwd()].find(root =>
64
+ ['.rtfm', '.rtfm-branding'].some(d => fs.existsSync(path.join(root, d, 'project_map.json')))
65
+ ) || process.cwd();
66
+
67
+ const t0 = Date.now();
68
+ const browser = await puppeteer.launch(launchOptions());
69
+ const launchMs = Date.now() - t0;
70
+
71
+ const results = [];
72
+ try {
73
+ // Simple worker pool: N tabs in flight at once.
74
+ let next = 0;
75
+ async function worker() {
76
+ while (next < htmlFiles.length) {
77
+ const htmlFile = htmlFiles[next++];
78
+ const png = htmlFile.replace(/\.html$/, '.png');
79
+ const start = Date.now();
80
+ const match = path.basename(htmlFile).match(/^(?:step_(\d+)|block_([a-z0-9]+(?:-[a-z0-9]+)*))\.html$/);
81
+ const stepIdx = match && (match[1] || match[2]);
82
+ try {
83
+ const evidence = evidenceByStep[stepIdx] || evidenceByStep[Number(stepIdx)];
84
+ const probe = probeStringsFor(projectRoot, viewSources, stepIdx, evidence);
85
+ const diag = await renderOnBrowser(browser, png, htmlFile, { evidence, probe });
86
+ results.push({
87
+ file: path.basename(htmlFile),
88
+ ok: true,
89
+ ms: Date.now() - start,
90
+ quality: diag.qualityScore.rating,
91
+ score: diag.qualityScore.score,
92
+ geometry_errors: ((diag.metrics || {}).regionGeometry || {}).errors || [],
93
+ evidence_invisible: ((diag.metrics || {}).evidenceVisibility || [])
94
+ .filter(e => !e.visible).map(e => e.string),
95
+ evidence_checked: ((diag.metrics || {}).evidenceVisibility || []).length,
96
+ });
97
+ } catch (err) {
98
+ results.push({
99
+ file: path.basename(htmlFile),
100
+ ok: false,
101
+ ms: Date.now() - start,
102
+ error: err.message,
103
+ });
104
+ }
105
+ }
106
+ }
107
+ await Promise.all(
108
+ Array.from({ length: Math.min(concurrency, htmlFiles.length) }, worker)
109
+ );
110
+ } finally {
111
+ await browser.close();
112
+ }
113
+
114
+ const totalMs = Date.now() - t0;
115
+ results.sort((a, b) => a.file.localeCompare(b.file));
116
+ for (const r of results) {
117
+ if (r.ok) {
118
+ console.log(` ${r.file} -> png in ${r.ms}ms (quality: ${r.quality}, ${r.score})`);
119
+ } else {
120
+ console.log(` ${r.file} FAILED in ${r.ms}ms: ${r.error}`);
121
+ }
122
+ }
123
+ const failed = results.filter(r => !r.ok).length;
124
+ console.log(`Rendered ${results.length - failed}/${results.length} step(s) in ${totalMs}ms ` +
125
+ `(browser launch ${launchMs}ms, ${concurrency} tab(s))${failed ? ` — ${failed} FAILED` : ''}`);
126
+ // A "poor" diagnostic almost always means the PNG is unusable (blank/collapsed
127
+ // content) even though the render itself succeeded — say so loudly instead of
128
+ // letting the quiet per-line score read as done. Exit code stays 0 on purpose:
129
+ // the model fixes the mockup and re-runs; a hard failure here would abort the
130
+ // whole post-process block.
131
+ for (const r of results) {
132
+ if (r.ok && r.quality === 'poor') {
133
+ console.log(`STOP: ${r.file.replace(/\.html$/, '.png')} rendered POOR (score ${r.score}) — ` +
134
+ 'the screenshot is likely blank or collapsed. Fix that mockup and re-run render_all before finishing.');
135
+ }
136
+ }
137
+
138
+ // Evidence-visibility gate: a MAJORITY of a step's verbatim-evidence
139
+ // strings not painting inside the cropped frame means the step's subject
140
+ // is clipped or collapsed (below the window fold, zero-height pane, …) —
141
+ // the screenshot does not show what the step is about, however faithful
142
+ // the markup. Majority (not any) so a single stray string in a tooltip
143
+ // or duplicate label doesn't false-positive. Same loud-STOP-exit-0
144
+ // convention as the POOR gate above.
145
+ for (const r of results) {
146
+ if (r.ok && r.evidence_checked
147
+ && r.evidence_invisible.length * 2 > r.evidence_checked) {
148
+ console.log(`STOP: ${r.file.replace(/\.html$/, '.png')} does not SHOW this step's subject — ` +
149
+ `${r.evidence_invisible.length}/${r.evidence_checked} evidence strings paint zero visible pixels ` +
150
+ `(${r.evidence_invisible.map(s => JSON.stringify(s)).join(', ')}). The content exists in the HTML ` +
151
+ 'but is clipped out of frame or collapsed to zero size. Depict the state where the subject is ' +
152
+ 'visible (scroll the content, fix the pane layout) and re-run render_all before finishing.');
153
+ }
154
+ }
155
+
156
+ const geometryFailed = results.filter(r => r.ok && r.geometry_errors && r.geometry_errors.length).length;
157
+ if (geometryFailed) {
158
+ console.log(`STOP: ${geometryFailed} step(s) violate the schema-v2 runtime-region geometry contract.`);
159
+ for (const r of results.filter(x => x.geometry_errors && x.geometry_errors.length)) {
160
+ console.log(` ${r.file}: ${r.geometry_errors.join('; ')}`);
161
+ }
162
+ }
163
+
164
+ fs.writeFileSync(path.join(dir, 'render_timing.json'), JSON.stringify({
165
+ total_ms: totalMs,
166
+ browser_launch_ms: launchMs,
167
+ concurrency,
168
+ all_passed: failed === 0 && geometryFailed === 0,
169
+ steps: results,
170
+ }, null, 2));
171
+ if (failed || geometryFailed) process.exitCode = 1;
172
+ }
173
+
174
+ main().catch(err => {
175
+ console.error('render_all failed:', err.message);
176
+ process.exit(1);
177
+ });