@agentskit/doc-bridge 1.5.2 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -3783,7 +3783,7 @@ declare const chunksFromMarkdown: (property: string, raw: string, sourceUrl: str
3783
3783
  declare const loadFederatedChunks: (root: string, config: DocBridgeConfigV1, options?: FederatedRetrieverOptions) => Promise<DocBridgeRetrievedChunk[]>;
3784
3784
  declare const retrieveHybridChunks: (root: string, config: DocBridgeConfigV1, index: DocBridgeIndexV1, query: string, options?: FederatedRetrieverOptions) => Promise<DocBridgeRetrievedChunk[]>;
3785
3785
 
3786
- declare const PACKAGE_VERSION = "1.5.2";
3786
+ declare const PACKAGE_VERSION = "1.6.0";
3787
3787
 
3788
3788
  type FrontmatterValue = string | boolean | readonly string[];
3789
3789
  type FrontmatterData = Record<string, FrontmatterValue>;
package/dist/index.js CHANGED
@@ -1853,7 +1853,7 @@ var buildLookup = (config, packages, corpus, indexOutFile, humanDocs = {}, root
1853
1853
  };
1854
1854
 
1855
1855
  // src/version.ts
1856
- var PACKAGE_VERSION = "1.5.2";
1856
+ var PACKAGE_VERSION = "1.6.0";
1857
1857
 
1858
1858
  // src/index-builder/capabilities.ts
1859
1859
  var renderCapabilitiesJson = (config, index, paths) => {
@@ -2365,7 +2365,7 @@ var discoverNxProjects = (root, config) => {
2365
2365
  for (const target of Object.keys(targetRecord)) targets.add(target);
2366
2366
  }
2367
2367
  if (!isProjectJson && isRecord(json.scripts)) {
2368
- for (const script of Object.keys(json.scripts)) targets.add(script);
2368
+ for (const script2 of Object.keys(json.scripts)) targets.add(script2);
2369
2369
  }
2370
2370
  const id = packageId(projectName2);
2371
2371
  const rank = isProjectJson ? 2 : 1;
@@ -3520,40 +3520,90 @@ var reconcileKnowledge = (observed, declared) => {
3520
3520
  // src/report/html.ts
3521
3521
  var escapeHtml = (value) => String(value).replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
3522
3522
  var anchor = (prefix, value) => `${prefix}-${value.replace(/[^A-Za-z0-9_-]+/g, "-")}`;
3523
- var evidenceText = (evidence2, includeSnippets) => {
3524
- const location = `${evidence2.path}${evidence2.lineStart ? `:${evidence2.lineStart}${evidence2.lineEnd && evidence2.lineEnd !== evidence2.lineStart ? `-${evidence2.lineEnd}` : ""}` : ""}`;
3525
- return `${location}${includeSnippets && evidence2.context ? ` \u2014 ${redactSecrets(evidence2.context)}` : ""}`;
3526
- };
3527
- var errorPage = (message) => `<!doctype html><html lang="en"><head><meta charset="utf-8"><title>Doc Bridge report error</title><style>body{font:16px system-ui;margin:3rem;color:#311}main{max-width:60rem;margin:auto;border:1px solid #d99;padding:2rem;border-radius:8px;background:#fff8f8}code{white-space:pre-wrap}</style></head><body><main><h1>Doc Bridge report unavailable</h1><p>The saved snapshot/report could not be rendered.</p><code>${escapeHtml(message)}</code><p>Run <code>ak-docs check</code> to regenerate valid artifacts.</p></main></body></html>`;
3528
3523
  var embeddedJson = (value) => JSON.stringify(value).replaceAll("<", "\\u003c");
3529
- var controls = (statusOptions) => `<section id="controls"><label>Search <input id="search" type="search" placeholder="entity, relation, diagnostic"></label><label>Severity <select id="severity"><option value="">Any</option><option>error</option><option>warn</option><option>info</option></select></label><label>Provenance <select id="provenance"><option value="">Any</option><option>observed</option><option>declared</option><option>proposed</option></select></label><label>Status <select id="status"><option value="">Any</option>${statusOptions.map((value) => `<option>${value}</option>`).join("")}</select></label><label>Analyzer <input id="analyzer" type="search" placeholder="js-ts, report"></label><label>Entity <input id="entity" type="search" placeholder="entity id"></label><label>Relation <input id="relation" type="search" placeholder="relation id"></label><button id="reset">Reset filters</button></section>`;
3530
- var renderCompact = (snapshot, report, entityAnchors, includeSnippets) => {
3531
- const entities = snapshot.entities.map((entity) => ({ id: entity.id, name: entity.name, kind: entity.kind, anchor: entityAnchors.get(entity.id) ?? anchor("entity", entity.id) }));
3532
- const entityIndexes = new Map(entities.map((entity, index) => [entity.id, index]));
3533
- const relations = snapshot.relations.map((relation) => ({ id: relation.id, from: entityIndexes.get(relation.from), to: entityIndexes.get(relation.to), kind: relation.kind, provenance: relation.provenance }));
3534
- const diagnostics = report.diagnostics.map((diagnostic2) => ({
3535
- ...diagnostic2,
3536
- evidence: diagnostic2.evidence.map(({ context, ...evidence2 }) => includeSnippets && context ? { ...evidence2, context: redactSecrets(context) } : evidence2)
3537
- }));
3538
- const data = embeddedJson({ entities, relations, diagnostics, coverage: snapshot.coverage });
3539
- const script = `const data=${data};const esc=(v)=>String(v).replaceAll('&','&amp;').replaceAll('<','&lt;').replaceAll('>','&gt;').replaceAll('"','&quot;').replaceAll("'",'&#39;');const entityById=new Map(data.entities.map((e)=>[e.id,e]));const entityLink=(id)=>{const e=entityById.get(id);return '<a href="#'+esc(e?.anchor??'entity-'+id)+'">'+esc(e?.name??id)+'</a>'};const evidenceText=(e)=>esc(e.path+(e.lineStart?':'+e.lineStart+(e.lineEnd&&e.lineEnd!==e.lineStart?'-'+e.lineEnd:''):'')+(e.context?' \u2014 '+e.context:''));const render=()=>{document.querySelector('#map').innerHTML=data.entities.map((e)=>'<div class="node" id="'+esc(e.anchor)+'" data-node="'+esc(e.anchor)+'" data-search="'+esc([e.id,e.name,e.kind].join(' '))+'"><a href="#'+esc(e.anchor)+'">'+esc(e.name)+'</a><br><span class="muted">'+esc(e.kind)+'</span></div>').join('');document.querySelector('#relations').innerHTML=data.relations.map((r)=>{const from=data.entities[r.from],to=data.entities[r.to];return '<tr id="relation-'+esc(r.id)+'" data-relation="'+esc(r.id)+'" data-from="'+esc(from?.anchor??'')+'" data-to="'+esc(to?.anchor??'')+'" data-kind="'+esc(r.kind)+'" data-provenance="'+esc(r.provenance)+'" data-analyzer="snapshot" data-search="'+esc([r.id,from?.id,to?.id,r.kind,r.provenance].join(' '))+'"><td>'+esc(r.kind)+'</td><td>'+entityLink(from?.id??'')+'</td><td>'+entityLink(to?.id??'')+'</td><td>'+esc(r.provenance)+'</td></tr>'}).join('');document.querySelector('#diagnostics').innerHTML=data.diagnostics.length?data.diagnostics.map((d)=>'<article id="diagnostic-'+esc(d.id)+'" class="diagnostic" data-status="'+esc(d.status)+'" data-severity="'+esc(d.severity)+'" data-analyzer="report" data-entity="'+esc((d.entityIds??[]).join(' '))+'" data-relation="'+esc((d.relationIds??[]).join(' '))+'" data-search="'+esc([d.id,d.code,d.message,...(d.entityIds??[]),...(d.relationIds??[])].join(' '))+'"><h3><a href="#diagnostic-'+esc(d.id)+'">'+esc(d.code)+'</a> <span class="badge '+esc(d.severity)+'">'+esc(d.severity)+'</span></h3><p>'+esc(d.message)+'</p><p>Status: <b>'+esc(d.status)+'</b></p><ul>'+d.evidence.map((e)=>'<li>'+evidenceText(e)+'</li>').join('')+'</ul>'+(d.entityIds?.length?'<p>Entities: '+d.entityIds.map(entityLink).join(', ')+'</p>':'')+'</article>').join(''):'<p class="muted">No diagnostics.</p>';document.querySelector('#coverage').innerHTML=data.coverage.length?data.coverage.map((c)=>'<li data-status="'+esc(c.status)+'" data-analyzer="'+esc(c.analyzer)+'" data-search="'+esc([c.analyzer,c.scope,c.status,c.reason??''].join(' '))+'"><b>'+esc(c.analyzer)+'</b> / '+esc(c.scope)+': '+esc(c.status)+(c.reason?' \u2014 '+esc(c.reason):'')+'</li>').join(''):'<li class="muted">No coverage metadata.</li>';document.querySelectorAll('[data-node]').forEach((node)=>node.onclick=()=>{const id=node.dataset.node;document.querySelectorAll('[data-from],[data-to]').forEach((edge)=>edge.classList.toggle('hidden',edge.dataset.from!==id&&edge.dataset.to!==id))});};const apply=()=>{const q=document.querySelector('#search').value.toLowerCase(),severity=document.querySelector('#severity').value,provenance=document.querySelector('#provenance').value,status=document.querySelector('#status').value,analyzer=document.querySelector('#analyzer').value.toLowerCase(),entity=document.querySelector('#entity').value.toLowerCase(),relation=document.querySelector('#relation').value.toLowerCase();document.querySelectorAll('[data-search]').forEach((el)=>el.classList.toggle('hidden',Boolean(q&&!el.dataset.search.toLowerCase().includes(q)||severity&&el.dataset.severity!==severity||provenance&&el.dataset.provenance!==provenance||status&&el.dataset.status!==status||analyzer&&!el.dataset.analyzer?.toLowerCase().includes(analyzer)||entity&&!el.dataset.entity?.toLowerCase().includes(entity)||relation&&!el.dataset.relation?.toLowerCase().includes(relation))));};render();for(const id of ['search','analyzer','entity','relation'])document.querySelector('#'+id).oninput=apply;for(const id of ['severity','provenance','status'])document.querySelector('#'+id).onchange=apply;document.querySelector('#reset').onclick=()=>{for(const id of ['search','analyzer','entity','relation'])document.querySelector('#'+id).value='';for(const id of ['severity','provenance','status'])document.querySelector('#'+id).value='';apply()};`;
3540
- return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Doc Bridge \u2014 ${escapeHtml(snapshot.project.name)}</title><style>body{font:14px system-ui;margin:0;color:#18202a;background:#f5f7fa}header,main{max-width:1200px;margin:auto;padding:1.25rem}header{background:#18202a;color:white;max-width:none;padding-left:calc((100% - 1200px)/2);padding-right:calc((100% - 1200px)/2)}main{background:white}section{margin:1.5rem 0;border-top:1px solid #d7dde5;padding-top:1rem}table{border-collapse:collapse;width:100%;margin-top:.75rem}td,th{border-bottom:1px solid #e5e9ef;text-align:left;padding:.45rem;vertical-align:top}input,select,button{padding:.45rem;margin:.15rem;border:1px solid #b9c3d0;border-radius:4px;background:white}.badge{border-radius:1rem;padding:.15rem .5rem;background:#dce4ee}.error{background:#ffd9d9}.warn{background:#fff0c2}.info{background:#dcecff}.diagnostic{border:1px solid #d7dde5;border-left:4px solid #9aa7b5;padding:.75rem;margin:.75rem 0}.diagnostic:target,tr:target{background:#fff8cf}.muted{color:#5d6a78}a{color:#0b5cad}#map{display:flex;gap:1rem;flex-wrap:wrap}.node{border:1px solid #9aa7b5;padding:.5rem;border-radius:4px}.hidden{display:none}</style></head><body><header><h1>Doc Bridge: ${escapeHtml(snapshot.project.name)}</h1><p>Offline architecture and documentation reconciliation report</p><p class="muted">Snapshot ${escapeHtml(snapshot.contentHash)} \xB7 Report ${escapeHtml(report.contentHash)} \xB7 Revision ${escapeHtml(snapshot.sourceRevision)}</p></header><main>${controls(["confirmed", "undocumented", "stale-or-unverified", "conflict", "unresolved", "not-analyzed"])}<section id="architecture"><h2>Architecture map</h2><p>Large snapshots are rendered from compact canonical data after load to keep the offline report bounded.</p><div id="map"></div><h3>Relations</h3><table><thead><tr><th>Kind</th><th>From</th><th>To</th><th>Provenance</th></tr></thead><tbody id="relations"></tbody></table></section><section id="diagnostic-lens"><h2>Diagnostic lens</h2><p>Findings: ${report.diagnostics.length}</p><div id="diagnostics"></div></section><section id="coverage"><h2>Coverage and unsupported areas</h2><ul></ul></section><section id="metadata"><h2>Run metadata</h2><dl><dt>Source revision</dt><dd>${escapeHtml(snapshot.sourceRevision)} (${escapeHtml(snapshot.sourceRevisionKind)})</dd><dt>Configuration hash</dt><dd>${escapeHtml(snapshot.configurationHash)}</dd><dt>Pipeline</dt><dd>${escapeHtml(snapshot.pipelineVersion)}</dd></dl></section></main><script>${script}</script></body></html>`;
3541
- };
3524
+ var errorPage = (message) => `<!doctype html><html lang="en"><head><meta charset="utf-8"><title>Doc Bridge report error</title><style>body{font:16px system-ui;margin:3rem;color:#311}main{max-width:60rem;margin:auto;border:1px solid #d99;padding:2rem;border-radius:8px;background:#fff8f8}code{white-space:pre-wrap}</style></head><body><main><h1>Doc Bridge report unavailable</h1><p>The saved snapshot/report could not be rendered.</p><code>${escapeHtml(message)}</code><p>Run <code>ak-docs check</code> to regenerate valid artifacts.</p></main></body></html>`;
3525
+ var reportData = (snapshot, report, includeSnippets) => ({
3526
+ project: snapshot.project,
3527
+ revision: snapshot.sourceRevision,
3528
+ revisionKind: snapshot.sourceRevisionKind,
3529
+ snapshotHash: snapshot.contentHash,
3530
+ reportHash: report.contentHash,
3531
+ configurationHash: snapshot.configurationHash,
3532
+ pipelineVersion: snapshot.pipelineVersion,
3533
+ analyzerVersions: snapshot.analyzerVersions,
3534
+ entities: snapshot.entities.map((entity, index) => ({
3535
+ id: entity.id,
3536
+ anchor: entity.id.length > 64 ? `entity-n${index}` : anchor("entity", entity.id),
3537
+ kind: entity.kind,
3538
+ name: entity.name,
3539
+ path: entity.path,
3540
+ provenance: entity.provenance,
3541
+ evidence: entity.evidence.map(({ context, ...item }) => includeSnippets && context ? { ...item, context: redactSecrets(context) } : item)
3542
+ })),
3543
+ relations: snapshot.relations.map((relation) => ({
3544
+ id: relation.id,
3545
+ kind: relation.kind,
3546
+ from: relation.from,
3547
+ to: relation.to,
3548
+ provenance: relation.provenance,
3549
+ evidence: relation.evidence.map(({ context, ...item }) => includeSnippets && context ? { ...item, context: redactSecrets(context) } : item)
3550
+ })),
3551
+ diagnostics: report.diagnostics.map((diagnostic2) => ({
3552
+ id: diagnostic2.id,
3553
+ code: diagnostic2.code,
3554
+ status: diagnostic2.status,
3555
+ severity: diagnostic2.severity,
3556
+ message: diagnostic2.message,
3557
+ entityIds: diagnostic2.entityIds ?? [],
3558
+ relationIds: diagnostic2.relationIds ?? [],
3559
+ remediation: diagnostic2.remediation,
3560
+ evidence: diagnostic2.evidence.map(({ context, ...item }) => includeSnippets && context ? { ...item, context: redactSecrets(context) } : item)
3561
+ })),
3562
+ coverage: snapshot.coverage
3563
+ });
3564
+ var styles = String.raw`
3565
+ :root{color-scheme:light;--ink:#17251f;--muted:#60706a;--paper:#f5f7f3;--panel:#fff;--line:#d9e0da;--green:#18794e;--blue:#236b83;--amber:#a9640b;--red:#b33c35;--shadow:0 12px 32px rgba(23,37,31,.08)}
3566
+ *{box-sizing:border-box}body{margin:0;background:var(--paper);color:var(--ink);font:14px/1.5 Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}button,input,select{font:inherit}button{cursor:pointer}a{color:var(--blue)}.shell{max-width:1500px;margin:auto;padding:24px}.masthead{display:flex;justify-content:space-between;gap:24px;align-items:flex-end;border-bottom:1px solid var(--line);padding:8px 0 24px}.eyebrow{color:var(--green);font-size:11px;font-weight:800;letter-spacing:.12em;text-transform:uppercase}.masthead h1{font:700 clamp(28px,4vw,52px)/1.03 Georgia,serif;letter-spacing:-.04em;margin:8px 0}.lede{color:var(--muted);font-size:16px;max-width:720px;margin:0}.run-meta{text-align:right;color:var(--muted);font-size:12px}.run-meta strong{display:block;color:var(--green);font-size:14px}.read-only{border:1px solid #b9d7c6;background:#edf8f0;color:#185b3b;border-radius:999px;display:inline-flex;padding:4px 10px;font-weight:700;font-size:12px}.summary{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:10px;margin:20px 0}.metric,.insight,.panel{background:var(--panel);border:1px solid var(--line);box-shadow:var(--shadow);border-radius:14px}.metric{padding:16px}.metric b{font-size:27px;display:block;line-height:1}.metric span{color:var(--muted);display:block;margin-top:7px}.metric.warn b{color:var(--amber)}.metric.bad b{color:var(--red)}.lens-bar{display:flex;align-items:center;justify-content:space-between;gap:14px;margin:22px 0 12px}.tabs{display:flex;gap:4px;flex-wrap:wrap}.tab,.level{border:1px solid transparent;background:transparent;border-radius:8px;padding:8px 11px;color:var(--muted)}.tab:hover,.level:hover{background:#e8eee9;color:var(--ink)}.tab[aria-selected=true],.level[aria-pressed=true]{background:var(--ink);color:#fff}.filters{display:flex;gap:8px;flex-wrap:wrap;margin:12px 0}.filters label{color:var(--muted);font-size:12px;display:flex;align-items:center;gap:6px}.filters input,.filters select,.filters button{border:1px solid var(--line);background:#fff;border-radius:8px;padding:8px 10px;color:var(--ink)}.filters input{min-width:220px}.filters button{color:var(--blue)}.insights{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px;margin:14px 0 20px}.insight{padding:14px}.insight h3{font-size:13px;margin:0 0 5px}.insight p{color:var(--muted);margin:0;font-size:12px}.tag{display:inline-block;border-radius:999px;padding:2px 7px;font-size:10px;font-weight:800;letter-spacing:.04em;text-transform:uppercase;background:#edf0ee;color:var(--muted)}.tag.heuristic{background:#fff3dc;color:#8a570a}.tag.error{background:#fde8e6;color:var(--red)}.tag.warn{background:#fff2d9;color:var(--amber)}.tag.info{background:#e6f1f5;color:var(--blue)}.workspace{display:grid;grid-template-columns:minmax(0,1fr) 320px;gap:14px}.panel{padding:18px}.panel h2{font:700 23px Georgia,serif;margin:0}.panel h3{font-size:14px;margin:0}.panel-head{display:flex;justify-content:space-between;gap:12px;align-items:flex-start;margin-bottom:14px}.subtle{color:var(--muted);font-size:12px}.map-wrap{border:1px solid var(--line);border-radius:10px;overflow:auto;background:linear-gradient(135deg,#fbfcfa,#f0f5f0)}#graph{display:block;width:100%;min-width:760px;height:560px}.edge{stroke:#b8c6bd;stroke-width:1.3;opacity:.65}.edge.alert{stroke:#d08a35;stroke-width:2}.edge-label{fill:var(--muted);font-size:10px}.graph-node{cursor:pointer}.graph-node rect{fill:#fff;stroke:#9eb0a4;stroke-width:1.2}.graph-node:hover rect,.graph-node:focus rect{stroke:var(--blue);stroke-width:2}.graph-node.selected rect{stroke:var(--green);stroke-width:3}.graph-node.issue rect{fill:#fffaf0;stroke:#c98a2c}.graph-node text{pointer-events:none}.node-label{font-size:12px;font-weight:750}.node-kind{font-size:10px;fill:var(--muted)}.node-count{font-size:10px;fill:var(--green);font-weight:800}.empty{padding:32px;text-align:center;color:var(--muted)}.side{position:sticky;top:14px;align-self:start}.detail-title{font:700 22px Georgia,serif;margin:4px 0}.detail-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px;border-top:1px solid var(--line);border-bottom:1px solid var(--line);padding:12px 0;margin:14px 0}.detail-grid b{display:block;font-size:19px}.detail-grid span{color:var(--muted);font-size:11px}.path{overflow-wrap:anywhere;color:var(--muted);font-size:12px}.list{list-style:none;margin:10px 0 0;padding:0}.list li{border-top:1px solid var(--line);padding:8px 0;font-size:12px}.run{margin-top:14px}.run-summary{display:flex;gap:8px;flex-wrap:wrap;margin:12px 0}.chip{border:1px solid var(--line);border-radius:999px;padding:5px 9px;color:var(--muted);font-size:12px}.chip b{color:var(--ink)}.finding{border-top:1px solid var(--line);padding:14px 0}.finding-head{display:flex;justify-content:space-between;gap:10px}.finding h3{margin:0;font-size:14px}.finding p{margin:5px 0;color:var(--muted)}.finding ul{margin:8px 0 0;padding-left:18px;color:var(--muted);font-size:12px}.coverage-row{display:grid;grid-template-columns:170px 1fr 90px;gap:10px;align-items:center;border-top:1px solid var(--line);padding:10px 0;font-size:12px}.bar{height:7px;background:#e7ece8;border-radius:99px;overflow:hidden}.bar i{display:block;height:100%;background:var(--green)}.bar i.partial{background:var(--amber)}.bar i.none{background:var(--red)}.metadata{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px;margin-top:14px}.metadata div{border-top:1px solid var(--line);padding-top:8px;color:var(--muted);font-size:12px;overflow-wrap:anywhere}.metadata b{display:block;color:var(--ink);font-size:11px;text-transform:uppercase;letter-spacing:.08em;margin-bottom:3px}.hidden{display:none!important}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}@media(max-width:900px){.summary{grid-template-columns:repeat(3,minmax(0,1fr))}.insights{grid-template-columns:repeat(2,minmax(0,1fr))}.workspace{grid-template-columns:1fr}.side{position:static}.run-meta{display:none}}@media(max-width:560px){.shell{padding:14px}.summary{grid-template-columns:repeat(2,minmax(0,1fr))}.insights{grid-template-columns:1fr}.masthead{display:block}.filters input{min-width:0;width:100%}.filters label:first-child{width:100%}.metadata{grid-template-columns:1fr}}@media(prefers-reduced-motion:reduce){*{scroll-behavior:auto!important;transition:none!important}}
3567
+ `;
3568
+ var script = String.raw`
3569
+ const data=${"${DATA}"};
3570
+ const esc=(value)=>String(value??'').replaceAll('&','&amp;').replaceAll('<','&lt;').replaceAll('>','&gt;').replaceAll('"','&quot;').replaceAll("'",'&#39;');
3571
+ const byId=new Map(data.entities.map((entity)=>[entity.id,entity]));
3572
+ const parent=new Map();
3573
+ const relationById=new Map(data.relations.map((relation)=>[relation.id,relation]));
3574
+ data.relations.forEach((relation)=>{if(relation.kind==='contains'&&!parent.has(relation.to))parent.set(relation.to,relation.from)});
3575
+ const state={lens:'architecture',level:'overview',selected:null,query:'',status:'',severity:''};
3576
+ const rootOf=(id)=>{let current=id;const seen=new Set();while(parent.has(current)&&!seen.has(current)){seen.add(current);current=parent.get(current)}return current};
3577
+ const packageNodes=()=>data.entities.filter((entity)=>entity.kind==='package'||entity.kind==='external');
3578
+ const groupFor=(id)=>{const entity=byId.get(id);if(!entity)return id;if(entity.kind==='package'||entity.kind==='external')return entity.id;const root=rootOf(id);return byId.has(root)?root:id};
3579
+ const label=(entity)=>entity?.name||entity?.id||'Unknown';
3580
+ const short=(value,max)=>{const text=String(value??'');return text.length>max?text.slice(0,max-1)+'…':text};
3581
+ const diagnosticsFor=(id)=>data.diagnostics.filter((finding)=>(finding.entityIds||[]).includes(id)||(finding.relationIds||[]).some((relationId)=>{const relation=relationById.get(relationId);return relation?.from===id||relation?.to===id}));
3582
+ const relationHealth=(relation)=>{const findings=data.diagnostics.filter((finding)=>(finding.relationIds||[]).includes(relation.id));return findings.some((finding)=>finding.severity==='error')?'error':findings.length?'warn':''};
3583
+ const degreeMap=(relations)=>{const degrees=new Map();relations.forEach((relation)=>{degrees.set(relation.from,(degrees.get(relation.from)||0)+1);degrees.set(relation.to,(degrees.get(relation.to)||0)+1)});return degrees};
3584
+ const selectedScope=()=>{if(!state.selected)return null;const selected=byId.get(state.selected);return selected?.kind==='package'?selected.id:groupFor(state.selected)};
3585
+ const nodesFor=()=>{const packages=packageNodes();if(state.level==='overview'||(state.level==='package'&&!state.selected))return packages.length?packages:data.entities.filter((entity)=>entity.kind!=='document').slice(0,80);const scope=selectedScope();let nodes=data.entities.filter((entity)=>{if(entity.kind==='package'||entity.kind==='external')return false;if(entity.kind==='document'&&state.level==='module')return false;return !scope||groupFor(entity.id)===scope});if(state.level==='module')nodes=nodes.filter((entity)=>entity.kind==='module');if(state.level==='file')nodes=nodes.filter((entity)=>Boolean(entity.path));const degrees=degreeMap(data.relations);return nodes.sort((a,b)=>(degrees.get(b.id)||0)-(degrees.get(a.id)||0)||a.id.localeCompare(b.id)).slice(0,160)};
3586
+ const graphModel=()=>{const nodes=nodesFor();const ids=new Set(nodes.map((node)=>node.id));const aggregate=state.level==='overview'||(state.level==='package'&&!state.selected);const edges=new Map();data.relations.forEach((relation)=>{const from=aggregate?groupFor(relation.from):relation.from;const to=aggregate?groupFor(relation.to):relation.to;if(from===to||!ids.has(from)||!ids.has(to))return;const key=from+'→'+to;const current=edges.get(key)||{from,to,count:0,kinds:new Set(),health:''};current.count++;current.kinds.add(relation.kind);current.health=current.health==='error'||relationHealth(relation)==='error'?'error':current.health||relationHealth(relation);edges.set(key,current)});return{nodes,edges:[...edges.values()].sort((a,b)=>b.count-a.count||a.from.localeCompare(b.from)).slice(0,600)}};
3587
+ const nodeIssues=(nodeId)=>{const ids=new Set([nodeId]);data.entities.forEach((entity)=>{if(groupFor(entity.id)===nodeId)ids.add(entity.id)});return data.diagnostics.filter((finding)=>(finding.entityIds||[]).some((id)=>ids.has(id))||(finding.relationIds||[]).some((id)=>{const relation=relationById.get(id);return relation&&ids.has(relation.from)&&ids.has(relation.to)}))};
3588
+ const renderInsights=()=>{const undocumented=data.diagnostics.filter((finding)=>finding.status==='undocumented').length;const drift=data.diagnostics.filter((finding)=>finding.status==='stale-or-unverified'||finding.status==='conflict').length;const unsupported=data.coverage.filter((entry)=>entry.status==='not-analyzed'||entry.status==='partial').length;const model=graphModel();const degrees=degreeMap(model.edges);const values=[...degrees.values()].sort((a,b)=>a-b);const median=values.length?values[Math.floor(values.length/2)]:0;const hot=[...degrees.values()].filter((value)=>value>=Math.max(4,median*2)).length;const isolated=model.nodes.filter((node)=>!model.edges.some((edge)=>edge.from===node.id||edge.to===node.id)).length;document.querySelector('#insights').innerHTML=[['Documentation drift',undocumented+drift,'Relations or docs needing comparison.',''],['Unanalyzed scope',unsupported,'Coverage gaps are explicit.',''],['Connectivity hotspots',hot,'Heuristic: unusually connected nodes.','heuristic'],['Disconnected nodes',isolated,'Heuristic: no visible edge at this level.','heuristic']].map(([title,count,copy,tag])=>'<article class="insight"><h3>'+esc(title)+' <span class="tag '+tag+'">'+(tag?'heuristic':'signal')+'</span></h3><p><strong>'+count+'</strong> · '+esc(copy)+'</p></article>').join('')};
3589
+ const renderGraph=()=>{const model=graphModel();const svg=document.querySelector('#graph');if(!model.nodes.length){svg.innerHTML='<text x="500" y="270" text-anchor="middle" class="subtle">No entities match this level.</text>';return}const width=1000,height=560,pad=56,columns=Math.max(1,Math.ceil(Math.sqrt(model.nodes.length))),rows=Math.ceil(model.nodes.length/columns),positions=new Map();model.nodes.forEach((node,index)=>{const col=index%columns,row=Math.floor(index/columns);positions.set(node.id,{x:pad+(col+0.5)*(width-2*pad)/columns,y:pad+(row+0.5)*(height-2*pad)/rows})});const defs='<defs><marker id="arrow" markerWidth="8" markerHeight="8" refX="7" refY="3" orient="auto"><path d="M0,0 L0,6 L7,3 z" fill="#9eb0a4"/></marker></defs>';const edges=model.edges.map((edge)=>{const from=positions.get(edge.from),to=positions.get(edge.to);if(!from||!to)return '';const midX=(from.x+to.x)/2,midY=(from.y+to.y)/2;return '<g><line class="edge '+(edge.health?'alert':'')+'" x1="'+from.x+'" y1="'+from.y+'" x2="'+to.x+'" y2="'+to.y+'" marker-end="url(#arrow)"/><text class="edge-label" x="'+midX+'" y="'+midY+'">'+esc(edge.count>1?edge.count+'×':'')+'</text></g>'}).join('');const nodes=model.nodes.map((node)=>{const pos=positions.get(node.id),issues=nodeIssues(node.id),degree=(degreeMap(model.edges).get(node.id)||0),selected=state.selected===node.id?' selected':'',issue=issues.length?' issue':'';return '<g class="graph-node'+selected+issue+'" role="button" tabindex="0" data-node="'+esc(node.id)+'" transform="translate('+(pos.x-62)+','+(pos.y-27)+')"><title>'+esc(label(node))+' · '+esc(node.kind)+'</title><rect width="124" height="54" rx="8"></rect><text class="node-label" x="10" y="19">'+esc(short(label(node),18))+'</text><text class="node-kind" x="10" y="34">'+esc(short(node.kind,18))+'</text><text class="node-count" x="112" y="19" text-anchor="end">'+(degree||'')+'</text></g>'}).join('');svg.innerHTML=defs+edges+nodes;svg.querySelectorAll('[data-node]').forEach((node)=>{node.addEventListener('click',()=>{state.selected=node.dataset.node;render()});node.addEventListener('keydown',(event)=>{if(event.key==='Enter'||event.key===' '){event.preventDefault();state.selected=node.dataset.node;render()}})});document.querySelector('#map-note').textContent=(state.level==='overview'?'Grouped package/external view. ':state.level+' view. ')+'Edges are aggregated from canonical relations; '+model.edges.length+' visible connection groups.'};
3590
+ const renderDetails=()=>{const panel=document.querySelector('#details'),entity=state.selected?byId.get(state.selected):null;if(!entity){panel.innerHTML='<p class="subtle">Select a node in the map to inspect its evidence, connectivity, and findings.</p>';return}const incoming=data.relations.filter((relation)=>relation.to===entity.id),outgoing=data.relations.filter((relation)=>relation.from===entity.id),findings=diagnosticsFor(entity.id),evidence=[...(entity.evidence||[]),...incoming.flatMap((relation)=>relation.evidence||[]),...outgoing.flatMap((relation)=>relation.evidence||[])].slice(0,8);panel.innerHTML='<div class="eyebrow">Selected entity</div><h2 class="detail-title">'+esc(label(entity))+'</h2><span class="tag">'+esc(entity.kind)+'</span><p class="path">'+esc(entity.path||entity.id)+'</p><div class="detail-grid"><div><b>'+incoming.length+'</b><span>incoming</span></div><div><b>'+outgoing.length+'</b><span>outgoing</span></div><div><b>'+findings.length+'</b><span>findings</span></div><div><b>'+evidence.length+'</b><span>evidence items</span></div></div><h3>Evidence</h3><ul class="list">'+(evidence.length?evidence.map((item)=>'<li>'+esc(item.path+(item.lineStart?':'+item.lineStart:'')+(item.context?' — '+item.context:''))+'</li>').join(''):'<li>No evidence recorded.</li>')+'</ul>'+(findings.length?'<h3 style="margin-top:16px">Attention</h3><ul class="list">'+findings.slice(0,5).map((finding)=>'<li><span class="tag '+esc(finding.severity)+'">'+esc(finding.severity)+'</span> '+esc(finding.code)+'</li>').join('')+'</ul>':'')};
3591
+ const findingMatches=(finding)=>{const query=state.query.toLowerCase();return(!query||[finding.id,finding.code,finding.message,...finding.entityIds,...finding.relationIds].join(' ').toLowerCase().includes(query))&&(!state.status||finding.status===state.status)&&(!state.severity||finding.severity===state.severity)};
3592
+ const renderFindings=()=>{let findings=data.diagnostics.filter(findingMatches);if(state.lens==='risks')findings=findings.filter((finding)=>finding.severity==='error'||finding.severity==='warn');if(state.lens==='evidence')findings=findings.filter((finding)=>finding.evidence.length);if(state.lens==='drift')findings=findings.filter((finding)=>finding.status!=='confirmed');document.querySelector('#finding-count').textContent=findings.length+' shown';document.querySelector('#findings').innerHTML=findings.slice(0,300).map((finding)=>'<article class="finding" id="'+esc('diagnostic-'+finding.id.replace(/[^A-Za-z0-9_-]+/g,'-'))+'"><div class="finding-head"><h3>'+esc(finding.code)+'</h3><span><span class="tag '+esc(finding.severity)+'">'+esc(finding.severity)+'</span> <span class="tag">'+esc(finding.status)+'</span></span></div><p>'+esc(finding.message)+'</p>'+(finding.evidence.length?'<ul>'+finding.evidence.slice(0,4).map((item)=>'<li>'+esc(item.path+(item.lineStart?':'+item.lineStart:'')+(item.context?' — '+item.context:''))+'</li>').join('')+'</ul>':'')+(finding.remediation?'<p><strong>Next check:</strong> '+esc(finding.remediation)+'</p>':'')+'</article>').join('')||'<p class="empty">No findings match the current lens and filters.</p>';};
3593
+ const renderCoverage=()=>{document.querySelector('#coverage-list').innerHTML=data.coverage.map((entry)=>{const width=entry.status==='complete'?100:entry.status==='partial'?55:12;return '<div class="coverage-row"><div><b>'+esc(entry.analyzer)+'</b><br><span class="subtle">'+esc(entry.scope)+'</span></div><div class="bar"><i class="'+(entry.status==='complete'?'':entry.status==='partial'?'partial':'none')+'" style="width:'+width+'%"></i></div><div>'+esc(entry.status)+'</div></div>'}).join('')||'<p class="empty">No coverage metadata.</p>'};
3594
+ const render=()=>{document.querySelectorAll('.tab[data-lens]').forEach((tab)=>tab.setAttribute('aria-selected',String(tab.dataset.lens===state.lens)));document.querySelectorAll('.level').forEach((button)=>button.setAttribute('aria-pressed',String(button.dataset.level===state.level)));document.querySelector('#lens-caption').textContent=state.lens==='architecture'?'The repository topology at the selected level.':state.lens==='drift'?'Where canonical code relations and documentation declarations need attention.':state.lens==='risks'?'Signals that deserve human review; connectivity warnings are heuristics, not architectural proof.':'What the analyzers observed, declared, or could not analyze.';renderInsights();renderGraph();renderDetails();renderFindings();renderCoverage()};
3595
+ document.querySelectorAll('.tab[data-lens]').forEach((tab)=>tab.addEventListener('click',()=>{state.lens=tab.dataset.lens;render()}));document.querySelectorAll('.level').forEach((button)=>button.addEventListener('click',()=>{state.level=button.dataset.level;render()}));document.querySelector('#search').addEventListener('input',(event)=>{state.query=event.target.value;renderFindings()});document.querySelector('#status').addEventListener('change',(event)=>{state.status=event.target.value;renderFindings()});document.querySelector('#severity').addEventListener('change',(event)=>{state.severity=event.target.value;renderFindings()});document.querySelector('#reset').addEventListener('click',()=>{state.query='';state.status='';state.severity='';document.querySelector('#search').value='';document.querySelector('#status').value='';document.querySelector('#severity').value='';renderFindings()});document.querySelector('#clear-selection').addEventListener('click',()=>{state.selected=null;render()});document.addEventListener('keydown',(event)=>{if(event.key==='/'&&document.activeElement.tagName!=='INPUT'){event.preventDefault();document.querySelector('#search').focus()}});render();
3596
+ `;
3542
3597
  var render = (input, options) => {
3543
3598
  const { snapshot, report } = input;
3544
3599
  const includeSnippets = options.includeSnippets === true;
3545
- const entityAnchors = new Map(snapshot.entities.map((entity, index) => [entity.id, entity.id.length > 64 ? `entity-n${index}` : anchor("entity", entity.id)]));
3546
- if (snapshot.entities.length > 500 || report.diagnostics.length > 1e3) return renderCompact(snapshot, report, entityAnchors, includeSnippets);
3547
- const entityNames = new Map(snapshot.entities.map((entity) => [entity.id, entity.name]));
3548
- const entityAnchor = (id) => entityAnchors.get(id) ?? anchor("entity", id);
3549
- const entityLabel = (id) => entityNames.get(id) ?? id;
3550
- const entityLink = (id) => `<a href="#${entityAnchor(id)}">${escapeHtml(entityLabel(id))}</a>`;
3551
- const relations = snapshot.relations.map((relation) => `<tr id="${anchor("relation", relation.id)}" data-relation="${escapeHtml(relation.id)}" data-from="${entityAnchor(relation.from)}" data-to="${entityAnchor(relation.to)}" data-kind="${escapeHtml(relation.kind)}" data-provenance="${escapeHtml(relation.provenance)}" data-analyzer="snapshot" data-search="${escapeHtml(`${relation.id} ${relation.from} ${relation.to} ${relation.kind} ${relation.provenance}`)}"><td>${escapeHtml(relation.kind)}</td><td>${entityLink(relation.from)}</td><td>${entityLink(relation.to)}</td><td>${escapeHtml(relation.provenance)}</td></tr>`).join("");
3552
- const diagnostics = report.diagnostics.map((diagnostic2) => `<article id="${anchor("diagnostic", diagnostic2.id)}" class="diagnostic" data-status="${escapeHtml(diagnostic2.status)}" data-severity="${escapeHtml(diagnostic2.severity)}" data-analyzer="report" data-entity="${escapeHtml(diagnostic2.entityIds?.join(" ") ?? "")}" data-relation="${escapeHtml(diagnostic2.relationIds?.join(" ") ?? "")}" data-search="${escapeHtml(`${diagnostic2.id} ${diagnostic2.code} ${diagnostic2.message} ${diagnostic2.entityIds?.join(" ") ?? ""} ${diagnostic2.relationIds?.join(" ") ?? ""}`)}"><h3><a href="#${anchor("diagnostic", diagnostic2.id)}">${escapeHtml(diagnostic2.code)}</a> <span class="badge ${escapeHtml(diagnostic2.severity)}">${escapeHtml(diagnostic2.severity)}</span></h3><p>${escapeHtml(diagnostic2.message)}</p><p>Status: <b>${escapeHtml(diagnostic2.status)}</b></p><ul>${diagnostic2.evidence.map((item) => `<li>${escapeHtml(evidenceText(item, includeSnippets))}</li>`).join("")}</ul>${diagnostic2.entityIds?.length ? `<p>Entities: ${diagnostic2.entityIds.map((id) => entityLink(id)).join(", ")}</p>` : ""}</article>`).join("");
3553
- const coverage = snapshot.coverage.map((entry) => `<li data-status="${escapeHtml(entry.status)}" data-analyzer="${escapeHtml(entry.analyzer)}" data-search="${escapeHtml(`${entry.analyzer} ${entry.scope} ${entry.status} ${entry.reason ?? ""}`)}"><b>${escapeHtml(entry.analyzer)}</b> / ${escapeHtml(entry.scope)}: ${escapeHtml(entry.status)}${entry.reason ? ` \u2014 ${escapeHtml(entry.reason)}` : ""}</li>`).join("");
3554
- const nodes = snapshot.entities.map((entity) => `<div class="node" id="${entityAnchor(entity.id)}" data-node="${entityAnchor(entity.id)}"><a href="#${entityAnchor(entity.id)}">${escapeHtml(entity.name)}</a><br><span class="muted">${escapeHtml(entity.kind)}</span></div>`).join("");
3555
- const script = `const q=document.querySelector('#search'),severity=document.querySelector('#severity'),provenance=document.querySelector('#provenance'),status=document.querySelector('#status'),analyzer=document.querySelector('#analyzer'),entity=document.querySelector('#entity'),relation=document.querySelector('#relation');function apply(){const term=q.value.toLowerCase(),entityTerm=entity.value.toLowerCase(),relationTerm=relation.value.toLowerCase();document.querySelectorAll('[data-search]').forEach((el)=>{const match=(!term||el.dataset.search.toLowerCase().includes(term))&&(!severity.value||el.dataset.severity===severity.value)&&(!provenance.value||el.dataset.provenance===provenance.value)&&(!status.value||el.dataset.status===status.value)&&(!analyzer.value||el.dataset.analyzer?.toLowerCase().includes(analyzer.value.toLowerCase()))&&(!entityTerm||el.dataset.entity?.toLowerCase().includes(entityTerm))&&(!relationTerm||el.dataset.relation?.toLowerCase().includes(relationTerm));el.classList.toggle('hidden',!match)});}q.oninput=apply;severity.onchange=apply;provenance.onchange=apply;status.onchange=apply;analyzer.oninput=apply;entity.oninput=apply;relation.oninput=apply;document.querySelector('#reset').onclick=()=>{q.value='';severity.value='';provenance.value='';status.value='';analyzer.value='';entity.value='';relation.value='';apply()};document.querySelectorAll('[data-node]').forEach((node)=>node.onclick=()=>{const id=node.dataset.node;document.querySelectorAll('[data-from],[data-to]').forEach((edge)=>edge.classList.toggle('hidden',edge.dataset.from!==id&&edge.dataset.to!==id));});`;
3556
- return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Doc Bridge \u2014 ${escapeHtml(snapshot.project.name)}</title><style>body{font:14px system-ui;margin:0;color:#18202a;background:#f5f7fa}header,main{max-width:1200px;margin:auto;padding:1.25rem}header{background:#18202a;color:white;max-width:none;padding-left:calc((100% - 1200px)/2);padding-right:calc((100% - 1200px)/2)}main{background:white}section{margin:1.5rem 0;border-top:1px solid #d7dde5;padding-top:1rem}table{border-collapse:collapse;width:100%;margin-top:.75rem}td,th{border-bottom:1px solid #e5e9ef;text-align:left;padding:.45rem;vertical-align:top}input,select,button{padding:.45rem;margin:.15rem;border:1px solid #b9c3d0;border-radius:4px;background:white}.badge{border-radius:1rem;padding:.15rem .5rem;background:#dce4ee}.error{background:#ffd9d9}.warn{background:#fff0c2}.info{background:#dcecff}.diagnostic{border:1px solid #d7dde5;border-left:4px solid #9aa7b5;padding:.75rem;margin:.75rem 0}.diagnostic:target,tr:target{background:#fff8cf}.muted{color:#5d6a78}a{color:#0b5cad}#map{display:flex;gap:1rem;flex-wrap:wrap}.node{border:1px solid #9aa7b5;padding:.5rem;border-radius:4px}.hidden{display:none}</style></head><body><header><h1>Doc Bridge: ${escapeHtml(snapshot.project.name)}</h1><p>Offline architecture and documentation reconciliation report</p><p class="muted">Snapshot ${escapeHtml(snapshot.contentHash)} \xB7 Report ${escapeHtml(report.contentHash)} \xB7 Revision ${escapeHtml(snapshot.sourceRevision)}</p></header><main><section id="controls"><label>Search <input id="search" type="search" placeholder="entity, relation, diagnostic"></label><label>Severity <select id="severity"><option value="">Any</option><option>error</option><option>warn</option><option>info</option></select></label><label>Provenance <select id="provenance"><option value="">Any</option><option>observed</option><option>declared</option><option>proposed</option></select></label><label>Status <select id="status"><option value="">Any</option>${["confirmed", "undocumented", "stale-or-unverified", "conflict", "unresolved", "not-analyzed"].map((value) => `<option>${value}</option>`).join("")}</select></label><label>Analyzer <input id="analyzer" type="search" placeholder="js-ts, report"></label><label>Entity <input id="entity" type="search" placeholder="entity id"></label><label>Relation <input id="relation" type="search" placeholder="relation id"></label><button id="reset">Reset filters</button></section><section id="architecture"><h2>Architecture map</h2><p>Observed and declared relations are rendered from the canonical snapshot; browser code does not infer edges.</p><div id="map">${nodes}</div><h3>Relations</h3><table><thead><tr><th>Kind</th><th>From</th><th>To</th><th>Provenance</th></tr></thead><tbody id="relations">${relations}</tbody></table></section><section id="diagnostic-lens"><h2>Diagnostic lens</h2><p>Findings: ${report.diagnostics.length}</p><div id="diagnostics">${diagnostics || '<p class="muted">No diagnostics.</p>'}</div></section><section id="coverage"><h2>Coverage and unsupported areas</h2><ul>${coverage || "<li>No coverage metadata.</li>"}</ul></section><section id="metadata"><h2>Run metadata</h2><dl><dt>Source revision</dt><dd>${escapeHtml(snapshot.sourceRevision)} (${escapeHtml(snapshot.sourceRevisionKind)})</dd><dt>Configuration hash</dt><dd>${escapeHtml(snapshot.configurationHash)}</dd><dt>Pipeline</dt><dd>${escapeHtml(snapshot.pipelineVersion)}</dd></dl></section></main><script>${script}</script></body></html>`;
3600
+ const data = reportData(snapshot, report, includeSnippets);
3601
+ const scriptBody = script.replace("${DATA}", embeddedJson(data));
3602
+ const largeNote = snapshot.entities.length > 500 || report.diagnostics.length > 1e3 ? "Large snapshots are rendered from compact canonical data with progressive graph levels." : "The viewer starts with a grouped topology and expands into canonical entities on demand.";
3603
+ const issueCount = report.diagnostics.filter((diagnostic2) => diagnostic2.severity === "error" || diagnostic2.severity === "warn").length;
3604
+ const unsupportedCount = snapshot.coverage.filter((entry) => entry.status !== "complete").length;
3605
+ const statusOptions = ["confirmed", "undocumented", "stale-or-unverified", "conflict", "unresolved", "not-analyzed"];
3606
+ return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Doc Bridge \u2014 ${escapeHtml(snapshot.project.name)}</title><style>${styles}</style></head><body><main class="shell"><header class="masthead"><div><div class="eyebrow">Doc Bridge / Knowledge report</div><h1>${escapeHtml(snapshot.project.name)}</h1><p class="lede">A read-only architecture and documentation map. Start broad, then follow evidence to the exact entity, relation, or finding.</p></div><div class="run-meta"><span class="read-only">Read-only snapshot</span><strong>${escapeHtml(snapshot.sourceRevision)}</strong><span>${escapeHtml(snapshot.sourceRevisionKind)} \xB7 pipeline ${escapeHtml(snapshot.pipelineVersion)}</span></div></header><section class="summary" aria-label="Snapshot summary"><div class="metric"><b>${snapshot.entities.length}</b><span>entities</span></div><div class="metric"><b>${snapshot.relations.length}</b><span>canonical relations</span></div><div class="metric ${issueCount ? "warn" : ""}"><b>${issueCount}</b><span>warn/error findings</span></div><div class="metric ${report.diagnostics.length ? "warn" : ""}"><b>${report.diagnostics.length}</b><span>total findings</span></div><div class="metric ${unsupportedCount ? "bad" : ""}"><b>${unsupportedCount}</b><span>partial / unanalyzed scopes</span></div></section><div class="lens-bar"><nav class="tabs" aria-label="Report lenses"><button class="tab" data-lens="architecture" aria-selected="true">Architecture</button><button class="tab" data-lens="drift" aria-selected="false">Documentation drift</button><button class="tab" data-lens="risks" aria-selected="false">Risks &amp; hotspots</button><button class="tab" data-lens="evidence" aria-selected="false">Evidence</button></nav><span class="subtle">${escapeHtml(largeNote)}</span></div><p id="lens-caption" class="subtle">The repository topology at the selected level.</p><section id="insights" class="insights" aria-label="Attention signals"></section><section class="filters" aria-label="Finding filters"><label>Search <input id="search" type="search" placeholder="Press / to search findings"></label><label>Status <select id="status"><option value="">Any status</option>${statusOptions.map((value) => `<option>${escapeHtml(value)}</option>`).join("")}</select></label><label>Severity <select id="severity"><option value="">Any severity</option><option>error</option><option>warn</option><option>info</option></select></label><button id="reset" type="button">Reset filters</button></section><div class="workspace"><section class="panel" aria-labelledby="architecture-title"><div class="panel-head"><div><h2 id="architecture-title">Architecture map</h2><p id="map-note" class="subtle">Grouped package/external view.</p></div><div class="tabs" aria-label="Graph level"><button class="level" data-level="overview" aria-pressed="true">Overview</button><button class="level" data-level="package" aria-pressed="false">Package</button><button class="level" data-level="module" aria-pressed="false">Module</button><button class="level" data-level="file" aria-pressed="false">File</button></div></div><div class="map-wrap"><svg id="graph" viewBox="0 0 1000 560" role="img" aria-label="Interactive architecture graph"></svg></div><p class="subtle">Node number = visible connection degree. Amber nodes/edges have findings. Click or focus a node to inspect its evidence. Grouped edges preserve canonical relation direction and count.</p></section><aside class="panel side" aria-labelledby="details-title"><div class="panel-head"><div><div class="eyebrow">Evidence trail</div><h2 id="details-title">Details</h2></div><button id="clear-selection" class="tab" type="button">Clear</button></div><div id="details"><p class="subtle">Select a node in the map to inspect its evidence, connectivity, and findings.</p></div></aside></div><section class="panel run" aria-labelledby="run-title"><div class="panel-head"><div><div class="eyebrow">Jest-like diagnostics</div><h2 id="run-title">Run report</h2></div><span id="finding-count" class="subtle">${report.diagnostics.length} shown</span></div><div class="run-summary"><span class="chip"><b>${report.diagnostics.filter((item) => item.severity === "error").length}</b> errors</span><span class="chip"><b>${report.diagnostics.filter((item) => item.severity === "warn").length}</b> warnings</span><span class="chip"><b>${report.diagnostics.filter((item) => item.status === "undocumented").length}</b> undocumented</span><span class="chip"><b>${report.diagnostics.filter((item) => item.status === "stale-or-unverified" || item.status === "conflict").length}</b> drift/conflict</span></div><div id="findings"></div></section><section class="panel run" aria-labelledby="coverage-title"><div class="panel-head"><div><div class="eyebrow">Analyzer boundaries</div><h2 id="coverage-title">Coverage &amp; unsupported areas</h2></div><span class="subtle">Explicit limits are part of the evidence</span></div><div id="coverage-list"></div></section><section class="metadata" aria-label="Run metadata"><div><b>Snapshot</b>${escapeHtml(snapshot.contentHash)}</div><div><b>Report</b>${escapeHtml(report.contentHash)}</div><div><b>Configuration</b>${escapeHtml(snapshot.configurationHash)}</div><div><b>Analyzers</b>${escapeHtml(Object.entries(snapshot.analyzerVersions).map(([name, version]) => `${name} ${version}`).join(", "))}</div><div><b>Source revision</b>${escapeHtml(snapshot.sourceRevision)} (${escapeHtml(snapshot.sourceRevisionKind)})</div><div><b>Mode</b>Read-only browser viewer; approvals and fixes remain outside this artifact.</div></section></main><script>${scriptBody}</script></body></html>`;
3557
3607
  };
3558
3608
  var renderOfflineReport = (input, options = {}) => {
3559
3609
  try {