@dreamdata_io/verify-tracking 0.1.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/LICENSE +21 -0
- package/README.md +756 -0
- package/dist/ai/checker.js +222 -0
- package/dist/ai/checker.js.map +1 -0
- package/dist/ai/checklist.js +190 -0
- package/dist/ai/checklist.js.map +1 -0
- package/dist/ai/cli-presets.js +27 -0
- package/dist/ai/cli-presets.js.map +1 -0
- package/dist/ai/evidence-view.js +143 -0
- package/dist/ai/evidence-view.js.map +1 -0
- package/dist/ai/local-cli.js +58 -0
- package/dist/ai/local-cli.js.map +1 -0
- package/dist/ai/openai-compatible.js +78 -0
- package/dist/ai/openai-compatible.js.map +1 -0
- package/dist/ai/response-parser.js +105 -0
- package/dist/ai/response-parser.js.map +1 -0
- package/dist/ai/types.js +2 -0
- package/dist/ai/types.js.map +1 -0
- package/dist/analysis/consent-mode.js +225 -0
- package/dist/analysis/consent-mode.js.map +1 -0
- package/dist/analysis/deployment.js +2658 -0
- package/dist/analysis/deployment.js.map +1 -0
- package/dist/analysis/diagnosis/copy.en.js +303 -0
- package/dist/analysis/diagnosis/copy.en.js.map +1 -0
- package/dist/analysis/diagnosis/diagnose.js +172 -0
- package/dist/analysis/diagnosis/diagnose.js.map +1 -0
- package/dist/analysis/diagnosis/docs.js +118 -0
- package/dist/analysis/diagnosis/docs.js.map +1 -0
- package/dist/analysis/diagnosis/index.js +5 -0
- package/dist/analysis/diagnosis/index.js.map +1 -0
- package/dist/analysis/diagnosis/rules.js +656 -0
- package/dist/analysis/diagnosis/rules.js.map +1 -0
- package/dist/analysis/diagnosis/types.js +2 -0
- package/dist/analysis/diagnosis/types.js.map +1 -0
- package/dist/cli.js +344 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/env.js +100 -0
- package/dist/config/env.js.map +1 -0
- package/dist/config/preflight.js +167 -0
- package/dist/config/preflight.js.map +1 -0
- package/dist/config/user-config.js +79 -0
- package/dist/config/user-config.js.map +1 -0
- package/dist/evidence/collect.js +527 -0
- package/dist/evidence/collect.js.map +1 -0
- package/dist/evidence/consent-accept.js +377 -0
- package/dist/evidence/consent-accept.js.map +1 -0
- package/dist/evidence/consent.js +340 -0
- package/dist/evidence/consent.js.map +1 -0
- package/dist/evidence/constants.js +615 -0
- package/dist/evidence/constants.js.map +1 -0
- package/dist/evidence/globals.js +132 -0
- package/dist/evidence/globals.js.map +1 -0
- package/dist/evidence/gtm-container.js +432 -0
- package/dist/evidence/gtm-container.js.map +1 -0
- package/dist/evidence/heuristic-banner.js +107 -0
- package/dist/evidence/heuristic-banner.js.map +1 -0
- package/dist/evidence/http-probe.js +139 -0
- package/dist/evidence/http-probe.js.map +1 -0
- package/dist/evidence/init-script.js +112 -0
- package/dist/evidence/init-script.js.map +1 -0
- package/dist/evidence/network.js +365 -0
- package/dist/evidence/network.js.map +1 -0
- package/dist/evidence/preference-center.js +368 -0
- package/dist/evidence/preference-center.js.map +1 -0
- package/dist/evidence/script.js +425 -0
- package/dist/evidence/script.js.map +1 -0
- package/dist/evidence/second-page.js +332 -0
- package/dist/evidence/second-page.js.map +1 -0
- package/dist/evidence/selector-probe.js +53 -0
- package/dist/evidence/selector-probe.js.map +1 -0
- package/dist/evidence/stealth.js +21 -0
- package/dist/evidence/stealth.js.map +1 -0
- package/dist/evidence/types.js +14 -0
- package/dist/evidence/types.js.map +1 -0
- package/dist/interactive/prompt.js +31 -0
- package/dist/interactive/prompt.js.map +1 -0
- package/dist/mcp/server.js +308 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/summary.js +41 -0
- package/dist/mcp/summary.js.map +1 -0
- package/dist/report/context.js +184 -0
- package/dist/report/context.js.map +1 -0
- package/dist/report/projection.js +125 -0
- package/dist/report/projection.js.map +1 -0
- package/dist/report/quote-check.js +90 -0
- package/dist/report/quote-check.js.map +1 -0
- package/dist/report/render.js +178 -0
- package/dist/report/render.js.map +1 -0
- package/dist/report/serializers.js +124 -0
- package/dist/report/serializers.js.map +1 -0
- package/dist/report/store.js +139 -0
- package/dist/report/store.js.map +1 -0
- package/dist/run/verify.js +77 -0
- package/dist/run/verify.js.map +1 -0
- package/dist/utils/duration.js +10 -0
- package/dist/utils/duration.js.map +1 -0
- package/dist/utils/errors.js +17 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/logger.js +59 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/runTimingsLog.js +27 -0
- package/dist/utils/runTimingsLog.js.map +1 -0
- package/dist/utils/slug.js +27 -0
- package/dist/utils/slug.js.map +1 -0
- package/dist/version.js +10 -0
- package/dist/version.js.map +1 -0
- package/package.json +57 -0
- package/scripts/install.sh +314 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { extractLibraries, extractMessageId, formatLibraryBuild } from "../evidence/network.js";
|
|
2
|
+
function compactRequest(request) {
|
|
3
|
+
const { payload, ...rest } = request;
|
|
4
|
+
return {
|
|
5
|
+
...rest,
|
|
6
|
+
messageId: extractMessageId(payload),
|
|
7
|
+
libraries: extractLibraries(payload).map(formatLibraryBuild),
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
function setNames(values) {
|
|
11
|
+
return Object.entries(values)
|
|
12
|
+
.filter(([, value]) => value !== null && value !== "")
|
|
13
|
+
.map(([name]) => name);
|
|
14
|
+
}
|
|
15
|
+
function compactStorage(storage) {
|
|
16
|
+
return {
|
|
17
|
+
cookieNames: setNames(storage.values),
|
|
18
|
+
localStorageNames: setNames(storage.localStorageValues),
|
|
19
|
+
ajsCookieNames: setNames(storage.ajsValues),
|
|
20
|
+
ajsLocalStorageNames: setNames(storage.ajsLocalStorageValues),
|
|
21
|
+
usesAjsCompatPrefix: storage.usesAjsCompatPrefix,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* `raw` is the whole gtag argument object. Only `analytics_storage` is ever
|
|
26
|
+
* read from it, and `analyticsStorage` alongside is already that value
|
|
27
|
+
* resolved; `kind` keeps the one other thing it said — whether this was the
|
|
28
|
+
* declared default or a visitor's decision.
|
|
29
|
+
*
|
|
30
|
+
* The dedupe is belt-and-braces. init-script.ts used to stack a wrapper on
|
|
31
|
+
* window.dataLayer per re-assignment and report one push up to 13 times; that
|
|
32
|
+
* is fixed at the source, and this makes a return of it cost nothing here.
|
|
33
|
+
*/
|
|
34
|
+
function compactConsentPushes(pushes) {
|
|
35
|
+
const seen = new Set();
|
|
36
|
+
const compact = [];
|
|
37
|
+
for (const push of pushes) {
|
|
38
|
+
const key = JSON.stringify(push);
|
|
39
|
+
if (seen.has(key))
|
|
40
|
+
continue;
|
|
41
|
+
seen.add(key);
|
|
42
|
+
compact.push({
|
|
43
|
+
// The typed field first; the reach into `raw` stays as the fallback
|
|
44
|
+
// because evidence collected before `verb` existed still has to project
|
|
45
|
+
// to the same `kind` it always did. The name stays `kind` so stored
|
|
46
|
+
// reports keep their shape.
|
|
47
|
+
kind: push.verb ?? (Array.isArray(push.raw) && typeof push.raw[1] === "string" ? push.raw[1] : null),
|
|
48
|
+
analyticsStorage: push.analyticsStorage,
|
|
49
|
+
region: push.region ?? null,
|
|
50
|
+
waitForUpdate: push.waitForUpdate ?? null,
|
|
51
|
+
timestamp: push.timestamp,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return compact;
|
|
55
|
+
}
|
|
56
|
+
function compactSecondPage(second) {
|
|
57
|
+
if (!second)
|
|
58
|
+
return null;
|
|
59
|
+
return {
|
|
60
|
+
...second,
|
|
61
|
+
network: { ...second.network, requests: second.network.requests.map(compactRequest) },
|
|
62
|
+
hardLoad: !second.hardLoad
|
|
63
|
+
? null
|
|
64
|
+
: {
|
|
65
|
+
...second.hardLoad,
|
|
66
|
+
network: {
|
|
67
|
+
...second.hardLoad.network,
|
|
68
|
+
requests: second.hardLoad.network.requests.map(compactRequest),
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
cookies: compactStorage(second.cookies),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Spread-and-override rather than a field list: a phase carries a lot that is
|
|
76
|
+
* already compact, and a new field added to EvidenceReport should reach the
|
|
77
|
+
* report on its own rather than being silently dropped here until someone
|
|
78
|
+
* notices.
|
|
79
|
+
*/
|
|
80
|
+
function compactPhase(phase) {
|
|
81
|
+
return {
|
|
82
|
+
...phase,
|
|
83
|
+
network: {
|
|
84
|
+
...phase.network,
|
|
85
|
+
requests: phase.network.requests.map(compactRequest),
|
|
86
|
+
// Compacted for the same reason as `requests`, and more urgently: an
|
|
87
|
+
// unattributed request's payload belongs to whatever tool sent it, so
|
|
88
|
+
// this file is the last place its raw body should be reproduced.
|
|
89
|
+
unattributedRequests: phase.network.unattributedRequests.map(compactRequest),
|
|
90
|
+
},
|
|
91
|
+
cookies: compactStorage(phase.cookies),
|
|
92
|
+
consent: {
|
|
93
|
+
...phase.consent,
|
|
94
|
+
dataLayerConsentPushes: compactConsentPushes(phase.consent.dataLayerConsentPushes),
|
|
95
|
+
},
|
|
96
|
+
secondPage: compactSecondPage(phase.secondPage),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* `rawResponse` is the model's unparsed reply, and `verdicts` /
|
|
101
|
+
* `remediationAdvice` / `overallPass` are what parsing it produced — except
|
|
102
|
+
* when parsing FAILED. response-parser.ts falls back to inconclusive verdicts
|
|
103
|
+
* that carry no findings of their own, and there the raw text is the only
|
|
104
|
+
* record of what the model actually said, so it stays.
|
|
105
|
+
*/
|
|
106
|
+
function compactResult(result) {
|
|
107
|
+
const { rawResponse, ...rest } = result;
|
|
108
|
+
return result.parseFailed ? { ...rest, rawResponse } : rest;
|
|
109
|
+
}
|
|
110
|
+
export function projectReport(evidence, analysis, result, meta) {
|
|
111
|
+
return {
|
|
112
|
+
slug: meta.slug ?? null,
|
|
113
|
+
url: evidence.url,
|
|
114
|
+
timestamp: evidence.timestamp,
|
|
115
|
+
// Run-level, not per-phase: nothing about what the site serves a plain
|
|
116
|
+
// HTTP GET varies with consent state. It is the raw form of
|
|
117
|
+
// deployment.pageReadability, kept so a reader can check the call.
|
|
118
|
+
httpProbe: evidence.httpProbe,
|
|
119
|
+
phases: evidence.phases.map(compactPhase),
|
|
120
|
+
deployment: analysis,
|
|
121
|
+
result: compactResult(result),
|
|
122
|
+
timings: meta.timings ?? null,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=projection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projection.js","sourceRoot":"","sources":["../../src/report/projection.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Does a verdict's `evidenceQuote` point at something real?
|
|
3
|
+
*
|
|
4
|
+
* The model is asked to cite a field path so a reader can check the claim
|
|
5
|
+
* against the JSON. Whether it did is worth measuring — a cited field that
|
|
6
|
+
* does not exist means the verdict rests on nothing — but the measurement has
|
|
7
|
+
* to distinguish two very different failures, and the first version of this
|
|
8
|
+
* did not:
|
|
9
|
+
*
|
|
10
|
+
* A quote naming `grant.network.invented` is a fabrication. A quote reading
|
|
11
|
+
* "clean-before-consent: fail, reject-blocks-normal: fail" cites real check
|
|
12
|
+
* ids in prose and fabricates nothing; it simply is not a path. Scoring both
|
|
13
|
+
* as "hallucinated" made the check unreadable — measured across the golden
|
|
14
|
+
* set, the trusted cloud baseline scored 2/7 on one account, worse than the
|
|
15
|
+
* local model it was there to judge.
|
|
16
|
+
*/
|
|
17
|
+
import { valueAtPath } from "./store.js";
|
|
18
|
+
const PHASE_PREFIXES = ["clean", "grant", "reject"];
|
|
19
|
+
/**
|
|
20
|
+
* Every dotted/bracketed token embedded in free text, e.g. the
|
|
21
|
+
* "script.writeKey" inside `script.present: true, script.writeKey: "abc"`. A
|
|
22
|
+
* capable model returns a bare path; a smaller one tends to return prose that
|
|
23
|
+
* still names real fields, which is not the same failure as inventing one.
|
|
24
|
+
*/
|
|
25
|
+
export function extractPathCandidates(text) {
|
|
26
|
+
// Brackets hold an index or a name, quoted or bare: `phases[0]`,
|
|
27
|
+
// `deployment.checks[page-view-counted-once]` and
|
|
28
|
+
// `deployment.checks['global-handoff']` are all citations, and the last two
|
|
29
|
+
// are the forms actually used for a cross-phase check — the quoted one by
|
|
30
|
+
// the model, the bare one by applyDeterministicChecks (see valueAtPath).
|
|
31
|
+
return [
|
|
32
|
+
...new Set(text.match(/\b[a-zA-Z_][\w]*(?:\.[a-zA-Z_][\w]*|\[['"]?[\w.-]+['"]?\])+/g) ?? []),
|
|
33
|
+
];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* A path resolves against the analysis when prefixed `deployment.` (the
|
|
37
|
+
* deterministic overrides quote it that way), against a named phase when
|
|
38
|
+
* prefixed `clean.`/`grant.`/`reject.`, and otherwise against any phase — the
|
|
39
|
+
* prompt asks for a phase prefix, but a bare field name that is real evidence
|
|
40
|
+
* should not be scored the same as an invented one.
|
|
41
|
+
*/
|
|
42
|
+
function lookup(report, candidate) {
|
|
43
|
+
const phases = Array.isArray(report.phases) ? report.phases : [];
|
|
44
|
+
if (candidate.startsWith("deployment.")) {
|
|
45
|
+
return valueAtPath(report.deployment, candidate.slice("deployment.".length));
|
|
46
|
+
}
|
|
47
|
+
const [first, ...rest] = candidate.split(".");
|
|
48
|
+
if (first !== undefined && PHASE_PREFIXES.includes(first) && rest.length > 0) {
|
|
49
|
+
const phase = phases.find((p) => p?.phase === first);
|
|
50
|
+
const value = phase ? valueAtPath(phase, rest.join(".")) : undefined;
|
|
51
|
+
if (value !== undefined)
|
|
52
|
+
return value;
|
|
53
|
+
}
|
|
54
|
+
for (const phase of phases) {
|
|
55
|
+
const value = valueAtPath(phase, candidate);
|
|
56
|
+
if (value !== undefined)
|
|
57
|
+
return value;
|
|
58
|
+
}
|
|
59
|
+
return valueAtPath(report.deployment, candidate);
|
|
60
|
+
}
|
|
61
|
+
function resolves(report, candidate) {
|
|
62
|
+
return lookup(report, candidate) !== undefined;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Every field path a quote names, with what the report actually holds there.
|
|
66
|
+
*
|
|
67
|
+
* A quote is usually prose with paths embedded in it — "script.present: true,
|
|
68
|
+
* script.writeKey: 'abc'" — so resolving it as one path answers the wrong
|
|
69
|
+
* question and answers it wrongly. This resolves each path it names, which is
|
|
70
|
+
* what a reader checking the claim needs anyway.
|
|
71
|
+
*/
|
|
72
|
+
export function resolveCitations(report, quote) {
|
|
73
|
+
return extractPathCandidates(quote ?? "").map((path) => {
|
|
74
|
+
const value = lookup(report, path);
|
|
75
|
+
return { path, resolved: value !== undefined, value: value ?? null };
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
export function checkQuote(report, quote) {
|
|
79
|
+
const candidates = extractPathCandidates(quote ?? "");
|
|
80
|
+
if (candidates.length === 0)
|
|
81
|
+
return "unverifiable";
|
|
82
|
+
return candidates.some((candidate) => resolves(report, candidate)) ? "resolved" : "invented";
|
|
83
|
+
}
|
|
84
|
+
export function tallyQuotes(report, verdicts) {
|
|
85
|
+
const tally = { resolved: 0, unverifiable: 0, invented: 0 };
|
|
86
|
+
for (const verdict of verdicts)
|
|
87
|
+
tally[checkQuote(report, verdict.evidenceQuote)] += 1;
|
|
88
|
+
return tally;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=quote-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quote-check.js","sourceRoot":"","sources":["../../src/report/quote-check.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { causeLabel } from "../analysis/diagnosis/copy.en.js";
|
|
2
|
+
import { formatDuration } from "../utils/duration.js";
|
|
3
|
+
import { buildContextLines, buildRunContextLines } from "./context.js";
|
|
4
|
+
const RESET = "\x1b[0m";
|
|
5
|
+
const GREEN = "\x1b[32m";
|
|
6
|
+
const RED = "\x1b[31m";
|
|
7
|
+
const GRAY = "\x1b[90m";
|
|
8
|
+
const YELLOW = "\x1b[33m";
|
|
9
|
+
const DIAGNOSIS_LABEL_WIDTH = 11;
|
|
10
|
+
const DIAGNOSIS_WRAP_AT = 76;
|
|
11
|
+
/**
|
|
12
|
+
* Pads to the label column by VISIBLE width.
|
|
13
|
+
*
|
|
14
|
+
* A kind label carries colour escapes, and padEnd counts those, so measuring
|
|
15
|
+
* the raw string left the coloured rows short by however many bytes the
|
|
16
|
+
* escapes took and knocked the whole block out of alignment.
|
|
17
|
+
*/
|
|
18
|
+
function padLabel(label) {
|
|
19
|
+
const visible = label.replace(/\x1b\[[0-9;]*m/g, "").length;
|
|
20
|
+
return label + " ".repeat(Math.max(1, DIAGNOSIS_LABEL_WIDTH - visible));
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Wraps at word boundaries onto a hanging indent, so a long line stays
|
|
24
|
+
* readable and every value lines up in the same column.
|
|
25
|
+
*
|
|
26
|
+
* `gutter` is the first line's indent and `indent` every later line's. They
|
|
27
|
+
* differ only on a cause's opening row, where the rank number lives in the
|
|
28
|
+
* gutter — putting the rank inside the label instead pushed the label column
|
|
29
|
+
* out by a character or two per cause and broke the alignment it exists for.
|
|
30
|
+
*/
|
|
31
|
+
function wrapInto(label, text, indent, gutter = indent) {
|
|
32
|
+
const lines = [];
|
|
33
|
+
let current = "";
|
|
34
|
+
for (const word of text.split(" ")) {
|
|
35
|
+
if (current.length > 0 && `${current} ${word}`.length > DIAGNOSIS_WRAP_AT) {
|
|
36
|
+
lines.push(current);
|
|
37
|
+
current = word;
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
current = current.length === 0 ? word : `${current} ${word}`;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (current.length > 0)
|
|
44
|
+
lines.push(current);
|
|
45
|
+
return lines.map((line, index) => index === 0 ? `${gutter}${padLabel(label)}${line}` : `${indent}${" ".repeat(DIAGNOSIS_LABEL_WIDTH)}${line}`);
|
|
46
|
+
}
|
|
47
|
+
function kindLabel(kind) {
|
|
48
|
+
if (kind === "blocking")
|
|
49
|
+
return `${RED}BLOCKING${RESET}`;
|
|
50
|
+
if (kind === "leaking")
|
|
51
|
+
return `${RED}LEAKING${RESET}`;
|
|
52
|
+
if (kind === "duplicate-counting")
|
|
53
|
+
return `${YELLOW}DUPLICATE${RESET}`;
|
|
54
|
+
if (kind === "hardening")
|
|
55
|
+
return `${YELLOW}HARDENING${RESET}`;
|
|
56
|
+
return `${GRAY}UNVERIFIED${RESET}`;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* The finding, first.
|
|
60
|
+
*
|
|
61
|
+
* On a terminal this is now the whole report: the phase matrix and both
|
|
62
|
+
* per-check lists used to sit under it and are gone (see renderTextReport),
|
|
63
|
+
* because the reader this section exists for — a CS or AM person who wants to
|
|
64
|
+
* know what to tell the customer — was scrolling past thirty rows of detail
|
|
65
|
+
* that this block has already collapsed into one cause and one fix.
|
|
66
|
+
*
|
|
67
|
+
* Every heading, label and note below is the same wording the review
|
|
68
|
+
* dashboard (verification/review/index.html) prints for the same diagnosis,
|
|
69
|
+
* so the terminal and the dashboard cannot describe one run two ways. The
|
|
70
|
+
* terminal adds one row the dashboard has no room for — Explains, the bridge
|
|
71
|
+
* down to the check list underneath it — and spells the docs link out as
|
|
72
|
+
* title plus URL, because a terminal has no anchor text.
|
|
73
|
+
*/
|
|
74
|
+
export function renderDiagnosis(diagnosis, unexplainedObservation) {
|
|
75
|
+
const lines = [];
|
|
76
|
+
if (diagnosis.causes.length === 0 && diagnosis.unexplainedChecks.length === 0 && !unexplainedObservation) {
|
|
77
|
+
// The dashboard renders an empty frame as nothing at all; a terminal has
|
|
78
|
+
// to say something, or the reader cannot tell a healthy run from a
|
|
79
|
+
// section that failed to print — and with the check lists gone there is
|
|
80
|
+
// nothing else on screen to read it off.
|
|
81
|
+
lines.push(diagnosis.hadFailures
|
|
82
|
+
? `${YELLOW}What is wrong: checks failed but no catalogued cause matched them.${RESET}`
|
|
83
|
+
: `${GREEN}Nothing is wrong: every check that could be judged passed.${RESET}`);
|
|
84
|
+
return lines;
|
|
85
|
+
}
|
|
86
|
+
const everyCauseIsHardening = diagnosis.causes.length > 0 && diagnosis.causes.every((cause) => cause.kind === "hardening");
|
|
87
|
+
const onlyNotAVerdict = diagnosis.causes.length > 0 && diagnosis.causes.every((cause) => cause.kind === "not-a-verdict");
|
|
88
|
+
lines.push(onlyNotAVerdict
|
|
89
|
+
? "Not a verdict about this site"
|
|
90
|
+
: everyCauseIsHardening
|
|
91
|
+
? "Nothing is broken — one thing to harden"
|
|
92
|
+
: diagnosis.causes.length === 0
|
|
93
|
+
? "Unexplained failures"
|
|
94
|
+
: "What is wrong — ranked, decided in code");
|
|
95
|
+
const ranked = diagnosis.causes.length > 1;
|
|
96
|
+
diagnosis.causes.forEach((cause, index) => {
|
|
97
|
+
// Numbered causes get a wider gutter so the rank sits outside the label
|
|
98
|
+
// column and every Fix/Docs/Explains value still lines up.
|
|
99
|
+
const indent = ranked ? " " : " ";
|
|
100
|
+
const gutter = ranked ? ` ${index + 1}. ` : " ";
|
|
101
|
+
// The kind sits in the label column, so the problem text lines up with
|
|
102
|
+
// the Fix and Docs values underneath it.
|
|
103
|
+
lines.push(...wrapInto(kindLabel(cause.kind), cause.problem, indent, gutter));
|
|
104
|
+
lines.push(...wrapInto("Fix", cause.fix, indent));
|
|
105
|
+
lines.push(`${indent}${padLabel("Docs")}${cause.docTitle} — ${cause.docUrl}`);
|
|
106
|
+
// The check ids the headline rests on. The terminal no longer prints the
|
|
107
|
+
// check list itself, so these are the handle a reader who does not trust
|
|
108
|
+
// the one-liner takes to the stored report or the review dashboard.
|
|
109
|
+
if (cause.explains.length > 0) {
|
|
110
|
+
lines.push(...wrapInto("Explains", cause.explains.join(", "), indent).map((line) => `${GRAY}${line}${RESET}`));
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
if (diagnosis.alsoFired.length > 0) {
|
|
114
|
+
lines.push(` ${GRAY}Also found, outside the top three: ${diagnosis.alsoFired.map(causeLabel).join("; ")}${RESET}`);
|
|
115
|
+
}
|
|
116
|
+
if (diagnosis.unexplainedChecks.length > 0) {
|
|
117
|
+
lines.push(` ${YELLOW}Not explained by any catalogued cause: ${diagnosis.unexplainedChecks.join(", ")} — a gap in the catalog, not a clean bill.${RESET}`);
|
|
118
|
+
}
|
|
119
|
+
// Labelled unverified, and visibly separated from the catalogued causes
|
|
120
|
+
// above: a model sentence must never be mistaken for a decided one. Printed
|
|
121
|
+
// whenever the run produced one — the dashboard does the same, and gating it
|
|
122
|
+
// on unexplainedChecks dropped the note on a run whose gap was a cause the
|
|
123
|
+
// catalog claimed but could not fix.
|
|
124
|
+
if (unexplainedObservation) {
|
|
125
|
+
lines.push(...wrapInto("Model's note", unexplainedObservation, " ").map((line) => `${GRAY}${line}${RESET}`), ` ${GRAY}${" ".repeat(DIAGNOSIS_LABEL_WIDTH)}(unverified, carries no fix or link)${RESET}`);
|
|
126
|
+
}
|
|
127
|
+
return lines;
|
|
128
|
+
}
|
|
129
|
+
export function renderTextReport(evidence, analysis, result, timings) {
|
|
130
|
+
const lines = [];
|
|
131
|
+
lines.push(`Dreamdata installation check for ${evidence.url}`);
|
|
132
|
+
lines.push(`Collected ${evidence.timestamp}`);
|
|
133
|
+
if (timings) {
|
|
134
|
+
lines.push(`${GRAY}Timing: collect evidence ${formatDuration(timings.collectEvidenceMs)}, analyze deployment ${formatDuration(timings.analyzeDeploymentMs)}, AI check ${formatDuration(timings.checkWithAIMs)}, total ${formatDuration(timings.totalMs)}${RESET}`);
|
|
135
|
+
}
|
|
136
|
+
lines.push("");
|
|
137
|
+
for (const line of buildRunContextLines(evidence, analysis))
|
|
138
|
+
lines.push(`${GRAY}${line}${RESET}`);
|
|
139
|
+
lines.push("");
|
|
140
|
+
for (const line of renderDiagnosis(analysis.diagnosis, result.unexplainedObservation))
|
|
141
|
+
lines.push(line);
|
|
142
|
+
lines.push("");
|
|
143
|
+
// No phase matrix, no per-check list, no checklist: on a run with eighteen
|
|
144
|
+
// checks and thirteen verdicts they filled a screen and a half with rows
|
|
145
|
+
// the diagnosis above has already collapsed into one cause and one fix, and
|
|
146
|
+
// the reader had to scroll past all of it to reach the verdict. Every one
|
|
147
|
+
// of them is still in the stored report — the markdown export renders the
|
|
148
|
+
// matrix and both lists, and the review dashboard renders them per check
|
|
149
|
+
// with the evidence behind a click. This is the terminal's summary view.
|
|
150
|
+
// A blocked run is not a failing site. Its verdicts are all inconclusive by
|
|
151
|
+
// then, so printing FAIL under them would be the report contradicting itself
|
|
152
|
+
// — and the contradiction a reader resolves in the site's disfavour.
|
|
153
|
+
lines.push(`Overall: ${analysis.pageReadability.unreadable
|
|
154
|
+
? `${GRAY}INCONCLUSIVE — the browser was never served this site's page${RESET}`
|
|
155
|
+
: result.overallPass
|
|
156
|
+
? `${GREEN}PASS${RESET}`
|
|
157
|
+
: `${RED}FAIL${RESET}`}`);
|
|
158
|
+
// No "Remediation advice" block: the fix for every catalogued cause is
|
|
159
|
+
// already printed above, decided in code and carrying a real docs link.
|
|
160
|
+
// The model's free-text version said the same thing less reliably — it is
|
|
161
|
+
// still stored on the report and still rendered by the dashboard and the
|
|
162
|
+
// markdown export, just not read out twice here.
|
|
163
|
+
const collectionErrors = evidence.phases.flatMap((phase) => phase.collectionErrors.map((err) => `${phase.phase}: ${err}`));
|
|
164
|
+
if (collectionErrors.length > 0) {
|
|
165
|
+
lines.push("");
|
|
166
|
+
lines.push(`${GRAY}Collection warnings:${RESET}`);
|
|
167
|
+
for (const err of collectionErrors)
|
|
168
|
+
lines.push(` - ${err}`);
|
|
169
|
+
}
|
|
170
|
+
return lines.join("\n");
|
|
171
|
+
}
|
|
172
|
+
/** Per-phase detail, printed only when the operator asks for it (--verbose). */
|
|
173
|
+
export function renderPhaseDetail(evidence) {
|
|
174
|
+
return evidence.phases
|
|
175
|
+
.map((phase) => [`${GRAY}${"─".repeat(60)}${RESET}`, ...buildContextLines(phase).map((l) => `${GRAY}${l}${RESET}`)].join("\n"))
|
|
176
|
+
.join("\n");
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/report/render.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { formatDuration } from "../utils/duration.js";
|
|
2
|
+
import { buildPhaseMatrixRows, buildRunContextLines } from "./context.js";
|
|
3
|
+
import { projectReport } from "./projection.js";
|
|
4
|
+
/**
|
|
5
|
+
* The report people and models read: every judgement, and the evidence behind
|
|
6
|
+
* it with the raw values that only fed those judgements taken back out. See
|
|
7
|
+
* projection.ts for what is dropped and why it is safe to drop.
|
|
8
|
+
*/
|
|
9
|
+
export function serializeToJson(evidence, analysis, result, meta = {}) {
|
|
10
|
+
return JSON.stringify(projectReport(evidence, analysis, result, meta), null, 2);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The archive companion to serializeToJson: the same run, nothing trimmed.
|
|
14
|
+
* cli.ts writes both from one in-memory collection in one step, so the pair on
|
|
15
|
+
* disk is always ONE run rather than two that happen to share a slug — which is
|
|
16
|
+
* the whole point of keeping it, since re-running a live site never reproduces
|
|
17
|
+
* its evidence exactly.
|
|
18
|
+
*/
|
|
19
|
+
export function serializeEvidenceToJson(evidence, analysis, result, meta = {}) {
|
|
20
|
+
return JSON.stringify({
|
|
21
|
+
slug: meta.slug ?? null,
|
|
22
|
+
url: evidence.url,
|
|
23
|
+
timestamp: evidence.timestamp,
|
|
24
|
+
browser: evidence.browser,
|
|
25
|
+
httpProbe: evidence.httpProbe,
|
|
26
|
+
phases: evidence.phases,
|
|
27
|
+
deployment: analysis,
|
|
28
|
+
result,
|
|
29
|
+
timings: meta.timings ?? null,
|
|
30
|
+
}, null, 2);
|
|
31
|
+
}
|
|
32
|
+
export function serializeToMarkdown(evidence, analysis, result, meta = {}) {
|
|
33
|
+
const lines = [];
|
|
34
|
+
lines.push(`# Dreamdata installation check — ${evidence.url}`);
|
|
35
|
+
lines.push("");
|
|
36
|
+
if (meta.slug)
|
|
37
|
+
lines.push(`Account: \`${meta.slug}\``);
|
|
38
|
+
lines.push(`Collected: ${evidence.timestamp}`);
|
|
39
|
+
lines.push(`Overall: **${analysis.pageReadability.unreadable
|
|
40
|
+
? "INCONCLUSIVE — the browser was never served this site's page"
|
|
41
|
+
: result.overallPass
|
|
42
|
+
? "PASS"
|
|
43
|
+
: "FAIL"}**`);
|
|
44
|
+
if (meta.timings) {
|
|
45
|
+
const t = meta.timings;
|
|
46
|
+
lines.push(`Timing: collect evidence ${formatDuration(t.collectEvidenceMs)}, analyze deployment ${formatDuration(t.analyzeDeploymentMs)}, AI check ${formatDuration(t.checkWithAIMs)}, total ${formatDuration(t.totalMs)}`);
|
|
47
|
+
}
|
|
48
|
+
lines.push("");
|
|
49
|
+
// Ahead of the observation context and the matrix, for the same reason the
|
|
50
|
+
// terminal puts it first: the reader who needs this section should not have
|
|
51
|
+
// to work through eighteen checks to reach it.
|
|
52
|
+
if (analysis.diagnosis.causes.length > 0) {
|
|
53
|
+
lines.push("## What is wrong");
|
|
54
|
+
lines.push("");
|
|
55
|
+
lines.push("Ranked root causes, decided in code from the resolved checks — not model-written.");
|
|
56
|
+
lines.push("");
|
|
57
|
+
for (const cause of analysis.diagnosis.causes) {
|
|
58
|
+
lines.push(`### ${cause.kind.toUpperCase()} — ${cause.problem}`);
|
|
59
|
+
lines.push("");
|
|
60
|
+
lines.push(`- **Fix:** ${cause.fix}`);
|
|
61
|
+
lines.push(`- **Docs:** [${cause.docTitle}](${cause.docUrl})`);
|
|
62
|
+
if (cause.explains.length > 0)
|
|
63
|
+
lines.push(`- **Explains:** ${cause.explains.map((id) => `\`${id}\``).join(", ")}`);
|
|
64
|
+
lines.push("");
|
|
65
|
+
}
|
|
66
|
+
if (analysis.diagnosis.alsoFired.length > 0) {
|
|
67
|
+
lines.push(`Also found, outside the top three: ${analysis.diagnosis.alsoFired.join(", ")}`);
|
|
68
|
+
lines.push("");
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (analysis.diagnosis.unexplainedChecks.length > 0) {
|
|
72
|
+
lines.push(`> Not explained by any catalogued cause: ${analysis.diagnosis.unexplainedChecks.map((id) => `\`${id}\``).join(", ")}`);
|
|
73
|
+
// Marked as the model's, and marked unverified: a sentence nobody decided
|
|
74
|
+
// must not sit in the same voice as the causes above it.
|
|
75
|
+
if (result.unexplainedObservation) {
|
|
76
|
+
lines.push(">");
|
|
77
|
+
lines.push(`> _Model's note (unverified, no fix or link): ${result.unexplainedObservation}_`);
|
|
78
|
+
}
|
|
79
|
+
lines.push("");
|
|
80
|
+
}
|
|
81
|
+
lines.push("## Observation context");
|
|
82
|
+
lines.push("");
|
|
83
|
+
for (const line of buildRunContextLines(evidence, analysis))
|
|
84
|
+
lines.push(`- ${line}`);
|
|
85
|
+
lines.push("");
|
|
86
|
+
lines.push("## Phase matrix");
|
|
87
|
+
lines.push("");
|
|
88
|
+
const [header, ...rows] = buildPhaseMatrixRows(analysis);
|
|
89
|
+
lines.push(`| ${header.join(" | ")} |`);
|
|
90
|
+
lines.push(`| ${header.map(() => "---").join(" | ")} |`);
|
|
91
|
+
for (const row of rows)
|
|
92
|
+
lines.push(`| ${row.map((cell) => cell.replace(/\|/g, "\\|")).join(" | ")} |`);
|
|
93
|
+
lines.push("");
|
|
94
|
+
lines.push("## Consent behaviour (compared across phases)");
|
|
95
|
+
lines.push("");
|
|
96
|
+
lines.push("| Check | Status | Detail |");
|
|
97
|
+
lines.push("| --- | --- | --- |");
|
|
98
|
+
for (const check of analysis.checks) {
|
|
99
|
+
lines.push(`| ${check.title} | ${check.status.toUpperCase()} | ${check.detail.replace(/\|/g, "\\|")} |`);
|
|
100
|
+
}
|
|
101
|
+
lines.push("");
|
|
102
|
+
lines.push("## Installation checklist");
|
|
103
|
+
lines.push("");
|
|
104
|
+
lines.push("| Item | Status | Feedback |");
|
|
105
|
+
lines.push("| --- | --- | --- |");
|
|
106
|
+
for (const verdict of result.verdicts) {
|
|
107
|
+
const status = verdict.passed === true ? "PASS" : verdict.passed === false ? "FAIL" : String(verdict.passed).toUpperCase();
|
|
108
|
+
lines.push(`| ${verdict.title} | ${status} | ${verdict.feedback.replace(/\|/g, "\\|")} |`);
|
|
109
|
+
}
|
|
110
|
+
if (result.remediationAdvice) {
|
|
111
|
+
lines.push("");
|
|
112
|
+
lines.push("## Remediation advice");
|
|
113
|
+
lines.push(result.remediationAdvice);
|
|
114
|
+
}
|
|
115
|
+
const collectionErrors = evidence.phases.flatMap((phase) => phase.collectionErrors.map((err) => `${phase.phase}: ${err}`));
|
|
116
|
+
if (collectionErrors.length > 0) {
|
|
117
|
+
lines.push("");
|
|
118
|
+
lines.push("## Collection warnings");
|
|
119
|
+
for (const err of collectionErrors)
|
|
120
|
+
lines.push(`- ${err}`);
|
|
121
|
+
}
|
|
122
|
+
return lines.join("\n");
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=serializers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serializers.js","sourceRoot":"","sources":["../../src/report/serializers.ts"],"names":[],"mappings":"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"}
|