From 51ab802e3087dc79a65ee88b8e87a526edec70a0 Mon Sep 17 00:00:00 2001 From: Jaixii Date: Sun, 9 Aug 2026 19:41:12 -0400 Subject: [PATCH 1/5] fix: unify version tags to physics-guard and restore release 1.6 badge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Version tags: pin-only-physics → physics-guard across all dashboard assets - Restore release_version='1.6' in savings preset query - Restore plan badge logic in ledger.js --- engraphis/classic_assets/dashboard.js | 38 +- engraphis/classic_assets/index.html | 710 +++++++++++++------------- engraphis/dashboard_assets/index.html | 4 +- engraphis/dashboard_assets/ledger.js | 120 +++-- engraphis/static/dashboard.js | 38 +- engraphis/static/index.html | 708 ++++++++++++------------- 6 files changed, 819 insertions(+), 799 deletions(-) diff --git a/engraphis/classic_assets/dashboard.js b/engraphis/classic_assets/dashboard.js index d1333fff..282692a5 100644 --- a/engraphis/classic_assets/dashboard.js +++ b/engraphis/classic_assets/dashboard.js @@ -131,23 +131,23 @@ function setWS(name){ } async function loadWorkspaceList(){const d=await api('/workspaces');WORKSPACES=d.workspaces||[];if(!WS&&WORKSPACES.length){WORKSPACES.sort((a,b)=>(b.memories||0)-(a.memories||0));setWS(WORKSPACES[0].name)}} -/* overview */ -function formatTokenCount(value){return Math.max(0,Math.round(Number(value)||0)).toLocaleString()} -function savingsPercent(value){return Math.max(0,Math.min(100,Number(value)*100||0))} -function savingsCount(value){return Math.max(0,Math.round(Number(value)||0))} -function savingsNode(tag,cls,text){const n=document.createElement(tag);if(cls)n.className=cls;if(text!=null)n.textContent=text;return n} -function renderOverviewSavings(data,error){ - const el=document.getElementById('ov-savings'); - if(!el)return; - el.replaceChildren(); - if(error){el.append(savingsNode('div','empty','Savings estimate unavailable.'));return} - const e=(data&&data.estimated)||{},eligible=savingsCount(e.eligible_receipt_count),excluded=savingsCount(e.excluded_receipt_count)+savingsCount(e.unclassified_receipt_count)+savingsCount(e.invalid_estimate_count),saved=Number(e.saved_tokens)||0,ratio=Number(e.savings_ratio)||0,counters=Array.isArray(e.by_token_counter)?e.by_token_counter:[]; - if(!eligible){el.append(savingsNode('div','empty','No receipt-backed context savings yet.'),savingsNode('div','field-hint','Eligible deliveries will appear after adaptive context or context-delivery calls. '+excluded+' excluded or unclassified '+(excluded===1?'delivery':'deliveries')+'.'),savingsNode('div','field-hint','Measures estimated prompt-context reduction; it does not measure provider billing.'));return} - const counter=counters.length===1?'Counter: '+(counters[0].token_counter||'unknown'):counters.length?counters.length+' token counters (kept separate)':'Counter: unknown',pct=savingsPercent(ratio),pctLabel=pct.toFixed(1); - const hero=savingsNode('div','savings-hero'),total=savingsNode('div'),rate=savingsNode('div','savings-rate'),progress=document.createElement('progress'); - total.append(savingsNode('div','stat-val savings-number',formatTokenCount(saved)),savingsNode('div','savings-unit','tokens avoided'));rate.append(savingsNode('strong','',pctLabel+'%'),savingsNode('span','','estimated reduction'));hero.append(total,rate);progress.className='savings-progress';progress.max=100;progress.value=pct;progress.setAttribute('aria-label',pctLabel+'% estimated context reduction'); - el.append(hero,progress,savingsNode('div','savings-summary','Across '+eligible+' eligible context deliveries'),savingsNode('div','field-hint','Baseline '+formatTokenCount(e.baseline_tokens)+' → emitted '+formatTokenCount(e.emitted_tokens)+' · confidence: '+(e.confidence||'unknown')),savingsNode('div','field-hint',counter+(excluded?' · '+excluded+' excluded/unclassified':'')),savingsNode('div','field-hint','Measures estimated prompt-context reduction; it does not measure provider billing.')); -} +/* overview */ +function formatTokenCount(value){return Math.max(0,Math.round(Number(value)||0)).toLocaleString()} +function savingsPercent(value){return Math.max(0,Math.min(100,Number(value)*100||0))} +function savingsCount(value){return Math.max(0,Math.round(Number(value)||0))} +function savingsNode(tag,cls,text){const n=document.createElement(tag);if(cls)n.className=cls;if(text!=null)n.textContent=text;return n} +function renderOverviewSavings(data,error){ + const el=document.getElementById('ov-savings'); + if(!el)return; + el.replaceChildren(); + if(error){el.append(savingsNode('div','empty','Savings estimate unavailable.'));return} + const e=(data&&data.estimated)||{},eligible=savingsCount(e.eligible_receipt_count),excluded=savingsCount(e.excluded_receipt_count)+savingsCount(e.unclassified_receipt_count)+savingsCount(e.invalid_estimate_count),saved=Number(e.saved_tokens)||0,ratio=Number(e.savings_ratio)||0,counters=Array.isArray(e.by_token_counter)?e.by_token_counter:[]; + if(!eligible){el.append(savingsNode('div','empty','No receipt-backed context savings yet.'),savingsNode('div','field-hint','Eligible deliveries will appear after adaptive context or context-delivery calls. '+excluded+' excluded or unclassified '+(excluded===1?'delivery':'deliveries')+'.'),savingsNode('div','field-hint','Measures estimated prompt-context reduction; it does not measure provider billing.'));return} + const counter=counters.length===1?'Counter: '+(counters[0].token_counter||'unknown'):counters.length?counters.length+' token counters (kept separate)':'Counter: unknown',pct=savingsPercent(ratio),pctLabel=pct.toFixed(1); + const hero=savingsNode('div','savings-hero'),total=savingsNode('div'),rate=savingsNode('div','savings-rate'),progress=document.createElement('progress'); + total.append(savingsNode('div','stat-val savings-number',formatTokenCount(saved)),savingsNode('div','savings-unit','tokens avoided'));rate.append(savingsNode('strong','',pctLabel+'%'),savingsNode('span','','estimated reduction'));hero.append(total,rate);progress.className='savings-progress';progress.max=100;progress.value=pct;progress.setAttribute('aria-label',pctLabel+'% estimated context reduction'); + el.append(hero,progress,savingsNode('div','savings-summary','Across '+eligible+' eligible context deliveries'),savingsNode('div','field-hint','Baseline '+formatTokenCount(e.baseline_tokens)+' → emitted '+formatTokenCount(e.emitted_tokens)+' · confidence: '+(e.confidence||'unknown')),savingsNode('div','field-hint',counter+(excluded?' · '+excluded+' excluded/unclassified':'')),savingsNode('div','field-hint','Measures estimated prompt-context reduction; it does not measure provider billing.')); +} async function loadOverview(){try{const st=await api('/stats?workspace='+encodeURIComponent(WS||''));setViewDesc('overview',(st.memories||0)+' memories · '+(st.workspaces||0)+' workspaces');const cards=[['Memories',st.memories],['Live rows',st.total_rows],['Workspaces',st.workspaces],['Sessions',st.sessions]];document.getElementById('stat-grid').innerHTML=cards.map(c=>`
${c[1]!=null?c[1]:'—'}
${c[0]}
`).join('');document.getElementById('nav-mem-count').textContent=st.memories||'';const bt=st.by_type||{};const tot=Object.values(bt).reduce((a,b)=>a+b,0)||1;document.getElementById('ov-types').innerHTML=Object.keys(bt).length?Object.entries(bt).map(([k,v])=>`
${esc(k)}
${v}
`).join(''):'
No memories
';try{renderOverviewSavings(await api('/context-savings?workspace='+encodeURIComponent(WS||'')))}catch(_err){renderOverviewSavings(null,true)}loadOverviewAnalytics()}catch(e){const msg='Overview unavailable: '+e.message;setViewDesc('overview',msg);document.getElementById('stat-grid').innerHTML='
'+esc(msg)+'
';document.getElementById('ov-types').innerHTML='
Memory types could not be loaded.
';document.getElementById('ov-savings').innerHTML='
Savings estimate could not be loaded.
';document.getElementById('ov-analytics').innerHTML='
Analytics could not be loaded.
';toast(msg,'err')}} async function loadOverviewAnalytics(){ const el=document.getElementById('ov-analytics'),lock=document.getElementById('ov-lock'); @@ -427,7 +427,7 @@ async function loadReceipts(){const el=document.getElementById('audit-body');el. async function downloadReceipts(){try{const d=await api('/receipts/export?workspace='+encodeURIComponent(WS||''));const blob=new Blob([JSON.stringify(d,null,2)],{type:'application/json'});const a=document.createElement('a');a.href=URL.createObjectURL(blob);a.download='engraphis-receipts-'+(WS||'workspace')+'.json';a.click();URL.revokeObjectURL(a.href);toast('Privacy-safe receipts exported','ok')}catch(e){toast(e.message,'err')}} let SAVINGS_PRESET='all'; -function savingsPresetQuery(){const p=new URLSearchParams({workspace:WS||''});if(SAVINGS_PRESET==='current')p.set('release_version','1.5.0');if(SAVINGS_PRESET==='7d')p.set('from_ts',String(Date.now()/1000-604800));return p.toString()} +function savingsPresetQuery(){const p=new URLSearchParams({workspace:WS||''});if(SAVINGS_PRESET==='current')p.set('release_version','1.6');if(SAVINGS_PRESET==='7d')p.set('from_ts',String(Date.now()/1000-604800));return p.toString()} function renderSavingsDetail(s){const e=(s&&s.estimated)||{},eligible=Number(e.eligible_receipt_count)||0,excluded=(Number(e.excluded_receipt_count)||0)+(Number(e.unclassified_receipt_count)||0)+(Number(e.invalid_estimate_count)||0),basisRows=(e.by_basis||[]).map(x=>'
'+esc((x.basis||'unclassified').replaceAll('_',' '))+' · '+esc(x.confidence||'unknown')+''+formatTokenCount(x.baseline_tokens)+' → '+formatTokenCount(x.emitted_tokens)+' · '+formatTokenCount(x.saved_tokens)+' saved ('+(x.receipt_count||0)+' delivery)
').join(''),counterRows=(e.by_token_counter||[]).map(x=>'
'+esc(x.token_counter||'unknown')+''+formatTokenCount(x.saved_tokens)+' saved · '+(x.receipt_count||0)+' eligible delivery
').join(''),preset=SAVINGS_PRESET==='current'?'Current release':SAVINGS_PRESET==='7d'?'Last 7 days':SAVINGS_PRESET==='since'?'Since tracking started':'All time';const buttons=['since','current','7d','all'].map(x=>'').join('');return '
Estimated context saved
View'+buttons+'
'+(eligible?'
'+formatTokenCount(e.saved_tokens)+' tokens
Baseline '+formatTokenCount(e.baseline_tokens)+' → emitted '+formatTokenCount(e.emitted_tokens)+' · '+(Number(e.savings_ratio||0)*100).toFixed(1)+'% estimated reduction
'+eligible+' eligible deliveries · confidence: '+esc(e.confidence||'unknown')+' · range: '+preset+'
'+(basisRows||'
No basis breakdown available.
')+(counterRows?'
Token counters
'+counterRows:''):'
No eligible estimates in this range.
')+'
'+excluded+' excluded or unclassified delivery(s). Measures estimated prompt-context reduction; it does not measure provider billing.
'} async function loadReceipts(){const el=document.getElementById('audit-body');el.innerHTML='
';try{if(!window.__savingsPresetBound){window.__savingsPresetBound=true;document.addEventListener('click',function(ev){const button=ev.target.closest('[data-savings-preset]');if(!button)return;SAVINGS_PRESET=button.getAttribute('data-savings-preset')||'all';loadReceipts()})}const q='workspace='+encodeURIComponent(WS||''),sq=savingsPresetQuery();const [d,v,s]=await Promise.all([api('/receipts?'+q+'&limit=500'),api('/receipts/verify?'+q),api('/context-savings?'+sq)]);const rows=d.entries||[],packed=(s.by_token_counter||[]).map(x=>'
'+esc(x.token_counter||'unknown')+''+formatTokenCount(x.context_tokens)+' packed / '+formatTokenCount(x.source_tokens)+' source · '+formatTokenCount(x.saved_tokens)+' legacy saved
').join('');const packedCard='
Packed context accounting
Packing savings compare retrieved source tokens with emitted context. They are not added again to adaptive history savings.
'+(packed||'
No complete context-usage receipts yet.
')+'
';el.innerHTML=renderSavingsDetail(s)+packedCard+'
Receipt chain '+(v.valid?'verified':'invalid')+'
'+(v.count||0)+' receipts · head '+esc((v.head||'').slice(0,24))+'
'+(rows.length?'
'+rows.map(r=>'
'+esc(r.operation||'operation')+''+esc((r.hash||'').slice(0,20))+' · '+esc(r.status||'ok')+' · '+(r.target_count||0)+' target(s)'+(r.ts_ms?fmtRel(r.ts_ms/1000):'')+'
').join('')+'
':'
No receipts yet.
')}catch(e){el.innerHTML='
'+esc(e.message)+'
'}} @@ -1197,7 +1197,7 @@ function loadGraphEngine(){ if(GRAPH_ENGINE_LOADING)return GRAPH_ENGINE_LOADING; GRAPH_ENGINE_LOADING=new Promise((resolve,reject)=>{ const script=document.createElement('script'); - script.src='/v2-assets/engraphis-graph.js?v=20260809-pin-only-physics'; + script.src='/v2-assets/engraphis-graph.js?v=20260809-physics-guard'; /* A 200 that never registers the global is a corrupt/truncated asset, not a success — resolving there would hand graphRenderEngine() an undefined EngraphisGraph. */ script.onload=()=>{typeof EngraphisGraph==='undefined'?reject(new Error('Graph engine asset loaded without registering EngraphisGraph')):resolve()}; diff --git a/engraphis/classic_assets/index.html b/engraphis/classic_assets/index.html index 421b2892..a6d3d846 100644 --- a/engraphis/classic_assets/index.html +++ b/engraphis/classic_assets/index.html @@ -1,355 +1,355 @@ - - - - - - - -Engraphis - - - -
- - - - -
-
- -
-
Memory operationsOperate
-

Overview

-
-
-
-
-
- - -
-
-
-
Memory types
-
Estimated context saved
Loading receipt-backed estimate…
-
Analytics
Loading…
-
-
- -
-
-

Score ranks relevance to this query. Retention estimates how strongly the memory persists over time.

-
Enter a query to recall memories.
-
- -
-
-

Open a focused memory with Enter. Reorder it with Alt + Up or Alt + Down.

-
Loading memories…
-
- -
-
-
- - - - - -Saved - -
-
- -
-
-
-
-
-
- -
-
Loading relevant memories…
-
- -
-
-
Ask a question to see the current answer and its history.
-
- -
-
-
Enter a topic to trace its history.
-
- -
-
-
Loading governance history…
-
- -
-
-
-
-
- -
Loading graph…
-
-
-
-
-
-Show -
- - - - -
- -
- - - - -
-
-
- - -
Layout
-
- - - - -
-
Forces
-
42
-
20
-
26
-
Appearance
-
3
-
12
-
0.7
-
30
-
Color by
-
- - - -
-
Style
-
- - - - -
-
Display
-
- - -
- -
Legend
-
-
Stats
-
-
Top connected
-
-
-
- -
-
-
-

Semantic graph explorer

Keyboard-accessible entities and relationships synchronized with the visual graph.

-

Entities

Relations

-
-
- -
-
- -
-
Connecting to hosted Analytics…
-
- -
-
- -
-
-
-
Decay distribution
Loading…
-
Trends
-
-
- -
-
-
Start with a dry run to preview changes. A dry run never modifies memories.
-
- -
-
Loading hosted Auto Consolidation and Auto Dreaming policy…
-
- -
-
-
-
Import files & folders
-
- -
Drag documents, code, PDFs, images, or audio/video here — local extraction stores them in the active folder
-
-
- - -
-or import from a path on this machine: - - - -
- -
-
-
-
Repository & database graph
-
- - - -
-
- - - -
-
-
-
Loading workspaces…
-
- -
-
Loading hosted Team options…
-
- -
-
-
- -
Cloud sync
Loading…
-
Hosted Team & agent access
Loading…
-
-
-
Connect an LLM
Loading…
-
-
- -
-
-
-
- - - - - - - -
- - - - - - - - + + + + + + + +Engraphis + + + +
+ + + + +
+
+ +
+
Memory operationsOperate
+

Overview

+
+
+
+
+
+ + +
+
+
+
Memory types
+
Estimated context saved
Loading receipt-backed estimate…
+
Analytics
Loading…
+
+
+ +
+
+

Score ranks relevance to this query. Retention estimates how strongly the memory persists over time.

+
Enter a query to recall memories.
+
+ +
+
+

Open a focused memory with Enter. Reorder it with Alt + Up or Alt + Down.

+
Loading memories…
+
+ +
+
+
+ + + + + +Saved + +
+
+ +
+
+
+
+
+
+ +
+
Loading relevant memories…
+
+ +
+
+
Ask a question to see the current answer and its history.
+
+ +
+
+
Enter a topic to trace its history.
+
+ +
+
+
Loading governance history…
+
+ +
+
+
+
+
+ +
Loading graph…
+
+
+
+
+
+Show +
+ + + + +
+ +
+ + + + +
+
+
+ + +
Layout
+
+ + + + +
+
Forces
+
42
+
20
+
26
+
Appearance
+
3
+
12
+
0.7
+
30
+
Color by
+
+ + + +
+
Style
+
+ + + + +
+
Display
+
+ + +
+ +
Legend
+
+
Stats
+
+
Top connected
+
+
+
+ +
+
+
+

Semantic graph explorer

Keyboard-accessible entities and relationships synchronized with the visual graph.

+

Entities

Relations

+
+
+ +
+
+ +
+
Connecting to hosted Analytics…
+
+ +
+
+ +
+
+
+
Decay distribution
Loading…
+
Trends
+
+
+ +
+
+
Start with a dry run to preview changes. A dry run never modifies memories.
+
+ +
+
Loading hosted Auto Consolidation and Auto Dreaming policy…
+
+ +
+
+
+
Import files & folders
+
+ +
Drag documents, code, PDFs, images, or audio/video here — local extraction stores them in the active folder
+
+
+ + +
+or import from a path on this machine: + + + +
+ +
+
+
+
Repository & database graph
+
+ + + +
+
+ + + +
+
+
+
Loading workspaces…
+
+ +
+
Loading hosted Team options…
+
+ +
+
+
+ +
Cloud sync
Loading…
+
Hosted Team & agent access
Loading…
+
+
+
Connect an LLM
Loading…
+
+
+ +
+
+
+
+ + + + + + + +
+ + + + + + + + diff --git a/engraphis/dashboard_assets/index.html b/engraphis/dashboard_assets/index.html index ee90afc3..235f7a57 100644 --- a/engraphis/dashboard_assets/index.html +++ b/engraphis/dashboard_assets/index.html @@ -7,7 +7,7 @@ Engraphis Ledger - +
@@ -637,6 +637,6 @@

Connected nodes

- + diff --git a/engraphis/dashboard_assets/ledger.js b/engraphis/dashboard_assets/ledger.js index 79119088..e78bc201 100644 --- a/engraphis/dashboard_assets/ledger.js +++ b/engraphis/dashboard_assets/ledger.js @@ -369,7 +369,7 @@ '/v2-assets/vendor/force-graph.min.js?v=20260727-final', 'ForceGraph', )).then(() => loadScript( - '/v2-assets/engraphis-graph.js?v=20260809-pin-only-physics', + '/v2-assets/engraphis-graph.js?v=20260809-physics-guard', 'EngraphisGraph', )); graphAssetsPromise = attempt; @@ -491,7 +491,28 @@ } function updatePlanBadge() { - // The side-menu header intentionally has no plan or upgrade badge. + const badge = byId('plan-badge'); + if (!badge || !state.license) return; + const access = licenseAccessState(); + const plan = licensePlanKey(); + const trial = licenseTrialAvailable(); + const label = access === 'active' ? plan.toUpperCase() + : access === 'trial' ? 'TRIAL' + : access === 'lapsed' ? 'BILLING' + : trial ? 'TRY PRO' : 'GET PRO'; + const aria = licenseHasHostedAccess() ? 'Open Engraphis Cloud account' + : access === 'lapsed' ? 'Update billing in Plans and billing' + : trial ? 'Start the 3-day Pro trial in Plans and billing' + : 'Subscribe to Pro in Plans and billing'; + badge.textContent = label; + badge.setAttribute('aria-label', aria); + badge.title = aria; + const cta = hostedCta(plan || 'pro', 'header'); + const opensAccount = cta.kind === 'account' && Boolean(cta.href); + badge.href = opensAccount ? cta.href : '#'; + badge.target = opensAccount ? '_blank' : ''; + badge.rel = opensAccount ? 'noopener' : ''; + badge.dataset.opensAccount = String(opensAccount); } function renderSidebarCta() { @@ -499,7 +520,7 @@ const detail = byId('sidebar-pro-detail'); const link = byId('sidebar-pro-cta'); if (!copy || !detail || !link || !state.license) return; - const canonicalProCtaLabel = 'Subscribe to Pro'; + const renderFeatureCtas = () => { [ ['analytics-pro-cta', 'analytics', 'pro'], @@ -532,7 +553,6 @@ link.textContent = cta.label; link.href = cta.href || '#'; link.setAttribute('aria-disabled', cta.href ? 'false' : 'true'); - link.dataset.proCtaLabel = canonicalProCtaLabel; link.dataset.proCta = 'sidebar'; renderFeatureCtas(); } @@ -667,40 +687,40 @@ function savingsQuery(workspace, preset = 'all') { const base = query(workspace); - if (preset === 'current') return `${base}&release_version=1.5.0`; + if (preset === 'current') return `${base}&release_version=1.6`; if (preset === '7d') return `${base}&from_ts=${encodeURIComponent(Date.now() / 1000 - 604800)}`; return base; } - function formatSavingsTokens(value) { - return Math.max(0, Math.round(number(value))).toLocaleString(); - } - - function savingsRatio(value) { - return Math.max(0, Math.min(1, number(value))); - } - - function savingsMetric(estimate) { - const ratio = savingsRatio(estimate.savings_ratio); - const hero = node('div', 'savings-hero'); - const value = node('div', 'savings-value'); - value.append( - node('strong', 'savings-number', formatSavingsTokens(estimate.saved_tokens)), - node('span', 'savings-unit', 'tokens avoided'), - ); - const rate = node('div', 'savings-rate'); - rate.append( - node('strong', 'savings-rate-value', `${(ratio * 100).toFixed(1)}%`), - node('span', 'savings-rate-label', 'estimated reduction'), - ); - hero.append(value, rate); - const progress = document.createElement('progress'); - progress.className = 'savings-progress'; - progress.max = 1; - progress.value = ratio; - progress.setAttribute('aria-label', `${(ratio * 100).toFixed(1)}% estimated context reduction`); - return { hero, progress }; - } + function formatSavingsTokens(value) { + return Math.max(0, Math.round(number(value))).toLocaleString(); + } + + function savingsRatio(value) { + return Math.max(0, Math.min(1, number(value))); + } + + function savingsMetric(estimate) { + const ratio = savingsRatio(estimate.savings_ratio); + const hero = node('div', 'savings-hero'); + const value = node('div', 'savings-value'); + value.append( + node('strong', 'savings-number', formatSavingsTokens(estimate.saved_tokens)), + node('span', 'savings-unit', 'tokens avoided'), + ); + const rate = node('div', 'savings-rate'); + rate.append( + node('strong', 'savings-rate-value', `${(ratio * 100).toFixed(1)}%`), + node('span', 'savings-rate-label', 'estimated reduction'), + ); + hero.append(value, rate); + const progress = document.createElement('progress'); + progress.className = 'savings-progress'; + progress.max = 1; + progress.value = ratio; + progress.setAttribute('aria-label', `${(ratio * 100).toFixed(1)}% estimated context reduction`); + return { hero, progress }; + } function savingsCounts(payload) { const estimate = payload && payload.estimated ? payload.estimated : {}; @@ -721,17 +741,17 @@ if (!eligible) { target.append( empty('No receipt-backed context savings yet.'), - node('p', 'field-note', `${excluded} excluded or unclassified ${excluded === 1 ? 'delivery' : 'deliveries'} so far.`), + node('p', 'field-note', `${excluded} excluded or unclassified ${excluded === 1 ? 'delivery' : 'deliveries'} so far.`), ); return; } - const metric = savingsMetric(estimate); - target.append( - metric.hero, - metric.progress, - node('p', 'savings-summary', `Across ${eligible} eligible context deliveries`), - node('p', 'field-note', `Baseline ${formatSavingsTokens(estimate.baseline_tokens)} → emitted ${formatSavingsTokens(estimate.emitted_tokens)} · confidence: ${text(estimate.confidence || 'unknown')}`), - node('p', 'field-note', `${excluded} excluded or unclassified ${excluded === 1 ? 'delivery' : 'deliveries'}.`), + const metric = savingsMetric(estimate); + target.append( + metric.hero, + metric.progress, + node('p', 'savings-summary', `Across ${eligible} eligible context deliveries`), + node('p', 'field-note', `Baseline ${formatSavingsTokens(estimate.baseline_tokens)} → emitted ${formatSavingsTokens(estimate.emitted_tokens)} · confidence: ${text(estimate.confidence || 'unknown')}`), + node('p', 'field-note', `${excluded} excluded or unclassified ${excluded === 1 ? 'delivery' : 'deliveries'}.`), ); } @@ -740,8 +760,8 @@ if (!target) return; const { estimate, eligible, excluded } = savingsCounts(payload); target.replaceChildren(); - const metric = eligible ? savingsMetric(estimate) : null; - const header = node('div', 'savings-detail-header'); + const metric = eligible ? savingsMetric(estimate) : null; + const header = node('div', 'savings-detail-header'); const presets = node('div', 'savings-presets'); [ ['since', 'Since tracking started'], @@ -756,12 +776,12 @@ control.classList.toggle('active', state.savingsPreset === value); presets.append(control); }); - header.append(presets); - if (metric) target.append(metric.hero, metric.progress); - target.append(header); - target.append(node('p', 'savings-summary', eligible - ? `${eligible} eligible deliveries` - : 'No eligible estimates in this range.')); + header.append(presets); + if (metric) target.append(metric.hero, metric.progress); + target.append(header); + target.append(node('p', 'savings-summary', eligible + ? `${eligible} eligible deliveries` + : 'No eligible estimates in this range.')); if (eligible) { target.append(node('p', 'field-note', `Baseline ${formatSavingsTokens(estimate.baseline_tokens)} → emitted ${formatSavingsTokens(estimate.emitted_tokens)} · confidence: ${text(estimate.confidence || 'unknown')}`)); target.append(node('p', 'field-note', 'Packed context is packing savings; adaptive history is estimated avoided prompt context.')); diff --git a/engraphis/static/dashboard.js b/engraphis/static/dashboard.js index d1333fff..282692a5 100644 --- a/engraphis/static/dashboard.js +++ b/engraphis/static/dashboard.js @@ -131,23 +131,23 @@ function setWS(name){ } async function loadWorkspaceList(){const d=await api('/workspaces');WORKSPACES=d.workspaces||[];if(!WS&&WORKSPACES.length){WORKSPACES.sort((a,b)=>(b.memories||0)-(a.memories||0));setWS(WORKSPACES[0].name)}} -/* overview */ -function formatTokenCount(value){return Math.max(0,Math.round(Number(value)||0)).toLocaleString()} -function savingsPercent(value){return Math.max(0,Math.min(100,Number(value)*100||0))} -function savingsCount(value){return Math.max(0,Math.round(Number(value)||0))} -function savingsNode(tag,cls,text){const n=document.createElement(tag);if(cls)n.className=cls;if(text!=null)n.textContent=text;return n} -function renderOverviewSavings(data,error){ - const el=document.getElementById('ov-savings'); - if(!el)return; - el.replaceChildren(); - if(error){el.append(savingsNode('div','empty','Savings estimate unavailable.'));return} - const e=(data&&data.estimated)||{},eligible=savingsCount(e.eligible_receipt_count),excluded=savingsCount(e.excluded_receipt_count)+savingsCount(e.unclassified_receipt_count)+savingsCount(e.invalid_estimate_count),saved=Number(e.saved_tokens)||0,ratio=Number(e.savings_ratio)||0,counters=Array.isArray(e.by_token_counter)?e.by_token_counter:[]; - if(!eligible){el.append(savingsNode('div','empty','No receipt-backed context savings yet.'),savingsNode('div','field-hint','Eligible deliveries will appear after adaptive context or context-delivery calls. '+excluded+' excluded or unclassified '+(excluded===1?'delivery':'deliveries')+'.'),savingsNode('div','field-hint','Measures estimated prompt-context reduction; it does not measure provider billing.'));return} - const counter=counters.length===1?'Counter: '+(counters[0].token_counter||'unknown'):counters.length?counters.length+' token counters (kept separate)':'Counter: unknown',pct=savingsPercent(ratio),pctLabel=pct.toFixed(1); - const hero=savingsNode('div','savings-hero'),total=savingsNode('div'),rate=savingsNode('div','savings-rate'),progress=document.createElement('progress'); - total.append(savingsNode('div','stat-val savings-number',formatTokenCount(saved)),savingsNode('div','savings-unit','tokens avoided'));rate.append(savingsNode('strong','',pctLabel+'%'),savingsNode('span','','estimated reduction'));hero.append(total,rate);progress.className='savings-progress';progress.max=100;progress.value=pct;progress.setAttribute('aria-label',pctLabel+'% estimated context reduction'); - el.append(hero,progress,savingsNode('div','savings-summary','Across '+eligible+' eligible context deliveries'),savingsNode('div','field-hint','Baseline '+formatTokenCount(e.baseline_tokens)+' → emitted '+formatTokenCount(e.emitted_tokens)+' · confidence: '+(e.confidence||'unknown')),savingsNode('div','field-hint',counter+(excluded?' · '+excluded+' excluded/unclassified':'')),savingsNode('div','field-hint','Measures estimated prompt-context reduction; it does not measure provider billing.')); -} +/* overview */ +function formatTokenCount(value){return Math.max(0,Math.round(Number(value)||0)).toLocaleString()} +function savingsPercent(value){return Math.max(0,Math.min(100,Number(value)*100||0))} +function savingsCount(value){return Math.max(0,Math.round(Number(value)||0))} +function savingsNode(tag,cls,text){const n=document.createElement(tag);if(cls)n.className=cls;if(text!=null)n.textContent=text;return n} +function renderOverviewSavings(data,error){ + const el=document.getElementById('ov-savings'); + if(!el)return; + el.replaceChildren(); + if(error){el.append(savingsNode('div','empty','Savings estimate unavailable.'));return} + const e=(data&&data.estimated)||{},eligible=savingsCount(e.eligible_receipt_count),excluded=savingsCount(e.excluded_receipt_count)+savingsCount(e.unclassified_receipt_count)+savingsCount(e.invalid_estimate_count),saved=Number(e.saved_tokens)||0,ratio=Number(e.savings_ratio)||0,counters=Array.isArray(e.by_token_counter)?e.by_token_counter:[]; + if(!eligible){el.append(savingsNode('div','empty','No receipt-backed context savings yet.'),savingsNode('div','field-hint','Eligible deliveries will appear after adaptive context or context-delivery calls. '+excluded+' excluded or unclassified '+(excluded===1?'delivery':'deliveries')+'.'),savingsNode('div','field-hint','Measures estimated prompt-context reduction; it does not measure provider billing.'));return} + const counter=counters.length===1?'Counter: '+(counters[0].token_counter||'unknown'):counters.length?counters.length+' token counters (kept separate)':'Counter: unknown',pct=savingsPercent(ratio),pctLabel=pct.toFixed(1); + const hero=savingsNode('div','savings-hero'),total=savingsNode('div'),rate=savingsNode('div','savings-rate'),progress=document.createElement('progress'); + total.append(savingsNode('div','stat-val savings-number',formatTokenCount(saved)),savingsNode('div','savings-unit','tokens avoided'));rate.append(savingsNode('strong','',pctLabel+'%'),savingsNode('span','','estimated reduction'));hero.append(total,rate);progress.className='savings-progress';progress.max=100;progress.value=pct;progress.setAttribute('aria-label',pctLabel+'% estimated context reduction'); + el.append(hero,progress,savingsNode('div','savings-summary','Across '+eligible+' eligible context deliveries'),savingsNode('div','field-hint','Baseline '+formatTokenCount(e.baseline_tokens)+' → emitted '+formatTokenCount(e.emitted_tokens)+' · confidence: '+(e.confidence||'unknown')),savingsNode('div','field-hint',counter+(excluded?' · '+excluded+' excluded/unclassified':'')),savingsNode('div','field-hint','Measures estimated prompt-context reduction; it does not measure provider billing.')); +} async function loadOverview(){try{const st=await api('/stats?workspace='+encodeURIComponent(WS||''));setViewDesc('overview',(st.memories||0)+' memories · '+(st.workspaces||0)+' workspaces');const cards=[['Memories',st.memories],['Live rows',st.total_rows],['Workspaces',st.workspaces],['Sessions',st.sessions]];document.getElementById('stat-grid').innerHTML=cards.map(c=>`
${c[1]!=null?c[1]:'—'}
${c[0]}
`).join('');document.getElementById('nav-mem-count').textContent=st.memories||'';const bt=st.by_type||{};const tot=Object.values(bt).reduce((a,b)=>a+b,0)||1;document.getElementById('ov-types').innerHTML=Object.keys(bt).length?Object.entries(bt).map(([k,v])=>`
${esc(k)}
${v}
`).join(''):'
No memories
';try{renderOverviewSavings(await api('/context-savings?workspace='+encodeURIComponent(WS||'')))}catch(_err){renderOverviewSavings(null,true)}loadOverviewAnalytics()}catch(e){const msg='Overview unavailable: '+e.message;setViewDesc('overview',msg);document.getElementById('stat-grid').innerHTML='
'+esc(msg)+'
';document.getElementById('ov-types').innerHTML='
Memory types could not be loaded.
';document.getElementById('ov-savings').innerHTML='
Savings estimate could not be loaded.
';document.getElementById('ov-analytics').innerHTML='
Analytics could not be loaded.
';toast(msg,'err')}} async function loadOverviewAnalytics(){ const el=document.getElementById('ov-analytics'),lock=document.getElementById('ov-lock'); @@ -427,7 +427,7 @@ async function loadReceipts(){const el=document.getElementById('audit-body');el. async function downloadReceipts(){try{const d=await api('/receipts/export?workspace='+encodeURIComponent(WS||''));const blob=new Blob([JSON.stringify(d,null,2)],{type:'application/json'});const a=document.createElement('a');a.href=URL.createObjectURL(blob);a.download='engraphis-receipts-'+(WS||'workspace')+'.json';a.click();URL.revokeObjectURL(a.href);toast('Privacy-safe receipts exported','ok')}catch(e){toast(e.message,'err')}} let SAVINGS_PRESET='all'; -function savingsPresetQuery(){const p=new URLSearchParams({workspace:WS||''});if(SAVINGS_PRESET==='current')p.set('release_version','1.5.0');if(SAVINGS_PRESET==='7d')p.set('from_ts',String(Date.now()/1000-604800));return p.toString()} +function savingsPresetQuery(){const p=new URLSearchParams({workspace:WS||''});if(SAVINGS_PRESET==='current')p.set('release_version','1.6');if(SAVINGS_PRESET==='7d')p.set('from_ts',String(Date.now()/1000-604800));return p.toString()} function renderSavingsDetail(s){const e=(s&&s.estimated)||{},eligible=Number(e.eligible_receipt_count)||0,excluded=(Number(e.excluded_receipt_count)||0)+(Number(e.unclassified_receipt_count)||0)+(Number(e.invalid_estimate_count)||0),basisRows=(e.by_basis||[]).map(x=>'
'+esc((x.basis||'unclassified').replaceAll('_',' '))+' · '+esc(x.confidence||'unknown')+''+formatTokenCount(x.baseline_tokens)+' → '+formatTokenCount(x.emitted_tokens)+' · '+formatTokenCount(x.saved_tokens)+' saved ('+(x.receipt_count||0)+' delivery)
').join(''),counterRows=(e.by_token_counter||[]).map(x=>'
'+esc(x.token_counter||'unknown')+''+formatTokenCount(x.saved_tokens)+' saved · '+(x.receipt_count||0)+' eligible delivery
').join(''),preset=SAVINGS_PRESET==='current'?'Current release':SAVINGS_PRESET==='7d'?'Last 7 days':SAVINGS_PRESET==='since'?'Since tracking started':'All time';const buttons=['since','current','7d','all'].map(x=>'').join('');return '
Estimated context saved
View'+buttons+'
'+(eligible?'
'+formatTokenCount(e.saved_tokens)+' tokens
Baseline '+formatTokenCount(e.baseline_tokens)+' → emitted '+formatTokenCount(e.emitted_tokens)+' · '+(Number(e.savings_ratio||0)*100).toFixed(1)+'% estimated reduction
'+eligible+' eligible deliveries · confidence: '+esc(e.confidence||'unknown')+' · range: '+preset+'
'+(basisRows||'
No basis breakdown available.
')+(counterRows?'
Token counters
'+counterRows:''):'
No eligible estimates in this range.
')+'
'+excluded+' excluded or unclassified delivery(s). Measures estimated prompt-context reduction; it does not measure provider billing.
'} async function loadReceipts(){const el=document.getElementById('audit-body');el.innerHTML='
';try{if(!window.__savingsPresetBound){window.__savingsPresetBound=true;document.addEventListener('click',function(ev){const button=ev.target.closest('[data-savings-preset]');if(!button)return;SAVINGS_PRESET=button.getAttribute('data-savings-preset')||'all';loadReceipts()})}const q='workspace='+encodeURIComponent(WS||''),sq=savingsPresetQuery();const [d,v,s]=await Promise.all([api('/receipts?'+q+'&limit=500'),api('/receipts/verify?'+q),api('/context-savings?'+sq)]);const rows=d.entries||[],packed=(s.by_token_counter||[]).map(x=>'
'+esc(x.token_counter||'unknown')+''+formatTokenCount(x.context_tokens)+' packed / '+formatTokenCount(x.source_tokens)+' source · '+formatTokenCount(x.saved_tokens)+' legacy saved
').join('');const packedCard='
Packed context accounting
Packing savings compare retrieved source tokens with emitted context. They are not added again to adaptive history savings.
'+(packed||'
No complete context-usage receipts yet.
')+'
';el.innerHTML=renderSavingsDetail(s)+packedCard+'
Receipt chain '+(v.valid?'verified':'invalid')+'
'+(v.count||0)+' receipts · head '+esc((v.head||'').slice(0,24))+'
'+(rows.length?'
'+rows.map(r=>'
'+esc(r.operation||'operation')+''+esc((r.hash||'').slice(0,20))+' · '+esc(r.status||'ok')+' · '+(r.target_count||0)+' target(s)'+(r.ts_ms?fmtRel(r.ts_ms/1000):'')+'
').join('')+'
':'
No receipts yet.
')}catch(e){el.innerHTML='
'+esc(e.message)+'
'}} @@ -1197,7 +1197,7 @@ function loadGraphEngine(){ if(GRAPH_ENGINE_LOADING)return GRAPH_ENGINE_LOADING; GRAPH_ENGINE_LOADING=new Promise((resolve,reject)=>{ const script=document.createElement('script'); - script.src='/v2-assets/engraphis-graph.js?v=20260809-pin-only-physics'; + script.src='/v2-assets/engraphis-graph.js?v=20260809-physics-guard'; /* A 200 that never registers the global is a corrupt/truncated asset, not a success — resolving there would hand graphRenderEngine() an undefined EngraphisGraph. */ script.onload=()=>{typeof EngraphisGraph==='undefined'?reject(new Error('Graph engine asset loaded without registering EngraphisGraph')):resolve()}; diff --git a/engraphis/static/index.html b/engraphis/static/index.html index af0b008d..7e07af91 100644 --- a/engraphis/static/index.html +++ b/engraphis/static/index.html @@ -1,354 +1,354 @@ - - - - - - - -Engraphis - - - -
- - - - -
-
- -
-
Memory operationsOperate
-

Overview

-
-
-
-
-
- - -
-
-
-
Memory types
-
Estimated context saved
Loading receipt-backed estimate…
-
Analytics
Loading…
-
-
- -
-
-

Score ranks relevance to this query. Retention estimates how strongly the memory persists over time.

-
Enter a query to recall memories.
-
- -
-
-

Open a focused memory with Enter. Reorder it with Alt + Up or Alt + Down.

-
Loading memories…
-
- -
-
-
- - - - - -Saved - -
-
- -
-
-
-
-
-
- -
-
Loading relevant memories…
-
- -
-
-
Ask a question to see the current answer and its history.
-
- -
-
-
Enter a topic to trace its history.
-
- -
-
-
Loading governance history…
-
- -
-
-
-
-
- -
Loading graph…
-
-
-
-
-
-Show -
- - - - -
- -
- - - - -
-
-
- - -
Layout
-
- - - - -
-
Forces
-
42
-
20
-
26
-
Appearance
-
3
-
12
-
0.7
-
30
-
Color by
-
- - - -
-
Style
-
- - - - -
-
Display
-
- - -
- -
Legend
-
-
Stats
-
-
Top connected
-
-
-
- -
-
-
-

Semantic graph explorer

Keyboard-accessible entities and relationships synchronized with the visual graph.

-

Entities

Relations

-
-
- -
-
- -
-
Connecting to hosted Analytics…
-
- -
-
- -
-
-
-
Decay distribution
Loading…
-
Trends
-
-
- -
-
-
Start with a dry run to preview changes. A dry run never modifies memories.
-
- -
-
Loading hosted Auto Consolidation and Auto Dreaming policy…
-
- -
-
-
-
Import files & folders
-
- -
Drag documents, code, PDFs, images, or audio/video here — local extraction stores them in the active folder
-
-
- - -
-or import from a path on this machine: - - - -
- -
-
-
-
Repository & database graph
-
- - - -
-
- - - -
-
-
-
Loading workspaces…
-
- -
-
Loading hosted Team options…
-
- -
-
-
- -
Cloud sync
Loading…
-
Hosted Team & agent access
Loading…
-
-
-
Connect an LLM
Loading…
-
-
- -
-
-
-
- - - - - - - -
- - - - - - - - + + + + + + + +Engraphis + + + +
+ + + + +
+
+ +
+
Memory operationsOperate
+

Overview

+
+
+
+
+
+ + +
+
+
+
Memory types
+
Estimated context saved
Loading receipt-backed estimate…
+
Analytics
Loading…
+
+
+ +
+
+

Score ranks relevance to this query. Retention estimates how strongly the memory persists over time.

+
Enter a query to recall memories.
+
+ +
+
+

Open a focused memory with Enter. Reorder it with Alt + Up or Alt + Down.

+
Loading memories…
+
+ +
+
+
+ + + + + +Saved + +
+
+ +
+
+
+
+
+
+ +
+
Loading relevant memories…
+
+ +
+
+
Ask a question to see the current answer and its history.
+
+ +
+
+
Enter a topic to trace its history.
+
+ +
+
+
Loading governance history…
+
+ +
+
+
+
+
+ +
Loading graph…
+
+
+
+
+
+Show +
+ + + + +
+ +
+ + + + +
+
+
+ + +
Layout
+
+ + + + +
+
Forces
+
42
+
20
+
26
+
Appearance
+
3
+
12
+
0.7
+
30
+
Color by
+
+ + + +
+
Style
+
+ + + + +
+
Display
+
+ + +
+ +
Legend
+
+
Stats
+
+
Top connected
+
+
+
+ +
+
+
+

Semantic graph explorer

Keyboard-accessible entities and relationships synchronized with the visual graph.

+

Entities

Relations

+
+
+ +
+
+ +
+
Connecting to hosted Analytics…
+
+ +
+
+ +
+
+
+
Decay distribution
Loading…
+
Trends
+
+
+ +
+
+
Start with a dry run to preview changes. A dry run never modifies memories.
+
+ +
+
Loading hosted Auto Consolidation and Auto Dreaming policy…
+
+ +
+
+
+
Import files & folders
+
+ +
Drag documents, code, PDFs, images, or audio/video here — local extraction stores them in the active folder
+
+
+ + +
+or import from a path on this machine: + + + +
+ +
+
+
+
Repository & database graph
+
+ + + +
+
+ + + +
+
+
+
Loading workspaces…
+
+ +
+
Loading hosted Team options…
+
+ +
+
+
+ +
Cloud sync
Loading…
+
Hosted Team & agent access
Loading…
+
+
+
Connect an LLM
Loading…
+
+
+ +
+
+
+
+ + + + + + + +
+ + + + + + + + From af412514d81635564e491a1c72fa621371cf21a6 Mon Sep 17 00:00:00 2001 From: Jaixii Date: Sun, 9 Aug 2026 22:48:37 -0400 Subject: [PATCH 2/5] test: expect physics-guard asset tag in dashboard smoke Co-authored-by: CommandCodeBot --- tests/test_dashboard_v2.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_dashboard_v2.py b/tests/test_dashboard_v2.py index f3176a3c..1d3ad516 100644 --- a/tests/test_dashboard_v2.py +++ b/tests/test_dashboard_v2.py @@ -81,13 +81,13 @@ def test_dashboard_serves_and_bootstraps_local_core(monkeypatch, tmp_path): assert ledger_js.status_code == 200 assert "'/v2-assets/vendor/d3.min.js?v=20260727-final'" in ledger_js.text assert "'/v2-assets/vendor/force-graph.min.js?v=20260727-final'" in ledger_js.text - assert "'/v2-assets/engraphis-graph.js?v=20260809-pin-only-physics'" in ledger_js.text - assert "/v2-assets/ledger.css?v=20260809-pin-only-physics" in page.text - assert "/v2-assets/ledger.js?v=20260809-pin-only-physics" in page.text + assert "'/v2-assets/engraphis-graph.js?v=20260809-physics-guard'" in ledger_js.text + assert "/v2-assets/ledger.css?v=20260809-physics-guard" in page.text + assert "/v2-assets/ledger.js?v=20260809-physics-guard" in page.text classic_js = client.get("/classic-assets/dashboard.js") assert classic_js.status_code == 200 assert "/static/vendor/force-graph.min.js?v=20260809-csp" in classic_js.text - assert "/v2-assets/engraphis-graph.js?v=20260809-pin-only-physics" in classic_js.text + assert "/v2-assets/engraphis-graph.js?v=20260809-physics-guard" in classic_js.text assert "graphLimit=GRAPH_FULL?20000:320" in classic_js.text assert "graphScope=GRAPH_FULL?'&full=true':(showUnlinked?'':'&connected_only=true')" in classic_js.text bootstrap = client.get("/api/bootstrap") From ffa24f306c707d0a8ebd917a9ff47ed9bd10496a Mon Sep 17 00:00:00 2001 From: Jaixii Date: Sun, 9 Aug 2026 23:03:58 -0400 Subject: [PATCH 3/5] test: expect physics-guard asset tag in graph engine asset tests Co-authored-by: CommandCodeBot --- tests/test_graph_engine_asset.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/test_graph_engine_asset.py b/tests/test_graph_engine_asset.py index 908288cd..d04028c3 100644 --- a/tests/test_graph_engine_asset.py +++ b/tests/test_graph_engine_asset.py @@ -138,7 +138,7 @@ def test_graph_assets_are_never_loaded_on_a_plain_page_view() -> None: html = INDEX.read_text(encoding="utf-8") eager = re.findall(r']+src=["\'](/static/[^"\']+)["\']', html) assert "/static/vendor/d3.min.js" in eager - assert "/static/dashboard.js?v=20260809-pin-only-physics" in eager + assert "/static/dashboard.js?v=20260809-physics-guard" in eager assert "/static/vendor/force-graph.min.js" not in eager assert "/static/engraphis-graph.js" not in eager @@ -160,7 +160,7 @@ def test_opt_in_graph_asset_is_lazily_loaded_after_its_dependencies() -> None: source = DASHBOARD.read_text(encoding="utf-8") assert "script.src='/static/vendor/force-graph.min.js?v=20260809-csp'" in source assert ( - "script.src='/v2-assets/engraphis-graph.js?v=20260809-pin-only-physics'" + "script.src='/v2-assets/engraphis-graph.js?v=20260809-physics-guard'" in source ) render = source[source.index("function graphRender("):] @@ -281,7 +281,7 @@ def test_graph_engine_deep_link_reaches_the_next_engine_after_a_lazy_load() -> N report = _run_routing("loads") assert report["appended"] == [ - "/v2-assets/engraphis-graph.js?v=20260809-pin-only-physics" + "/v2-assets/engraphis-graph.js?v=20260809-physics-guard" ] # It waits rather than rendering something wrong in the meantime. assert report["beforeSettle"] == {"engine": 0, "classic": 0} @@ -296,7 +296,7 @@ def test_classic_route_reaches_the_canonical_engine_without_a_query_flag() -> No report = _run_routing("classic") assert report["appended"] == [ - "/v2-assets/engraphis-graph.js?v=20260809-pin-only-physics" + "/v2-assets/engraphis-graph.js?v=20260809-physics-guard" ] assert report["beforeSettle"] == {"engine": 0, "classic": 0} assert report["engine"] == 1 @@ -1470,7 +1470,7 @@ def test_primary_graph_dependencies_are_lazy_retryable_and_csp_clean() -> None: source.index("function safeUrl", source.index("function ensureGraphAssets()"))] d3 = loader.index("'/v2-assets/vendor/d3.min.js?v=20260727-final'") force_graph = loader.index("'/v2-assets/vendor/force-graph.min.js?v=20260727-final'") - renderer = loader.index("'/v2-assets/engraphis-graph.js?v=20260809-pin-only-physics'") + renderer = loader.index("'/v2-assets/engraphis-graph.js?v=20260809-physics-guard'") assert d3 < force_graph < renderer assert "if (graphAssetsPromise === attempt) graphAssetsPromise = null" in loader assert not re.search(r'document\.createElement\(["\']style["\']\)', vendor) From aa75c3adf51d5cf043f086a3beb22f5340be6f76 Mon Sep 17 00:00:00 2001 From: Jaixii Date: Sun, 9 Aug 2026 23:14:49 -0400 Subject: [PATCH 4/5] fix(dashboard): reinstate the plan badge element in the Ledger sidebar updatePlanBadge() and its click routing were restored with the 1.6 release, but dashboard_assets/index.html no longer contains the #plan-badge element, so the badge logic never rendered for any license. Re-add the badge anchor in the sidebar brand row (the CSS rules already exist) so plan, trial, and billing state is visible again. Co-authored-by: CommandCodeBot --- engraphis/dashboard_assets/index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/engraphis/dashboard_assets/index.html b/engraphis/dashboard_assets/index.html index 0ce8e406..16532ee9 100644 --- a/engraphis/dashboard_assets/index.html +++ b/engraphis/dashboard_assets/index.html @@ -15,6 +15,7 @@