@brandlab-digital/conformance 3.16.0 → 3.17.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.
- package/dist/current-release.js +5 -5
- package/dist/current-release.js.map +1 -1
- package/dist/site-system/rules/enquiry/k8-sections.d.ts +4 -1
- package/dist/site-system/rules/enquiry/k8-sections.d.ts.map +1 -1
- package/dist/site-system/rules/enquiry/k8-sections.js +6 -1
- package/dist/site-system/rules/enquiry/k8-sections.js.map +1 -1
- package/dist/site-system/steps.d.ts +1 -1
- package/dist/site-system/steps.js +1 -1
- package/dist/site-system/steps.js.map +1 -1
- package/package.json +2 -2
package/dist/current-release.js
CHANGED
|
@@ -23,19 +23,19 @@ export const BRANDLAB_CURRENT_RUNTIME_RELEASE = Object.freeze({
|
|
|
23
23
|
"kind": "brandlab-runtime-release-contract",
|
|
24
24
|
"packages": {
|
|
25
25
|
"@brandlab-digital/conformance": {
|
|
26
|
-
"version": "3.
|
|
26
|
+
"version": "3.17.0"
|
|
27
27
|
},
|
|
28
28
|
"@brandlab-digital/eslint-plugin": {
|
|
29
|
-
"version": "3.
|
|
29
|
+
"version": "3.17.0"
|
|
30
30
|
},
|
|
31
31
|
"@brandlab-digital/site-core": {
|
|
32
|
-
"version": "3.
|
|
32
|
+
"version": "3.17.0"
|
|
33
33
|
},
|
|
34
34
|
"@brandlab-digital/site-next": {
|
|
35
|
-
"version": "3.
|
|
35
|
+
"version": "3.17.0"
|
|
36
36
|
}
|
|
37
37
|
},
|
|
38
|
-
"releaseVersion": "3.
|
|
38
|
+
"releaseVersion": "3.17.0",
|
|
39
39
|
"runtime": {
|
|
40
40
|
"bundler": "turbopack",
|
|
41
41
|
"cacheComponents": false,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"current-release.js","sourceRoot":"","sources":["../src/current-release.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,wEAAwE;AACxE,OAAO,EAEL,sCAAsC,GACvC,MAAM,uBAAuB,CAAC;AAE/B,MAAM,CAAC,MAAM,gCAAgC,GAAG,MAAM,CAAC,MAAM,CAC7D;IACE,cAAc,EAAE;QACd,uCAAuC;QACvC,8BAA8B;QAC9B,yBAAyB;QACzB,6BAA6B;QAC7B,0BAA0B;QAC1B,4BAA4B;QAC5B,kCAAkC;QAClC,wCAAwC;QACxC,yBAAyB;KAC1B;IACD,WAAW,EAAE;QACX,WAAW,EAAE,cAAc;QAC3B,cAAc,EAAE,iBAAiB;QACjC,eAAe,EAAE,mBAAmB;QACpC,SAAS,EAAE,YAAY;QACvB,KAAK,EAAE,QAAQ;KAChB;IACD,MAAM,EAAE,mCAAmC;IAC3C,UAAU,EAAE;QACV,+BAA+B,EAAE;YAC/B,SAAS,EAAE,QAAQ;SACpB;QACD,iCAAiC,EAAE;YACjC,SAAS,EAAE,QAAQ;SACpB;QACD,6BAA6B,EAAE;YAC7B,SAAS,EAAE,QAAQ;SACpB;QACD,6BAA6B,EAAE;YAC7B,SAAS,EAAE,QAAQ;SACpB;KACF;IACD,gBAAgB,EAAE,QAAQ;IAC1B,SAAS,EAAE;QACT,SAAS,EAAE,WAAW;QACtB,iBAAiB,EAAE,KAAK;QACxB,MAAM,EAAE,QAAQ;QAChB,mBAAmB,EAAE,OAAO;QAC5B,MAAM,EAAE,MAAM;QACd,gBAAgB,EAAE,aAAa;QAC/B,OAAO,EAAE,QAAQ;QACjB,UAAU,EAAE,QAAQ;QACpB,QAAQ,EAAE,KAAK;QACf,OAAO,EAAE,QAAQ;QACjB,YAAY,EAAE,OAAO;KACtB;IACD,eAAe,EAAE,sCAAsC;CACxD,CAC6C,CAAC;AAE/C,sCAAsC,CAAC,gCAAgC,CAAC,CAAC","sourcesContent":["// Generated by standards/scripts/generate-conformance-current-release.mjs.\n// Each exact package release verifies its own current runtime contract.\nimport {\n type BrandlabRuntimeReleaseContract,\n validateBrandlabRuntimeReleaseContract,\n} from \"./release-manifest.js\";\n\nexport const BRANDLAB_CURRENT_RUNTIME_RELEASE = Object.freeze(\n{\n \"capabilities\": [\n \"typed-project-business-and-route-data\",\n \"essential-search-foundations\",\n \"shared-contact-delivery\",\n \"privacy-safe-lead-analytics\",\n \"security-patched-runtime\",\n \"live-google-review-summary\",\n \"high-signal-project-verification\",\n \"packed-positive-and-negative-consumers\",\n \"changesets-oidc-release\"\n ],\n \"contracts\": {\n \"analytics\": \"analytics-v4\",\n \"architecture\": \"architecture-v7\",\n \"configuration\": \"project-config-v7\",\n \"contact\": \"contact-v4\",\n \"seo\": \"seo-v4\"\n },\n \"kind\": \"brandlab-runtime-release-contract\",\n \"packages\": {\n \"@brandlab-digital/conformance\": {\n \"version\": \"3.
|
|
1
|
+
{"version":3,"file":"current-release.js","sourceRoot":"","sources":["../src/current-release.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,wEAAwE;AACxE,OAAO,EAEL,sCAAsC,GACvC,MAAM,uBAAuB,CAAC;AAE/B,MAAM,CAAC,MAAM,gCAAgC,GAAG,MAAM,CAAC,MAAM,CAC7D;IACE,cAAc,EAAE;QACd,uCAAuC;QACvC,8BAA8B;QAC9B,yBAAyB;QACzB,6BAA6B;QAC7B,0BAA0B;QAC1B,4BAA4B;QAC5B,kCAAkC;QAClC,wCAAwC;QACxC,yBAAyB;KAC1B;IACD,WAAW,EAAE;QACX,WAAW,EAAE,cAAc;QAC3B,cAAc,EAAE,iBAAiB;QACjC,eAAe,EAAE,mBAAmB;QACpC,SAAS,EAAE,YAAY;QACvB,KAAK,EAAE,QAAQ;KAChB;IACD,MAAM,EAAE,mCAAmC;IAC3C,UAAU,EAAE;QACV,+BAA+B,EAAE;YAC/B,SAAS,EAAE,QAAQ;SACpB;QACD,iCAAiC,EAAE;YACjC,SAAS,EAAE,QAAQ;SACpB;QACD,6BAA6B,EAAE;YAC7B,SAAS,EAAE,QAAQ;SACpB;QACD,6BAA6B,EAAE;YAC7B,SAAS,EAAE,QAAQ;SACpB;KACF;IACD,gBAAgB,EAAE,QAAQ;IAC1B,SAAS,EAAE;QACT,SAAS,EAAE,WAAW;QACtB,iBAAiB,EAAE,KAAK;QACxB,MAAM,EAAE,QAAQ;QAChB,mBAAmB,EAAE,OAAO;QAC5B,MAAM,EAAE,MAAM;QACd,gBAAgB,EAAE,aAAa;QAC/B,OAAO,EAAE,QAAQ;QACjB,UAAU,EAAE,QAAQ;QACpB,QAAQ,EAAE,KAAK;QACf,OAAO,EAAE,QAAQ;QACjB,YAAY,EAAE,OAAO;KACtB;IACD,eAAe,EAAE,sCAAsC;CACxD,CAC6C,CAAC;AAE/C,sCAAsC,CAAC,gCAAgC,CAAC,CAAC","sourcesContent":["// Generated by standards/scripts/generate-conformance-current-release.mjs.\n// Each exact package release verifies its own current runtime contract.\nimport {\n type BrandlabRuntimeReleaseContract,\n validateBrandlabRuntimeReleaseContract,\n} from \"./release-manifest.js\";\n\nexport const BRANDLAB_CURRENT_RUNTIME_RELEASE = Object.freeze(\n{\n \"capabilities\": [\n \"typed-project-business-and-route-data\",\n \"essential-search-foundations\",\n \"shared-contact-delivery\",\n \"privacy-safe-lead-analytics\",\n \"security-patched-runtime\",\n \"live-google-review-summary\",\n \"high-signal-project-verification\",\n \"packed-positive-and-negative-consumers\",\n \"changesets-oidc-release\"\n ],\n \"contracts\": {\n \"analytics\": \"analytics-v4\",\n \"architecture\": \"architecture-v7\",\n \"configuration\": \"project-config-v7\",\n \"contact\": \"contact-v4\",\n \"seo\": \"seo-v4\"\n },\n \"kind\": \"brandlab-runtime-release-contract\",\n \"packages\": {\n \"@brandlab-digital/conformance\": {\n \"version\": \"3.17.0\"\n },\n \"@brandlab-digital/eslint-plugin\": {\n \"version\": \"3.17.0\"\n },\n \"@brandlab-digital/site-core\": {\n \"version\": \"3.17.0\"\n },\n \"@brandlab-digital/site-next\": {\n \"version\": \"3.17.0\"\n }\n },\n \"releaseVersion\": \"3.17.0\",\n \"runtime\": {\n \"bundler\": \"turbopack\",\n \"cacheComponents\": false,\n \"next\": \"16.4.2\",\n \"nextVersionPolicy\": \"exact\",\n \"node\": \"24.x\",\n \"packageManager\": \"npm@11.14.1\",\n \"react\": \"19.2.8\",\n \"reactDom\": \"19.2.8\",\n \"router\": \"app\",\n \"sharp\": \"0.35.5\",\n \"typescript\": \"6.0.3\"\n },\n \"schemaVersion\": \"brandlab.runtime-release-contract.v1\"\n},\n) as unknown as BrandlabRuntimeReleaseContract;\n\nvalidateBrandlabRuntimeReleaseContract(BRANDLAB_CURRENT_RUNTIME_RELEASE);\n"]}
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Every `data-bl-section` has exactly one H2 (the section that holds the page's H1 may have none), is not empty, and
|
|
5
5
|
* sits in a tone that differs from its neighbours'. A page has at most one `band` tone. A page that opens with a hero
|
|
6
|
-
* (the money pages) shows proof within its first five sections.
|
|
6
|
+
* (the money pages) shows proof within its first five sections. The emergency band and signature sections sit outside
|
|
7
|
+
* the caps and don't take a place in that window either (3.17.0), so a signature placed early never pushes the proof out.
|
|
7
8
|
*/
|
|
8
9
|
import type { SiteSystemFinding } from "../../checks.js";
|
|
9
10
|
import { type ParsedPage } from "./common.js";
|
|
@@ -12,6 +13,8 @@ import { type HtmlElement } from "./html.js";
|
|
|
12
13
|
export declare const K8_PROOF_SECTIONS: readonly ["reviews", "proof-strip", "projects"];
|
|
13
14
|
/** The first sections of a money page are where proof has to appear. */
|
|
14
15
|
export declare const K8_PROOF_WITHIN = 5;
|
|
16
|
+
/** Uncapped sections that aren't proof: they are skipped when counting the first five (3.17.0). */
|
|
17
|
+
export declare const K8_WINDOW_EXEMPT: readonly ["emergency", "signature"];
|
|
15
18
|
export declare function isEmptySection(section: HtmlElement): boolean;
|
|
16
19
|
export declare function checkK8(pages: readonly ParsedPage[]): SiteSystemFinding[];
|
|
17
20
|
//# sourceMappingURL=k8-sections.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"k8-sections.d.ts","sourceRoot":"","sources":["../../../../src/site-system/rules/enquiry/k8-sections.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"k8-sections.d.ts","sourceRoot":"","sources":["../../../../src/site-system/rules/enquiry/k8-sections.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAsE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAClH,OAAO,EAAoC,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AAE/E,gEAAgE;AAChE,eAAO,MAAM,iBAAiB,iDAAkD,CAAC;AAEjF,wEAAwE;AACxE,eAAO,MAAM,eAAe,IAAI,CAAC;AAEjC,mGAAmG;AACnG,eAAO,MAAM,gBAAgB,qCAAsC,CAAC;AAsBpE,wBAAgB,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAO5D;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,GAAG,iBAAiB,EAAE,CA+CzE"}
|
|
@@ -4,6 +4,8 @@ import { attr, descendants, normaliseText } from "./html.js";
|
|
|
4
4
|
export const K8_PROOF_SECTIONS = ["reviews", "proof-strip", "projects"];
|
|
5
5
|
/** The first sections of a money page are where proof has to appear. */
|
|
6
6
|
export const K8_PROOF_WITHIN = 5;
|
|
7
|
+
/** Uncapped sections that aren't proof: they are skipped when counting the first five (3.17.0). */
|
|
8
|
+
export const K8_WINDOW_EXEMPT = ["emergency", "signature"];
|
|
7
9
|
/** Elements that are content on their own, without any text. */
|
|
8
10
|
const CONTENT_TAGS = new Set(["img", "picture", "video", "audio", "iframe", "canvas", "input", "textarea", "select", "button", "table"]);
|
|
9
11
|
function headings(section, tag) {
|
|
@@ -63,7 +65,10 @@ export function checkK8(pages) {
|
|
|
63
65
|
}
|
|
64
66
|
const opensWithHero = sections.some((section) => sectionId(section) === "hero");
|
|
65
67
|
if (opensWithHero) {
|
|
66
|
-
const firstFive = sections
|
|
68
|
+
const firstFive = sections
|
|
69
|
+
.map(sectionId)
|
|
70
|
+
.filter((id) => !K8_WINDOW_EXEMPT.includes(id))
|
|
71
|
+
.slice(0, K8_PROOF_WITHIN);
|
|
67
72
|
if (!firstFive.some((id) => K8_PROOF_SECTIONS.includes(id))) {
|
|
68
73
|
failures.push(fail("K8", page.path, `${page.path} shows no proof (${K8_PROOF_SECTIONS.join(", ")}) in its first ${K8_PROOF_WITHIN} sections.`));
|
|
69
74
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"k8-sections.js","sourceRoot":"","sources":["../../../../src/site-system/rules/enquiry/k8-sections.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"k8-sections.js","sourceRoot":"","sources":["../../../../src/site-system/rules/enquiry/k8-sections.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * K8: the section contract, page by page (Site System v2 §13.2).\n *\n * Every `data-bl-section` has exactly one H2 (the section that holds the page's H1 may have none), is not empty, and\n * sits in a tone that differs from its neighbours'. A page has at most one `band` tone. A page that opens with a hero\n * (the money pages) shows proof within its first five sections. The emergency band and signature sections sit outside\n * the caps and don't take a place in that window either (3.17.0), so a signature placed early never pushes the proof out.\n */\nimport type { SiteSystemFinding } from \"../../checks.js\";\nimport { fail, notApplicable, plural, resultOf, sectionId, topLevelSections, type ParsedPage } from \"./common.js\";\nimport { attr, descendants, normaliseText, type HtmlElement } from \"./html.js\";\n\n/** The sections that count as proof for the first-five rule. */\nexport const K8_PROOF_SECTIONS = [\"reviews\", \"proof-strip\", \"projects\"] as const;\n\n/** The first sections of a money page are where proof has to appear. */\nexport const K8_PROOF_WITHIN = 5;\n\n/** Uncapped sections that aren't proof: they are skipped when counting the first five (3.17.0). */\nexport const K8_WINDOW_EXEMPT = [\"emergency\", \"signature\"] as const;\n\n/** Elements that are content on their own, without any text. */\nconst CONTENT_TAGS = new Set([\"img\", \"picture\", \"video\", \"audio\", \"iframe\", \"canvas\", \"input\", \"textarea\", \"select\", \"button\", \"table\"]);\n\nfunction headings(section: HtmlElement, tag: string): HtmlElement[] {\n return descendants(section).filter((element) => element.tag === tag);\n}\n\n/** The text of a section outside its headings. */\nfunction bodyText(element: HtmlElement): string {\n let text = \"\";\n for (const child of element.children) {\n if (child.type === \"text\") {\n text += child.text;\n } else if (!/^h[1-6]$/.test(child.tag) && ![\"script\", \"style\", \"template\"].includes(child.tag)) {\n text += ` ${bodyText(child)} `;\n }\n }\n return text;\n}\n\nexport function isEmptySection(section: HtmlElement): boolean {\n if (normaliseText(bodyText(section)) !== \"\") return false;\n return !descendants(section).some(\n (element) =>\n CONTENT_TAGS.has(element.tag) ||\n (element.tag === \"svg\" && (attr(element, \"role\") === \"img\" || attr(element, \"aria-label\") !== undefined)),\n );\n}\n\nexport function checkK8(pages: readonly ParsedPage[]): SiteSystemFinding[] {\n const failures: SiteSystemFinding[] = [];\n let checked = 0;\n for (const page of pages) {\n const sections = topLevelSections(page.root);\n if (sections.length === 0) continue;\n checked += 1;\n const at = (index: number) => `\"${sectionId(sections[index]!)}\" (section ${index + 1})`;\n\n sections.forEach((section, index) => {\n const h2 = headings(section, \"h2\").length;\n const holdsH1 = headings(section, \"h1\").length > 0;\n if (h2 > 1) {\n failures.push(fail(\"K8\", page.path, `The section ${at(index)} on ${page.path} has ${h2} H2 headings; each section has one.`));\n } else if (h2 === 0 && !holdsH1) {\n failures.push(fail(\"K8\", page.path, `The section ${at(index)} on ${page.path} has no H2 heading; each section has one.`));\n }\n if (isEmptySection(section)) {\n failures.push(fail(\"K8\", page.path, `The section ${at(index)} on ${page.path} is empty; a section with nothing to show is left out.`));\n }\n });\n\n const bands = sections.filter((section) => attr(section, \"data-tone\") === \"band\");\n if (bands.length > 1) {\n failures.push(fail(\"K8\", page.path, `${page.path} has ${bands.length} sections in the band tone; a page has at most one.`));\n }\n for (let index = 1; index < sections.length; index += 1) {\n const previous = attr(sections[index - 1]!, \"data-tone\");\n const tone = attr(sections[index]!, \"data-tone\");\n if (tone !== undefined && tone === previous) {\n failures.push(fail(\"K8\", page.path, `The sections ${at(index - 1)} and ${at(index)} on ${page.path} are both in the ${tone} tone; neighbours differ.`));\n }\n }\n\n const opensWithHero = sections.some((section) => sectionId(section) === \"hero\");\n if (opensWithHero) {\n const firstFive = sections\n .map(sectionId)\n .filter((id) => !(K8_WINDOW_EXEMPT as readonly (string | undefined)[]).includes(id))\n .slice(0, K8_PROOF_WITHIN);\n if (!firstFive.some((id) => (K8_PROOF_SECTIONS as readonly string[]).includes(id))) {\n failures.push(fail(\"K8\", page.path, `${page.path} shows no proof (${K8_PROOF_SECTIONS.join(\", \")}) in its first ${K8_PROOF_WITHIN} sections.`));\n }\n }\n }\n if (checked === 0) return [notApplicable(\"K8\", \"None of the sampled pages has a data-bl-section.\")];\n return resultOf(\"K8\", failures, `Checked ${plural(checked, \"page\")}: each follows the section contract.`);\n}\n"]}
|
|
@@ -53,7 +53,7 @@ export declare const SITE_SYSTEM_HUMAN_STEPS: readonly [{
|
|
|
53
53
|
readonly summary: "Launch proof (the builder, 10 min): the delivered email timestamp and the captured hits.";
|
|
54
54
|
}, {
|
|
55
55
|
readonly id: "look-pick";
|
|
56
|
-
readonly summary: "Look pick and side-by-side
|
|
56
|
+
readonly summary: "Look pick (new builds: A or B, then the look freezes) and the side-by-side pack for a live site or approved look, judged on \"as good as or better than live\", not \"identical\" (Romie, one reply each): A, B, yes or a note.";
|
|
57
57
|
}];
|
|
58
58
|
export type SiteSystemLaunchStepId = (typeof SITE_SYSTEM_LAUNCH_STEPS)[number]["id"];
|
|
59
59
|
export type SiteSystemHumanStepId = (typeof SITE_SYSTEM_HUMAN_STEPS)[number]["id"];
|
|
@@ -26,7 +26,7 @@ export const SITE_SYSTEM_HUMAN_STEPS = [
|
|
|
26
26
|
{ id: "usefulness-read", summary: "Head-page usefulness test and 5-page side-by-side read per family (the builder, 20 min per family): pass, or the pages fixed." },
|
|
27
27
|
{ id: "codex-second-opinion", summary: "Codex second opinion (one run, one fix round): findings, then fixed or answered." },
|
|
28
28
|
{ id: "launch-proof", summary: "Launch proof (the builder, 10 min): the delivered email timestamp and the captured hits." },
|
|
29
|
-
{ id: "look-pick", summary: "Look pick and side-by-side
|
|
29
|
+
{ id: "look-pick", summary: "Look pick (new builds: A or B, then the look freezes) and the side-by-side pack for a live site or approved look, judged on \"as good as or better than live\", not \"identical\" (Romie, one reply each): A, B, yes or a note." },
|
|
30
30
|
];
|
|
31
31
|
/** Registry checks that are recorded rather than run (K10 harness parity, X1 legacy URLs). */
|
|
32
32
|
export const SITE_SYSTEM_RECORDED_CHECK_IDS = SITE_SYSTEM_CHECKS
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"steps.js","sourceRoot":"","sources":["../../src/site-system/steps.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAOjD,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACtC,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,sHAAsH,EAAE;IAC7I,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,6GAA6G,EAAE;IACtI,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,2LAA2L,EAAE;IAClN,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,uFAAuF,EAAE;IAC9G,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,4FAA4F,EAAE;IACnH,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,sDAAsD,EAAE;IAC7E,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,oCAAoC,EAAE;IAC3D,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,0FAA0F,EAAE;IACjH,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,0FAA0F,EAAE;IACjH,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,mIAAmI,EAAE;IAC1J,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,mFAAmF,EAAE;CAC/D,CAAC;AAE/C,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC,EAAE,EAAE,EAAE,YAAY,EAAE,OAAO,EAAE,wEAAwE,EAAE;IACvG,EAAE,EAAE,EAAE,kBAAkB,EAAE,OAAO,EAAE,qHAAqH,EAAE;IAC1J,EAAE,EAAE,EAAE,iBAAiB,EAAE,OAAO,EAAE,+HAA+H,EAAE;IACnK,EAAE,EAAE,EAAE,sBAAsB,EAAE,OAAO,EAAE,kFAAkF,EAAE;IAC3H,EAAE,EAAE,EAAE,cAAc,EAAE,OAAO,EAAE,0FAA0F,EAAE;IAC3H,EAAE,EAAE,EAAE,WAAW,EAAE,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"steps.js","sourceRoot":"","sources":["../../src/site-system/steps.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAOjD,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACtC,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,sHAAsH,EAAE;IAC7I,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,6GAA6G,EAAE;IACtI,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,2LAA2L,EAAE;IAClN,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,uFAAuF,EAAE;IAC9G,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,4FAA4F,EAAE;IACnH,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,sDAAsD,EAAE;IAC7E,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,oCAAoC,EAAE;IAC3D,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,0FAA0F,EAAE;IACjH,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,0FAA0F,EAAE;IACjH,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,mIAAmI,EAAE;IAC1J,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,mFAAmF,EAAE;CAC/D,CAAC;AAE/C,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC,EAAE,EAAE,EAAE,YAAY,EAAE,OAAO,EAAE,wEAAwE,EAAE;IACvG,EAAE,EAAE,EAAE,kBAAkB,EAAE,OAAO,EAAE,qHAAqH,EAAE;IAC1J,EAAE,EAAE,EAAE,iBAAiB,EAAE,OAAO,EAAE,+HAA+H,EAAE;IACnK,EAAE,EAAE,EAAE,sBAAsB,EAAE,OAAO,EAAE,kFAAkF,EAAE;IAC3H,EAAE,EAAE,EAAE,cAAc,EAAE,OAAO,EAAE,0FAA0F,EAAE;IAC3H,EAAE,EAAE,EAAE,WAAW,EAAE,OAAO,EAAE,iOAAiO,EAAE;CACnN,CAAC;AAQ/C,8FAA8F;AAC9F,MAAM,CAAC,MAAM,8BAA8B,GAAsB,kBAAkB;KAChF,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC;KAC1C,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AAE5B,MAAM,UAAU,GAAG,IAAI,GAAG,CAAS,CAAC,GAAG,wBAAwB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,GAAG,8BAA8B,CAAC,CAAC,CAAC;AAC5H,MAAM,SAAS,GAAG,IAAI,GAAG,CAAS,uBAAuB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;AAElF,oGAAoG;AACpG,MAAM,UAAU,oBAAoB,CAAC,EAAU;IAC7C,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAAE,OAAO,QAAQ,CAAC;IACxC,IAAI,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;QAAE,OAAO,OAAO,CAAC;IACtC,OAAO,IAAI,CAAC;AACd,CAAC","sourcesContent":["/**\n * The launch-day agent steps (§13.3) and the time-boxed human steps (§13.6) the ready report lists.\n *\n * Each is recorded with `brandlab-ready record <id> --evidence <ref>`: launch-day steps, and the registry's\n * `record` checks (K10, X1), go to docs/ready/launch.json; human steps go to docs/ready/human.json. A site is\n * launch-ready only when every step is recorded; a default run reports missing steps without failing, and\n * `brandlab-ready --launch` fails until they are all recorded.\n */\nimport { SITE_SYSTEM_CHECKS } from \"./checks.js\";\n\nexport interface SiteSystemStep {\n readonly id: string;\n readonly summary: string;\n}\n\nexport const SITE_SYSTEM_LAUNCH_STEPS = [\n { id: \"T2\", summary: \"The Business Profile matches the site: name, phone, primary category, services, service area, hours and description.\" },\n { id: \"T3-R\", summary: \"Each licence checked on the NSW register within 30 days and not expired (warning inside 60 days of expiry).\" },\n { id: \"C6\", summary: \"Launch proof: a delivered test enquiry with a photo, the contact_form hit, a test phone tap and the next-day GA4 read; the owner saw it; the test marker kept every hit out of reporting.\" },\n { id: \"X3\", summary: \"The Codex second opinion (codex review --base main); real findings fixed or answered.\" },\n { id: \"X4\", summary: \"Production environment variable names present (values never read) and the domain resolves.\" },\n { id: \"X5\", summary: \"Rollback recorded and the cutover inside the window.\" },\n { id: \"X6\", summary: \"The legacy security sweep is done.\" },\n { id: \"X7\", summary: \"Ads final URLs and the Business Profile's tagged website URL mapped and handed to Codex.\" },\n { id: \"X8\", summary: \"No sentence shared with a related or same-trade client's site, library strings included.\" },\n { id: \"X9\", summary: \"DNS diff before and after cutover (MX, SPF, DKIM, DMARC, verification TXT) and a test email into and out of the client's mailbox.\" },\n { id: \"X10\", summary: \"The sending domain is verified in Resend with SPF and DKIM, and DMARC is present.\" },\n] as const satisfies readonly SiteSystemStep[];\n\nexport const SITE_SYSTEM_HUMAN_STEPS = [\n { id: \"truth-pass\", summary: \"Truth pass (the builder, 30 to 60 min): the claims and evidence table.\" },\n { id: \"trust-carry-over\", summary: \"Trust carry-over (the builder, 20 min): every old badge, claim, price and review block kept, or held with a reason.\" },\n { id: \"usefulness-read\", summary: \"Head-page usefulness test and 5-page side-by-side read per family (the builder, 20 min per family): pass, or the pages fixed.\" },\n { id: \"codex-second-opinion\", summary: \"Codex second opinion (one run, one fix round): findings, then fixed or answered.\" },\n { id: \"launch-proof\", summary: \"Launch proof (the builder, 10 min): the delivered email timestamp and the captured hits.\" },\n { id: \"look-pick\", summary: \"Look pick (new builds: A or B, then the look freezes) and the side-by-side pack for a live site or approved look, judged on \\\"as good as or better than live\\\", not \\\"identical\\\" (Romie, one reply each): A, B, yes or a note.\" },\n] as const satisfies readonly SiteSystemStep[];\n\nexport type SiteSystemLaunchStepId = (typeof SITE_SYSTEM_LAUNCH_STEPS)[number][\"id\"];\nexport type SiteSystemHumanStepId = (typeof SITE_SYSTEM_HUMAN_STEPS)[number][\"id\"];\n\n/** `launch` is docs/ready/launch.json, `human` is docs/ready/human.json. */\nexport type SiteSystemRecordFile = \"launch\" | \"human\";\n\n/** Registry checks that are recorded rather than run (K10 harness parity, X1 legacy URLs). */\nexport const SITE_SYSTEM_RECORDED_CHECK_IDS: readonly string[] = SITE_SYSTEM_CHECKS\n .filter((check) => check.kind === \"record\")\n .map((check) => check.id);\n\nconst LAUNCH_IDS = new Set<string>([...SITE_SYSTEM_LAUNCH_STEPS.map((step) => step.id), ...SITE_SYSTEM_RECORDED_CHECK_IDS]);\nconst HUMAN_IDS = new Set<string>(SITE_SYSTEM_HUMAN_STEPS.map((step) => step.id));\n\n/** The records file an id is recorded in, or null when the id is not a step or a recorded check. */\nexport function siteSystemRecordFile(id: string): SiteSystemRecordFile | null {\n if (LAUNCH_IDS.has(id)) return \"launch\";\n if (HUMAN_IDS.has(id)) return \"human\";\n return null;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brandlab-digital/conformance",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.17.0",
|
|
4
4
|
"license": "UNLICENSED",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"typecheck": "tsc --noEmit -p tsconfig.json"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@brandlab-digital/site-core": "3.
|
|
32
|
+
"@brandlab-digital/site-core": "3.17.0",
|
|
33
33
|
"typescript": "6.0.3"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|