feat(intelligence): expose DeductionPanel on web as PRO feature (#2385)

* feat(intelligence): expose DeductionPanel on web as PRO feature

Previously desktop-only (isDesktopApp guard). Now available on web
with premium: 'locked' gating — free users see lock + PRO badge,
PRO users get full access.

4-layer checklist applied:
- Layer 1: added 'deduction' to ALL_PANELS with premium: 'locked'
- Layer 2: added 'deduction' to apiKeyPanels in isPanelEntitled
- Layer 3: added /api/intelligence/v1/deduct-situation to PREMIUM_RPC_PATHS
- Layer 4: removed isDesktopApp guard (SITE_VARIANT === 'full' only)
- Bonus: removed now-redundant deduction exclusion from dev panel warning

* docs: add analytical frameworks review findings plan

* docs: fix markdownlint blank line before list in plan

* fix(panel-layout): replay settings for async deduction panel

The web-only DeductionPanel mounts after the initial startup pass, so saved disabled state was never replayed on reload.

Reapply panel settings after the async mount and add a guardrail test to keep the lazy panel path aligned with the rest of the layout.

* fix(intelligence): add deduction to WEB_PREMIUM_PANELS, replay updatePanelGating after async mount, fix stale test comment

---------

Co-authored-by: lspassos1 <lspassos@icloud.com>
This commit is contained in:
Elie Habib
2026-03-28 01:11:22 +04:00
committed by GitHub
parent f783bf2d2d
commit 63858a8711
5 changed files with 492 additions and 4 deletions

View File

@@ -39,7 +39,7 @@ describe('panel-config guardrails', () => {
const allowedContexts = [
/this\.ctx\.panels\[key\]\s*=/, // createPanel helper
/this\.ctx\.panels\['deduction'\]/, // desktop-only, intentionally ungated
/this\.ctx\.panels\['deduction'\]/, // async-mounted PRO panel — gated via WEB_PREMIUM_PANELS
/this\.ctx\.panels\['runtime-config'\]/, // desktop-only, intentionally ungated
/this\.ctx\.panels\['live-news'\]/, // mountLiveNewsIfReady — has its own channel guard
/panel as unknown as/, // lazyPanel generic cast
@@ -73,6 +73,19 @@ describe('panel-config guardrails', () => {
);
});
it('reapplies panel settings after mounting the async deduction panel', () => {
const deductionMount = panelLayoutSrc.match(
/import\('@\/components\/DeductionPanel'\)\.then\(\(\{ DeductionPanel \}\) => \{([\s\S]*?)\n\s*\}\);/
);
assert.ok(deductionMount, 'expected async DeductionPanel mount block in panel-layout.ts');
assert.match(
deductionMount[1],
/this\.applyPanelSettings\(\);/,
'async DeductionPanel mount must replay saved panel settings after insertion',
);
});
it('panel keys are consistent across variant configs (no typos)', () => {
const allKeys = new Map();
for (const v of VARIANT_FILES) {