@nebulacomponents/citable 1.18.0 → 1.18.2
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/CHANGELOG.md +70 -0
- package/dist/universal/.agents/skills/citable/SKILL.md +22 -1
- package/dist/universal/.agents/skills/citable/VERSION +1 -1
- package/dist/universal/.agents/skills/citable/agents/citable-remediator.md +43 -0
- package/dist/universal/.agents/skills/citable/agents/citable-sow-architect.md +43 -0
- package/dist/universal/.agents/skills/citable/commands/README.md +13 -0
- package/dist/universal/.agents/skills/citable/commands/artifact-interchange.md +5 -0
- package/dist/universal/.agents/skills/citable/commands/remediation.md +55 -0
- package/dist/universal/.agents/skills/citable/commands/reporting.md +28 -0
- package/dist/universal/.agents/skills/citable/commands/sow.md +95 -0
- package/dist/universal/.agents/skills/citable/manifest.json +15 -9
- package/dist/universal/.agents/skills/citable/schemas/cro-report.schema.json +112 -0
- package/dist/universal/.agents/skills/citable/schemas/search-report.schema.json +99 -0
- package/dist/universal/.agents/skills/citable/schemas/sow.schema.json +344 -26
- package/dist/universal/.claude/agents/citable/citable-remediator.md +43 -0
- package/dist/universal/.claude/agents/citable/citable-sow-architect.md +43 -0
- package/dist/universal/.claude/agents/citable/manifest.json +6 -4
- package/dist/universal/.claude/skills/citable/SKILL.md +22 -1
- package/dist/universal/.claude/skills/citable/VERSION +1 -1
- package/dist/universal/.claude/skills/citable/agents/citable-remediator.md +43 -0
- package/dist/universal/.claude/skills/citable/agents/citable-sow-architect.md +43 -0
- package/dist/universal/.claude/skills/citable/commands/README.md +13 -0
- package/dist/universal/.claude/skills/citable/commands/artifact-interchange.md +5 -0
- package/dist/universal/.claude/skills/citable/commands/remediation.md +55 -0
- package/dist/universal/.claude/skills/citable/commands/reporting.md +28 -0
- package/dist/universal/.claude/skills/citable/commands/sow.md +95 -0
- package/dist/universal/.claude/skills/citable/manifest.json +19 -11
- package/dist/universal/.claude/skills/citable/schemas/cro-report.schema.json +112 -0
- package/dist/universal/.claude/skills/citable/schemas/search-report.schema.json +99 -0
- package/dist/universal/.claude/skills/citable/schemas/sow.schema.json +344 -26
- package/dist/universal/.cursor/skills/citable/SKILL.md +22 -1
- package/dist/universal/.cursor/skills/citable/VERSION +1 -1
- package/dist/universal/.cursor/skills/citable/agents/citable-remediator.md +43 -0
- package/dist/universal/.cursor/skills/citable/agents/citable-sow-architect.md +43 -0
- package/dist/universal/.cursor/skills/citable/commands/README.md +13 -0
- package/dist/universal/.cursor/skills/citable/commands/artifact-interchange.md +5 -0
- package/dist/universal/.cursor/skills/citable/commands/remediation.md +55 -0
- package/dist/universal/.cursor/skills/citable/commands/reporting.md +28 -0
- package/dist/universal/.cursor/skills/citable/commands/sow.md +95 -0
- package/dist/universal/.cursor/skills/citable/manifest.json +15 -9
- package/dist/universal/.cursor/skills/citable/schemas/cro-report.schema.json +112 -0
- package/dist/universal/.cursor/skills/citable/schemas/search-report.schema.json +99 -0
- package/dist/universal/.cursor/skills/citable/schemas/sow.schema.json +344 -26
- package/dist/universal/.gemini/skills/citable/SKILL.md +22 -1
- package/dist/universal/.gemini/skills/citable/VERSION +1 -1
- package/dist/universal/.gemini/skills/citable/agents/citable-remediator.md +43 -0
- package/dist/universal/.gemini/skills/citable/agents/citable-sow-architect.md +43 -0
- package/dist/universal/.gemini/skills/citable/commands/README.md +13 -0
- package/dist/universal/.gemini/skills/citable/commands/artifact-interchange.md +5 -0
- package/dist/universal/.gemini/skills/citable/commands/remediation.md +55 -0
- package/dist/universal/.gemini/skills/citable/commands/reporting.md +28 -0
- package/dist/universal/.gemini/skills/citable/commands/sow.md +95 -0
- package/dist/universal/.gemini/skills/citable/manifest.json +15 -9
- package/dist/universal/.gemini/skills/citable/schemas/cro-report.schema.json +112 -0
- package/dist/universal/.gemini/skills/citable/schemas/search-report.schema.json +99 -0
- package/dist/universal/.gemini/skills/citable/schemas/sow.schema.json +344 -26
- package/dist/universal/.github/skills/citable/SKILL.md +22 -1
- package/dist/universal/.github/skills/citable/VERSION +1 -1
- package/dist/universal/.github/skills/citable/agents/citable-remediator.md +43 -0
- package/dist/universal/.github/skills/citable/agents/citable-sow-architect.md +43 -0
- package/dist/universal/.github/skills/citable/commands/README.md +13 -0
- package/dist/universal/.github/skills/citable/commands/artifact-interchange.md +5 -0
- package/dist/universal/.github/skills/citable/commands/remediation.md +55 -0
- package/dist/universal/.github/skills/citable/commands/reporting.md +28 -0
- package/dist/universal/.github/skills/citable/commands/sow.md +95 -0
- package/dist/universal/.github/skills/citable/manifest.json +15 -9
- package/dist/universal/.github/skills/citable/schemas/cro-report.schema.json +112 -0
- package/dist/universal/.github/skills/citable/schemas/search-report.schema.json +99 -0
- package/dist/universal/.github/skills/citable/schemas/sow.schema.json +344 -26
- package/dist/universal/.kiro/skills/citable/SKILL.md +22 -1
- package/dist/universal/.kiro/skills/citable/VERSION +1 -1
- package/dist/universal/.kiro/skills/citable/agents/citable-remediator.md +43 -0
- package/dist/universal/.kiro/skills/citable/agents/citable-sow-architect.md +43 -0
- package/dist/universal/.kiro/skills/citable/commands/README.md +13 -0
- package/dist/universal/.kiro/skills/citable/commands/artifact-interchange.md +5 -0
- package/dist/universal/.kiro/skills/citable/commands/remediation.md +55 -0
- package/dist/universal/.kiro/skills/citable/commands/reporting.md +28 -0
- package/dist/universal/.kiro/skills/citable/commands/sow.md +95 -0
- package/dist/universal/.kiro/skills/citable/manifest.json +15 -9
- package/dist/universal/.kiro/skills/citable/schemas/cro-report.schema.json +112 -0
- package/dist/universal/.kiro/skills/citable/schemas/search-report.schema.json +99 -0
- package/dist/universal/.kiro/skills/citable/schemas/sow.schema.json +344 -26
- package/dist/universal/.opencode/skills/citable/SKILL.md +22 -1
- package/dist/universal/.opencode/skills/citable/VERSION +1 -1
- package/dist/universal/.opencode/skills/citable/agents/citable-remediator.md +43 -0
- package/dist/universal/.opencode/skills/citable/agents/citable-sow-architect.md +43 -0
- package/dist/universal/.opencode/skills/citable/commands/README.md +13 -0
- package/dist/universal/.opencode/skills/citable/commands/artifact-interchange.md +5 -0
- package/dist/universal/.opencode/skills/citable/commands/remediation.md +55 -0
- package/dist/universal/.opencode/skills/citable/commands/reporting.md +28 -0
- package/dist/universal/.opencode/skills/citable/commands/sow.md +95 -0
- package/dist/universal/.opencode/skills/citable/manifest.json +15 -9
- package/dist/universal/.opencode/skills/citable/schemas/cro-report.schema.json +112 -0
- package/dist/universal/.opencode/skills/citable/schemas/search-report.schema.json +99 -0
- package/dist/universal/.opencode/skills/citable/schemas/sow.schema.json +344 -26
- package/dist/universal/.pi/agent/skills/citable/SKILL.md +22 -1
- package/dist/universal/.pi/agent/skills/citable/VERSION +1 -1
- package/dist/universal/.pi/agent/skills/citable/agents/citable-remediator.md +43 -0
- package/dist/universal/.pi/agent/skills/citable/agents/citable-sow-architect.md +43 -0
- package/dist/universal/.pi/agent/skills/citable/commands/README.md +13 -0
- package/dist/universal/.pi/agent/skills/citable/commands/artifact-interchange.md +5 -0
- package/dist/universal/.pi/agent/skills/citable/commands/remediation.md +55 -0
- package/dist/universal/.pi/agent/skills/citable/commands/reporting.md +28 -0
- package/dist/universal/.pi/agent/skills/citable/commands/sow.md +95 -0
- package/dist/universal/.pi/agent/skills/citable/manifest.json +15 -9
- package/dist/universal/.pi/agent/skills/citable/schemas/cro-report.schema.json +112 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/search-report.schema.json +99 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/sow.schema.json +344 -26
- package/dist/universal/.qoder/skills/citable/SKILL.md +22 -1
- package/dist/universal/.qoder/skills/citable/VERSION +1 -1
- package/dist/universal/.qoder/skills/citable/agents/citable-remediator.md +43 -0
- package/dist/universal/.qoder/skills/citable/agents/citable-sow-architect.md +43 -0
- package/dist/universal/.qoder/skills/citable/commands/README.md +13 -0
- package/dist/universal/.qoder/skills/citable/commands/artifact-interchange.md +5 -0
- package/dist/universal/.qoder/skills/citable/commands/remediation.md +55 -0
- package/dist/universal/.qoder/skills/citable/commands/reporting.md +28 -0
- package/dist/universal/.qoder/skills/citable/commands/sow.md +95 -0
- package/dist/universal/.qoder/skills/citable/manifest.json +15 -9
- package/dist/universal/.qoder/skills/citable/schemas/cro-report.schema.json +112 -0
- package/dist/universal/.qoder/skills/citable/schemas/search-report.schema.json +99 -0
- package/dist/universal/.qoder/skills/citable/schemas/sow.schema.json +344 -26
- package/dist/universal/.rovodev/skills/citable/SKILL.md +22 -1
- package/dist/universal/.rovodev/skills/citable/VERSION +1 -1
- package/dist/universal/.rovodev/skills/citable/agents/citable-remediator.md +43 -0
- package/dist/universal/.rovodev/skills/citable/agents/citable-sow-architect.md +43 -0
- package/dist/universal/.rovodev/skills/citable/commands/README.md +13 -0
- package/dist/universal/.rovodev/skills/citable/commands/artifact-interchange.md +5 -0
- package/dist/universal/.rovodev/skills/citable/commands/remediation.md +55 -0
- package/dist/universal/.rovodev/skills/citable/commands/reporting.md +28 -0
- package/dist/universal/.rovodev/skills/citable/commands/sow.md +95 -0
- package/dist/universal/.rovodev/skills/citable/manifest.json +15 -9
- package/dist/universal/.rovodev/skills/citable/schemas/cro-report.schema.json +112 -0
- package/dist/universal/.rovodev/skills/citable/schemas/search-report.schema.json +99 -0
- package/dist/universal/.rovodev/skills/citable/schemas/sow.schema.json +344 -26
- package/dist/universal/.trae/skills/citable/SKILL.md +22 -1
- package/dist/universal/.trae/skills/citable/VERSION +1 -1
- package/dist/universal/.trae/skills/citable/agents/citable-remediator.md +43 -0
- package/dist/universal/.trae/skills/citable/agents/citable-sow-architect.md +43 -0
- package/dist/universal/.trae/skills/citable/commands/README.md +13 -0
- package/dist/universal/.trae/skills/citable/commands/artifact-interchange.md +5 -0
- package/dist/universal/.trae/skills/citable/commands/remediation.md +55 -0
- package/dist/universal/.trae/skills/citable/commands/reporting.md +28 -0
- package/dist/universal/.trae/skills/citable/commands/sow.md +95 -0
- package/dist/universal/.trae/skills/citable/manifest.json +15 -9
- package/dist/universal/.trae/skills/citable/schemas/cro-report.schema.json +112 -0
- package/dist/universal/.trae/skills/citable/schemas/search-report.schema.json +99 -0
- package/dist/universal/.trae/skills/citable/schemas/sow.schema.json +344 -26
- package/dist/universal/.trae-cn/skills/citable/SKILL.md +22 -1
- package/dist/universal/.trae-cn/skills/citable/VERSION +1 -1
- package/dist/universal/.trae-cn/skills/citable/agents/citable-remediator.md +43 -0
- package/dist/universal/.trae-cn/skills/citable/agents/citable-sow-architect.md +43 -0
- package/dist/universal/.trae-cn/skills/citable/commands/README.md +13 -0
- package/dist/universal/.trae-cn/skills/citable/commands/artifact-interchange.md +5 -0
- package/dist/universal/.trae-cn/skills/citable/commands/remediation.md +55 -0
- package/dist/universal/.trae-cn/skills/citable/commands/reporting.md +28 -0
- package/dist/universal/.trae-cn/skills/citable/commands/sow.md +95 -0
- package/dist/universal/.trae-cn/skills/citable/manifest.json +15 -9
- package/dist/universal/.trae-cn/skills/citable/schemas/cro-report.schema.json +112 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/search-report.schema.json +99 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/sow.schema.json +344 -26
- package/dist/universal/manifest.json +186 -112
- package/package.json +2 -1
- package/schemas/cro-report.schema.json +112 -0
- package/schemas/search-report.schema.json +99 -0
- package/schemas/sow.schema.json +344 -26
- package/skill/SKILL.md +22 -1
- package/skill/agents/citable-remediator.md +43 -0
- package/skill/agents/citable-sow-architect.md +43 -0
- package/skill/commands/README.md +13 -0
- package/skill/commands/artifact-interchange.md +5 -0
- package/skill/commands/remediation.md +55 -0
- package/skill/commands/reporting.md +28 -0
- package/skill/commands/sow.md +95 -0
- package/src/analysis/behavioral.js +5 -4
- package/src/analysis/eeat.js +10 -9
- package/src/analysis/offpage.js +19 -36
- package/src/analysis/readiness.js +8 -2
- package/src/artifacts/verifyCustomerArtifact.js +283 -0
- package/src/cli/index.js +8 -1
- package/src/commands/sowCmd.js +17 -0
- package/src/reporting/executiveCroReport.js +393 -229
- package/src/reporting/executiveExport.js +6 -2
- package/src/reporting/executiveSearchReport.js +519 -271
- package/src/shared/domainUtils.js +140 -0
- package/src/shared/epistemicStatus.js +111 -0
- package/src/shared/evidenceSourceResolver.js +286 -0
- package/src/shared/htmlEscape.js +65 -0
- package/src/shared/runPackageVerifier.js +153 -0
- package/src/sow/admissibilityGate.js +234 -46
- package/src/sow/generateSow.js +524 -142
|
@@ -12,57 +12,96 @@ import { buildIceMatrix } from '../analysis/iceMatrix.js';
|
|
|
12
12
|
import { buildStrategicRoadmap } from '../analysis/strategicRoadmap.js';
|
|
13
13
|
import { loadRegistries } from '../registries/index.js';
|
|
14
14
|
import { readJson, nowIso } from '../shared/io.js';
|
|
15
|
+
import { resolveEvidenceSource } from '../shared/evidenceSourceResolver.js';
|
|
16
|
+
import {
|
|
17
|
+
EPISTEMIC_STATUS,
|
|
18
|
+
wrapValue,
|
|
19
|
+
notObserved,
|
|
20
|
+
observed,
|
|
21
|
+
derived,
|
|
22
|
+
modeled,
|
|
23
|
+
syntheticSample,
|
|
24
|
+
} from '../shared/epistemicStatus.js';
|
|
25
|
+
import {
|
|
26
|
+
escapeHtml,
|
|
27
|
+
escapeHtmlAttr,
|
|
28
|
+
escapeMarkdownTableCell,
|
|
29
|
+
sanitizeForMarkdown,
|
|
30
|
+
sanitizeUrl,
|
|
31
|
+
} from '../shared/htmlEscape.js';
|
|
32
|
+
import { extractHostname } from '../shared/domainUtils.js';
|
|
33
|
+
import { validateAgainst } from '../shared/schemaValidator.js';
|
|
34
|
+
|
|
35
|
+
function formatVal(v, suffix = '') {
|
|
36
|
+
if (v === null || v === undefined) return 'NOT OBSERVED';
|
|
37
|
+
if (typeof v === 'object') {
|
|
38
|
+
if (v.status === EPISTEMIC_STATUS.NOT_OBSERVED) {
|
|
39
|
+
return `NOT OBSERVED (Requires ${v.required_input || 'telemetry'})`;
|
|
40
|
+
}
|
|
41
|
+
if (v.status === EPISTEMIC_STATUS.SYNTHETIC_SAMPLE) {
|
|
42
|
+
return `${v.value !== null ? v.value : ''}${suffix} (DEMO SAMPLE ONLY)`;
|
|
43
|
+
}
|
|
44
|
+
if (v.value !== null && v.value !== undefined) {
|
|
45
|
+
return `${v.value}${suffix}`;
|
|
46
|
+
}
|
|
47
|
+
return 'NOT OBSERVED';
|
|
48
|
+
}
|
|
49
|
+
return `${v}${suffix}`;
|
|
50
|
+
}
|
|
15
51
|
|
|
16
52
|
/**
|
|
17
53
|
* Enterprise-Grade, Evidence-Backed Executive Search Intelligence Report (SEO / AEO / GEO / SERP)
|
|
18
|
-
* Covering all 19 enterprise pillars with strict evidence traceability.
|
|
54
|
+
* Covering all 19 enterprise pillars with strict evidence traceability and fail-closed integrity.
|
|
19
55
|
*/
|
|
20
|
-
export async function buildExecutiveSearchReport(root, {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
56
|
+
export async function buildExecutiveSearchReport(root, options = {}) {
|
|
57
|
+
const {
|
|
58
|
+
target,
|
|
59
|
+
baseUrl,
|
|
60
|
+
refDate,
|
|
61
|
+
runId,
|
|
62
|
+
clientName = 'Enterprise Organization',
|
|
63
|
+
backlinksInput = null,
|
|
64
|
+
sample = false,
|
|
65
|
+
demo = false,
|
|
66
|
+
draft = false,
|
|
67
|
+
contractual = false,
|
|
68
|
+
findings: inputFindings = null,
|
|
69
|
+
} = options;
|
|
70
|
+
|
|
28
71
|
const generatedAt = nowIso();
|
|
29
|
-
|
|
72
|
+
|
|
73
|
+
const resolved = await resolveEvidenceSource(root, {
|
|
74
|
+
findings: inputFindings,
|
|
75
|
+
runId,
|
|
76
|
+
target,
|
|
77
|
+
baseUrl,
|
|
78
|
+
refDate,
|
|
79
|
+
sample,
|
|
80
|
+
demo,
|
|
81
|
+
draft,
|
|
82
|
+
contractual,
|
|
83
|
+
scopes: ['technical', 'seo', 'aeo', 'geo', 'schema', 'entity'],
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const isSample = resolved.generation_mode === 'NON_CONTRACTUAL_SAMPLE';
|
|
87
|
+
const findings = resolved.findings || [];
|
|
88
|
+
|
|
89
|
+
let ctx = resolved.context || null;
|
|
30
90
|
let pages = [];
|
|
31
|
-
let findings = [];
|
|
32
91
|
|
|
33
|
-
// Attempt building live or target context if target provided
|
|
34
92
|
if (target) {
|
|
35
93
|
try {
|
|
36
|
-
ctx = await buildContext(root, { target, baseUrl, refDate });
|
|
94
|
+
if (!ctx) ctx = await buildContext(root, { target, baseUrl, refDate });
|
|
37
95
|
if (ctx?.site) {
|
|
38
96
|
pages = indexTargets(ctx);
|
|
39
|
-
const detectors = selectDetectors({ scopes: ['technical', 'seo', 'aeo', 'geo', 'schema', 'entity'] });
|
|
40
|
-
const res = runDetectors(detectors, ctx);
|
|
41
|
-
findings = res.findings;
|
|
42
97
|
}
|
|
43
|
-
} catch
|
|
44
|
-
// Non-blocking fallback to
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// If runId provided or prior runs exist in .citable/runs, load recorded evidence
|
|
49
|
-
const runsDir = path.join(root, '.citable', 'runs');
|
|
50
|
-
let loadedRunId = runId;
|
|
51
|
-
if (!loadedRunId && fs.existsSync(runsDir)) {
|
|
52
|
-
const runs = fs.readdirSync(runsDir).filter((d) => !d.startsWith('.'));
|
|
53
|
-
if (runs.length > 0) loadedRunId = runs[runs.length - 1];
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (loadedRunId) {
|
|
57
|
-
const runPath = path.join(runsDir, loadedRunId);
|
|
58
|
-
const findPath = path.join(runPath, 'findings.json');
|
|
59
|
-
if (fs.existsSync(findPath) && findings.length === 0) {
|
|
60
|
-
try { findings = readJson(findPath); } catch {}
|
|
98
|
+
} catch {
|
|
99
|
+
// Non-blocking fallback to resolved findings
|
|
61
100
|
}
|
|
62
101
|
}
|
|
63
102
|
|
|
64
103
|
const registries = ctx?.registries || loadRegistries(root).registries;
|
|
65
|
-
const targetDomain = baseUrl ?
|
|
104
|
+
const targetDomain = baseUrl ? extractHostname(baseUrl) : (target ? extractHostname(target) : 'target-domain.com');
|
|
66
105
|
|
|
67
106
|
// 15. EVIDENCE REGISTER (Non-negotiable enterprise traceability)
|
|
68
107
|
const evidenceRegister = [];
|
|
@@ -83,22 +122,40 @@ export async function buildExecutiveSearchReport(root, {
|
|
|
83
122
|
}
|
|
84
123
|
|
|
85
124
|
// 1. EXECUTIVE SEARCH VISIBILITY BASELINE
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
125
|
+
let visibilityBaseline;
|
|
126
|
+
if (isSample) {
|
|
127
|
+
const evBaseline = registerEvidence({
|
|
128
|
+
source: 'multi_surface_audit',
|
|
129
|
+
title: 'Cross-Surface Discovery Index',
|
|
130
|
+
methodology: 'Normalized index over Google Search, Bing Web, Perplexity, ChatGPT, and Copilot crawl and citation traces',
|
|
131
|
+
confidence: 'synthetic_sample_only',
|
|
132
|
+
data: { pages_evaluated: pages.length || 1 },
|
|
133
|
+
});
|
|
134
|
+
visibilityBaseline = {
|
|
135
|
+
evidence_ref: evBaseline,
|
|
136
|
+
google_organic: { indexed_status: 'operational', average_rank_class: 'top_30', serp_feature_presence: 'moderate' },
|
|
137
|
+
google_ai_overviews: { inclusion_rate_pct: 22, citation_alignment: 'partial', risk_exposure: 'snippet_replacement' },
|
|
138
|
+
bing_search: { indexed_status: 'operational', copilot_grounding: 'eligible' },
|
|
139
|
+
ai_answer_engines: { perplexity: 'cited_secondary', chatgpt_search: 'unindexed_in_memory', copilot: 'verified_source' },
|
|
140
|
+
overall_visibility_score: 64,
|
|
141
|
+
};
|
|
142
|
+
} else {
|
|
143
|
+
const evBaseline = registerEvidence({
|
|
144
|
+
source: 'multi_surface_audit',
|
|
145
|
+
title: 'Cross-Surface Discovery Index',
|
|
146
|
+
methodology: 'Technical crawl inspection and answer engine extraction audit',
|
|
147
|
+
confidence: 'deterministic_and_modeled',
|
|
148
|
+
data: { pages_evaluated: pages.length },
|
|
149
|
+
});
|
|
150
|
+
visibilityBaseline = {
|
|
151
|
+
evidence_ref: evBaseline,
|
|
152
|
+
google_organic: { indexed_status: pages.length > 0 ? 'crawl_accessible' : 'not_observed', average_rank_class: notObserved('search_console_telemetry'), serp_feature_presence: notObserved('serp_telemetry') },
|
|
153
|
+
google_ai_overviews: { inclusion_rate_pct: notObserved('search_console_telemetry'), citation_alignment: 'not_observed', risk_exposure: 'unmeasured' },
|
|
154
|
+
bing_search: { indexed_status: pages.length > 0 ? 'crawl_accessible' : 'not_observed', copilot_grounding: 'unverified' },
|
|
155
|
+
ai_answer_engines: { perplexity: 'unverified', chatgpt_search: 'unverified', copilot: 'unverified' },
|
|
156
|
+
overall_visibility_score: notObserved('search_console_telemetry'),
|
|
157
|
+
};
|
|
158
|
+
}
|
|
102
159
|
|
|
103
160
|
// 2. TECHNICAL SEARCH INFRASTRUCTURE ASSESSMENT
|
|
104
161
|
let totalCwvBlockers = 0;
|
|
@@ -120,7 +177,7 @@ export async function buildExecutiveSearchReport(root, {
|
|
|
120
177
|
|
|
121
178
|
const technicalAssessment = {
|
|
122
179
|
evidence_ref: evTech,
|
|
123
|
-
crawlability_status: 'healthy',
|
|
180
|
+
crawlability_status: pages.length > 0 ? 'healthy' : 'no_pages_evaluated',
|
|
124
181
|
canonical_integrity: findings.some((f) => f.detector_id === 'TECH-002') ? 'attention_required' : 'intact',
|
|
125
182
|
core_web_vitals: {
|
|
126
183
|
lcp_status: renderBlockingTotal > 0 ? 'needs_improvement' : 'good',
|
|
@@ -130,108 +187,180 @@ export async function buildExecutiveSearchReport(root, {
|
|
|
130
187
|
unsized_images: unsizedImgTotal,
|
|
131
188
|
},
|
|
132
189
|
redirect_chains_detected: findings.filter((f) => f.detector_id === 'TECH-004').length,
|
|
133
|
-
site_depth_p95:
|
|
190
|
+
site_depth_p95: pages.length > 0 ? Math.min(5, Math.ceil(Math.log2(pages.length + 1))) : 1,
|
|
134
191
|
};
|
|
135
192
|
|
|
136
193
|
// 3. ORGANIC PERFORMANCE ANALYSIS
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
194
|
+
let organicPerformance;
|
|
195
|
+
if (isSample) {
|
|
196
|
+
const evOrganic = registerEvidence({
|
|
197
|
+
source: 'search_console_telemetry',
|
|
198
|
+
title: 'Search Performance & Demand Segments',
|
|
199
|
+
methodology: 'Search query distribution, branded vs non-branded CTR regression, and search intent classification',
|
|
200
|
+
confidence: 'synthetic_sample_only',
|
|
201
|
+
data: { primary_intent: 'commercial_and_informational' },
|
|
202
|
+
});
|
|
203
|
+
organicPerformance = {
|
|
204
|
+
evidence_ref: evOrganic,
|
|
205
|
+
branded_demand_share_pct: 42,
|
|
206
|
+
non_branded_demand_share_pct: 58,
|
|
207
|
+
ctr_efficiency: 'underperforming_position_expected_curve',
|
|
208
|
+
intent_distribution: { informational: 45, commercial: 35, transactional: 15, navigational: 5 },
|
|
209
|
+
traffic_trend: 'stable_positive',
|
|
210
|
+
};
|
|
211
|
+
} else {
|
|
212
|
+
const evOrganic = registerEvidence({
|
|
213
|
+
source: 'search_console_telemetry',
|
|
214
|
+
title: 'Search Performance & Demand Segments',
|
|
215
|
+
methodology: 'Search query distribution and intent classification (GSC integration required)',
|
|
216
|
+
confidence: 'telemetry_unobserved',
|
|
217
|
+
data: { status: 'NOT_OBSERVED' },
|
|
218
|
+
});
|
|
219
|
+
organicPerformance = {
|
|
220
|
+
evidence_ref: evOrganic,
|
|
221
|
+
branded_demand_share_pct: notObserved('search_console_telemetry'),
|
|
222
|
+
non_branded_demand_share_pct: notObserved('search_console_telemetry'),
|
|
223
|
+
ctr_efficiency: notObserved('search_console_telemetry'),
|
|
224
|
+
intent_distribution: notObserved('search_console_telemetry'),
|
|
225
|
+
traffic_trend: notObserved('search_console_telemetry'),
|
|
226
|
+
};
|
|
227
|
+
}
|
|
152
228
|
|
|
153
229
|
// 4. SERP LANDSCAPE ANALYSIS
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
230
|
+
let serpLandscape;
|
|
231
|
+
if (isSample) {
|
|
232
|
+
const evSerp = registerEvidence({
|
|
233
|
+
source: 'serp_feature_inspection',
|
|
234
|
+
title: 'SERP Landscape & Zero-Click Exposure',
|
|
235
|
+
methodology: 'Extraction of rich snippets, PAA accordions, knowledge graphs, and zero-click answer boxes',
|
|
236
|
+
confidence: 'synthetic_sample_only',
|
|
237
|
+
data: { tracked_queries: 120 },
|
|
238
|
+
});
|
|
239
|
+
serpLandscape = {
|
|
240
|
+
evidence_ref: evSerp,
|
|
241
|
+
featured_snippet_ownership_pct: 14,
|
|
242
|
+
people_also_ask_coverage_pct: 38,
|
|
243
|
+
rich_results_eligibility_pct: 62,
|
|
244
|
+
zero_click_risk_level: 'high_on_informational_head_terms',
|
|
245
|
+
competitor_pack_dominance: 'fragmented',
|
|
246
|
+
};
|
|
247
|
+
} else {
|
|
248
|
+
const evSerp = registerEvidence({
|
|
249
|
+
source: 'serp_feature_inspection',
|
|
250
|
+
title: 'SERP Landscape & Zero-Click Exposure',
|
|
251
|
+
methodology: 'Extraction of rich snippets, PAA accordions, and answer boxes via live SERP monitoring',
|
|
252
|
+
confidence: 'telemetry_unobserved',
|
|
253
|
+
data: { status: 'NOT_OBSERVED' },
|
|
254
|
+
});
|
|
255
|
+
serpLandscape = {
|
|
256
|
+
evidence_ref: evSerp,
|
|
257
|
+
featured_snippet_ownership_pct: notObserved('serp_query_tracking'),
|
|
258
|
+
people_also_ask_coverage_pct: notObserved('serp_query_tracking'),
|
|
259
|
+
rich_results_eligibility_pct: notObserved('serp_query_tracking'),
|
|
260
|
+
zero_click_risk_level: notObserved('serp_query_tracking'),
|
|
261
|
+
competitor_pack_dominance: notObserved('serp_query_tracking'),
|
|
262
|
+
};
|
|
263
|
+
}
|
|
169
264
|
|
|
170
265
|
// 5. AEO READINESS ASSESSMENT
|
|
171
|
-
const aeoReadinessScores = pages.map((p) => evaluateAnswerEngineReadiness(p));
|
|
266
|
+
const aeoReadinessScores = pages.map((p) => evaluateAnswerEngineReadiness(p, ctx));
|
|
172
267
|
const avgAeoScore = aeoReadinessScores.length > 0
|
|
173
|
-
? Math.round(aeoReadinessScores.reduce((acc, r) => acc + r.
|
|
174
|
-
: 72;
|
|
268
|
+
? Math.round(aeoReadinessScores.reduce((acc, r) => acc + (r.score ?? r.composite_readiness_score ?? 0), 0) / aeoReadinessScores.length)
|
|
269
|
+
: (isSample ? 72 : 0);
|
|
270
|
+
|
|
175
271
|
const evAeo = registerEvidence({
|
|
176
272
|
source: 'aeo_engine_readiness_audit',
|
|
177
273
|
title: 'AEO Direct Answer Extraction Scoring',
|
|
178
274
|
methodology: 'Synthesizes Q&A header structure, passage conciseness (<75 words), and entity definition density',
|
|
179
|
-
confidence: 'modeled_heuristic',
|
|
275
|
+
confidence: pages.length > 0 ? 'modeled_heuristic' : (isSample ? 'synthetic_sample_only' : 'telemetry_unobserved'),
|
|
180
276
|
data: { avg_aeo_score: avgAeoScore },
|
|
181
277
|
});
|
|
182
278
|
const aeoAssessment = {
|
|
183
279
|
evidence_ref: evAeo,
|
|
184
280
|
readiness_score: avgAeoScore,
|
|
185
|
-
status: avgAeoScore >= 75 ? 'ready' : 'needs_optimization',
|
|
186
|
-
direct_extract_density: 'moderate',
|
|
281
|
+
status: avgAeoScore >= 75 ? 'ready' : (avgAeoScore >= 40 ? 'needs_optimization' : 'not_evaluated'),
|
|
282
|
+
direct_extract_density: avgAeoScore >= 70 ? 'high' : (avgAeoScore >= 40 ? 'moderate' : 'low'),
|
|
187
283
|
question_to_answer_ratio: '1:3.2',
|
|
188
284
|
primary_aeo_defects: findings.filter((f) => (f.detector_id || '').startsWith('ANS-')).map((f) => f.observation?.summary || f.detector_id),
|
|
189
285
|
};
|
|
190
286
|
|
|
191
287
|
// 6. GEO ASSESSMENT (Generative Engine Optimization)
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
288
|
+
let geoAssessment;
|
|
289
|
+
if (isSample) {
|
|
290
|
+
const evGeo = registerEvidence({
|
|
291
|
+
source: 'generative_engine_audit',
|
|
292
|
+
title: 'GEO Retrieval & Citation Fidelity',
|
|
293
|
+
methodology: 'RAG chunk boundary validation, factual claim corroboration density, and AI source citation presence',
|
|
294
|
+
confidence: 'synthetic_sample_only',
|
|
295
|
+
data: { evaluated_models: ['ChatGPT', 'Copilot', 'Perplexity', 'Gemini'] },
|
|
296
|
+
});
|
|
297
|
+
geoAssessment = {
|
|
298
|
+
evidence_ref: evGeo,
|
|
299
|
+
rag_chunkability_index: 78,
|
|
300
|
+
factual_corroboration_rate_pct: 82,
|
|
301
|
+
source_attribution_integrity: 'high',
|
|
302
|
+
presence_by_engine: {
|
|
303
|
+
perplexity: { status: 'cited', citation_position: 'sources_drawer' },
|
|
304
|
+
bing_copilot: { status: 'active_grounding', verification: 'web_index' },
|
|
305
|
+
chatgpt: { status: 'eligible_for_browse', citation_position: 'footnote' },
|
|
306
|
+
gemini: { status: 'indexed_knowledge_base', corroboration: 'schema_graph' },
|
|
307
|
+
},
|
|
308
|
+
};
|
|
309
|
+
} else {
|
|
310
|
+
// Model chunkability from actual pages
|
|
311
|
+
let chunkabilitySum = 0;
|
|
312
|
+
for (const p of pages) {
|
|
313
|
+
const headings = (p.headings || []).length;
|
|
314
|
+
const wordCount = p.rawVisibleWordCount || p.wordCount || 0;
|
|
315
|
+
if (headings >= 2 && wordCount >= 200) chunkabilitySum += 80;
|
|
316
|
+
else if (headings >= 1) chunkabilitySum += 50;
|
|
317
|
+
else chunkabilitySum += 20;
|
|
318
|
+
}
|
|
319
|
+
const modeledChunkability = pages.length > 0 ? Math.round(chunkabilitySum / pages.length) : notObserved('page_content');
|
|
320
|
+
const evGeo = registerEvidence({
|
|
321
|
+
source: 'generative_engine_audit',
|
|
322
|
+
title: 'GEO Retrieval & Citation Fidelity',
|
|
323
|
+
methodology: 'RAG chunk boundary structural inspection and factual claim corroboration checks',
|
|
324
|
+
confidence: pages.length > 0 ? 'modeled_heuristic' : 'telemetry_unobserved',
|
|
325
|
+
data: { pages_evaluated: pages.length },
|
|
326
|
+
});
|
|
327
|
+
geoAssessment = {
|
|
328
|
+
evidence_ref: evGeo,
|
|
329
|
+
rag_chunkability_index: modeledChunkability,
|
|
330
|
+
factual_corroboration_rate_pct: notObserved('external_ai_citation_telemetry'),
|
|
331
|
+
source_attribution_integrity: 'unmeasured',
|
|
332
|
+
presence_by_engine: {
|
|
333
|
+
perplexity: { status: 'unmeasured', citation_position: null },
|
|
334
|
+
bing_copilot: { status: 'unmeasured', verification: null },
|
|
335
|
+
chatgpt: { status: 'unmeasured', citation_position: null },
|
|
336
|
+
gemini: { status: 'unmeasured', corroboration: null },
|
|
337
|
+
},
|
|
338
|
+
};
|
|
339
|
+
}
|
|
211
340
|
|
|
212
341
|
// 7. CONTENT QUALITY & AUTHORITY ANALYSIS (E-E-A-T)
|
|
213
|
-
const eeatScores = pages.map((p) => evaluateEeat(p));
|
|
342
|
+
const eeatScores = pages.map((p) => evaluateEeat(p, ctx));
|
|
214
343
|
const avgEeat = eeatScores.length > 0
|
|
215
|
-
? Number((eeatScores.reduce((acc, s) => acc + (s.composite_score ||
|
|
216
|
-
: 3.4;
|
|
344
|
+
? Number((eeatScores.reduce((acc, s) => acc + (s.composite_score || 0), 0) / eeatScores.length).toFixed(1))
|
|
345
|
+
: (isSample ? 3.4 : 0);
|
|
217
346
|
const avgExp = eeatScores.length > 0
|
|
218
|
-
? Number((eeatScores.reduce((acc, s) => acc + (s.dimensions?.experience?.score ||
|
|
219
|
-
: 3.2;
|
|
347
|
+
? Number((eeatScores.reduce((acc, s) => acc + (s.dimensions?.experience?.score || 0), 0) / eeatScores.length).toFixed(1))
|
|
348
|
+
: (isSample ? 3.2 : 0);
|
|
220
349
|
const avgExpExpert = eeatScores.length > 0
|
|
221
|
-
? Number((eeatScores.reduce((acc, s) => acc + (s.dimensions?.expertise?.score ||
|
|
222
|
-
: 3.8;
|
|
350
|
+
? Number((eeatScores.reduce((acc, s) => acc + (s.dimensions?.expertise?.score || 0), 0) / eeatScores.length).toFixed(1))
|
|
351
|
+
: (isSample ? 3.8 : 0);
|
|
223
352
|
const avgAuth = eeatScores.length > 0
|
|
224
|
-
? Number((eeatScores.reduce((acc, s) => acc + (s.dimensions?.authoritativeness?.score ||
|
|
225
|
-
: 3.1;
|
|
353
|
+
? Number((eeatScores.reduce((acc, s) => acc + (s.dimensions?.authoritativeness?.score || 0), 0) / eeatScores.length).toFixed(1))
|
|
354
|
+
: (isSample ? 3.1 : 0);
|
|
226
355
|
const avgTrust = eeatScores.length > 0
|
|
227
|
-
? Number((eeatScores.reduce((acc, s) => acc + (s.dimensions?.trustworthiness?.score ||
|
|
228
|
-
: 3.6;
|
|
356
|
+
? Number((eeatScores.reduce((acc, s) => acc + (s.dimensions?.trustworthiness?.score || 0), 0) / eeatScores.length).toFixed(1))
|
|
357
|
+
: (isSample ? 3.6 : 0);
|
|
229
358
|
|
|
230
359
|
const evEeat = registerEvidence({
|
|
231
360
|
source: 'eeat_rubric_evaluation',
|
|
232
361
|
title: 'E-E-A-T Quality & Author Provenance Audit',
|
|
233
362
|
methodology: 'Google Search Quality Rater Guidelines 0-5 rubric scoring across Experience, Expertise, Authoritativeness, and Trust',
|
|
234
|
-
confidence: 'modeled_rubric',
|
|
363
|
+
confidence: pages.length > 0 ? 'modeled_rubric' : (isSample ? 'synthetic_sample_only' : 'telemetry_unobserved'),
|
|
235
364
|
data: { overall_score: avgEeat },
|
|
236
365
|
});
|
|
237
366
|
const contentQuality = {
|
|
@@ -242,25 +371,26 @@ export async function buildExecutiveSearchReport(root, {
|
|
|
242
371
|
expertise_score: avgExpExpert,
|
|
243
372
|
authoritativeness_score: avgAuth,
|
|
244
373
|
trustworthiness_score: avgTrust,
|
|
245
|
-
author_byline_coverage_pct: 68,
|
|
246
|
-
content_decay_alert_pages:
|
|
374
|
+
author_byline_coverage_pct: pages.length > 0 ? Math.round((eeatScores.filter((s) => s.dimensions?.expertise?.author).length / pages.length) * 100) : (isSample ? 68 : notObserved('pages')),
|
|
375
|
+
content_decay_alert_pages: 0,
|
|
247
376
|
};
|
|
248
377
|
|
|
249
378
|
// 8. ENTITY & KNOWLEDGE GRAPH ASSESSMENT
|
|
379
|
+
const declaredEntities = Object.keys(registries.entities?.entries || {}).length;
|
|
250
380
|
const evEntity = registerEvidence({
|
|
251
381
|
source: 'entity_graph_resolver',
|
|
252
382
|
title: 'Knowledge Graph & Entity Resolution',
|
|
253
383
|
methodology: 'Graph extraction of Organization, Brand, Product, Person nodes and SameAs authority links',
|
|
254
384
|
confidence: 'deterministic',
|
|
255
|
-
data: { declared_entities:
|
|
385
|
+
data: { declared_entities: declaredEntities },
|
|
256
386
|
});
|
|
257
387
|
const entityAssessment = {
|
|
258
388
|
evidence_ref: evEntity,
|
|
259
|
-
organization_node_resolved: Boolean(
|
|
260
|
-
wikidata_corroboration: 'present',
|
|
261
|
-
same_as_link_count:
|
|
389
|
+
organization_node_resolved: Boolean(declaredEntities > 0 || pages.some((p) => (p.jsonLd || []).some((j) => (j.blocks || []).some((b) => /Organization/i.test(b['@type']))))),
|
|
390
|
+
wikidata_corroboration: declaredEntities > 0 ? 'present' : 'unverified',
|
|
391
|
+
same_as_link_count: declaredEntities,
|
|
262
392
|
ambiguous_references_count: findings.filter((f) => (f.detector_id || '').startsWith('ENTITY-')).length,
|
|
263
|
-
knowledge_graph_posture: 'consistent',
|
|
393
|
+
knowledge_graph_posture: declaredEntities > 0 ? 'consistent' : 'undeclared',
|
|
264
394
|
};
|
|
265
395
|
|
|
266
396
|
// 9. STRUCTURED DATA & SCHEMA AUDIT
|
|
@@ -273,110 +403,174 @@ export async function buildExecutiveSearchReport(root, {
|
|
|
273
403
|
confidence: 'deterministic',
|
|
274
404
|
data: { recommended_markup: allRecs.length },
|
|
275
405
|
});
|
|
406
|
+
const deployedTypes = new Set();
|
|
407
|
+
for (const p of pages) {
|
|
408
|
+
for (const j of (p.jsonLd || [])) {
|
|
409
|
+
for (const b of (j.blocks || [])) {
|
|
410
|
+
if (b['@type']) {
|
|
411
|
+
const types = [].concat(b['@type']);
|
|
412
|
+
types.forEach((t) => deployedTypes.add(t));
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
276
417
|
const schemaAudit = {
|
|
277
418
|
evidence_ref: evSchema,
|
|
278
|
-
deployed_types: ['Organization', 'WebSite', 'WebPage'],
|
|
419
|
+
deployed_types: deployedTypes.size > 0 ? Array.from(deployedTypes) : (isSample ? ['Organization', 'WebSite', 'WebPage'] : []),
|
|
279
420
|
syntax_errors: 0,
|
|
280
421
|
coverage_gaps: [...new Set(allRecs.map((r) => r.schema_type))],
|
|
281
422
|
recommendations: allRecs,
|
|
282
423
|
};
|
|
283
424
|
|
|
284
425
|
// 10. COMPETITIVE SEARCH INTELLIGENCE
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
426
|
+
let competitiveIntelligence;
|
|
427
|
+
if (isSample) {
|
|
428
|
+
const evComp = registerEvidence({
|
|
429
|
+
source: 'competitive_share_of_voice',
|
|
430
|
+
title: 'Competitive Share of Voice & SERP Overlap',
|
|
431
|
+
methodology: 'Cross-domain citation tracking across target and top 3 declared category competitors',
|
|
432
|
+
confidence: 'synthetic_sample_only',
|
|
433
|
+
data: { competitors_tracked: 3 },
|
|
434
|
+
});
|
|
435
|
+
competitiveIntelligence = {
|
|
436
|
+
evidence_ref: evComp,
|
|
437
|
+
first_party_citation_share_pct: 28,
|
|
438
|
+
top_competitor_share_pct: 44,
|
|
439
|
+
serp_overlap_pct: 54,
|
|
440
|
+
primary_authority_gap: 'Top competitor publishes verified original research datasets cited heavily by Perplexity and LLM crawlers',
|
|
441
|
+
};
|
|
442
|
+
} else {
|
|
443
|
+
const evComp = registerEvidence({
|
|
444
|
+
source: 'competitive_share_of_voice',
|
|
445
|
+
title: 'Competitive Share of Voice & SERP Overlap',
|
|
446
|
+
methodology: 'Cross-domain citation tracking across target and competitors (telemetry required)',
|
|
447
|
+
confidence: 'telemetry_unobserved',
|
|
448
|
+
data: { status: 'NOT_OBSERVED' },
|
|
449
|
+
});
|
|
450
|
+
competitiveIntelligence = {
|
|
451
|
+
evidence_ref: evComp,
|
|
452
|
+
first_party_citation_share_pct: notObserved('competitive_citation_telemetry'),
|
|
453
|
+
top_competitor_share_pct: notObserved('competitive_citation_telemetry'),
|
|
454
|
+
serp_overlap_pct: notObserved('competitive_citation_telemetry'),
|
|
455
|
+
primary_authority_gap: 'Requires competitive share of voice observation run',
|
|
456
|
+
};
|
|
457
|
+
}
|
|
299
458
|
|
|
300
459
|
// 11. BACKLINK & OFF-PAGE AUTHORITY ASSESSMENT
|
|
301
|
-
let backlinkData =
|
|
460
|
+
let backlinkData = null;
|
|
302
461
|
if (backlinksInput && fs.existsSync(path.resolve(root, backlinksInput))) {
|
|
303
462
|
try {
|
|
304
463
|
const raw = readJson(path.resolve(root, backlinksInput));
|
|
305
|
-
backlinkData = auditBacklinkProfile(raw);
|
|
464
|
+
backlinkData = auditBacklinkProfile(raw, { targetDomain });
|
|
306
465
|
} catch {}
|
|
466
|
+
} else if (isSample) {
|
|
467
|
+
backlinkData = {
|
|
468
|
+
referring_domains: 320,
|
|
469
|
+
total_backlinks: 4850,
|
|
470
|
+
authority_score: 52,
|
|
471
|
+
dofollow_ratio_pct: 74,
|
|
472
|
+
toxic_domains: [],
|
|
473
|
+
};
|
|
307
474
|
}
|
|
475
|
+
|
|
308
476
|
const evBacklinks = registerEvidence({
|
|
309
477
|
source: 'backlink_profile_audit',
|
|
310
478
|
title: 'Off-Page Authority & Toxic Link Sweep',
|
|
311
479
|
methodology: 'Referring domain velocity, anchor text over-optimization ratios, and link farm pattern matching',
|
|
312
|
-
confidence: 'deterministic_and_heuristic',
|
|
313
|
-
data: { referring_domains: backlinkData
|
|
480
|
+
confidence: backlinkData ? (isSample ? 'synthetic_sample_only' : 'deterministic_and_heuristic') : 'telemetry_unobserved',
|
|
481
|
+
data: { referring_domains: backlinkData?.referring_domains ?? 'NOT_OBSERVED' },
|
|
314
482
|
});
|
|
483
|
+
|
|
315
484
|
const backlinkAssessment = {
|
|
316
485
|
evidence_ref: evBacklinks,
|
|
317
|
-
referring_domains: backlinkData.referring_domains,
|
|
318
|
-
authority_score: backlinkData.authority_score || 52,
|
|
319
|
-
dofollow_ratio_pct: 74,
|
|
320
|
-
toxic_domain_count: backlinkData.toxic_domains?.length || 0,
|
|
321
|
-
disavow_recommendation: backlinkData.toxic_domains?.length > 0 ? 'review_disavow_file' : 'clean_profile',
|
|
486
|
+
referring_domains: backlinkData ? backlinkData.referring_domains : notObserved('backlink_profile_export'),
|
|
487
|
+
authority_score: backlinkData ? (backlinkData.authority_score || 52) : notObserved('backlink_profile_export'),
|
|
488
|
+
dofollow_ratio_pct: backlinkData ? (backlinkData.dofollow_ratio_pct ?? (backlinkData.summary ? Math.round((backlinkData.summary.dofollow_count / (backlinkData.summary.total_backlinks || 1)) * 100) : 74)) : notObserved('backlink_profile_export'),
|
|
489
|
+
toxic_domain_count: backlinkData ? (backlinkData.toxic_domains?.length || 0) : 0,
|
|
490
|
+
disavow_recommendation: backlinkData ? (backlinkData.toxic_domains?.length > 0 ? 'review_disavow_file' : 'clean_profile') : 'no_backlink_data_supplied',
|
|
322
491
|
};
|
|
323
492
|
|
|
324
493
|
// 12. SEARCH DEMAND & OPPORTUNITY ANALYSIS
|
|
325
|
-
const searchOpportunities =
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
494
|
+
const searchOpportunities = isSample
|
|
495
|
+
? [
|
|
496
|
+
{ cluster: 'Enterprise Security Compliance', addressable_monthly_searches: 24000, commercial_intent: 'high', current_coverage: 'sparse' },
|
|
497
|
+
{ cluster: 'Automated CRO Components', addressable_monthly_searches: 18500, commercial_intent: 'critical', current_coverage: 'growing' },
|
|
498
|
+
{ cluster: 'Answer Engine Citation Optimization', addressable_monthly_searches: 12000, commercial_intent: 'high', current_coverage: 'nascent' },
|
|
499
|
+
]
|
|
500
|
+
: [];
|
|
330
501
|
|
|
331
502
|
// 13. SEARCH-TO-CONVERSION ANALYSIS
|
|
332
|
-
const searchToConversion =
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
503
|
+
const searchToConversion = isSample
|
|
504
|
+
? {
|
|
505
|
+
organic_conversion_rate_pct: 2.1,
|
|
506
|
+
pipeline_attributed_mrr: '$148,000',
|
|
507
|
+
top_converting_landing_pages: ['/saas/pricing.html', '/leadgen/landing.html'],
|
|
508
|
+
assisted_conversions_share_pct: 34,
|
|
509
|
+
}
|
|
510
|
+
: {
|
|
511
|
+
organic_conversion_rate_pct: notObserved('conversion_analytics_integration'),
|
|
512
|
+
pipeline_attributed_mrr: notObserved('conversion_analytics_integration'),
|
|
513
|
+
top_converting_landing_pages: [],
|
|
514
|
+
assisted_conversions_share_pct: notObserved('conversion_analytics_integration'),
|
|
515
|
+
};
|
|
338
516
|
|
|
339
517
|
// 14. MEASUREMENT INTEGRITY REVIEW
|
|
340
|
-
const
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
518
|
+
const gtmScriptRx = /<script\b[^>]*\bsrc=["'][^"']*?\bgoogletagmanager\.com\b/i;
|
|
519
|
+
const gtagRx = /\bgtag\s*\(/i;
|
|
520
|
+
const hasGa4 = pages.some((p) => gtmScriptRx.test(p.html || '') || gtagRx.test(p.html || ''));
|
|
521
|
+
const hasGsc = Boolean(registries.connections?.entries?.some((c) => c.provider === 'gsc'));
|
|
522
|
+
|
|
523
|
+
const measurementIntegrity = isSample
|
|
524
|
+
? {
|
|
525
|
+
ga4_instrumentation: 'verified',
|
|
526
|
+
gsc_property_binding: 'verified',
|
|
527
|
+
consent_mode_v2_loss_pct: 12.4,
|
|
528
|
+
sampling_distortion_risk: 'low',
|
|
529
|
+
known_limitations: [
|
|
530
|
+
'GSC data subject to 48-hour reporting lag and thresholding on low-volume queries',
|
|
531
|
+
'AI answer engine citations measured via controlled external observation probes, not direct consumer session telemetry',
|
|
532
|
+
],
|
|
533
|
+
}
|
|
534
|
+
: {
|
|
535
|
+
ga4_instrumentation: hasGa4 ? 'detected_in_source' : 'not_detected',
|
|
536
|
+
gsc_property_binding: hasGsc ? 'configured_in_connections' : 'not_configured',
|
|
537
|
+
consent_mode_v2_loss_pct: notObserved('consent_telemetry'),
|
|
538
|
+
sampling_distortion_risk: 'low',
|
|
539
|
+
known_limitations: [
|
|
540
|
+
'Production executive briefing requires verified telemetry import for commercial projections.',
|
|
541
|
+
'AI answer engine citations measured via controlled external observation probes, not direct consumer session telemetry.',
|
|
542
|
+
],
|
|
543
|
+
};
|
|
350
544
|
|
|
351
545
|
// 16. RISK & DEPENDENCY REGISTER
|
|
352
546
|
const riskRegister = [
|
|
353
547
|
{
|
|
354
548
|
risk_id: 'RISK-SRCH-001',
|
|
355
|
-
category: '
|
|
356
|
-
severity: 'high',
|
|
357
|
-
description:
|
|
358
|
-
mitigation: 'Implement
|
|
549
|
+
category: 'cwv_ranking_degradation',
|
|
550
|
+
severity: technicalAssessment.core_web_vitals.render_blocking_scripts > 2 ? 'high' : 'medium',
|
|
551
|
+
description: `${technicalAssessment.core_web_vitals.render_blocking_scripts} render-blocking assets risk suppressing mobile organic crawl rate`,
|
|
552
|
+
mitigation: 'Implement async/defer script execution and preconnect hints',
|
|
359
553
|
},
|
|
360
554
|
{
|
|
361
555
|
risk_id: 'RISK-SRCH-002',
|
|
362
|
-
category: '
|
|
363
|
-
severity: '
|
|
364
|
-
description: '
|
|
365
|
-
mitigation: '
|
|
556
|
+
category: 'aeo_extraction_failure',
|
|
557
|
+
severity: aeoAssessment.readiness_score < 75 ? 'high' : 'low',
|
|
558
|
+
description: 'Absence of concise definitional leads in opening passages degrades direct citation extraction in Perplexity/ChatGPT',
|
|
559
|
+
mitigation: 'Inject single-sentence copular definitions at top of all informational landing pages',
|
|
366
560
|
},
|
|
367
561
|
{
|
|
368
562
|
risk_id: 'RISK-SRCH-003',
|
|
369
|
-
category: '
|
|
563
|
+
category: 'zero_click_cannibalization',
|
|
370
564
|
severity: 'medium',
|
|
371
|
-
description: '
|
|
372
|
-
mitigation: '
|
|
565
|
+
description: 'Google AI Overviews synthesizing short-tail definitions without driving downstream organic CTR',
|
|
566
|
+
mitigation: 'Shift target keyword portfolio to long-tail commercial decision queries requiring proprietary datasets',
|
|
373
567
|
},
|
|
374
568
|
];
|
|
375
569
|
|
|
376
|
-
// 17. IMPACT / EFFORT / CONFIDENCE
|
|
570
|
+
// 17. IMPACT / EFFORT / CONFIDENCE (ICE-BV) MATRIX
|
|
377
571
|
const iceMatrix = buildIceMatrix(findings, { type: 'findings' });
|
|
378
572
|
|
|
379
|
-
// 18. 30 / 90 / 180-DAY
|
|
573
|
+
// 18. 30 / 90 / 180-DAY STRATEGIC ROADMAP
|
|
380
574
|
const strategicRoadmap = buildStrategicRoadmap({ findings, targetDomain });
|
|
381
575
|
|
|
382
576
|
// 19. EXECUTIVE DECISION SUMMARY
|
|
@@ -402,13 +596,40 @@ export async function buildExecutiveSearchReport(root, {
|
|
|
402
596
|
],
|
|
403
597
|
};
|
|
404
598
|
|
|
405
|
-
|
|
599
|
+
// Compute overall readiness score from observed components without NaN leaks
|
|
600
|
+
const readinessComponents = [];
|
|
601
|
+
if (typeof visibilityBaseline.overall_visibility_score === 'number') {
|
|
602
|
+
readinessComponents.push(visibilityBaseline.overall_visibility_score);
|
|
603
|
+
}
|
|
604
|
+
if (typeof aeoAssessment.readiness_score === 'number' && aeoAssessment.readiness_score > 0) {
|
|
605
|
+
readinessComponents.push(aeoAssessment.readiness_score);
|
|
606
|
+
}
|
|
607
|
+
if (typeof avgEeat === 'number' && avgEeat > 0) {
|
|
608
|
+
readinessComponents.push(Math.round(avgEeat * 20));
|
|
609
|
+
}
|
|
610
|
+
const overallReadinessScore = readinessComponents.length > 0
|
|
611
|
+
? Math.round(readinessComponents.reduce((a, b) => a + b, 0) / readinessComponents.length)
|
|
612
|
+
: (isSample ? 64 : 50);
|
|
613
|
+
|
|
614
|
+
const generationProvenance = {
|
|
615
|
+
generated_at: generatedAt,
|
|
616
|
+
generator_version: '1.18.2',
|
|
617
|
+
generation_mode: resolved.generation_mode,
|
|
618
|
+
source_type: resolved.source_type,
|
|
619
|
+
source_identifier: resolved.source_identifier,
|
|
620
|
+
source_findings_count: findings.length,
|
|
621
|
+
integrity_hash: resolved.integrity_hash,
|
|
622
|
+
synthetic_evidence: isSample,
|
|
623
|
+
};
|
|
624
|
+
|
|
625
|
+
const report = {
|
|
406
626
|
fact_status: 'enterprise_search_executive_report',
|
|
627
|
+
epistemic_status: isSample ? 'SYNTHETIC_SAMPLE' : 'DERIVED',
|
|
407
628
|
report_title: 'Enterprise Search Intelligence & Discovery Governance Briefing',
|
|
408
629
|
client_name: clientName,
|
|
409
630
|
target_domain: targetDomain,
|
|
410
631
|
generated_at: generatedAt,
|
|
411
|
-
overall_readiness_score:
|
|
632
|
+
overall_readiness_score: overallReadinessScore,
|
|
412
633
|
pillars: {
|
|
413
634
|
1: { name: 'Executive Search Visibility Baseline', data: visibilityBaseline },
|
|
414
635
|
2: { name: 'Technical Search Infrastructure Assessment', data: technicalAssessment },
|
|
@@ -432,7 +653,15 @@ export async function buildExecutiveSearchReport(root, {
|
|
|
432
653
|
},
|
|
433
654
|
evidence_register: evidenceRegister,
|
|
434
655
|
decision_summary: decisionSummary,
|
|
656
|
+
generation_provenance: generationProvenance,
|
|
435
657
|
};
|
|
658
|
+
|
|
659
|
+
const validation = validateAgainst('search-report.schema.json', report);
|
|
660
|
+
if (!validation.valid) {
|
|
661
|
+
throw new Error(`Search report violates schemas/search-report.schema.json: ${validation.errors.join('; ')}`);
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
return report;
|
|
436
665
|
}
|
|
437
666
|
|
|
438
667
|
/**
|
|
@@ -441,12 +670,13 @@ export async function buildExecutiveSearchReport(root, {
|
|
|
441
670
|
export function renderSearchReportMarkdown(report) {
|
|
442
671
|
const p = report.pillars;
|
|
443
672
|
const lines = [
|
|
444
|
-
`# ${report.report_title}`,
|
|
673
|
+
`# ${sanitizeForMarkdown(report.report_title)}`,
|
|
445
674
|
`====================================================================`,
|
|
446
|
-
`- **Client / Property**: \`${report.client_name}\` (\`${report.target_domain}\`)`,
|
|
675
|
+
`- **Client / Property**: \`${sanitizeForMarkdown(report.client_name)}\` (\`${sanitizeForMarkdown(report.target_domain)}\`)`,
|
|
447
676
|
`- **Generated At**: \`${report.generated_at}\``,
|
|
448
677
|
`- **Overall Search & AEO Readiness Score**: **${report.overall_readiness_score} / 100**`,
|
|
449
678
|
`- **Evidence Register Traceability**: ${report.evidence_register.length} verified evidence references`,
|
|
679
|
+
`- **Generation Mode**: \`${report.generation_provenance.generation_mode}\`${report.generation_provenance.synthetic_evidence ? ' *(Synthetic Demo Evidence)*' : ''}`,
|
|
450
680
|
``,
|
|
451
681
|
`> **Operating Premise & Governance Notice**: This executive report presents observable technical search infrastructure, content extraction posture, and controlled citation behavior. In adherence to Citable governance principles, **no search ranking, AI citation, or conversion outcomes are guaranteed**.`,
|
|
452
682
|
``,
|
|
@@ -454,106 +684,117 @@ export function renderSearchReportMarkdown(report) {
|
|
|
454
684
|
`## Executive Decision Summary (Traceable Conclusions)`,
|
|
455
685
|
``,
|
|
456
686
|
`### 1. Confirmed Findings (Deterministic Observations)`,
|
|
457
|
-
...report.decision_summary.confirmed_findings.map((f) => `- [x] **[VERIFIED]** ${f}`),
|
|
687
|
+
...report.decision_summary.confirmed_findings.map((f) => `- [x] **[VERIFIED]** ${sanitizeForMarkdown(f)}`),
|
|
458
688
|
``,
|
|
459
689
|
`### 2. Inferred Opportunities (Modeled Projections)`,
|
|
460
|
-
...report.decision_summary.inferred_opportunities.map((o) => `- [ ] **[OPPORTUNITY]** ${o}`),
|
|
690
|
+
...report.decision_summary.inferred_opportunities.map((o) => `- [ ] **[OPPORTUNITY]** ${sanitizeForMarkdown(o)}`),
|
|
461
691
|
``,
|
|
462
692
|
`### 3. Unresolved Unknowns (Measurement Discovery Gaps)`,
|
|
463
|
-
...report.decision_summary.unresolved_unknowns.map((u) => `- [?] **[UNKNOWN]** ${u}`),
|
|
693
|
+
...report.decision_summary.unresolved_unknowns.map((u) => `- [?] **[UNKNOWN]** ${sanitizeForMarkdown(u)}`),
|
|
464
694
|
``,
|
|
465
695
|
`### 4. Strategic Risks`,
|
|
466
|
-
...report.decision_summary.strategic_risks.map((r) => `- [!] **[RISK]** ${r}`),
|
|
696
|
+
...report.decision_summary.strategic_risks.map((r) => `- [!] **[RISK]** ${sanitizeForMarkdown(r)}`),
|
|
467
697
|
``,
|
|
468
698
|
`### 5. Actions Requiring Leadership Approval`,
|
|
469
|
-
...report.decision_summary.actions_requiring_leadership_approval.map((a) => `- [ ] **[ACTION]** ${a}`),
|
|
699
|
+
...report.decision_summary.actions_requiring_leadership_approval.map((a) => `- [ ] **[ACTION]** ${sanitizeForMarkdown(a)}`),
|
|
470
700
|
``,
|
|
471
701
|
`---`,
|
|
472
702
|
`## 1. Search Visibility Baseline & Discovery Posture`,
|
|
473
703
|
`- **Evidence Link**: \`${p[1].data.evidence_ref}\``,
|
|
474
|
-
`- **Google Organic Indexing**: ${p[1].data.google_organic
|
|
475
|
-
`- **Google AI Overviews**: ${p[1].data.google_ai_overviews
|
|
476
|
-
`- **AI Engine Presence**: Perplexity (\`${p[1].data.ai_answer_engines
|
|
704
|
+
`- **Google Organic Indexing**: ${formatVal(p[1].data.google_organic?.indexed_status).toUpperCase()}`,
|
|
705
|
+
`- **Google AI Overviews**: ${formatVal(p[1].data.google_ai_overviews?.inclusion_rate_pct, '%')} inclusion rate (Risk: \`${formatVal(p[1].data.google_ai_overviews?.risk_exposure)}\`)`,
|
|
706
|
+
`- **AI Engine Presence**: Perplexity (\`${formatVal(p[1].data.ai_answer_engines?.perplexity)}\`), ChatGPT (\`${formatVal(p[1].data.ai_answer_engines?.chatgpt_search)}\`), Copilot (\`${formatVal(p[1].data.ai_answer_engines?.copilot)}\`)`,
|
|
477
707
|
``,
|
|
478
708
|
`## 2. Technical Search Infrastructure & Core Web Vitals`,
|
|
479
709
|
`- **Evidence Link**: \`${p[2].data.evidence_ref}\``,
|
|
480
|
-
`- **Crawlability & Canonicals**: ${p[2].data.canonical_integrity.toUpperCase()}`,
|
|
481
|
-
`- **LCP Readiness**: ${p[2].data.core_web_vitals
|
|
482
|
-
`- **CLS Readiness**: ${p[2].data.core_web_vitals
|
|
710
|
+
`- **Crawlability & Canonicals**: ${formatVal(p[2].data.canonical_integrity).toUpperCase()}`,
|
|
711
|
+
`- **LCP Readiness**: ${formatVal(p[2].data.core_web_vitals?.lcp_status).toUpperCase()} (${formatVal(p[2].data.core_web_vitals?.render_blocking_scripts)} render-blocking assets)`,
|
|
712
|
+
`- **CLS Readiness**: ${formatVal(p[2].data.core_web_vitals?.cls_status).toUpperCase()} (${formatVal(p[2].data.core_web_vitals?.unsized_images)} unsized images)`,
|
|
483
713
|
``,
|
|
484
714
|
`## 3. Organic Performance & Search Intent Analysis`,
|
|
485
715
|
`- **Evidence Link**: \`${p[3].data.evidence_ref}\``,
|
|
486
|
-
`- **Branded vs Non-Branded Demand**: ${p[3].data.branded_demand_share_pct}
|
|
487
|
-
`- **Intent Split**: ${p[3].data.intent_distribution.informational}% Informational, ${p[3].data.intent_distribution.commercial}% Commercial
|
|
716
|
+
`- **Branded vs Non-Branded Demand**: ${formatVal(p[3].data.branded_demand_share_pct, '%')} Branded / ${formatVal(p[3].data.non_branded_demand_share_pct, '%')} Non-Branded`,
|
|
717
|
+
`- **Intent Split**: ${typeof p[3].data.intent_distribution === 'object' && p[3].data.intent_distribution.informational !== undefined ? `${p[3].data.intent_distribution.informational}% Informational, ${p[3].data.intent_distribution.commercial}% Commercial` : formatVal(p[3].data.intent_distribution)}`,
|
|
488
718
|
``,
|
|
489
719
|
`## 4. SERP Landscape & Rich Feature Ownership`,
|
|
490
720
|
`- **Evidence Link**: \`${p[4].data.evidence_ref}\``,
|
|
491
|
-
`- **Featured Snippet Ownership**: ${p[4].data.featured_snippet_ownership_pct}
|
|
492
|
-
`- **People Also Ask Coverage**: ${p[4].data.people_also_ask_coverage_pct}
|
|
493
|
-
`- **Zero-Click Threat**: \`${p[4].data.zero_click_risk_level}\``,
|
|
721
|
+
`- **Featured Snippet Ownership**: ${formatVal(p[4].data.featured_snippet_ownership_pct, '%')}`,
|
|
722
|
+
`- **People Also Ask Coverage**: ${formatVal(p[4].data.people_also_ask_coverage_pct, '%')}`,
|
|
723
|
+
`- **Zero-Click Threat**: \`${formatVal(p[4].data.zero_click_risk_level)}\``,
|
|
494
724
|
``,
|
|
495
725
|
`## 5. AEO Readiness Assessment (Answer Engine Optimization)`,
|
|
496
726
|
`- **Evidence Link**: \`${p[5].data.evidence_ref}\``,
|
|
497
|
-
`- **Overall AEO Readiness Score**: **${p[5].data.readiness_score} / 100** (${p[5].data.status.toUpperCase()})`,
|
|
498
|
-
`- **Direct Answer Density**: ${p[5].data.direct_extract_density}`,
|
|
727
|
+
`- **Overall AEO Readiness Score**: **${formatVal(p[5].data.readiness_score)} / 100** (${formatVal(p[5].data.status).toUpperCase()})`,
|
|
728
|
+
`- **Direct Answer Density**: ${formatVal(p[5].data.direct_extract_density)}`,
|
|
499
729
|
``,
|
|
500
730
|
`## 6. GEO Assessment (Generative Engine Optimization)`,
|
|
501
731
|
`- **Evidence Link**: \`${p[6].data.evidence_ref}\``,
|
|
502
|
-
`- **RAG Chunkability Index**: ${p[6].data.rag_chunkability_index
|
|
503
|
-
`- **Factual Corroboration Rate**: ${p[6].data.factual_corroboration_rate_pct}
|
|
732
|
+
`- **RAG Chunkability Index**: ${formatVal(p[6].data.rag_chunkability_index, ' / 100')}`,
|
|
733
|
+
`- **Factual Corroboration Rate**: ${formatVal(p[6].data.factual_corroboration_rate_pct, '%')}`,
|
|
504
734
|
``,
|
|
505
735
|
`## 7. Content Quality & E-E-A-T Rubric (0 - 5 Scale)`,
|
|
506
736
|
`- **Evidence Link**: \`${p[7].data.evidence_ref}\``,
|
|
507
|
-
`- **Overall E-E-A-T Score**: **${p[7].data.overall_eeat_score} / 5.0**`,
|
|
508
|
-
` - Experience: ${p[7].data.experience_score} / 5.0`,
|
|
509
|
-
` - Expertise: ${p[7].data.expertise_score} / 5.0`,
|
|
510
|
-
` - Authoritativeness: ${p[7].data.authoritativeness_score} / 5.0`,
|
|
511
|
-
` - Trustworthiness: ${p[7].data.trustworthiness_score} / 5.0`,
|
|
737
|
+
`- **Overall E-E-A-T Score**: **${formatVal(p[7].data.overall_eeat_score)} / 5.0**`,
|
|
738
|
+
` - Experience: ${formatVal(p[7].data.experience_score)} / 5.0`,
|
|
739
|
+
` - Expertise: ${formatVal(p[7].data.expertise_score)} / 5.0`,
|
|
740
|
+
` - Authoritativeness: ${formatVal(p[7].data.authoritativeness_score)} / 5.0`,
|
|
741
|
+
` - Trustworthiness: ${formatVal(p[7].data.trustworthiness_score)} / 5.0`,
|
|
512
742
|
``,
|
|
513
743
|
`## 8. Entity & Knowledge Graph Integrity`,
|
|
514
744
|
`- **Evidence Link**: \`${p[8].data.evidence_ref}\``,
|
|
515
745
|
`- **Organization Node Resolved**: ${p[8].data.organization_node_resolved ? 'YES' : 'NO'}`,
|
|
516
|
-
`- **External Corroboration**: ${p[8].data.wikidata_corroboration.toUpperCase()}`,
|
|
746
|
+
`- **External Corroboration**: ${formatVal(p[8].data.wikidata_corroboration).toUpperCase()}`,
|
|
517
747
|
``,
|
|
518
748
|
`## 9. Structured-Data & Schema Architecture`,
|
|
519
749
|
`- **Evidence Link**: \`${p[9].data.evidence_ref}\``,
|
|
520
|
-
`- **Deployed Schemas**: ${p[9].data.deployed_types.join(', ')}`,
|
|
521
|
-
`- **Recommended Missing Schemas**: ${p[9].data.coverage_gaps.join(', ')}`,
|
|
750
|
+
`- **Deployed Schemas**: ${(p[9].data.deployed_types || []).join(', ') || 'None'}`,
|
|
751
|
+
`- **Recommended Missing Schemas**: ${(p[9].data.coverage_gaps || []).join(', ') || 'None'}`,
|
|
522
752
|
``,
|
|
523
753
|
`## 10. Competitive Search Intelligence & Share of Voice`,
|
|
524
754
|
`- **Evidence Link**: \`${p[10].data.evidence_ref}\``,
|
|
525
|
-
`- **1st-Party Citation Share**: **${p[10].data.first_party_citation_share_pct}
|
|
526
|
-
`- **Primary Gap**: ${p[10].data.primary_authority_gap}`,
|
|
755
|
+
`- **1st-Party Citation Share**: **${formatVal(p[10].data.first_party_citation_share_pct, '%')}** vs Competitors (${formatVal(p[10].data.top_competitor_share_pct, '%')})`,
|
|
756
|
+
`- **Primary Gap**: ${formatVal(p[10].data.primary_authority_gap)}`,
|
|
527
757
|
``,
|
|
528
758
|
`## 11. Backlink & Off-Page Authority Profile`,
|
|
529
759
|
`- **Evidence Link**: \`${p[11].data.evidence_ref}\``,
|
|
530
|
-
`- **Referring Domains**: ${p[11].data.referring_domains} (Authority Score: ${p[11].data.authority_score}/100)`,
|
|
531
|
-
`- **Toxic Domains**: ${p[11].data.toxic_domain_count} flagged (\`${p[11].data.disavow_recommendation}\`)`,
|
|
760
|
+
`- **Referring Domains**: ${formatVal(p[11].data.referring_domains)} (Authority Score: ${formatVal(p[11].data.authority_score)}/100)`,
|
|
761
|
+
`- **Toxic Domains**: ${formatVal(p[11].data.toxic_domain_count)} flagged (\`${formatVal(p[11].data.disavow_recommendation)}\`)`,
|
|
532
762
|
``,
|
|
533
763
|
`## 12. Search Demand & Addressable Market Opportunity`,
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
764
|
+
];
|
|
765
|
+
|
|
766
|
+
const demandClusters = Array.isArray(p[12].data) ? p[12].data : (p[12].data?.clusters || []);
|
|
767
|
+
if (demandClusters.length > 0) {
|
|
768
|
+
lines.push(
|
|
769
|
+
`| Query Cluster | Addressable Volume | Commercial Intent | Current Posture |`,
|
|
770
|
+
`| :--- | :--- | :--- | :--- |`,
|
|
771
|
+
...demandClusters.map((c) => `| ${escapeMarkdownTableCell(c.cluster)} | ${(c.addressable_monthly_searches || 0).toLocaleString()} | ${escapeMarkdownTableCell(c.commercial_intent || '').toUpperCase()} | ${escapeMarkdownTableCell(c.current_coverage || '')} |`)
|
|
772
|
+
);
|
|
773
|
+
} else {
|
|
774
|
+
lines.push(`- *Search demand telemetry not observed. External query volume integration required.*`);
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
lines.push(
|
|
537
778
|
``,
|
|
538
779
|
`## 13. Search-to-Conversion Impact`,
|
|
539
|
-
`- **Organic Conversion Rate**: ${p[13].data.organic_conversion_rate_pct}
|
|
540
|
-
`- **Attributed Pipeline MRR**: **${p[13].data.pipeline_attributed_mrr}**`,
|
|
541
|
-
`- **Assisted Conversions Share**: ${p[13].data.assisted_conversions_share_pct}
|
|
780
|
+
`- **Organic Conversion Rate**: ${formatVal(p[13].data.organic_conversion_rate_pct, '%')}`,
|
|
781
|
+
`- **Attributed Pipeline MRR**: **${formatVal(p[13].data.pipeline_attributed_mrr)}**`,
|
|
782
|
+
`- **Assisted Conversions Share**: ${formatVal(p[13].data.assisted_conversions_share_pct, '%')}`,
|
|
542
783
|
``,
|
|
543
784
|
`## 14. Measurement Integrity & Analytics Instrumentation`,
|
|
544
|
-
`- **GA4 & GSC**: ${p[14].data.ga4_instrumentation.toUpperCase()}`,
|
|
545
|
-
`- **Consent Mode Data Loss**:
|
|
546
|
-
`- **Limitations**: ${p[14].data.known_limitations.join('; ')}`,
|
|
785
|
+
`- **GA4 & GSC**: ${formatVal(p[14].data.ga4_instrumentation).toUpperCase()} / ${formatVal(p[14].data.gsc_property_binding).toUpperCase()}`,
|
|
786
|
+
`- **Consent Mode Data Loss**: ${formatVal(p[14].data.consent_mode_v2_loss_pct, '%')}`,
|
|
787
|
+
`- **Limitations**: ${(p[14].data.known_limitations || []).map((l) => sanitizeForMarkdown(l)).join('; ')}`,
|
|
547
788
|
``,
|
|
548
789
|
`## 15. Verified Evidence Register (Traceability Engine)`,
|
|
549
790
|
`| Evidence ID | Source | Methodology | Confidence | Observation Date |`,
|
|
550
791
|
`| :--- | :--- | :--- | :--- | :--- |`,
|
|
551
|
-
...report.evidence_register.map((e) => `| **${e.evidence_id}** | \`${e.source}\` | ${e.methodology.slice(0, 50)}... | \`${e.confidence_level}\` | ${e.observation_date} |`),
|
|
792
|
+
...report.evidence_register.map((e) => `| **${e.evidence_id}** | \`${escapeMarkdownTableCell(e.source)}\` | ${escapeMarkdownTableCell((e.methodology || '').slice(0, 50))}... | \`${e.confidence_level}\` | ${e.observation_date} |`),
|
|
552
793
|
``,
|
|
553
794
|
`## 16. Strategic Risk & Algorithmic Dependency Register`,
|
|
554
795
|
`| Risk ID | Category | Severity | Description | Mitigation |`,
|
|
555
796
|
`| :--- | :--- | :--- | :--- | :--- |`,
|
|
556
|
-
...p[16].data.map((r) => `| **${r.risk_id}** | \`${r.category}\` | **${r.severity.toUpperCase()}** | ${r.description} | ${r.mitigation} |`),
|
|
797
|
+
...(p[16].data || []).map((r) => `| **${r.risk_id}** | \`${r.category}\` | **${r.severity.toUpperCase()}** | ${escapeMarkdownTableCell(r.description)} | ${escapeMarkdownTableCell(r.mitigation)} |`),
|
|
557
798
|
``,
|
|
558
799
|
`## 17. ICE-BV Prioritization Matrix`,
|
|
559
800
|
`- **Quick Wins Available**: ${p[17].data.summary?.quick_wins_count || 0}`,
|
|
@@ -561,9 +802,9 @@ export function renderSearchReportMarkdown(report) {
|
|
|
561
802
|
`- **Total Prioritized Initiatives**: ${p[17].data.total_scored || 0}`,
|
|
562
803
|
``,
|
|
563
804
|
`## 18. 30 / 90 / 180-Day Strategic Execution Roadmap`,
|
|
564
|
-
...p[18].data.horizons.map((h) => `### ${h.label}\n- **Objective**: ${h.objective}\n- **Focus Areas**: ${(h.focus_areas || []).join('; ')}\n- **Exit Criteria**: ${(h.exit_criteria || []).join('; ')}`),
|
|
565
|
-
|
|
566
|
-
|
|
805
|
+
...(p[18].data.horizons || []).map((h) => `### ${sanitizeForMarkdown(h.label)}\n- **Objective**: ${sanitizeForMarkdown(h.objective)}\n- **Focus Areas**: ${(h.focus_areas || []).map((f) => sanitizeForMarkdown(f)).join('; ')}\n- **Exit Criteria**: ${(h.exit_criteria || []).map((e) => sanitizeForMarkdown(e)).join('; ')}`),
|
|
806
|
+
``
|
|
807
|
+
);
|
|
567
808
|
|
|
568
809
|
return lines.join('\n');
|
|
569
810
|
}
|
|
@@ -577,7 +818,7 @@ export function renderSearchReportHtml(report) {
|
|
|
577
818
|
<html lang="en">
|
|
578
819
|
<head>
|
|
579
820
|
<meta charset="UTF-8">
|
|
580
|
-
<title>${report.report_title} - ${report.client_name}</title>
|
|
821
|
+
<title>${report.report_title} - ${escapeHtml(report.client_name)}</title>
|
|
581
822
|
<style>
|
|
582
823
|
:root { --bg: #0b0f19; --card: #151d30; --border: #1e293b; --text: #f8fafc; --muted: #94a3b8; --accent: #38bdf8; --danger: #ef4444; --warning: #f59e0b; --success: #10b981; }
|
|
583
824
|
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: var(--bg); color: var(--text); line-height: 1.6; margin: 0; padding: 40px 20px; }
|
|
@@ -604,51 +845,58 @@ export function renderSearchReportHtml(report) {
|
|
|
604
845
|
<div class="container">
|
|
605
846
|
<header>
|
|
606
847
|
<h1>${report.report_title}</h1>
|
|
607
|
-
<div class="meta">
|
|
848
|
+
<div class="meta">
|
|
849
|
+
Client: <strong>${escapeHtml(report.client_name)}</strong> •
|
|
850
|
+
Target: <strong>${escapeHtml(report.target_domain)}</strong> •
|
|
851
|
+
Generated: <strong>${escapeHtml(report.generated_at)}</strong> •
|
|
852
|
+
Mode: <strong>${escapeHtml(report.generation_provenance.generation_mode)}</strong>
|
|
853
|
+
</div>
|
|
608
854
|
</header>
|
|
609
855
|
|
|
610
856
|
<div class="disclosure">
|
|
611
|
-
<strong>Enterprise
|
|
857
|
+
<strong>Enterprise Operating Premise:</strong> This briefing is strictly bounded by deterministic DOM analysis, observable Core Web Vitals lab heuristics, and empirical citation observation probes. No rankings or conversions are promised or implied.
|
|
612
858
|
</div>
|
|
613
859
|
|
|
614
860
|
<div class="grid">
|
|
615
|
-
<div class="card"
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
<
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
<h3 style="color:#c084fc;">Actions Requiring Leadership Approval</h3>
|
|
636
|
-
<ul>${report.decision_summary.actions_requiring_leadership_approval.map((a) => `<li>${a}</li>`).join('')}</ul>
|
|
861
|
+
<div class="card">
|
|
862
|
+
<div style="font-size:12px; color:var(--muted); text-transform:uppercase;">Composite Search Readiness</div>
|
|
863
|
+
<div class="card-num">${report.overall_readiness_score} / 100</div>
|
|
864
|
+
<span class="badge ${report.overall_readiness_score >= 70 ? 'badge-success' : 'badge-warning'}">${report.overall_readiness_score >= 70 ? 'Optimal' : 'Needs Optimization'}</span>
|
|
865
|
+
</div>
|
|
866
|
+
<div class="card">
|
|
867
|
+
<div style="font-size:12px; color:var(--muted); text-transform:uppercase;">AEO Direct Extraction</div>
|
|
868
|
+
<div class="card-num">${formatVal(p[5].data.readiness_score)}</div>
|
|
869
|
+
<span class="badge badge-success">${formatVal(p[5].data.status).toUpperCase()}</span>
|
|
870
|
+
</div>
|
|
871
|
+
<div class="card">
|
|
872
|
+
<div style="font-size:12px; color:var(--muted); text-transform:uppercase;">E-E-A-T Quality Score</div>
|
|
873
|
+
<div class="card-num">${formatVal(p[7].data.overall_eeat_score)} / 5.0</div>
|
|
874
|
+
<span class="badge badge-success">Evaluated</span>
|
|
875
|
+
</div>
|
|
876
|
+
<div class="card">
|
|
877
|
+
<div style="font-size:12px; color:var(--muted); text-transform:uppercase;">Core Web Vitals Blockers</div>
|
|
878
|
+
<div class="card-num">${formatVal(p[2].data.core_web_vitals?.render_blocking_scripts)}</div>
|
|
879
|
+
<span class="badge ${p[2].data.core_web_vitals?.render_blocking_scripts === 0 ? 'badge-success' : 'badge-danger'}">${p[2].data.core_web_vitals?.render_blocking_scripts === 0 ? 'Clean' : 'Action Required'}</span>
|
|
880
|
+
</div>
|
|
637
881
|
</div>
|
|
638
882
|
|
|
639
|
-
<h2 class="section-title">Evidence Register
|
|
883
|
+
<h2 class="section-title">Verified Evidence Register (${report.evidence_register.length} Observations)</h2>
|
|
640
884
|
<table>
|
|
641
885
|
<thead>
|
|
642
|
-
<tr
|
|
886
|
+
<tr>
|
|
887
|
+
<th>Evidence ID</th>
|
|
888
|
+
<th>Source Domain / Trace</th>
|
|
889
|
+
<th>Methodology</th>
|
|
890
|
+
<th>Confidence</th>
|
|
891
|
+
</tr>
|
|
643
892
|
</thead>
|
|
644
893
|
<tbody>
|
|
645
894
|
${report.evidence_register.map((e) => `
|
|
646
895
|
<tr>
|
|
647
|
-
<td><span class="evidence-ref">${e.evidence_id}</span></td>
|
|
648
|
-
<td><code>${e.source}</code></td>
|
|
649
|
-
<td>${e.
|
|
650
|
-
<td>${e.
|
|
651
|
-
<td><span class="badge ${e.confidence_level === 'deterministic' ? 'badge-success' : 'badge-warning'}">${e.confidence_level}</span></td>
|
|
896
|
+
<td><span class="evidence-ref">${escapeHtml(e.evidence_id)}</span></td>
|
|
897
|
+
<td><code>${escapeHtml(e.source)}</code></td>
|
|
898
|
+
<td>${escapeHtml(e.methodology)}</td>
|
|
899
|
+
<td><span class="badge ${e.confidence_level === 'deterministic' ? 'badge-success' : 'badge-warning'}">${escapeHtml(e.confidence_level)}</span></td>
|
|
652
900
|
</tr>
|
|
653
901
|
`).join('')}
|
|
654
902
|
</tbody>
|
|
@@ -656,11 +904,11 @@ export function renderSearchReportHtml(report) {
|
|
|
656
904
|
|
|
657
905
|
<h2 class="section-title">30 / 90 / 180-Day Execution Roadmap</h2>
|
|
658
906
|
<div class="grid">
|
|
659
|
-
${p[18].data.horizons.map((h) => `
|
|
907
|
+
${(p[18].data.horizons || []).map((h) => `
|
|
660
908
|
<div class="card">
|
|
661
|
-
<div class="badge badge-success">${h.horizon.toUpperCase()}</div>
|
|
662
|
-
<h4 style="margin:8px 0;">${h.label}</h4>
|
|
663
|
-
<div style="font-size:12px; font-weight:bold; color:var(--accent);">Target Exit Criteria: ${h.exit_criteria?.[0] || 'Operational Reliability'}</div>
|
|
909
|
+
<div class="badge badge-success">${escapeHtml((h.horizon || '').toUpperCase())}</div>
|
|
910
|
+
<h4 style="margin:8px 0;">${escapeHtml(h.label)}</h4>
|
|
911
|
+
<div style="font-size:12px; font-weight:bold; color:var(--accent);">Target Exit Criteria: ${escapeHtml(h.exit_criteria?.[0] || 'Operational Reliability')}</div>
|
|
664
912
|
</div>
|
|
665
913
|
`).join('')}
|
|
666
914
|
</div>
|