@tangle-network/browser-agent-driver 0.29.0 → 0.31.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/brain/index.d.ts +15 -1
- package/dist/brain/index.d.ts.map +1 -1
- package/dist/brain/index.js +67 -5
- package/dist/brain/index.js.map +1 -1
- package/dist/cli-ack-patch.d.ts +39 -0
- package/dist/cli-ack-patch.d.ts.map +1 -0
- package/dist/cli-ack-patch.js +47 -0
- package/dist/cli-ack-patch.js.map +1 -0
- package/dist/cli-design-audit.d.ts +16 -0
- package/dist/cli-design-audit.d.ts.map +1 -1
- package/dist/cli-design-audit.js +232 -7
- package/dist/cli-design-audit.js.map +1 -1
- package/dist/cli-jobs.d.ts +13 -0
- package/dist/cli-jobs.d.ts.map +1 -0
- package/dist/cli-jobs.js +262 -0
- package/dist/cli-jobs.js.map +1 -0
- package/dist/cli-patterns.d.ts +26 -0
- package/dist/cli-patterns.d.ts.map +1 -0
- package/dist/cli-patterns.js +49 -0
- package/dist/cli-patterns.js.map +1 -0
- package/dist/cli-reports.d.ts +12 -0
- package/dist/cli-reports.d.ts.map +1 -0
- package/dist/cli-reports.js +97 -0
- package/dist/cli-reports.js.map +1 -0
- package/dist/cli-ui.d.ts.map +1 -1
- package/dist/cli-ui.js +5 -1
- package/dist/cli-ui.js.map +1 -1
- package/dist/cli.js +52 -2
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +4 -4
- package/dist/config.d.ts.map +1 -1
- package/dist/design/audit/attribution/aggregate.d.ts +16 -0
- package/dist/design/audit/attribution/aggregate.d.ts.map +1 -0
- package/dist/design/audit/attribution/aggregate.js +81 -0
- package/dist/design/audit/attribution/aggregate.js.map +1 -0
- package/dist/design/audit/attribution/store.d.ts +36 -0
- package/dist/design/audit/attribution/store.d.ts.map +1 -0
- package/dist/design/audit/attribution/store.js +108 -0
- package/dist/design/audit/attribution/store.js.map +1 -0
- package/dist/design/audit/attribution/types.d.ts +13 -0
- package/dist/design/audit/attribution/types.d.ts.map +1 -0
- package/dist/design/audit/attribution/types.js +10 -0
- package/dist/design/audit/attribution/types.js.map +1 -0
- package/dist/design/audit/classify-ensemble.d.ts +46 -0
- package/dist/design/audit/classify-ensemble.d.ts.map +1 -0
- package/dist/design/audit/classify-ensemble.js +207 -0
- package/dist/design/audit/classify-ensemble.js.map +1 -0
- package/dist/design/audit/ethics/check.d.ts +59 -0
- package/dist/design/audit/ethics/check.d.ts.map +1 -0
- package/dist/design/audit/ethics/check.js +137 -0
- package/dist/design/audit/ethics/check.js.map +1 -0
- package/dist/design/audit/ethics/loader.d.ts +21 -0
- package/dist/design/audit/ethics/loader.d.ts.map +1 -0
- package/dist/design/audit/ethics/loader.js +207 -0
- package/dist/design/audit/ethics/loader.js.map +1 -0
- package/dist/design/audit/ethics/rules/finance.yaml +37 -0
- package/dist/design/audit/ethics/rules/kids.yaml +37 -0
- package/dist/design/audit/ethics/rules/legal.yaml +35 -0
- package/dist/design/audit/ethics/rules/medical.yaml +36 -0
- package/dist/design/audit/evaluate.d.ts +69 -0
- package/dist/design/audit/evaluate.d.ts.map +1 -1
- package/dist/design/audit/evaluate.js +417 -55
- package/dist/design/audit/evaluate.js.map +1 -1
- package/dist/design/audit/first-principles-mode.d.ts +42 -0
- package/dist/design/audit/first-principles-mode.d.ts.map +1 -0
- package/dist/design/audit/first-principles-mode.js +77 -0
- package/dist/design/audit/first-principles-mode.js.map +1 -0
- package/dist/design/audit/modality/android.d.ts +14 -0
- package/dist/design/audit/modality/android.d.ts.map +1 -0
- package/dist/design/audit/modality/android.js +18 -0
- package/dist/design/audit/modality/android.js.map +1 -0
- package/dist/design/audit/modality/html.d.ts +22 -0
- package/dist/design/audit/modality/html.d.ts.map +1 -0
- package/dist/design/audit/modality/html.js +51 -0
- package/dist/design/audit/modality/html.js.map +1 -0
- package/dist/design/audit/modality/index.d.ts +8 -0
- package/dist/design/audit/modality/index.d.ts.map +1 -0
- package/dist/design/audit/modality/index.js +15 -0
- package/dist/design/audit/modality/index.js.map +1 -0
- package/dist/design/audit/modality/ios.d.ts +16 -0
- package/dist/design/audit/modality/ios.d.ts.map +1 -0
- package/dist/design/audit/modality/ios.js +19 -0
- package/dist/design/audit/modality/ios.js.map +1 -0
- package/dist/design/audit/modality/types.d.ts +9 -0
- package/dist/design/audit/modality/types.d.ts.map +1 -0
- package/dist/design/audit/modality/types.js +9 -0
- package/dist/design/audit/modality/types.js.map +1 -0
- package/dist/design/audit/patches/index.d.ts +8 -0
- package/dist/design/audit/patches/index.d.ts.map +1 -0
- package/dist/design/audit/patches/index.js +5 -0
- package/dist/design/audit/patches/index.js.map +1 -0
- package/dist/design/audit/patches/parse.d.ts +29 -0
- package/dist/design/audit/patches/parse.d.ts.map +1 -0
- package/dist/design/audit/patches/parse.js +153 -0
- package/dist/design/audit/patches/parse.js.map +1 -0
- package/dist/design/audit/patches/render.d.ts +20 -0
- package/dist/design/audit/patches/render.d.ts.map +1 -0
- package/dist/design/audit/patches/render.js +55 -0
- package/dist/design/audit/patches/render.js.map +1 -0
- package/dist/design/audit/patches/severity-enforcement.d.ts +23 -0
- package/dist/design/audit/patches/severity-enforcement.d.ts.map +1 -0
- package/dist/design/audit/patches/severity-enforcement.js +42 -0
- package/dist/design/audit/patches/severity-enforcement.js.map +1 -0
- package/dist/design/audit/patches/validate.d.ts +34 -0
- package/dist/design/audit/patches/validate.d.ts.map +1 -0
- package/dist/design/audit/patches/validate.js +53 -0
- package/dist/design/audit/patches/validate.js.map +1 -0
- package/dist/design/audit/patterns/match.d.ts +24 -0
- package/dist/design/audit/patterns/match.d.ts.map +1 -0
- package/dist/design/audit/patterns/match.js +48 -0
- package/dist/design/audit/patterns/match.js.map +1 -0
- package/dist/design/audit/patterns/mine.d.ts +31 -0
- package/dist/design/audit/patterns/mine.d.ts.map +1 -0
- package/dist/design/audit/patterns/mine.js +35 -0
- package/dist/design/audit/patterns/mine.js.map +1 -0
- package/dist/design/audit/patterns/store.d.ts +14 -0
- package/dist/design/audit/patterns/store.d.ts.map +1 -0
- package/dist/design/audit/patterns/store.js +53 -0
- package/dist/design/audit/patterns/store.js.map +1 -0
- package/dist/design/audit/patterns/types.d.ts +53 -0
- package/dist/design/audit/patterns/types.d.ts.map +1 -0
- package/dist/design/audit/patterns/types.js +13 -0
- package/dist/design/audit/patterns/types.js.map +1 -0
- package/dist/design/audit/pipeline.d.ts +26 -0
- package/dist/design/audit/pipeline.d.ts.map +1 -1
- package/dist/design/audit/pipeline.js +131 -4
- package/dist/design/audit/pipeline.js.map +1 -1
- package/dist/design/audit/rubric/anchor-loader.d.ts +44 -0
- package/dist/design/audit/rubric/anchor-loader.d.ts.map +1 -0
- package/dist/design/audit/rubric/anchor-loader.js +210 -0
- package/dist/design/audit/rubric/anchor-loader.js.map +1 -0
- package/dist/design/audit/rubric/anchors/blog.yaml +33 -0
- package/dist/design/audit/rubric/anchors/dashboard.yaml +35 -0
- package/dist/design/audit/rubric/anchors/docs.yaml +35 -0
- package/dist/design/audit/rubric/anchors/ecommerce.yaml +33 -0
- package/dist/design/audit/rubric/anchors/marketing.yaml +34 -0
- package/dist/design/audit/rubric/anchors/saas-app.yaml +38 -0
- package/dist/design/audit/rubric/anchors/social.yaml +33 -0
- package/dist/design/audit/rubric/anchors/tool.yaml +33 -0
- package/dist/design/audit/rubric/anchors/utility.yaml +33 -0
- package/dist/design/audit/rubric/fragments/audience-clinician.md +42 -0
- package/dist/design/audit/rubric/fragments/audience-developer.md +40 -0
- package/dist/design/audit/rubric/fragments/audience-kids.md +35 -0
- package/dist/design/audit/rubric/fragments/audience-vulnerability-minor-facing.md +37 -0
- package/dist/design/audit/rubric/fragments/first-principles.md +65 -0
- package/dist/design/audit/rubric/fragments/modality-mobile.md +39 -0
- package/dist/design/audit/rubric/fragments/modality-tablet.md +35 -0
- package/dist/design/audit/rubric/fragments/regulatory-coppa.md +32 -0
- package/dist/design/audit/rubric/fragments/regulatory-gdpr.md +33 -0
- package/dist/design/audit/rubric/fragments/regulatory-hipaa.md +36 -0
- package/dist/design/audit/rubric/fragments/universal-product-intent.md +48 -0
- package/dist/design/audit/rubric/loader.d.ts +26 -6
- package/dist/design/audit/rubric/loader.d.ts.map +1 -1
- package/dist/design/audit/rubric/loader.js +77 -27
- package/dist/design/audit/rubric/loader.js.map +1 -1
- package/dist/design/audit/rubric/rollup-weights.d.ts +25 -0
- package/dist/design/audit/rubric/rollup-weights.d.ts.map +1 -0
- package/dist/design/audit/rubric/rollup-weights.js +51 -0
- package/dist/design/audit/rubric/rollup-weights.js.map +1 -0
- package/dist/design/audit/types.d.ts +28 -0
- package/dist/design/audit/types.d.ts.map +1 -1
- package/dist/design/audit/v2/build-result.d.ts +47 -0
- package/dist/design/audit/v2/build-result.d.ts.map +1 -0
- package/dist/design/audit/v2/build-result.js +163 -0
- package/dist/design/audit/v2/build-result.js.map +1 -0
- package/dist/design/audit/v2/score.d.ts +49 -0
- package/dist/design/audit/v2/score.d.ts.map +1 -0
- package/dist/design/audit/v2/score.js +211 -0
- package/dist/design/audit/v2/score.js.map +1 -0
- package/dist/design/audit/v2/types.d.ts +431 -0
- package/dist/design/audit/v2/types.d.ts.map +1 -0
- package/dist/design/audit/v2/types.js +26 -0
- package/dist/design/audit/v2/types.js.map +1 -0
- package/dist/discover/index.d.ts +16 -0
- package/dist/discover/index.d.ts.map +1 -0
- package/dist/discover/index.js +25 -0
- package/dist/discover/index.js.map +1 -0
- package/dist/discover/wayback.d.ts +49 -0
- package/dist/discover/wayback.d.ts.map +1 -0
- package/dist/discover/wayback.js +123 -0
- package/dist/discover/wayback.js.map +1 -0
- package/dist/jobs/anti-bot.d.ts +19 -0
- package/dist/jobs/anti-bot.d.ts.map +1 -0
- package/dist/jobs/anti-bot.js +64 -0
- package/dist/jobs/anti-bot.js.map +1 -0
- package/dist/jobs/cost-estimate.d.ts +12 -0
- package/dist/jobs/cost-estimate.d.ts.map +1 -0
- package/dist/jobs/cost-estimate.js +21 -0
- package/dist/jobs/cost-estimate.js.map +1 -0
- package/dist/jobs/cost-history.d.ts +19 -0
- package/dist/jobs/cost-history.d.ts.map +1 -0
- package/dist/jobs/cost-history.js +42 -0
- package/dist/jobs/cost-history.js.map +1 -0
- package/dist/jobs/index.d.ts +31 -0
- package/dist/jobs/index.d.ts.map +1 -0
- package/dist/jobs/index.js +37 -0
- package/dist/jobs/index.js.map +1 -0
- package/dist/jobs/orchestrator.d.ts +42 -0
- package/dist/jobs/orchestrator.d.ts.map +1 -0
- package/dist/jobs/orchestrator.js +282 -0
- package/dist/jobs/orchestrator.js.map +1 -0
- package/dist/jobs/queue.d.ts +45 -0
- package/dist/jobs/queue.d.ts.map +1 -0
- package/dist/jobs/queue.js +114 -0
- package/dist/jobs/queue.js.map +1 -0
- package/dist/jobs/retry.d.ts +36 -0
- package/dist/jobs/retry.d.ts.map +1 -0
- package/dist/jobs/retry.js +77 -0
- package/dist/jobs/retry.js.map +1 -0
- package/dist/jobs/store.d.ts +31 -0
- package/dist/jobs/store.d.ts.map +1 -0
- package/dist/jobs/store.js +92 -0
- package/dist/jobs/store.js.map +1 -0
- package/dist/jobs/types.d.ts +106 -0
- package/dist/jobs/types.d.ts.map +1 -0
- package/dist/jobs/types.js +13 -0
- package/dist/jobs/types.js.map +1 -0
- package/dist/provider-defaults.d.ts +1 -1
- package/dist/provider-defaults.d.ts.map +1 -1
- package/dist/provider-defaults.js +8 -0
- package/dist/provider-defaults.js.map +1 -1
- package/dist/reports/aggregate.d.ts +38 -0
- package/dist/reports/aggregate.d.ts.map +1 -0
- package/dist/reports/aggregate.js +145 -0
- package/dist/reports/aggregate.js.map +1 -0
- package/dist/reports/index.d.ts +21 -0
- package/dist/reports/index.d.ts.map +1 -0
- package/dist/reports/index.js +15 -0
- package/dist/reports/index.js.map +1 -0
- package/dist/reports/narrate.d.ts +19 -0
- package/dist/reports/narrate.d.ts.map +1 -0
- package/dist/reports/narrate.js +40 -0
- package/dist/reports/narrate.js.map +1 -0
- package/dist/reports/templates.d.ts +43 -0
- package/dist/reports/templates.d.ts.map +1 -0
- package/dist/reports/templates.js +200 -0
- package/dist/reports/templates.js.map +1 -0
- package/dist/reports/tokens.d.ts +77 -0
- package/dist/reports/tokens.d.ts.map +1 -0
- package/dist/reports/tokens.js +88 -0
- package/dist/reports/tokens.js.map +1 -0
- package/dist/reports/tools.d.ts +76 -0
- package/dist/reports/tools.d.ts.map +1 -0
- package/dist/reports/tools.js +205 -0
- package/dist/reports/tools.js.map +1 -0
- package/dist/reports/types.d.ts +51 -0
- package/dist/reports/types.d.ts.map +1 -0
- package/dist/reports/types.js +9 -0
- package/dist/reports/types.js.map +1 -0
- package/dist/supervisor/critic.d.ts +1 -1
- package/dist/supervisor/critic.d.ts.map +1 -1
- package/dist/supervisor/critic.js +50 -5
- package/dist/supervisor/critic.js.map +1 -1
- package/dist/telemetry/client.d.ts +42 -0
- package/dist/telemetry/client.d.ts.map +1 -0
- package/dist/telemetry/client.js +203 -0
- package/dist/telemetry/client.js.map +1 -0
- package/dist/telemetry/hash.d.ts +4 -0
- package/dist/telemetry/hash.d.ts.map +1 -0
- package/dist/telemetry/hash.js +7 -0
- package/dist/telemetry/hash.js.map +1 -0
- package/dist/telemetry/index.d.ts +6 -0
- package/dist/telemetry/index.d.ts.map +1 -0
- package/dist/telemetry/index.js +5 -0
- package/dist/telemetry/index.js.map +1 -0
- package/dist/telemetry/schema.d.ts +85 -0
- package/dist/telemetry/schema.d.ts.map +1 -0
- package/dist/telemetry/schema.js +13 -0
- package/dist/telemetry/schema.js.map +1 -0
- package/dist/telemetry/sink.d.ts +46 -0
- package/dist/telemetry/sink.d.ts.map +1 -0
- package/dist/telemetry/sink.js +97 -0
- package/dist/telemetry/sink.js.map +1 -0
- package/dist/types.d.ts +5 -5
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -1
|
@@ -7,10 +7,16 @@
|
|
|
7
7
|
* a pure function elsewhere in this module — easy to unit test in isolation.
|
|
8
8
|
*/
|
|
9
9
|
import * as path from 'node:path';
|
|
10
|
-
import {
|
|
10
|
+
import { defaultClassification } from './classify.js';
|
|
11
11
|
import { composeRubric, composeRubricFromProfile } from './rubric/loader.js';
|
|
12
12
|
import { gatherMeasurements } from './measure/index.js';
|
|
13
13
|
import { evaluatePage } from './evaluate.js';
|
|
14
|
+
import { getTelemetry, shortHash } from '../../telemetry/index.js';
|
|
15
|
+
import { loadEthicsRules } from './ethics/loader.js';
|
|
16
|
+
import { checkEthics, pageTextBlob } from './ethics/check.js';
|
|
17
|
+
import { classifyEnsemble } from './classify-ensemble.js';
|
|
18
|
+
import { loadAnchors } from './rubric/anchor-loader.js';
|
|
19
|
+
import { buildAuditResultV2 } from './v2/build-result.js';
|
|
14
20
|
const COOKIE_BANNER_SELECTORS = [
|
|
15
21
|
'button:has-text("Accept all")',
|
|
16
22
|
'button:has-text("Accept")',
|
|
@@ -32,7 +38,8 @@ async function dismissCookieBanners(page) {
|
|
|
32
38
|
* Audit one page through the full Gen 2 pipeline.
|
|
33
39
|
*/
|
|
34
40
|
export async function auditOnePage(opts) {
|
|
35
|
-
const { brain, driver, page, url, profileOverride, screenshotDir, userRubricsDir } = opts;
|
|
41
|
+
const { brain, driver, page, url, profileOverride, screenshotDir, userRubricsDir, auditPasses, runId, parentRunId, provider, model, overrides, skipEthics, ethicsRulesDir, audience, modality, regulatoryContext, audienceVulnerability, } = opts;
|
|
42
|
+
const startedAt = Date.now();
|
|
36
43
|
try {
|
|
37
44
|
// ── 1. Load the page ──
|
|
38
45
|
await page
|
|
@@ -49,7 +56,9 @@ export async function auditOnePage(opts) {
|
|
|
49
56
|
await page.screenshot({ path: screenshotPath, fullPage: false });
|
|
50
57
|
}
|
|
51
58
|
// ── 3. Classify (or use profile override) ──
|
|
59
|
+
// Layer 1: ensemble classifier (URL + DOM + LLM) when no override is set.
|
|
52
60
|
let classification;
|
|
61
|
+
let ensemble;
|
|
53
62
|
if (profileOverride) {
|
|
54
63
|
// Build a synthetic classification matching the explicit profile
|
|
55
64
|
classification = {
|
|
@@ -59,7 +68,8 @@ export async function auditOnePage(opts) {
|
|
|
59
68
|
};
|
|
60
69
|
}
|
|
61
70
|
else {
|
|
62
|
-
|
|
71
|
+
ensemble = await classifyEnsemble({ brain, state, url });
|
|
72
|
+
classification = ensemble;
|
|
63
73
|
}
|
|
64
74
|
// ── 4. Compose rubric ──
|
|
65
75
|
const rubric = profileOverride
|
|
@@ -84,17 +94,134 @@ export async function auditOnePage(opts) {
|
|
|
84
94
|
rubric,
|
|
85
95
|
measurements,
|
|
86
96
|
screenshotPath,
|
|
97
|
+
auditPasses,
|
|
98
|
+
overrides,
|
|
87
99
|
});
|
|
100
|
+
// ── 7. Ethics gate (Layer 7) — apply rollup floor when rules fire ──
|
|
101
|
+
if (skipEthics) {
|
|
102
|
+
console.warn(`[ethics] --skip-ethics: gate bypassed for ${url} (test-only)`);
|
|
103
|
+
result.ethicsViolations = [];
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
const rules = loadEthicsRules(ethicsRulesDir);
|
|
107
|
+
const ethicsViolations = await checkEthics(rules, {
|
|
108
|
+
pageText: pageTextBlob(state.snapshot, { url, title: state.title }),
|
|
109
|
+
snapshot: state.snapshot,
|
|
110
|
+
classification,
|
|
111
|
+
audience,
|
|
112
|
+
modality,
|
|
113
|
+
regulatoryContext,
|
|
114
|
+
audienceVulnerability,
|
|
115
|
+
}, { brain });
|
|
116
|
+
if (ethicsViolations.length > 0) {
|
|
117
|
+
const minCap = Math.min(...ethicsViolations.map((v) => v.rollupCap));
|
|
118
|
+
if (typeof result.score === 'number' && result.score > minCap) {
|
|
119
|
+
result.preEthicsScore = result.score;
|
|
120
|
+
result.score = minCap;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
result.ethicsViolations = ethicsViolations;
|
|
124
|
+
}
|
|
125
|
+
// ── 8. Layer 1 v2 — multi-dim scoring + rollup, emitted alongside v1 ──
|
|
126
|
+
if (ensemble) {
|
|
127
|
+
try {
|
|
128
|
+
const anchors = loadAnchors();
|
|
129
|
+
const anchor = anchors.get(ensemble.type);
|
|
130
|
+
const v2 = await buildAuditResultV2({
|
|
131
|
+
brain,
|
|
132
|
+
state,
|
|
133
|
+
pageRef: url,
|
|
134
|
+
ensemble,
|
|
135
|
+
rubric,
|
|
136
|
+
measurements,
|
|
137
|
+
v1Result: result,
|
|
138
|
+
anchor,
|
|
139
|
+
runId,
|
|
140
|
+
});
|
|
141
|
+
result.auditResultV2 = v2;
|
|
142
|
+
result.ensembleClassification = ensemble;
|
|
143
|
+
}
|
|
144
|
+
catch (v2Err) {
|
|
145
|
+
// Don't let v2 failures break v1. Log + move on.
|
|
146
|
+
console.warn(`[audit/v2] failed to build v2 result for ${url}: ${v2Err.message}`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (runId) {
|
|
150
|
+
const findings = result.findings ?? [];
|
|
151
|
+
const ethicsViolations = result.ethicsViolations ?? [];
|
|
152
|
+
getTelemetry().emit({
|
|
153
|
+
kind: 'design-audit-page',
|
|
154
|
+
runId,
|
|
155
|
+
parentRunId,
|
|
156
|
+
ok: !result.error,
|
|
157
|
+
durationMs: Date.now() - startedAt,
|
|
158
|
+
...(provider && model ? { model: { provider, name: model, rubricHash: shortHash(rubric.body) } } : {}),
|
|
159
|
+
data: {
|
|
160
|
+
url,
|
|
161
|
+
classification,
|
|
162
|
+
rubricFragments: rubric.fragments.map((f) => f.id),
|
|
163
|
+
rubricDimensions: rubric.dimensions,
|
|
164
|
+
auditPasses: auditPasses ?? ['standard'],
|
|
165
|
+
designSystemScore: result.designSystemScore,
|
|
166
|
+
summary: result.summary,
|
|
167
|
+
strengths: result.strengths,
|
|
168
|
+
findings: findings.map((f) => ({
|
|
169
|
+
category: f.category,
|
|
170
|
+
severity: f.severity,
|
|
171
|
+
description: f.description,
|
|
172
|
+
location: f.location,
|
|
173
|
+
cssSelector: f.cssSelector,
|
|
174
|
+
impact: f.impact,
|
|
175
|
+
effort: f.effort,
|
|
176
|
+
blast: f.blast,
|
|
177
|
+
})),
|
|
178
|
+
},
|
|
179
|
+
metrics: {
|
|
180
|
+
score: result.score,
|
|
181
|
+
findingCount: findings.length,
|
|
182
|
+
criticalCount: findings.filter((f) => f.severity === 'critical').length,
|
|
183
|
+
majorCount: findings.filter((f) => f.severity === 'major').length,
|
|
184
|
+
minorCount: findings.filter((f) => f.severity === 'minor').length,
|
|
185
|
+
contrastAaPassRate: measurements.contrast.summary.aaPassRate,
|
|
186
|
+
a11yViolations: measurements.a11y.violations.length,
|
|
187
|
+
tokensUsed: result.tokensUsed ?? 0,
|
|
188
|
+
ethicsViolations: ethicsViolations.length,
|
|
189
|
+
ethicsCriticalFloor: ethicsViolations.filter((v) => v.severity === 'critical-floor').length,
|
|
190
|
+
ethicsMajorFloor: ethicsViolations.filter((v) => v.severity === 'major-floor').length,
|
|
191
|
+
},
|
|
192
|
+
tags: {
|
|
193
|
+
pageType: classification.type,
|
|
194
|
+
domain: classification.domain,
|
|
195
|
+
maturity: classification.maturity,
|
|
196
|
+
designSystem: classification.designSystem,
|
|
197
|
+
},
|
|
198
|
+
});
|
|
199
|
+
}
|
|
88
200
|
return result;
|
|
89
201
|
}
|
|
90
202
|
catch (err) {
|
|
203
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
204
|
+
if (runId) {
|
|
205
|
+
getTelemetry().emit({
|
|
206
|
+
kind: 'design-audit-page',
|
|
207
|
+
runId,
|
|
208
|
+
parentRunId,
|
|
209
|
+
ok: false,
|
|
210
|
+
durationMs: Date.now() - startedAt,
|
|
211
|
+
...(provider && model ? { model: { provider, name: model } } : {}),
|
|
212
|
+
data: { url },
|
|
213
|
+
metrics: { score: 0, findingCount: 0 },
|
|
214
|
+
error,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
91
217
|
return {
|
|
92
218
|
url,
|
|
93
219
|
score: 0,
|
|
94
220
|
summary: 'Audit failed',
|
|
95
221
|
strengths: [],
|
|
96
222
|
findings: [],
|
|
97
|
-
|
|
223
|
+
ethicsViolations: [],
|
|
224
|
+
error,
|
|
98
225
|
};
|
|
99
226
|
}
|
|
100
227
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pipeline.js","sourceRoot":"","sources":["../../../src/design/audit/pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AAIjC,OAAO,
|
|
1
|
+
{"version":3,"file":"pipeline.js","sourceRoot":"","sources":["../../../src/design/audit/pipeline.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anchor loader — Layer 1 of the world-class design-audit architecture.
|
|
3
|
+
*
|
|
4
|
+
* Loads per-page-type calibration anchors from `anchors/<type>.yaml`. Each
|
|
5
|
+
* anchor encodes score-band criteria + reference fixtures so the LLM scores
|
|
6
|
+
* an saas-app like Linear's app, not like Linear's marketing site.
|
|
7
|
+
*
|
|
8
|
+
* Schema:
|
|
9
|
+
* type: <PageType>
|
|
10
|
+
* score_9_10: { criteria: string[], fixtures: string[] }
|
|
11
|
+
* score_7_8: { criteria: string[], fixtures: string[] }
|
|
12
|
+
* score_5_6: { criteria: string[], fixtures: string[] }
|
|
13
|
+
* score_3_4: { criteria: string[], fixtures: string[] }
|
|
14
|
+
*/
|
|
15
|
+
import type { PageType } from '../types.js';
|
|
16
|
+
export interface AnchorBand {
|
|
17
|
+
criteria: string[];
|
|
18
|
+
fixtures: string[];
|
|
19
|
+
}
|
|
20
|
+
export interface CalibrationAnchor {
|
|
21
|
+
type: PageType;
|
|
22
|
+
score_9_10: AnchorBand;
|
|
23
|
+
score_7_8: AnchorBand;
|
|
24
|
+
score_5_6: AnchorBand;
|
|
25
|
+
score_3_4: AnchorBand;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Parse one anchor YAML. Uses a minimal YAML reader that handles the shape:
|
|
29
|
+
* type: saas-app
|
|
30
|
+
* score_9_10:
|
|
31
|
+
* criteria:
|
|
32
|
+
* - line one
|
|
33
|
+
* - line two
|
|
34
|
+
* fixtures:
|
|
35
|
+
* - fixture:linear-app
|
|
36
|
+
*
|
|
37
|
+
* Avoids pulling in a YAML dep for ~9 small files. Throws on malformed input.
|
|
38
|
+
*/
|
|
39
|
+
export declare function parseAnchorFile(filePath: string): CalibrationAnchor;
|
|
40
|
+
/** Load all anchors from `anchors/` into a map keyed by PageType. */
|
|
41
|
+
export declare function loadAnchors(dir?: string): Map<PageType, CalibrationAnchor>;
|
|
42
|
+
/** Render an anchor as a markdown block for prompt injection. */
|
|
43
|
+
export declare function renderAnchor(anchor: CalibrationAnchor): string;
|
|
44
|
+
//# sourceMappingURL=anchor-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anchor-loader.d.ts","sourceRoot":"","sources":["../../../../src/design/audit/rubric/anchor-loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAK3C,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAA;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,QAAQ,CAAA;IACd,UAAU,EAAE,UAAU,CAAA;IACtB,SAAS,EAAE,UAAU,CAAA;IACrB,SAAS,EAAE,UAAU,CAAA;IACrB,SAAS,EAAE,UAAU,CAAA;CACtB;AAID;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,iBAAiB,CAuBnE;AAED,qEAAqE;AACrE,wBAAgB,WAAW,CAAC,GAAG,GAAE,MAAoB,GAAG,GAAG,CAAC,QAAQ,EAAE,iBAAiB,CAAC,CASvF;AAED,iEAAiE;AACjE,wBAAgB,YAAY,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CAU9D"}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anchor loader — Layer 1 of the world-class design-audit architecture.
|
|
3
|
+
*
|
|
4
|
+
* Loads per-page-type calibration anchors from `anchors/<type>.yaml`. Each
|
|
5
|
+
* anchor encodes score-band criteria + reference fixtures so the LLM scores
|
|
6
|
+
* an saas-app like Linear's app, not like Linear's marketing site.
|
|
7
|
+
*
|
|
8
|
+
* Schema:
|
|
9
|
+
* type: <PageType>
|
|
10
|
+
* score_9_10: { criteria: string[], fixtures: string[] }
|
|
11
|
+
* score_7_8: { criteria: string[], fixtures: string[] }
|
|
12
|
+
* score_5_6: { criteria: string[], fixtures: string[] }
|
|
13
|
+
* score_3_4: { criteria: string[], fixtures: string[] }
|
|
14
|
+
*/
|
|
15
|
+
import * as fs from 'node:fs';
|
|
16
|
+
import * as path from 'node:path';
|
|
17
|
+
import { fileURLToPath } from 'node:url';
|
|
18
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
const ANCHORS_DIR = path.join(__dirname, 'anchors');
|
|
20
|
+
const REQUIRED_BANDS = ['score_9_10', 'score_7_8', 'score_5_6', 'score_3_4'];
|
|
21
|
+
/**
|
|
22
|
+
* Parse one anchor YAML. Uses a minimal YAML reader that handles the shape:
|
|
23
|
+
* type: saas-app
|
|
24
|
+
* score_9_10:
|
|
25
|
+
* criteria:
|
|
26
|
+
* - line one
|
|
27
|
+
* - line two
|
|
28
|
+
* fixtures:
|
|
29
|
+
* - fixture:linear-app
|
|
30
|
+
*
|
|
31
|
+
* Avoids pulling in a YAML dep for ~9 small files. Throws on malformed input.
|
|
32
|
+
*/
|
|
33
|
+
export function parseAnchorFile(filePath) {
|
|
34
|
+
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
35
|
+
const parsed = parseAnchorYaml(raw);
|
|
36
|
+
if (!parsed.type || typeof parsed.type !== 'string') {
|
|
37
|
+
throw new Error(`anchor ${filePath} missing 'type' field`);
|
|
38
|
+
}
|
|
39
|
+
for (const band of REQUIRED_BANDS) {
|
|
40
|
+
const node = parsed[band];
|
|
41
|
+
if (!node || typeof node !== 'object') {
|
|
42
|
+
throw new Error(`anchor ${filePath} missing '${band}' band`);
|
|
43
|
+
}
|
|
44
|
+
const b = node;
|
|
45
|
+
if (!Array.isArray(b.criteria) || b.criteria.length === 0) {
|
|
46
|
+
throw new Error(`anchor ${filePath} '${band}.criteria' must be a non-empty array`);
|
|
47
|
+
}
|
|
48
|
+
if (!Array.isArray(b.fixtures) || b.fixtures.length === 0) {
|
|
49
|
+
throw new Error(`anchor ${filePath} '${band}.fixtures' must be a non-empty array`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return parsed;
|
|
53
|
+
}
|
|
54
|
+
/** Load all anchors from `anchors/` into a map keyed by PageType. */
|
|
55
|
+
export function loadAnchors(dir = ANCHORS_DIR) {
|
|
56
|
+
const out = new Map();
|
|
57
|
+
if (!fs.existsSync(dir))
|
|
58
|
+
return out;
|
|
59
|
+
for (const file of fs.readdirSync(dir)) {
|
|
60
|
+
if (!file.endsWith('.yaml') && !file.endsWith('.yml'))
|
|
61
|
+
continue;
|
|
62
|
+
const anchor = parseAnchorFile(path.join(dir, file));
|
|
63
|
+
out.set(anchor.type, anchor);
|
|
64
|
+
}
|
|
65
|
+
return out;
|
|
66
|
+
}
|
|
67
|
+
/** Render an anchor as a markdown block for prompt injection. */
|
|
68
|
+
export function renderAnchor(anchor) {
|
|
69
|
+
const band = (label, b) => `${label}\n${b.criteria.map((c) => `- ${c}`).join('\n')}\nReferences: ${b.fixtures.join(', ')}`;
|
|
70
|
+
return [
|
|
71
|
+
`Calibration anchor for ${anchor.type}:`,
|
|
72
|
+
band('Score 9-10:', anchor.score_9_10),
|
|
73
|
+
band('Score 7-8:', anchor.score_7_8),
|
|
74
|
+
band('Score 5-6:', anchor.score_5_6),
|
|
75
|
+
band('Score 3-4:', anchor.score_3_4),
|
|
76
|
+
].join('\n\n');
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Minimal YAML parser scoped to the anchor file shape. Supports:
|
|
80
|
+
* key: scalar
|
|
81
|
+
* key:
|
|
82
|
+
* subkey: scalar
|
|
83
|
+
* subkey:
|
|
84
|
+
* - list item
|
|
85
|
+
*
|
|
86
|
+
* Indentation is normalized to spaces; tabs are not supported.
|
|
87
|
+
*/
|
|
88
|
+
function parseAnchorYaml(text) {
|
|
89
|
+
const lines = text.split('\n').map((l) => l.replace(/\r$/, ''));
|
|
90
|
+
const root = {};
|
|
91
|
+
let i = 0;
|
|
92
|
+
while (i < lines.length) {
|
|
93
|
+
const line = lines[i];
|
|
94
|
+
if (!line.trim() || line.trim().startsWith('#')) {
|
|
95
|
+
i++;
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const indent = leadingSpaces(line);
|
|
99
|
+
if (indent !== 0) {
|
|
100
|
+
i++;
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
const m = line.match(/^([a-zA-Z_][\w-]*):\s*(.*)$/);
|
|
104
|
+
if (!m) {
|
|
105
|
+
i++;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
const [, key, valueRaw] = m;
|
|
109
|
+
const value = valueRaw.trim();
|
|
110
|
+
if (value === '') {
|
|
111
|
+
const { node, nextIndex } = readBlock(lines, i + 1, 2);
|
|
112
|
+
root[key] = node;
|
|
113
|
+
i = nextIndex;
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
root[key] = parseScalar(value);
|
|
117
|
+
i++;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return root;
|
|
121
|
+
}
|
|
122
|
+
function readBlock(lines, startIndex, baseIndent) {
|
|
123
|
+
// Detect: is this a list ("- item") or a map?
|
|
124
|
+
let i = startIndex;
|
|
125
|
+
while (i < lines.length && !lines[i].trim())
|
|
126
|
+
i++;
|
|
127
|
+
if (i >= lines.length)
|
|
128
|
+
return { node: {}, nextIndex: i };
|
|
129
|
+
const firstIndent = leadingSpaces(lines[i]);
|
|
130
|
+
if (firstIndent < baseIndent)
|
|
131
|
+
return { node: {}, nextIndex: i };
|
|
132
|
+
if (lines[i].trim().startsWith('- ') || lines[i].trim() === '-') {
|
|
133
|
+
const items = [];
|
|
134
|
+
while (i < lines.length) {
|
|
135
|
+
const line = lines[i];
|
|
136
|
+
if (!line.trim()) {
|
|
137
|
+
i++;
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
const indent = leadingSpaces(line);
|
|
141
|
+
if (indent < baseIndent)
|
|
142
|
+
break;
|
|
143
|
+
const trimmed = line.trim();
|
|
144
|
+
if (!trimmed.startsWith('-'))
|
|
145
|
+
break;
|
|
146
|
+
const item = trimmed.replace(/^-\s*/, '');
|
|
147
|
+
items.push(parseScalar(item));
|
|
148
|
+
i++;
|
|
149
|
+
}
|
|
150
|
+
return { node: items, nextIndex: i };
|
|
151
|
+
}
|
|
152
|
+
const map = {};
|
|
153
|
+
while (i < lines.length) {
|
|
154
|
+
const line = lines[i];
|
|
155
|
+
if (!line.trim() || line.trim().startsWith('#')) {
|
|
156
|
+
i++;
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
const indent = leadingSpaces(line);
|
|
160
|
+
if (indent < baseIndent)
|
|
161
|
+
break;
|
|
162
|
+
if (indent > baseIndent) {
|
|
163
|
+
i++;
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
const m = line.match(/^\s*([a-zA-Z_][\w-]*):\s*(.*)$/);
|
|
167
|
+
if (!m) {
|
|
168
|
+
i++;
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
const [, key, valueRaw] = m;
|
|
172
|
+
const value = valueRaw.trim();
|
|
173
|
+
if (value === '') {
|
|
174
|
+
const { node, nextIndex } = readBlock(lines, i + 1, baseIndent + 2);
|
|
175
|
+
map[key] = node;
|
|
176
|
+
i = nextIndex;
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
map[key] = parseScalar(value);
|
|
180
|
+
i++;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return { node: map, nextIndex: i };
|
|
184
|
+
}
|
|
185
|
+
function leadingSpaces(line) {
|
|
186
|
+
let n = 0;
|
|
187
|
+
while (n < line.length && line[n] === ' ')
|
|
188
|
+
n++;
|
|
189
|
+
return n;
|
|
190
|
+
}
|
|
191
|
+
function parseScalar(raw) {
|
|
192
|
+
let value = raw.trim();
|
|
193
|
+
if (value === '')
|
|
194
|
+
return '';
|
|
195
|
+
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
|
|
196
|
+
value = value.slice(1, -1);
|
|
197
|
+
}
|
|
198
|
+
if (value === 'true')
|
|
199
|
+
return true;
|
|
200
|
+
if (value === 'false')
|
|
201
|
+
return false;
|
|
202
|
+
if (value === 'null' || value === '~')
|
|
203
|
+
return null;
|
|
204
|
+
if (/^-?\d+$/.test(value))
|
|
205
|
+
return Number(value);
|
|
206
|
+
if (/^-?\d+\.\d+$/.test(value))
|
|
207
|
+
return Number(value);
|
|
208
|
+
return value;
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=anchor-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anchor-loader.js","sourceRoot":"","sources":["../../../../src/design/audit/rubric/anchor-loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
type: blog
|
|
2
|
+
score_9_10:
|
|
3
|
+
criteria:
|
|
4
|
+
- Reading column tuned to 60-75 character measure with deliberate vertical rhythm
|
|
5
|
+
- Typography scale supports headings, body, callouts, code, captions distinctly
|
|
6
|
+
- Author identity and publication date prominent without being chrome
|
|
7
|
+
- Inline media (images, code, embeds) integrate cleanly with text flow
|
|
8
|
+
- Navigation between posts (next, related, archive) supports the reading habit
|
|
9
|
+
fixtures:
|
|
10
|
+
- fixture:stratechery
|
|
11
|
+
- fixture:substack-post
|
|
12
|
+
- fixture:notion-blog-template
|
|
13
|
+
score_7_8:
|
|
14
|
+
criteria:
|
|
15
|
+
- Strong reading experience but typographic scale is one step shy of intentional
|
|
16
|
+
- Author and date present but visually inconsistent
|
|
17
|
+
- Inline media works but breaks rhythm at boundaries
|
|
18
|
+
fixtures:
|
|
19
|
+
- fixture:medium-default
|
|
20
|
+
score_5_6:
|
|
21
|
+
criteria:
|
|
22
|
+
- Default theme typography (no custom scale, line-height defaults)
|
|
23
|
+
- Heavy chrome (sidebar, social rail) competing with the article
|
|
24
|
+
- Author/date hidden in footer
|
|
25
|
+
fixtures:
|
|
26
|
+
- fixture:default-ghost
|
|
27
|
+
score_3_4:
|
|
28
|
+
criteria:
|
|
29
|
+
- Article copy crammed against gutters; no measure control
|
|
30
|
+
- Inline ads break reading flow every paragraph
|
|
31
|
+
- No publication date — content is unanchored in time
|
|
32
|
+
fixtures:
|
|
33
|
+
- fixture:ad-heavy-article
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
type: dashboard
|
|
2
|
+
score_9_10:
|
|
3
|
+
criteria:
|
|
4
|
+
- Real metrics with units, time windows, and comparison baselines visible at first glance
|
|
5
|
+
- Charts use deliberate scales, axis labels, and selective color (not rainbow defaults)
|
|
6
|
+
- Filters, time ranges, and segment selectors are obvious and persistent
|
|
7
|
+
- Empty states preview real data shape (sample rows, last-7-days skeleton)
|
|
8
|
+
- Density tuned to operator workflow — no decorative whitespace where dense data belongs
|
|
9
|
+
fixtures:
|
|
10
|
+
- fixture:linear-app
|
|
11
|
+
- fixture:figma-file-ui
|
|
12
|
+
- fixture:datadog-dashboard
|
|
13
|
+
- fixture:vercel-dashboard
|
|
14
|
+
score_7_8:
|
|
15
|
+
criteria:
|
|
16
|
+
- Metrics present and useful but lack comparison baselines or time-window controls
|
|
17
|
+
- Charts readable but use default color palette; minor density issues
|
|
18
|
+
- Filters present but discoverability unclear
|
|
19
|
+
fixtures:
|
|
20
|
+
- fixture:grafana-default
|
|
21
|
+
score_5_6:
|
|
22
|
+
criteria:
|
|
23
|
+
- Generic stat cards (4 equal boxes) without context or comparison
|
|
24
|
+
- Charts are rainbow defaults from the component library
|
|
25
|
+
- No filters or time-range affordance
|
|
26
|
+
- Empty states are illustrations + platitudes
|
|
27
|
+
fixtures:
|
|
28
|
+
- fixture:generic-dashboard
|
|
29
|
+
score_3_4:
|
|
30
|
+
criteria:
|
|
31
|
+
- Numbers without units, labels, or context — operator cannot tell good from bad
|
|
32
|
+
- Decorative chart visualizations (donuts, gauges) instead of operational data
|
|
33
|
+
- Equal-weight UI controls; no clear workflow path
|
|
34
|
+
fixtures:
|
|
35
|
+
- fixture:empty-state-noise
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
type: docs
|
|
2
|
+
score_9_10:
|
|
3
|
+
criteria:
|
|
4
|
+
- Quickstart path from landing to a working result is obvious within one viewport
|
|
5
|
+
- Code samples are runnable, copy-button-equipped, and language-tabbed where relevant
|
|
6
|
+
- Reference structure (sidebar, breadcrumbs, search) supports both linear and lookup reading
|
|
7
|
+
- Versioning, deprecation, and last-updated signals are visible
|
|
8
|
+
- Typography scale and line-length tuned for sustained reading
|
|
9
|
+
fixtures:
|
|
10
|
+
- fixture:stripe-docs
|
|
11
|
+
- fixture:tailwind-docs
|
|
12
|
+
- fixture:mdn-docs
|
|
13
|
+
- fixture:vercel-docs
|
|
14
|
+
score_7_8:
|
|
15
|
+
criteria:
|
|
16
|
+
- Quickstart present but buried; copy is solid but examples are partial
|
|
17
|
+
- Sidebar IA works but search is weak or missing
|
|
18
|
+
- Versioning unclear
|
|
19
|
+
fixtures:
|
|
20
|
+
- fixture:generic-mkdocs
|
|
21
|
+
score_5_6:
|
|
22
|
+
criteria:
|
|
23
|
+
- Wall-of-text reference with no quickstart
|
|
24
|
+
- Code samples without copy buttons or language switching
|
|
25
|
+
- No search, no breadcrumbs, no last-updated metadata
|
|
26
|
+
- Default theme typography (inconsistent line-height, no scale)
|
|
27
|
+
fixtures:
|
|
28
|
+
- fixture:default-docusaurus
|
|
29
|
+
score_3_4:
|
|
30
|
+
criteria:
|
|
31
|
+
- Marketing page disguised as docs (heavy hero, no actual reference)
|
|
32
|
+
- No code samples or syntax highlighting
|
|
33
|
+
- IA broken — "Getting Started" buried under marketing copy
|
|
34
|
+
fixtures:
|
|
35
|
+
- fixture:marketing-as-docs
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
type: ecommerce
|
|
2
|
+
score_9_10:
|
|
3
|
+
criteria:
|
|
4
|
+
- Product photography is real, detailed, and honest (not stock or AI-rendered)
|
|
5
|
+
- Price, fees, taxes, shipping, and total are surfaced before the commit button
|
|
6
|
+
- Stock, delivery, and return-policy signals are visible at decision points
|
|
7
|
+
- Cart and checkout flows preserve context — user always knows what they are buying
|
|
8
|
+
- Trust signals (verified payment, secure checkout, real merchant identity) appear where commitment occurs
|
|
9
|
+
fixtures:
|
|
10
|
+
- fixture:apple-store
|
|
11
|
+
- fixture:shopify-storefront
|
|
12
|
+
- fixture:allbirds-pdp
|
|
13
|
+
score_7_8:
|
|
14
|
+
criteria:
|
|
15
|
+
- Solid PDPs but checkout exposes fees only at the last step
|
|
16
|
+
- Stock and delivery info present but inconsistent across pages
|
|
17
|
+
- Photography mixes real and stock
|
|
18
|
+
fixtures:
|
|
19
|
+
- fixture:generic-shopify
|
|
20
|
+
score_5_6:
|
|
21
|
+
criteria:
|
|
22
|
+
- Fees surfaced only after pressing Pay; no shipping calculator
|
|
23
|
+
- Stock/delivery signals missing on PDPs
|
|
24
|
+
- Generic merchant identity, no trust badges, no return policy summary
|
|
25
|
+
fixtures:
|
|
26
|
+
- fixture:hidden-fees-checkout
|
|
27
|
+
score_3_4:
|
|
28
|
+
criteria:
|
|
29
|
+
- Total never shown until after commitment
|
|
30
|
+
- No merchant identity, no return policy, no trust signals
|
|
31
|
+
- Forced account creation before checkout, dark-pattern upsells
|
|
32
|
+
fixtures:
|
|
33
|
+
- fixture:dark-pattern-checkout
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
type: marketing
|
|
2
|
+
score_9_10:
|
|
3
|
+
criteria:
|
|
4
|
+
- Hero answers product, audience, and outcome within five seconds
|
|
5
|
+
- One dominant CTA, secondary actions clearly subordinate
|
|
6
|
+
- Concrete proof (real customer logos, real metrics, real screenshots) above the fold
|
|
7
|
+
- Visual craft (typography ramp, spacing rhythm, color system) is intentional, not template-default
|
|
8
|
+
- Differentiation is shown, not asserted; copy avoids vague hype
|
|
9
|
+
fixtures:
|
|
10
|
+
- fixture:stripe-marketing
|
|
11
|
+
- fixture:linear-marketing
|
|
12
|
+
- fixture:vercel-marketing
|
|
13
|
+
- fixture:apple-marketing
|
|
14
|
+
score_7_8:
|
|
15
|
+
criteria:
|
|
16
|
+
- Hero clear, but proof is generic (lorem-style logo cloud) or differentiation thin
|
|
17
|
+
- Visual system coherent but unremarkable; one or two minor rhythm breaks
|
|
18
|
+
- CTA dominant on hero but competes elsewhere
|
|
19
|
+
fixtures:
|
|
20
|
+
- fixture:generic-saas-marketing
|
|
21
|
+
score_5_6:
|
|
22
|
+
criteria:
|
|
23
|
+
- Hero copy explains the product instead of selling the outcome
|
|
24
|
+
- Visual hierarchy works but feels like a Tailwind starter
|
|
25
|
+
- Equal-weight CTAs, vague social proof, stock illustrations
|
|
26
|
+
fixtures:
|
|
27
|
+
- fixture:meta-copy-feature
|
|
28
|
+
score_3_4:
|
|
29
|
+
criteria:
|
|
30
|
+
- No primary message; could swap nouns and apply to any startup
|
|
31
|
+
- No CTA hierarchy; the page is a wall of equal sections
|
|
32
|
+
- Stock photography, generic gradients, default UI kit components
|
|
33
|
+
fixtures:
|
|
34
|
+
- fixture:ambiguous-deploy
|