feat(pulse): hide nav tabs whose module is switched off - #1749
Open
elhoim wants to merge 2 commits into
Open
Conversation
…d flags loadModules() gates on config.local_intelligence, config.hypotheses and config.upgrades, but loadPulseConfig() builds its return object key by key and never copies those three through. The gates read undefined, every one of them is written '?.enabled !== false', and undefined !== false is true — so all three modules always loaded and setting [local_intelligence] enabled = false in PULSE.toml silently did nothing. Fourteen more user-facing modules (docs, memory, conduit, books, synapse, ledger, projects, gear, atlas, threatmodel, usage, evals, algorithm, hermes) had no switch at all, and the HEALTH/FINANCES/BUSINESS/GROWTH surfaces have no module of their own to switch. Adds a [modules] table as the single place to turn a surface off, resolved in lib/modules.ts from three layers: defaults, the older [section].enabled flags, then [modules]. Existing configs need no changes and behave identically — the shipped defaults leave everything on except the opt-in imessage and syslog. GET /api/config/modules exposes the resolved map so the dashboard can hide a disabled module's tab. Infrastructure Pulse needs to serve anything at all (observability, hooks, tab-freshness, menubar, siri, doctor) is deliberately not switchable.
Switching a module off in PULSE.toml stopped its backend but left its tab in both nav rows, the mobile menu and the command palette. Clicking one opened a page with nothing answering it — an empty view rather than an honest absence. Nav entries now carry the [modules] key that backs them, and AppHeader and CommandPalette filter on the resolved map from GET /api/config/modules. Entries with no key (Home, Agents, Skills, Hooks, Arbol, Security) are infrastructure and always render. The hook fails open: until the fetch lands, and permanently if it errors or the daemon predates the endpoint, every entry stays visible. Briefly showing a tab that then disappears beats a nav that empties itself because one request failed. Depends on the [modules] table and /api/config/modules from feat/pulse-module-toggles.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Switching a module off stops its backend but leaves its tab in both nav rows, the mobile menu and the command palette. Clicking one opens a page with nothing answering it — an empty view rather than an honest absence.
GROWTH shows this without any config at all: it's backed by an optional USER customization that isn't shipped, so on a fresh install the tab is always there and always empty.
Change
nav-manifest.ts—NavItem.module?: string, set on 21 entries. Entries without one (Home, Agents, Skills, Hooks, Arbol, Security) are infrastructure and always render.lib/use-enabled-modules.ts(new) — fetches/api/config/modulesonce, returns auseCallback-stable predicate so callers can memoise on it.AppHeader.tsx— filterstier1NavandsystemNavat all four render sites (desktop tier-1, desktop system row, mobile sections, mobile system).CommandPalette.tsx— searches the filtered set, soCmd-Kcan't jump to a page the nav is hiding.Fails open, deliberately
Until the fetch lands — and permanently if it errors, or the daemon predates the endpoint — every entry stays visible. A dashboard briefly showing a tab it then hides is a much smaller problem than a nav that empties itself because one request failed.
Verification
Ported to my own install, built (
next build, 40 static routes), restarted Pulse withlocal = false.Backend:
/api/config/modulesreportslocaloff;/api/local-intelligence/latest→ 404;LocalIntelligence module loadedabsent from the current boot's journal.Rendered nav — the real
AppHeaderat 1440px, read out of the live DOM:14 links.
/localsits between/growthand/assetsin the manifest and is absent from the render; the sequence jumps straight from/growthto/assets. Every other entry is untouched.Visually — captured the desktop header in a real Chrome and zoomed in on the nav strip. Row 1 reads
TELOS WORK CONTENT PROJECTS HEALTH FINANCES BUSINESS, row 2 readsGROWTH ASSETS KNOWLEDGE AMBER. No LOCAL.tsc --noEmit: 26 pre-existing errors in unrelated files, none in the four files this touches.Not run: a fresh-system install verification (step 3 of the contributing process).