@brandlab-digital/conformance 3.7.0 → 3.8.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 (200) hide show
  1. package/README.md +26 -0
  2. package/dist/current-release.js +5 -5
  3. package/dist/current-release.js.map +1 -1
  4. package/dist/public-copy.d.ts +7 -0
  5. package/dist/public-copy.d.ts.map +1 -1
  6. package/dist/public-copy.js +8 -3
  7. package/dist/public-copy.js.map +1 -1
  8. package/dist/ready-cli.d.ts +38 -0
  9. package/dist/ready-cli.d.ts.map +1 -0
  10. package/dist/ready-cli.js +327 -0
  11. package/dist/ready-cli.js.map +1 -0
  12. package/dist/site-system/checks.d.ts +389 -0
  13. package/dist/site-system/checks.d.ts.map +1 -0
  14. package/dist/site-system/checks.js +96 -0
  15. package/dist/site-system/checks.js.map +1 -0
  16. package/dist/site-system/config.d.ts +40 -0
  17. package/dist/site-system/config.d.ts.map +1 -0
  18. package/dist/site-system/config.js +214 -0
  19. package/dist/site-system/config.js.map +1 -0
  20. package/dist/site-system/index.d.ts +14 -0
  21. package/dist/site-system/index.d.ts.map +1 -0
  22. package/dist/site-system/index.js +14 -0
  23. package/dist/site-system/index.js.map +1 -0
  24. package/dist/site-system/journeys/access.d.ts +7 -0
  25. package/dist/site-system/journeys/access.d.ts.map +1 -0
  26. package/dist/site-system/journeys/access.js +263 -0
  27. package/dist/site-system/journeys/access.js.map +1 -0
  28. package/dist/site-system/journeys/browser.d.ts +79 -0
  29. package/dist/site-system/journeys/browser.d.ts.map +1 -0
  30. package/dist/site-system/journeys/browser.js +218 -0
  31. package/dist/site-system/journeys/browser.js.map +1 -0
  32. package/dist/site-system/journeys/conversion.d.ts +3 -0
  33. package/dist/site-system/journeys/conversion.d.ts.map +1 -0
  34. package/dist/site-system/journeys/conversion.js +152 -0
  35. package/dist/site-system/journeys/conversion.js.map +1 -0
  36. package/dist/site-system/journeys/menu.d.ts +11 -0
  37. package/dist/site-system/journeys/menu.d.ts.map +1 -0
  38. package/dist/site-system/journeys/menu.js +395 -0
  39. package/dist/site-system/journeys/menu.js.map +1 -0
  40. package/dist/site-system/journeys/page-helpers.d.ts +146 -0
  41. package/dist/site-system/journeys/page-helpers.d.ts.map +1 -0
  42. package/dist/site-system/journeys/page-helpers.js +426 -0
  43. package/dist/site-system/journeys/page-helpers.js.map +1 -0
  44. package/dist/site-system/journeys/plan.d.ts +82 -0
  45. package/dist/site-system/journeys/plan.d.ts.map +1 -0
  46. package/dist/site-system/journeys/plan.js +112 -0
  47. package/dist/site-system/journeys/plan.js.map +1 -0
  48. package/dist/site-system/journeys/shell.d.ts +24 -0
  49. package/dist/site-system/journeys/shell.d.ts.map +1 -0
  50. package/dist/site-system/journeys/shell.js +269 -0
  51. package/dist/site-system/journeys/shell.js.map +1 -0
  52. package/dist/site-system/playwright-report.d.ts +22 -0
  53. package/dist/site-system/playwright-report.d.ts.map +1 -0
  54. package/dist/site-system/playwright-report.js +131 -0
  55. package/dist/site-system/playwright-report.js.map +1 -0
  56. package/dist/site-system/records.d.ts +30 -0
  57. package/dist/site-system/records.d.ts.map +1 -0
  58. package/dist/site-system/records.js +98 -0
  59. package/dist/site-system/records.js.map +1 -0
  60. package/dist/site-system/report.d.ts +116 -0
  61. package/dist/site-system/report.d.ts.map +1 -0
  62. package/dist/site-system/report.js +366 -0
  63. package/dist/site-system/report.js.map +1 -0
  64. package/dist/site-system/rule-modules.d.ts +8 -0
  65. package/dist/site-system/rule-modules.d.ts.map +1 -0
  66. package/dist/site-system/rule-modules.js +12 -0
  67. package/dist/site-system/rule-modules.js.map +1 -0
  68. package/dist/site-system/rules/design/d4-distinctness.d.ts +36 -0
  69. package/dist/site-system/rules/design/d4-distinctness.d.ts.map +1 -0
  70. package/dist/site-system/rules/design/d4-distinctness.js +207 -0
  71. package/dist/site-system/rules/design/d4-distinctness.js.map +1 -0
  72. package/dist/site-system/rules/design/index.d.ts +17 -0
  73. package/dist/site-system/rules/design/index.d.ts.map +1 -0
  74. package/dist/site-system/rules/design/index.js +7 -0
  75. package/dist/site-system/rules/design/index.js.map +1 -0
  76. package/dist/site-system/rules/enquiry/c2-quote-form.d.ts +31 -0
  77. package/dist/site-system/rules/enquiry/c2-quote-form.d.ts.map +1 -0
  78. package/dist/site-system/rules/enquiry/c2-quote-form.js +211 -0
  79. package/dist/site-system/rules/enquiry/c2-quote-form.js.map +1 -0
  80. package/dist/site-system/rules/enquiry/common.d.ts +37 -0
  81. package/dist/site-system/rules/enquiry/common.d.ts.map +1 -0
  82. package/dist/site-system/rules/enquiry/common.js +59 -0
  83. package/dist/site-system/rules/enquiry/common.js.map +1 -0
  84. package/dist/site-system/rules/enquiry/e1-recorded-titles.d.ts +8 -0
  85. package/dist/site-system/rules/enquiry/e1-recorded-titles.d.ts.map +1 -0
  86. package/dist/site-system/rules/enquiry/e1-recorded-titles.js +59 -0
  87. package/dist/site-system/rules/enquiry/e1-recorded-titles.js.map +1 -0
  88. package/dist/site-system/rules/enquiry/e9-cost-guides.d.ts +22 -0
  89. package/dist/site-system/rules/enquiry/e9-cost-guides.d.ts.map +1 -0
  90. package/dist/site-system/rules/enquiry/e9-cost-guides.js +55 -0
  91. package/dist/site-system/rules/enquiry/e9-cost-guides.js.map +1 -0
  92. package/dist/site-system/rules/enquiry/html.d.ts +53 -0
  93. package/dist/site-system/rules/enquiry/html.d.ts.map +1 -0
  94. package/dist/site-system/rules/enquiry/html.js +294 -0
  95. package/dist/site-system/rules/enquiry/html.js.map +1 -0
  96. package/dist/site-system/rules/enquiry/index.d.ts +17 -0
  97. package/dist/site-system/rules/enquiry/index.d.ts.map +1 -0
  98. package/dist/site-system/rules/enquiry/index.js +26 -0
  99. package/dist/site-system/rules/enquiry/index.js.map +1 -0
  100. package/dist/site-system/rules/enquiry/k8-sections.d.ts +17 -0
  101. package/dist/site-system/rules/enquiry/k8-sections.d.ts.map +1 -0
  102. package/dist/site-system/rules/enquiry/k8-sections.js +76 -0
  103. package/dist/site-system/rules/enquiry/k8-sections.js.map +1 -0
  104. package/dist/site-system/rules/enquiry/literal.d.ts +29 -0
  105. package/dist/site-system/rules/enquiry/literal.d.ts.map +1 -0
  106. package/dist/site-system/rules/enquiry/literal.js +189 -0
  107. package/dist/site-system/rules/enquiry/literal.js.map +1 -0
  108. package/dist/site-system/rules/enquiry/t11-licence-classes.d.ts +73 -0
  109. package/dist/site-system/rules/enquiry/t11-licence-classes.d.ts.map +1 -0
  110. package/dist/site-system/rules/enquiry/t11-licence-classes.js +275 -0
  111. package/dist/site-system/rules/enquiry/t11-licence-classes.js.map +1 -0
  112. package/dist/site-system/rules/enquiry/t5-reviews.d.ts +23 -0
  113. package/dist/site-system/rules/enquiry/t5-reviews.d.ts.map +1 -0
  114. package/dist/site-system/rules/enquiry/t5-reviews.js +91 -0
  115. package/dist/site-system/rules/enquiry/t5-reviews.js.map +1 -0
  116. package/dist/site-system/rules/shell/banned-wording.d.ts +24 -0
  117. package/dist/site-system/rules/shell/banned-wording.d.ts.map +1 -0
  118. package/dist/site-system/rules/shell/banned-wording.js +212 -0
  119. package/dist/site-system/rules/shell/banned-wording.js.map +1 -0
  120. package/dist/site-system/rules/shell/budgets.d.ts +43 -0
  121. package/dist/site-system/rules/shell/budgets.d.ts.map +1 -0
  122. package/dist/site-system/rules/shell/budgets.js +297 -0
  123. package/dist/site-system/rules/shell/budgets.js.map +1 -0
  124. package/dist/site-system/rules/shell/cta-labels.d.ts +17 -0
  125. package/dist/site-system/rules/shell/cta-labels.d.ts.map +1 -0
  126. package/dist/site-system/rules/shell/cta-labels.js +86 -0
  127. package/dist/site-system/rules/shell/cta-labels.js.map +1 -0
  128. package/dist/site-system/rules/shell/facts.d.ts +33 -0
  129. package/dist/site-system/rules/shell/facts.d.ts.map +1 -0
  130. package/dist/site-system/rules/shell/facts.js +217 -0
  131. package/dist/site-system/rules/shell/facts.js.map +1 -0
  132. package/dist/site-system/rules/shell/headings.d.ts +5 -0
  133. package/dist/site-system/rules/shell/headings.d.ts.map +1 -0
  134. package/dist/site-system/rules/shell/headings.js +23 -0
  135. package/dist/site-system/rules/shell/headings.js.map +1 -0
  136. package/dist/site-system/rules/shell/hero-location.d.ts +4 -0
  137. package/dist/site-system/rules/shell/hero-location.d.ts.map +1 -0
  138. package/dist/site-system/rules/shell/hero-location.js +45 -0
  139. package/dist/site-system/rules/shell/hero-location.js.map +1 -0
  140. package/dist/site-system/rules/shell/html.d.ts +53 -0
  141. package/dist/site-system/rules/shell/html.d.ts.map +1 -0
  142. package/dist/site-system/rules/shell/html.js +341 -0
  143. package/dist/site-system/rules/shell/html.js.map +1 -0
  144. package/dist/site-system/rules/shell/index.d.ts +18 -0
  145. package/dist/site-system/rules/shell/index.d.ts.map +1 -0
  146. package/dist/site-system/rules/shell/index.js +65 -0
  147. package/dist/site-system/rules/shell/index.js.map +1 -0
  148. package/dist/site-system/rules/shell/licence.d.ts +4 -0
  149. package/dist/site-system/rules/shell/licence.d.ts.map +1 -0
  150. package/dist/site-system/rules/shell/licence.js +60 -0
  151. package/dist/site-system/rules/shell/licence.js.map +1 -0
  152. package/dist/site-system/rules/shell/location-wording.d.ts +13 -0
  153. package/dist/site-system/rules/shell/location-wording.d.ts.map +1 -0
  154. package/dist/site-system/rules/shell/location-wording.js +15 -0
  155. package/dist/site-system/rules/shell/location-wording.js.map +1 -0
  156. package/dist/site-system/rules/shell/navigation.d.ts +5 -0
  157. package/dist/site-system/rules/shell/navigation.d.ts.map +1 -0
  158. package/dist/site-system/rules/shell/navigation.js +272 -0
  159. package/dist/site-system/rules/shell/navigation.js.map +1 -0
  160. package/dist/site-system/rules/shell/overlays.d.ts +4 -0
  161. package/dist/site-system/rules/shell/overlays.d.ts.map +1 -0
  162. package/dist/site-system/rules/shell/overlays.js +93 -0
  163. package/dist/site-system/rules/shell/overlays.js.map +1 -0
  164. package/dist/site-system/rules/shell/page.d.ts +51 -0
  165. package/dist/site-system/rules/shell/page.d.ts.map +1 -0
  166. package/dist/site-system/rules/shell/page.js +256 -0
  167. package/dist/site-system/rules/shell/page.js.map +1 -0
  168. package/dist/site-system/rules/shell/phone.d.ts +13 -0
  169. package/dist/site-system/rules/shell/phone.d.ts.map +1 -0
  170. package/dist/site-system/rules/shell/phone.js +99 -0
  171. package/dist/site-system/rules/shell/phone.js.map +1 -0
  172. package/dist/site-system/rules/shell/proof-numbers.d.ts +7 -0
  173. package/dist/site-system/rules/shell/proof-numbers.d.ts.map +1 -0
  174. package/dist/site-system/rules/shell/proof-numbers.js +81 -0
  175. package/dist/site-system/rules/shell/proof-numbers.js.map +1 -0
  176. package/dist/site-system/rules/shell/types.d.ts +80 -0
  177. package/dist/site-system/rules/shell/types.d.ts.map +1 -0
  178. package/dist/site-system/rules/shell/types.js +17 -0
  179. package/dist/site-system/rules/shell/types.js.map +1 -0
  180. package/dist/site-system/runners.d.ts +37 -0
  181. package/dist/site-system/runners.d.ts.map +1 -0
  182. package/dist/site-system/runners.js +48 -0
  183. package/dist/site-system/runners.js.map +1 -0
  184. package/dist/site-system/samples.d.ts +43 -0
  185. package/dist/site-system/samples.d.ts.map +1 -0
  186. package/dist/site-system/samples.js +99 -0
  187. package/dist/site-system/samples.js.map +1 -0
  188. package/dist/site-system/steps.d.ts +66 -0
  189. package/dist/site-system/steps.d.ts.map +1 -0
  190. package/dist/site-system/steps.js +45 -0
  191. package/dist/site-system/steps.js.map +1 -0
  192. package/dist/site-system/streamed-html.d.ts +9 -0
  193. package/dist/site-system/streamed-html.d.ts.map +1 -0
  194. package/dist/site-system/streamed-html.js +43 -0
  195. package/dist/site-system/streamed-html.js.map +1 -0
  196. package/dist/site-system-e2e.d.ts +6 -0
  197. package/dist/site-system-e2e.d.ts.map +1 -0
  198. package/dist/site-system-e2e.js +54 -0
  199. package/dist/site-system-e2e.js.map +1 -0
  200. package/package.json +22 -5
@@ -0,0 +1,389 @@
1
+ /**
2
+ * The Site System v2 ready gate: every check, the adoption tier it blocks at and the release that brings it.
3
+ *
4
+ * A site declares `siteSystem: { tier }` in brandlab.config.ts. Checks of that tier and the tiers below it block;
5
+ * checks of higher tiers report without blocking. A check blocks only from the release named here, so new checks
6
+ * arrive with version bumps and never turn the fleet red at once. Holdouts use the project's Romie-owned
7
+ * `exceptions` record. D6 (leave reviews as they are) removes every review-exclusion clause; D7 (no call tracking)
8
+ * means no number-swap check.
9
+ *
10
+ * `kind` says where a check runs:
11
+ * - `journey`: the Playwright site-system journeys (`defineBrandlabSiteSystemTests`), in the site's e2e run;
12
+ * - `rendered`: rules over the built site's server HTML and assets (`brandlab-ready` reads them);
13
+ * - `static`: source and data rules (`brandlab-verify` and `brandlab-ready`);
14
+ * - `record`: launch-day and human steps recorded with `brandlab-ready record <id> --evidence <ref>`.
15
+ */
16
+ export declare const SITE_SYSTEM_TIERS: readonly ["shell", "enquiry", "full"];
17
+ export type SiteSystemTier = (typeof SITE_SYSTEM_TIERS)[number];
18
+ /** The design check D4 blocks every new build and redesign, whatever its tier. */
19
+ export type SiteSystemCheckScope = SiteSystemTier | "new-build";
20
+ export type SiteSystemCheckKind = "journey" | "rendered" | "static" | "record";
21
+ export interface SiteSystemCheck {
22
+ readonly id: string;
23
+ readonly tier: SiteSystemCheckScope;
24
+ /** The first release in which the check blocks at its tier. */
25
+ readonly from: `3.${number}.0`;
26
+ readonly kind: SiteSystemCheckKind;
27
+ readonly summary: string;
28
+ }
29
+ export declare const SITE_SYSTEM_CHECKS: readonly [{
30
+ readonly id: "N1";
31
+ readonly tier: "shell";
32
+ readonly from: "3.9.0";
33
+ readonly kind: "static";
34
+ readonly summary: "Required nav links, About in the first two, services before Areas, Contact last, labels from the closed list, recorded orders kept.";
35
+ }, {
36
+ readonly id: "N2";
37
+ readonly tier: "shell";
38
+ readonly from: "3.8.0";
39
+ readonly kind: "rendered";
40
+ readonly summary: "Every dropdown link and hub link is an <a href> in the server HTML.";
41
+ }, {
42
+ readonly id: "N3";
43
+ readonly tier: "shell";
44
+ readonly from: "3.8.0";
45
+ readonly kind: "journey";
46
+ readonly summary: "Hover opens after a short delay and a diagonal path stays open; mouse-out closes; nothing sticks open.";
47
+ }, {
48
+ readonly id: "N4";
49
+ readonly tier: "shell";
50
+ readonly from: "3.8.0";
51
+ readonly kind: "journey";
52
+ readonly summary: "A chevron click after hover keeps the panel open; a link click closes it; click, Enter and outside click work.";
53
+ }, {
54
+ readonly id: "N5";
55
+ readonly tier: "shell";
56
+ readonly from: "3.8.0";
57
+ readonly kind: "journey";
58
+ readonly summary: "Keyboard and ARIA: aria-expanded follows the panel, Arrow keys move, Escape closes and returns focus; axe passes open and closed.";
59
+ }, {
60
+ readonly id: "N6";
61
+ readonly tier: "shell";
62
+ readonly from: "3.8.0";
63
+ readonly kind: "journey";
64
+ readonly summary: "The header stays at the top with the phone and quote visible at every desktop width; the compact band shows them from 1024 to 1279 px.";
65
+ }, {
66
+ readonly id: "N8";
67
+ readonly tier: "shell";
68
+ readonly from: "3.9.0";
69
+ readonly kind: "journey";
70
+ readonly summary: "The drawer is a modal dialog that traps focus and resets; the call bar uses placement fab; back-to-top appears (from 3.11.0).";
71
+ }, {
72
+ readonly id: "K1";
73
+ readonly tier: "shell";
74
+ readonly from: "3.9.0";
75
+ readonly kind: "rendered";
76
+ readonly summary: "Shared components in use for the parts the tier covers (data-bl present); no literal tel: outside PhoneLink.";
77
+ }, {
78
+ readonly id: "K3";
79
+ readonly tier: "shell";
80
+ readonly from: "3.8.0";
81
+ readonly kind: "journey";
82
+ readonly summary: "Text contrast 4.5:1 and non-text 3:1 in the browser.";
83
+ }, {
84
+ readonly id: "K4";
85
+ readonly tier: "shell";
86
+ readonly from: "3.8.0";
87
+ readonly kind: "journey";
88
+ readonly summary: "Targets at least 24 px, and 44 px for primary actions (call, quote, menu).";
89
+ }, {
90
+ readonly id: "K5";
91
+ readonly tier: "shell";
92
+ readonly from: "3.8.0";
93
+ readonly kind: "journey";
94
+ readonly summary: "Focus is never fully hidden by the sticky header or call bar at 390 px, in Chromium and WebKit.";
95
+ }, {
96
+ readonly id: "K6";
97
+ readonly tier: "shell";
98
+ readonly from: "3.8.0";
99
+ readonly kind: "journey";
100
+ readonly summary: "Phone and quote in the same relative order on every page, in the header, hero and call bar.";
101
+ }, {
102
+ readonly id: "K7";
103
+ readonly tier: "shell";
104
+ readonly from: "3.8.0";
105
+ readonly kind: "rendered";
106
+ readonly summary: "Dropdown links, accordion and tab panels and full review text are in the server HTML.";
107
+ }, {
108
+ readonly id: "K10";
109
+ readonly tier: "shell";
110
+ readonly from: "3.8.0";
111
+ readonly kind: "record";
112
+ readonly summary: "Adoption parity in the lenient harness: no section, link or text lost against the baseline.";
113
+ }, {
114
+ readonly id: "C4";
115
+ readonly tier: "shell";
116
+ readonly from: "3.8.0";
117
+ readonly kind: "journey";
118
+ readonly summary: "Phone taps report an allowed placement (header, footer, hero, cta, fab, contact, location, page).";
119
+ }, {
120
+ readonly id: "C5";
121
+ readonly tier: "shell";
122
+ readonly from: "3.8.0";
123
+ readonly kind: "rendered";
124
+ readonly summary: "Call-to-action labels come from the closed list or a recorded label.";
125
+ }, {
126
+ readonly id: "C9";
127
+ readonly tier: "shell";
128
+ readonly from: "3.8.0";
129
+ readonly kind: "rendered";
130
+ readonly summary: "No interstitial overlay in the first screen.";
131
+ }, {
132
+ readonly id: "C10";
133
+ readonly tier: "shell";
134
+ readonly from: "3.8.0";
135
+ readonly kind: "rendered";
136
+ readonly summary: "Every tel: href is E.164 (national digits for 13, 1300 and 1800) and matches the evidence-file phone.";
137
+ }, {
138
+ readonly id: "E1";
139
+ readonly tier: "shell";
140
+ readonly from: "3.8.0";
141
+ readonly kind: "static";
142
+ readonly summary: "Title patterns on new or changed URLs; recorded titles unchanged.";
143
+ }, {
144
+ readonly id: "E2";
145
+ readonly tier: "shell";
146
+ readonly from: "3.8.0";
147
+ readonly kind: "rendered";
148
+ readonly summary: "Exactly one H1 on every page, rendered once.";
149
+ }, {
150
+ readonly id: "E8";
151
+ readonly tier: "shell";
152
+ readonly from: "3.8.0";
153
+ readonly kind: "rendered";
154
+ readonly summary: "No banned phrase from any of the three layers, environmental claims included.";
155
+ }, {
156
+ readonly id: "T3";
157
+ readonly tier: "shell";
158
+ readonly from: "3.8.0";
159
+ readonly kind: "rendered";
160
+ readonly summary: "A licence line renders wherever licensed work is advertised, with the licensee where it differs.";
161
+ }, {
162
+ readonly id: "T4";
163
+ readonly tier: "shell";
164
+ readonly from: "3.8.0";
165
+ readonly kind: "rendered";
166
+ readonly summary: "No hard-coded rating or review numbers; proof claims resolve to renderable evidence.";
167
+ }, {
168
+ readonly id: "T11";
169
+ readonly tier: "shell";
170
+ readonly from: "3.8.0";
171
+ readonly kind: "static";
172
+ readonly summary: "No page for work over $5,000 outside the licence classes held; licence conditions carried.";
173
+ }, {
174
+ readonly id: "X1";
175
+ readonly tier: "shell";
176
+ readonly from: "3.8.0";
177
+ readonly kind: "record";
178
+ readonly summary: "Legacy URLs answer 200 or one redirect hop; earning-URL depth, titles and dollar figures kept.";
179
+ }, {
180
+ readonly id: "B1";
181
+ readonly tier: "shell";
182
+ readonly from: "3.8.0";
183
+ readonly kind: "rendered";
184
+ readonly summary: "Lab budgets: JavaScript, third-party requests, hero image and HTML within budget; no unrecorded chat widget, pixel, heatmap or tag manager.";
185
+ }, {
186
+ readonly id: "L10-hero";
187
+ readonly tier: "shell";
188
+ readonly from: "3.8.0";
189
+ readonly kind: "rendered";
190
+ readonly summary: "No kilometre figures, \"km from\" or \"based in\" in hero copy.";
191
+ }, {
192
+ readonly id: "C1";
193
+ readonly tier: "enquiry";
194
+ readonly from: "3.8.0";
195
+ readonly kind: "journey";
196
+ readonly summary: "Money pages: the form in the first section; at 390 x 844 the H1, a call and a quote action in the first viewport; at 1440 x 900 the form.";
197
+ }, {
198
+ readonly id: "C2";
199
+ readonly tier: "enquiry";
200
+ readonly from: "3.8.0";
201
+ readonly kind: "rendered";
202
+ readonly summary: "Form layout and labels; phone and email required; no helper or reassurance lines, no \"(required)\", no job select.";
203
+ }, {
204
+ readonly id: "E9";
205
+ readonly tier: "enquiry";
206
+ readonly from: "3.8.0";
207
+ readonly kind: "rendered";
208
+ readonly summary: "Every cost guide carries at least 8 dollar figures in a renderable price state.";
209
+ }, {
210
+ readonly id: "T5";
211
+ readonly tier: "enquiry";
212
+ readonly from: "3.8.0";
213
+ readonly kind: "rendered";
214
+ readonly summary: "Reviews from the feed, with Google's rating, count and link beside any selection; none on the contact page; no empty reviews section.";
215
+ }, {
216
+ readonly id: "K8";
217
+ readonly tier: "enquiry";
218
+ readonly from: "3.8.0";
219
+ readonly kind: "rendered";
220
+ readonly summary: "Section contract: no empty section, one H2 each, at most one band tone, neighbours differ, proof within the first five sections.";
221
+ }, {
222
+ readonly id: "C7";
223
+ readonly tier: "enquiry";
224
+ readonly from: "3.10.0";
225
+ readonly kind: "rendered";
226
+ readonly summary: "Thank-you page: a reply time, a tel: link and noindex, follow.";
227
+ }, {
228
+ readonly id: "C8";
229
+ readonly tier: "enquiry";
230
+ readonly from: "3.10.0";
231
+ readonly kind: "rendered";
232
+ readonly summary: "Forgiving form: tolerant phone input (13, 1300 and 1800 too), autocomplete tokens, no capture on the photo input.";
233
+ }, {
234
+ readonly id: "C11";
235
+ readonly tier: "enquiry";
236
+ readonly from: "3.10.0";
237
+ readonly kind: "static";
238
+ readonly summary: "The test marker is stripped before delivery and reporting; /api/contact is rate-limited.";
239
+ }, {
240
+ readonly id: "N9";
241
+ readonly tier: "enquiry";
242
+ readonly from: "3.11.0";
243
+ readonly kind: "rendered";
244
+ readonly summary: "Footer content: column 1 brand, licences, socials and contact; services, areas and company columns; © name, ABN and legal links; no CTA, \"based in\", hours or guides column.";
245
+ }, {
246
+ readonly id: "K2";
247
+ readonly tier: "enquiry";
248
+ readonly from: "3.11.0";
249
+ readonly kind: "rendered";
250
+ readonly summary: "Every rendered shared component has a skin entry; no reference-skin class ships.";
251
+ }, {
252
+ readonly id: "K11";
253
+ readonly tier: "enquiry";
254
+ readonly from: "3.11.0";
255
+ readonly kind: "rendered";
256
+ readonly summary: "Density: no section with more than one H2, more than 8 cards or more than 2 calls to action; measure and section padding within limits.";
257
+ }, {
258
+ readonly id: "K12-contact";
259
+ readonly tier: "enquiry";
260
+ readonly from: "3.11.0";
261
+ readonly kind: "rendered";
262
+ readonly summary: "The contact page is the page header, then the contact block, and nothing else.";
263
+ }, {
264
+ readonly id: "T6";
265
+ readonly tier: "enquiry";
266
+ readonly from: "3.11.0";
267
+ readonly kind: "static";
268
+ readonly summary: "Every image has a media.ts entry with provenance.";
269
+ }, {
270
+ readonly id: "T7";
271
+ readonly tier: "enquiry";
272
+ readonly from: "3.11.0";
273
+ readonly kind: "rendered";
274
+ readonly summary: "No image is the main image of two pages.";
275
+ }, {
276
+ readonly id: "T8";
277
+ readonly tier: "enquiry";
278
+ readonly from: "3.11.0";
279
+ readonly kind: "rendered";
280
+ readonly summary: "Prices: GST flag set; no \"+ GST\" or \"from $X\" teasers on cards and menus; one band per job site-wide.";
281
+ }, {
282
+ readonly id: "T9";
283
+ readonly tier: "enquiry";
284
+ readonly from: "3.11.0";
285
+ readonly kind: "static";
286
+ readonly summary: "No image in /public carries location metadata.";
287
+ }, {
288
+ readonly id: "K12";
289
+ readonly tier: "full";
290
+ readonly from: "3.12.0";
291
+ readonly kind: "rendered";
292
+ readonly summary: "Section manifest: every required section rendered or skipped with a data-backed reason; conditional sections render when their data exists; canonical or recorded order.";
293
+ }, {
294
+ readonly id: "E5";
295
+ readonly tier: "full";
296
+ readonly from: "3.12.0";
297
+ readonly kind: "static";
298
+ readonly summary: "No orphan indexable pages, from the route catalogue and link data.";
299
+ }, {
300
+ readonly id: "E10";
301
+ readonly tier: "full";
302
+ readonly from: "3.12.0";
303
+ readonly kind: "static";
304
+ readonly summary: "Phrase-owner uniqueness in strategy data; no service-in-suburb page for the suburb page phrase; no service-region hub for the main trade.";
305
+ }, {
306
+ readonly id: "L3";
307
+ readonly tier: "full";
308
+ readonly from: "3.12.0";
309
+ readonly kind: "static";
310
+ readonly summary: "Neighbours by real distance.";
311
+ }, {
312
+ readonly id: "L4";
313
+ readonly tier: "full";
314
+ readonly from: "3.12.0";
315
+ readonly kind: "rendered";
316
+ readonly summary: "No copy rotated by page address, no name-swapped FAQs, no other suburb's job without the nearby label.";
317
+ }, {
318
+ readonly id: "L6";
319
+ readonly tier: "full";
320
+ readonly from: "3.12.0";
321
+ readonly kind: "static";
322
+ readonly summary: "Location pages have 6 or more inbound links from 3 or more page types.";
323
+ }, {
324
+ readonly id: "L7";
325
+ readonly tier: "full";
326
+ readonly from: "3.12.0";
327
+ readonly kind: "static";
328
+ readonly summary: "Location pages are 3 clicks or fewer from home.";
329
+ }, {
330
+ readonly id: "L8";
331
+ readonly tier: "full";
332
+ readonly from: "3.12.0";
333
+ readonly kind: "static";
334
+ readonly summary: "A hub per family per region.";
335
+ }, {
336
+ readonly id: "L9";
337
+ readonly tier: "full";
338
+ readonly from: "3.12.0";
339
+ readonly kind: "rendered";
340
+ readonly summary: "Data contract on service-location pages: price or cost-guide link, reviews or fallback, local facts; the nearby job card or none.";
341
+ }, {
342
+ readonly id: "L10";
343
+ readonly tier: "full";
344
+ readonly from: "3.12.0";
345
+ readonly kind: "rendered";
346
+ readonly summary: "No kilometre figures, \"km from\", \"based in\" or year stamps as local detail in location copy.";
347
+ }, {
348
+ readonly id: "S4";
349
+ readonly tier: "full";
350
+ readonly from: "3.12.0";
351
+ readonly kind: "static";
352
+ readonly summary: "No hand-written per-service or per-suburb files; no dynamic route with zero pages.";
353
+ }, {
354
+ readonly id: "LOC-INDEX";
355
+ readonly tier: "full";
356
+ readonly from: "3.12.0";
357
+ readonly kind: "static";
358
+ readonly summary: "No location route is noindex or left out of the sitemap unless the recorded wave schedule dates it (Romie's 20 Oct decision: every suburb page stays indexed).";
359
+ }, {
360
+ readonly id: "D4";
361
+ readonly tier: "new-build";
362
+ readonly from: "3.8.0";
363
+ readonly kind: "static";
364
+ readonly summary: "Same-trade distinctness: at least four of seven look levers differ from every listed competitor (advisory in 3.8.0, blocking from 3.9.0).";
365
+ }];
366
+ export type SiteSystemCheckId = (typeof SITE_SYSTEM_CHECKS)[number]["id"];
367
+ /** A release as one comparable number: "3.8.0" < "3.10.0". */
368
+ export declare function siteSystemReleaseRank(version: string): number;
369
+ /**
370
+ * Whether a check blocks for a site at `tier` on `release`. Checks of higher tiers, and checks newer than the
371
+ * installed release, report without blocking. D4 blocks new builds and redesigns from 3.9.0 whatever their tier.
372
+ */
373
+ export declare function siteSystemCheckBlocks(check: Pick<SiteSystemCheck, "id" | "tier" | "from">, site: {
374
+ tier: SiteSystemTier;
375
+ release: string;
376
+ newBuild?: boolean;
377
+ }): boolean;
378
+ /** One result of one check, from any runner. `brandlab-ready` merges them into the ready report. */
379
+ export interface SiteSystemFinding {
380
+ readonly checkId: SiteSystemCheckId;
381
+ readonly status: "pass" | "fail" | "not-applicable";
382
+ /** The page path or file the result is about, when it is about one. */
383
+ readonly target?: string;
384
+ /** One plain sentence: what passed or what is wrong and where. */
385
+ readonly message: string;
386
+ /** A warning never blocks: the check still passes and the report shows "pass (warning)". */
387
+ readonly severity?: "warning";
388
+ }
389
+ //# sourceMappingURL=checks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checks.d.ts","sourceRoot":"","sources":["../../src/site-system/checks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,eAAO,MAAM,iBAAiB,uCAAwC,CAAC;AACvE,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEhE,kFAAkF;AAClF,MAAM,MAAM,oBAAoB,GAAG,cAAc,GAAG,WAAW,CAAC;AAEhE,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,UAAU,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE/E,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;IACpC,+DAA+D;IAC/D,QAAQ,CAAC,IAAI,EAAE,KAAK,MAAM,IAAI,CAAC;IAC/B,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgEgB,CAAC;AAEhD,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC;AAI1E,8DAA8D;AAC9D,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAG7D;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,IAAI,CAAC,eAAe,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC,EACpD,IAAI,EAAE;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAClE,OAAO,CAIT;AAED,oGAAoG;AACpG,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,gBAAgB,CAAC;IACpD,uEAAuE;IACvE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,kEAAkE;IAClE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,4FAA4F;IAC5F,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAC;CAC/B"}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * The Site System v2 ready gate: every check, the adoption tier it blocks at and the release that brings it.
3
+ *
4
+ * A site declares `siteSystem: { tier }` in brandlab.config.ts. Checks of that tier and the tiers below it block;
5
+ * checks of higher tiers report without blocking. A check blocks only from the release named here, so new checks
6
+ * arrive with version bumps and never turn the fleet red at once. Holdouts use the project's Romie-owned
7
+ * `exceptions` record. D6 (leave reviews as they are) removes every review-exclusion clause; D7 (no call tracking)
8
+ * means no number-swap check.
9
+ *
10
+ * `kind` says where a check runs:
11
+ * - `journey`: the Playwright site-system journeys (`defineBrandlabSiteSystemTests`), in the site's e2e run;
12
+ * - `rendered`: rules over the built site's server HTML and assets (`brandlab-ready` reads them);
13
+ * - `static`: source and data rules (`brandlab-verify` and `brandlab-ready`);
14
+ * - `record`: launch-day and human steps recorded with `brandlab-ready record <id> --evidence <ref>`.
15
+ */
16
+ export const SITE_SYSTEM_TIERS = ["shell", "enquiry", "full"];
17
+ export const SITE_SYSTEM_CHECKS = [
18
+ // shell: header, nav, call bar, placements, crawlable links, contrast and targets, call links, budgets, legal
19
+ { id: "N1", tier: "shell", from: "3.9.0", kind: "static", summary: "Required nav links, About in the first two, services before Areas, Contact last, labels from the closed list, recorded orders kept." },
20
+ { id: "N2", tier: "shell", from: "3.8.0", kind: "rendered", summary: "Every dropdown link and hub link is an <a href> in the server HTML." },
21
+ { id: "N3", tier: "shell", from: "3.8.0", kind: "journey", summary: "Hover opens after a short delay and a diagonal path stays open; mouse-out closes; nothing sticks open." },
22
+ { id: "N4", tier: "shell", from: "3.8.0", kind: "journey", summary: "A chevron click after hover keeps the panel open; a link click closes it; click, Enter and outside click work." },
23
+ { id: "N5", tier: "shell", from: "3.8.0", kind: "journey", summary: "Keyboard and ARIA: aria-expanded follows the panel, Arrow keys move, Escape closes and returns focus; axe passes open and closed." },
24
+ { id: "N6", tier: "shell", from: "3.8.0", kind: "journey", summary: "The header stays at the top with the phone and quote visible at every desktop width; the compact band shows them from 1024 to 1279 px." },
25
+ { id: "N8", tier: "shell", from: "3.9.0", kind: "journey", summary: "The drawer is a modal dialog that traps focus and resets; the call bar uses placement fab; back-to-top appears (from 3.11.0)." },
26
+ { id: "K1", tier: "shell", from: "3.9.0", kind: "rendered", summary: "Shared components in use for the parts the tier covers (data-bl present); no literal tel: outside PhoneLink." },
27
+ { id: "K3", tier: "shell", from: "3.8.0", kind: "journey", summary: "Text contrast 4.5:1 and non-text 3:1 in the browser." },
28
+ { id: "K4", tier: "shell", from: "3.8.0", kind: "journey", summary: "Targets at least 24 px, and 44 px for primary actions (call, quote, menu)." },
29
+ { id: "K5", tier: "shell", from: "3.8.0", kind: "journey", summary: "Focus is never fully hidden by the sticky header or call bar at 390 px, in Chromium and WebKit." },
30
+ { id: "K6", tier: "shell", from: "3.8.0", kind: "journey", summary: "Phone and quote in the same relative order on every page, in the header, hero and call bar." },
31
+ { id: "K7", tier: "shell", from: "3.8.0", kind: "rendered", summary: "Dropdown links, accordion and tab panels and full review text are in the server HTML." },
32
+ { id: "K10", tier: "shell", from: "3.8.0", kind: "record", summary: "Adoption parity in the lenient harness: no section, link or text lost against the baseline." },
33
+ { id: "C4", tier: "shell", from: "3.8.0", kind: "journey", summary: "Phone taps report an allowed placement (header, footer, hero, cta, fab, contact, location, page)." },
34
+ { id: "C5", tier: "shell", from: "3.8.0", kind: "rendered", summary: "Call-to-action labels come from the closed list or a recorded label." },
35
+ { id: "C9", tier: "shell", from: "3.8.0", kind: "rendered", summary: "No interstitial overlay in the first screen." },
36
+ { id: "C10", tier: "shell", from: "3.8.0", kind: "rendered", summary: "Every tel: href is E.164 (national digits for 13, 1300 and 1800) and matches the evidence-file phone." },
37
+ { id: "E1", tier: "shell", from: "3.8.0", kind: "static", summary: "Title patterns on new or changed URLs; recorded titles unchanged." },
38
+ { id: "E2", tier: "shell", from: "3.8.0", kind: "rendered", summary: "Exactly one H1 on every page, rendered once." },
39
+ { id: "E8", tier: "shell", from: "3.8.0", kind: "rendered", summary: "No banned phrase from any of the three layers, environmental claims included." },
40
+ { id: "T3", tier: "shell", from: "3.8.0", kind: "rendered", summary: "A licence line renders wherever licensed work is advertised, with the licensee where it differs." },
41
+ { id: "T4", tier: "shell", from: "3.8.0", kind: "rendered", summary: "No hard-coded rating or review numbers; proof claims resolve to renderable evidence." },
42
+ { id: "T11", tier: "shell", from: "3.8.0", kind: "static", summary: "No page for work over $5,000 outside the licence classes held; licence conditions carried." },
43
+ { id: "X1", tier: "shell", from: "3.8.0", kind: "record", summary: "Legacy URLs answer 200 or one redirect hop; earning-URL depth, titles and dollar figures kept." },
44
+ { id: "B1", tier: "shell", from: "3.8.0", kind: "rendered", summary: "Lab budgets: JavaScript, third-party requests, hero image and HTML within budget; no unrecorded chat widget, pixel, heatmap or tag manager." },
45
+ { id: "L10-hero", tier: "shell", from: "3.8.0", kind: "rendered", summary: 'No kilometre figures, "km from" or "based in" in hero copy.' },
46
+ // enquiry: the form, thank-you page, reviews, proof strip, footer, CTA band, contact page, prices, media, density
47
+ { id: "C1", tier: "enquiry", from: "3.8.0", kind: "journey", summary: "Money pages: the form in the first section; at 390 x 844 the H1, a call and a quote action in the first viewport; at 1440 x 900 the form." },
48
+ { id: "C2", tier: "enquiry", from: "3.8.0", kind: "rendered", summary: 'Form layout and labels; phone and email required; no helper or reassurance lines, no "(required)", no job select.' },
49
+ { id: "E9", tier: "enquiry", from: "3.8.0", kind: "rendered", summary: "Every cost guide carries at least 8 dollar figures in a renderable price state." },
50
+ { id: "T5", tier: "enquiry", from: "3.8.0", kind: "rendered", summary: "Reviews from the feed, with Google's rating, count and link beside any selection; none on the contact page; no empty reviews section." },
51
+ { id: "K8", tier: "enquiry", from: "3.8.0", kind: "rendered", summary: "Section contract: no empty section, one H2 each, at most one band tone, neighbours differ, proof within the first five sections." },
52
+ { id: "C7", tier: "enquiry", from: "3.10.0", kind: "rendered", summary: "Thank-you page: a reply time, a tel: link and noindex, follow." },
53
+ { id: "C8", tier: "enquiry", from: "3.10.0", kind: "rendered", summary: "Forgiving form: tolerant phone input (13, 1300 and 1800 too), autocomplete tokens, no capture on the photo input." },
54
+ { id: "C11", tier: "enquiry", from: "3.10.0", kind: "static", summary: "The test marker is stripped before delivery and reporting; /api/contact is rate-limited." },
55
+ { id: "N9", tier: "enquiry", from: "3.11.0", kind: "rendered", summary: 'Footer content: column 1 brand, licences, socials and contact; services, areas and company columns; © name, ABN and legal links; no CTA, "based in", hours or guides column.' },
56
+ { id: "K2", tier: "enquiry", from: "3.11.0", kind: "rendered", summary: "Every rendered shared component has a skin entry; no reference-skin class ships." },
57
+ { id: "K11", tier: "enquiry", from: "3.11.0", kind: "rendered", summary: "Density: no section with more than one H2, more than 8 cards or more than 2 calls to action; measure and section padding within limits." },
58
+ { id: "K12-contact", tier: "enquiry", from: "3.11.0", kind: "rendered", summary: "The contact page is the page header, then the contact block, and nothing else." },
59
+ { id: "T6", tier: "enquiry", from: "3.11.0", kind: "static", summary: "Every image has a media.ts entry with provenance." },
60
+ { id: "T7", tier: "enquiry", from: "3.11.0", kind: "rendered", summary: "No image is the main image of two pages." },
61
+ { id: "T8", tier: "enquiry", from: "3.11.0", kind: "rendered", summary: 'Prices: GST flag set; no "+ GST" or "from $X" teasers on cards and menus; one band per job site-wide.' },
62
+ { id: "T9", tier: "enquiry", from: "3.11.0", kind: "static", summary: "No image in /public carries location metadata." },
63
+ // full: page families, location estates, section manifests, links and the data contract
64
+ { id: "K12", tier: "full", from: "3.12.0", kind: "rendered", summary: "Section manifest: every required section rendered or skipped with a data-backed reason; conditional sections render when their data exists; canonical or recorded order." },
65
+ { id: "E5", tier: "full", from: "3.12.0", kind: "static", summary: "No orphan indexable pages, from the route catalogue and link data." },
66
+ { id: "E10", tier: "full", from: "3.12.0", kind: "static", summary: "Phrase-owner uniqueness in strategy data; no service-in-suburb page for the suburb page phrase; no service-region hub for the main trade." },
67
+ { id: "L3", tier: "full", from: "3.12.0", kind: "static", summary: "Neighbours by real distance." },
68
+ { id: "L4", tier: "full", from: "3.12.0", kind: "rendered", summary: "No copy rotated by page address, no name-swapped FAQs, no other suburb's job without the nearby label." },
69
+ { id: "L6", tier: "full", from: "3.12.0", kind: "static", summary: "Location pages have 6 or more inbound links from 3 or more page types." },
70
+ { id: "L7", tier: "full", from: "3.12.0", kind: "static", summary: "Location pages are 3 clicks or fewer from home." },
71
+ { id: "L8", tier: "full", from: "3.12.0", kind: "static", summary: "A hub per family per region." },
72
+ { id: "L9", tier: "full", from: "3.12.0", kind: "rendered", summary: "Data contract on service-location pages: price or cost-guide link, reviews or fallback, local facts; the nearby job card or none." },
73
+ { id: "L10", tier: "full", from: "3.12.0", kind: "rendered", summary: 'No kilometre figures, "km from", "based in" or year stamps as local detail in location copy.' },
74
+ { id: "S4", tier: "full", from: "3.12.0", kind: "static", summary: "No hand-written per-service or per-suburb files; no dynamic route with zero pages." },
75
+ { id: "LOC-INDEX", tier: "full", from: "3.12.0", kind: "static", summary: "No location route is noindex or left out of the sitemap unless the recorded wave schedule dates it (Romie's 20 Oct decision: every suburb page stays indexed)." },
76
+ // new builds and redesigns
77
+ { id: "D4", tier: "new-build", from: "3.8.0", kind: "static", summary: "Same-trade distinctness: at least four of seven look levers differ from every listed competitor (advisory in 3.8.0, blocking from 3.9.0)." },
78
+ ];
79
+ const TIER_RANK = { shell: 0, enquiry: 1, full: 2 };
80
+ /** A release as one comparable number: "3.8.0" < "3.10.0". */
81
+ export function siteSystemReleaseRank(version) {
82
+ const [major = 0, minor = 0, patch = 0] = version.split(/[.-]/).map((part) => Number.parseInt(part, 10) || 0);
83
+ return major * 1_000_000 + minor * 1_000 + patch;
84
+ }
85
+ /**
86
+ * Whether a check blocks for a site at `tier` on `release`. Checks of higher tiers, and checks newer than the
87
+ * installed release, report without blocking. D4 blocks new builds and redesigns from 3.9.0 whatever their tier.
88
+ */
89
+ export function siteSystemCheckBlocks(check, site) {
90
+ if (check.tier === "new-build")
91
+ return site.newBuild === true && siteSystemReleaseRank(site.release) >= siteSystemReleaseRank("3.9.0");
92
+ if (siteSystemReleaseRank(site.release) < siteSystemReleaseRank(check.from))
93
+ return false;
94
+ return TIER_RANK[check.tier] <= TIER_RANK[site.tier];
95
+ }
96
+ //# sourceMappingURL=checks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checks.js","sourceRoot":"","sources":["../../src/site-system/checks.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The Site System v2 ready gate: every check, the adoption tier it blocks at and the release that brings it.\n *\n * A site declares `siteSystem: { tier }` in brandlab.config.ts. Checks of that tier and the tiers below it block;\n * checks of higher tiers report without blocking. A check blocks only from the release named here, so new checks\n * arrive with version bumps and never turn the fleet red at once. Holdouts use the project's Romie-owned\n * `exceptions` record. D6 (leave reviews as they are) removes every review-exclusion clause; D7 (no call tracking)\n * means no number-swap check.\n *\n * `kind` says where a check runs:\n * - `journey`: the Playwright site-system journeys (`defineBrandlabSiteSystemTests`), in the site's e2e run;\n * - `rendered`: rules over the built site's server HTML and assets (`brandlab-ready` reads them);\n * - `static`: source and data rules (`brandlab-verify` and `brandlab-ready`);\n * - `record`: launch-day and human steps recorded with `brandlab-ready record <id> --evidence <ref>`.\n */\n\nexport const SITE_SYSTEM_TIERS = [\"shell\", \"enquiry\", \"full\"] as const;\nexport type SiteSystemTier = (typeof SITE_SYSTEM_TIERS)[number];\n\n/** The design check D4 blocks every new build and redesign, whatever its tier. */\nexport type SiteSystemCheckScope = SiteSystemTier | \"new-build\";\n\nexport type SiteSystemCheckKind = \"journey\" | \"rendered\" | \"static\" | \"record\";\n\nexport interface SiteSystemCheck {\n readonly id: string;\n readonly tier: SiteSystemCheckScope;\n /** The first release in which the check blocks at its tier. */\n readonly from: `3.${number}.0`;\n readonly kind: SiteSystemCheckKind;\n readonly summary: string;\n}\n\nexport const SITE_SYSTEM_CHECKS = [\n // shell: header, nav, call bar, placements, crawlable links, contrast and targets, call links, budgets, legal\n { id: \"N1\", tier: \"shell\", from: \"3.9.0\", kind: \"static\", summary: \"Required nav links, About in the first two, services before Areas, Contact last, labels from the closed list, recorded orders kept.\" },\n { id: \"N2\", tier: \"shell\", from: \"3.8.0\", kind: \"rendered\", summary: \"Every dropdown link and hub link is an <a href> in the server HTML.\" },\n { id: \"N3\", tier: \"shell\", from: \"3.8.0\", kind: \"journey\", summary: \"Hover opens after a short delay and a diagonal path stays open; mouse-out closes; nothing sticks open.\" },\n { id: \"N4\", tier: \"shell\", from: \"3.8.0\", kind: \"journey\", summary: \"A chevron click after hover keeps the panel open; a link click closes it; click, Enter and outside click work.\" },\n { id: \"N5\", tier: \"shell\", from: \"3.8.0\", kind: \"journey\", summary: \"Keyboard and ARIA: aria-expanded follows the panel, Arrow keys move, Escape closes and returns focus; axe passes open and closed.\" },\n { id: \"N6\", tier: \"shell\", from: \"3.8.0\", kind: \"journey\", summary: \"The header stays at the top with the phone and quote visible at every desktop width; the compact band shows them from 1024 to 1279 px.\" },\n { id: \"N8\", tier: \"shell\", from: \"3.9.0\", kind: \"journey\", summary: \"The drawer is a modal dialog that traps focus and resets; the call bar uses placement fab; back-to-top appears (from 3.11.0).\" },\n { id: \"K1\", tier: \"shell\", from: \"3.9.0\", kind: \"rendered\", summary: \"Shared components in use for the parts the tier covers (data-bl present); no literal tel: outside PhoneLink.\" },\n { id: \"K3\", tier: \"shell\", from: \"3.8.0\", kind: \"journey\", summary: \"Text contrast 4.5:1 and non-text 3:1 in the browser.\" },\n { id: \"K4\", tier: \"shell\", from: \"3.8.0\", kind: \"journey\", summary: \"Targets at least 24 px, and 44 px for primary actions (call, quote, menu).\" },\n { id: \"K5\", tier: \"shell\", from: \"3.8.0\", kind: \"journey\", summary: \"Focus is never fully hidden by the sticky header or call bar at 390 px, in Chromium and WebKit.\" },\n { id: \"K6\", tier: \"shell\", from: \"3.8.0\", kind: \"journey\", summary: \"Phone and quote in the same relative order on every page, in the header, hero and call bar.\" },\n { id: \"K7\", tier: \"shell\", from: \"3.8.0\", kind: \"rendered\", summary: \"Dropdown links, accordion and tab panels and full review text are in the server HTML.\" },\n { id: \"K10\", tier: \"shell\", from: \"3.8.0\", kind: \"record\", summary: \"Adoption parity in the lenient harness: no section, link or text lost against the baseline.\" },\n { id: \"C4\", tier: \"shell\", from: \"3.8.0\", kind: \"journey\", summary: \"Phone taps report an allowed placement (header, footer, hero, cta, fab, contact, location, page).\" },\n { id: \"C5\", tier: \"shell\", from: \"3.8.0\", kind: \"rendered\", summary: \"Call-to-action labels come from the closed list or a recorded label.\" },\n { id: \"C9\", tier: \"shell\", from: \"3.8.0\", kind: \"rendered\", summary: \"No interstitial overlay in the first screen.\" },\n { id: \"C10\", tier: \"shell\", from: \"3.8.0\", kind: \"rendered\", summary: \"Every tel: href is E.164 (national digits for 13, 1300 and 1800) and matches the evidence-file phone.\" },\n { id: \"E1\", tier: \"shell\", from: \"3.8.0\", kind: \"static\", summary: \"Title patterns on new or changed URLs; recorded titles unchanged.\" },\n { id: \"E2\", tier: \"shell\", from: \"3.8.0\", kind: \"rendered\", summary: \"Exactly one H1 on every page, rendered once.\" },\n { id: \"E8\", tier: \"shell\", from: \"3.8.0\", kind: \"rendered\", summary: \"No banned phrase from any of the three layers, environmental claims included.\" },\n { id: \"T3\", tier: \"shell\", from: \"3.8.0\", kind: \"rendered\", summary: \"A licence line renders wherever licensed work is advertised, with the licensee where it differs.\" },\n { id: \"T4\", tier: \"shell\", from: \"3.8.0\", kind: \"rendered\", summary: \"No hard-coded rating or review numbers; proof claims resolve to renderable evidence.\" },\n { id: \"T11\", tier: \"shell\", from: \"3.8.0\", kind: \"static\", summary: \"No page for work over $5,000 outside the licence classes held; licence conditions carried.\" },\n { id: \"X1\", tier: \"shell\", from: \"3.8.0\", kind: \"record\", summary: \"Legacy URLs answer 200 or one redirect hop; earning-URL depth, titles and dollar figures kept.\" },\n { id: \"B1\", tier: \"shell\", from: \"3.8.0\", kind: \"rendered\", summary: \"Lab budgets: JavaScript, third-party requests, hero image and HTML within budget; no unrecorded chat widget, pixel, heatmap or tag manager.\" },\n { id: \"L10-hero\", tier: \"shell\", from: \"3.8.0\", kind: \"rendered\", summary: 'No kilometre figures, \"km from\" or \"based in\" in hero copy.' },\n\n // enquiry: the form, thank-you page, reviews, proof strip, footer, CTA band, contact page, prices, media, density\n { id: \"C1\", tier: \"enquiry\", from: \"3.8.0\", kind: \"journey\", summary: \"Money pages: the form in the first section; at 390 x 844 the H1, a call and a quote action in the first viewport; at 1440 x 900 the form.\" },\n { id: \"C2\", tier: \"enquiry\", from: \"3.8.0\", kind: \"rendered\", summary: 'Form layout and labels; phone and email required; no helper or reassurance lines, no \"(required)\", no job select.' },\n { id: \"E9\", tier: \"enquiry\", from: \"3.8.0\", kind: \"rendered\", summary: \"Every cost guide carries at least 8 dollar figures in a renderable price state.\" },\n { id: \"T5\", tier: \"enquiry\", from: \"3.8.0\", kind: \"rendered\", summary: \"Reviews from the feed, with Google's rating, count and link beside any selection; none on the contact page; no empty reviews section.\" },\n { id: \"K8\", tier: \"enquiry\", from: \"3.8.0\", kind: \"rendered\", summary: \"Section contract: no empty section, one H2 each, at most one band tone, neighbours differ, proof within the first five sections.\" },\n { id: \"C7\", tier: \"enquiry\", from: \"3.10.0\", kind: \"rendered\", summary: \"Thank-you page: a reply time, a tel: link and noindex, follow.\" },\n { id: \"C8\", tier: \"enquiry\", from: \"3.10.0\", kind: \"rendered\", summary: \"Forgiving form: tolerant phone input (13, 1300 and 1800 too), autocomplete tokens, no capture on the photo input.\" },\n { id: \"C11\", tier: \"enquiry\", from: \"3.10.0\", kind: \"static\", summary: \"The test marker is stripped before delivery and reporting; /api/contact is rate-limited.\" },\n { id: \"N9\", tier: \"enquiry\", from: \"3.11.0\", kind: \"rendered\", summary: 'Footer content: column 1 brand, licences, socials and contact; services, areas and company columns; © name, ABN and legal links; no CTA, \"based in\", hours or guides column.' },\n { id: \"K2\", tier: \"enquiry\", from: \"3.11.0\", kind: \"rendered\", summary: \"Every rendered shared component has a skin entry; no reference-skin class ships.\" },\n { id: \"K11\", tier: \"enquiry\", from: \"3.11.0\", kind: \"rendered\", summary: \"Density: no section with more than one H2, more than 8 cards or more than 2 calls to action; measure and section padding within limits.\" },\n { id: \"K12-contact\", tier: \"enquiry\", from: \"3.11.0\", kind: \"rendered\", summary: \"The contact page is the page header, then the contact block, and nothing else.\" },\n { id: \"T6\", tier: \"enquiry\", from: \"3.11.0\", kind: \"static\", summary: \"Every image has a media.ts entry with provenance.\" },\n { id: \"T7\", tier: \"enquiry\", from: \"3.11.0\", kind: \"rendered\", summary: \"No image is the main image of two pages.\" },\n { id: \"T8\", tier: \"enquiry\", from: \"3.11.0\", kind: \"rendered\", summary: 'Prices: GST flag set; no \"+ GST\" or \"from $X\" teasers on cards and menus; one band per job site-wide.' },\n { id: \"T9\", tier: \"enquiry\", from: \"3.11.0\", kind: \"static\", summary: \"No image in /public carries location metadata.\" },\n\n // full: page families, location estates, section manifests, links and the data contract\n { id: \"K12\", tier: \"full\", from: \"3.12.0\", kind: \"rendered\", summary: \"Section manifest: every required section rendered or skipped with a data-backed reason; conditional sections render when their data exists; canonical or recorded order.\" },\n { id: \"E5\", tier: \"full\", from: \"3.12.0\", kind: \"static\", summary: \"No orphan indexable pages, from the route catalogue and link data.\" },\n { id: \"E10\", tier: \"full\", from: \"3.12.0\", kind: \"static\", summary: \"Phrase-owner uniqueness in strategy data; no service-in-suburb page for the suburb page phrase; no service-region hub for the main trade.\" },\n { id: \"L3\", tier: \"full\", from: \"3.12.0\", kind: \"static\", summary: \"Neighbours by real distance.\" },\n { id: \"L4\", tier: \"full\", from: \"3.12.0\", kind: \"rendered\", summary: \"No copy rotated by page address, no name-swapped FAQs, no other suburb's job without the nearby label.\" },\n { id: \"L6\", tier: \"full\", from: \"3.12.0\", kind: \"static\", summary: \"Location pages have 6 or more inbound links from 3 or more page types.\" },\n { id: \"L7\", tier: \"full\", from: \"3.12.0\", kind: \"static\", summary: \"Location pages are 3 clicks or fewer from home.\" },\n { id: \"L8\", tier: \"full\", from: \"3.12.0\", kind: \"static\", summary: \"A hub per family per region.\" },\n { id: \"L9\", tier: \"full\", from: \"3.12.0\", kind: \"rendered\", summary: \"Data contract on service-location pages: price or cost-guide link, reviews or fallback, local facts; the nearby job card or none.\" },\n { id: \"L10\", tier: \"full\", from: \"3.12.0\", kind: \"rendered\", summary: 'No kilometre figures, \"km from\", \"based in\" or year stamps as local detail in location copy.' },\n { id: \"S4\", tier: \"full\", from: \"3.12.0\", kind: \"static\", summary: \"No hand-written per-service or per-suburb files; no dynamic route with zero pages.\" },\n { id: \"LOC-INDEX\", tier: \"full\", from: \"3.12.0\", kind: \"static\", summary: \"No location route is noindex or left out of the sitemap unless the recorded wave schedule dates it (Romie's 20 Oct decision: every suburb page stays indexed).\" },\n\n // new builds and redesigns\n { id: \"D4\", tier: \"new-build\", from: \"3.8.0\", kind: \"static\", summary: \"Same-trade distinctness: at least four of seven look levers differ from every listed competitor (advisory in 3.8.0, blocking from 3.9.0).\" },\n] as const satisfies readonly SiteSystemCheck[];\n\nexport type SiteSystemCheckId = (typeof SITE_SYSTEM_CHECKS)[number][\"id\"];\n\nconst TIER_RANK: Record<SiteSystemTier, number> = { shell: 0, enquiry: 1, full: 2 };\n\n/** A release as one comparable number: \"3.8.0\" < \"3.10.0\". */\nexport function siteSystemReleaseRank(version: string): number {\n const [major = 0, minor = 0, patch = 0] = version.split(/[.-]/).map((part) => Number.parseInt(part, 10) || 0);\n return major * 1_000_000 + minor * 1_000 + patch;\n}\n\n/**\n * Whether a check blocks for a site at `tier` on `release`. Checks of higher tiers, and checks newer than the\n * installed release, report without blocking. D4 blocks new builds and redesigns from 3.9.0 whatever their tier.\n */\nexport function siteSystemCheckBlocks(\n check: Pick<SiteSystemCheck, \"id\" | \"tier\" | \"from\">,\n site: { tier: SiteSystemTier; release: string; newBuild?: boolean },\n): boolean {\n if (check.tier === \"new-build\") return site.newBuild === true && siteSystemReleaseRank(site.release) >= siteSystemReleaseRank(\"3.9.0\");\n if (siteSystemReleaseRank(site.release) < siteSystemReleaseRank(check.from)) return false;\n return TIER_RANK[check.tier] <= TIER_RANK[site.tier];\n}\n\n/** One result of one check, from any runner. `brandlab-ready` merges them into the ready report. */\nexport interface SiteSystemFinding {\n readonly checkId: SiteSystemCheckId;\n readonly status: \"pass\" | \"fail\" | \"not-applicable\";\n /** The page path or file the result is about, when it is about one. */\n readonly target?: string;\n /** One plain sentence: what passed or what is wrong and where. */\n readonly message: string;\n /** A warning never blocks: the check still passes and the report shows \"pass (warning)\". */\n readonly severity?: \"warning\";\n}\n"]}
@@ -0,0 +1,40 @@
1
+ import { type SiteSystemTier } from "./checks.js";
2
+ export interface SiteSystemSettings {
3
+ readonly tier: SiteSystemTier;
4
+ /** A new build or redesign: D4 blocks whatever the tier. */
5
+ readonly newBuild: boolean;
6
+ /** Extra pages to sample for the rendered checks. */
7
+ readonly samples: readonly string[];
8
+ /** Quote and call-to-action labels recorded for this site, beyond the closed list (C5). */
9
+ readonly labels: readonly string[];
10
+ /** Whether the business offers free quotes; null when not recorded. */
11
+ readonly freeQuotes: boolean | null;
12
+ /** Third-party hostnames the site may load (B1). */
13
+ readonly thirdParties: readonly string[];
14
+ /** Extra tel: links the site legitimately shows (C10). */
15
+ readonly phones: readonly string[];
16
+ }
17
+ /** A Romie-owned holdout; `ruleId` names the check it covers. */
18
+ export interface SiteSystemException {
19
+ readonly ruleId: string;
20
+ readonly rationale: string;
21
+ readonly evidence: string;
22
+ readonly expiresAt?: string;
23
+ readonly reviewTrigger: string;
24
+ }
25
+ export interface SiteSystemProjectConfig {
26
+ readonly projectId: string | null;
27
+ /** Null when the site has not adopted Site System v2: nothing in the gate blocks. */
28
+ readonly siteSystem: SiteSystemSettings | null;
29
+ readonly exceptions: readonly SiteSystemException[];
30
+ /** `searchCertification.representativeRoutes` (home, contact, service, location, blog). */
31
+ readonly representativeRoutes: Readonly<Record<string, string>>;
32
+ /** False when defineBrandlabProject({ ... }) spreads or computes a member, so a tier could be hidden. */
33
+ readonly complete: boolean;
34
+ /** Fields that could not be read; the gate reports them and carries on without them. */
35
+ readonly problems: readonly string[];
36
+ }
37
+ export declare function readSiteSystemProjectConfig(root: string): SiteSystemProjectConfig;
38
+ /** Throws when siteSystem is present but unreadable or invalid: a declared tier must never be skipped silently. */
39
+ export declare function parseSiteSystemProjectConfig(text: string): SiteSystemProjectConfig;
40
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/site-system/config.ts"],"names":[],"mappings":"AAQA,OAAO,EAAqB,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAErE,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,4DAA4D;IAC5D,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,qDAAqD;IACrD,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,2FAA2F;IAC3F,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,uEAAuE;IACvE,QAAQ,CAAC,UAAU,EAAE,OAAO,GAAG,IAAI,CAAC;IACpC,oDAAoD;IACpD,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,0DAA0D;IAC1D,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC;AAED,iEAAiE;AACjE,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,qFAAqF;IACrF,QAAQ,CAAC,UAAU,EAAE,kBAAkB,GAAG,IAAI,CAAC;IAC/C,QAAQ,CAAC,UAAU,EAAE,SAAS,mBAAmB,EAAE,CAAC;IACpD,2FAA2F;IAC3F,QAAQ,CAAC,oBAAoB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAChE,yGAAyG;IACzG,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,wFAAwF;IACxF,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC;AAQD,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,GAAG,uBAAuB,CAIjF;AAED,mHAAmH;AACnH,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,MAAM,GAAG,uBAAuB,CAmDlF"}