Repository navigation
Description
Written-by: Chief
At: 2026-11-11T12:27:38.328Z
The owner's requirement, verbatim: “我需要project插件不装的时候,完全没有project的概念,任何project相关的都不要显示。”
Today the Project domain is a plugin (plugins/project, #2829/#2830) and the switch in Settings → Plugins turns it off at the next start — but the Project surfaces are drawn by the core client and never consult that switch, so an instance with the plugin off still shows every one of them.
What is still drawn when the plugin is off
All read at main 0b638ffa0f51:
- Sidebar row.
core/src/client/sidebar.jsrendersh(ProjectsNav, { wide })unconditionally, next to Inbox and Task board.ProjectsNav(core/src/client/projects-nav.js) has no gate at all — expanding it calls the core endpointprojects, which still answers because the Project record lives in the core. - Settings page.
core/src/client/settings.jshas a hard-codedSETTINGS_PAGESarray containing['projects', 'Projects'], plusPAGE_ICONS.projects.shown === 'projects'rendersProjectsPage(New Project form +ProjectsList). Every other settings page is unrelated to any plugin, so a plugin switch is the first thing this list has to filter. - Board overlay.
core/src/client/index.jsinstallsshadow('shell.overlay', { id: 'project-board', order: 57, inject: face }, ProjectBoardOverlay)and both stylesheets (PROJECT_BOARD_CSS,PROJECTS_NAV_CSS) through the shell's own slot system.#project=<id>(projectHashinprojects-nav.js) opens it directly, so a bookmark still lands on a full board. - Bot header seat.
core/src/client/header.js:11importsProjectAgentEntry/projectAgentForand draws the project-agent entry atheader.js:104wheneverprojectAgentFor(bot, roster.self?.memberId)returns non-null.
Note why the existing filter does not catch these: core/src/client/flybot-registry.js setDisabledPlugins() drops slot entries by ownerOf(entry.id), and index.js feeds it from plugins/list at start. That filter only sees entries a plugin registered through the flybot registry; the five surfaces above are registered/drawn by the core itself.
The switch is not the same question as “installed”
plugins/list (core/src/host/hooks.js:1111) answers from pluginSwitches(state) (core/src/host/plugins.js), i.e. state.json, and defaults to enabled: true for every id in FEATURE_PLUGINS — whether or not the package is present and whether or not its apply() actually ran. So an instance where @flybot/project is not installed reads project: enabled and would keep drawing all five surfaces even after the client gate exists, while the project_state / project_digest tools are simply missing.
The honest signal is whether the domain registered on this start. Either way the answer must come from the host, not be guessed in the browser.
What the fix has to do
- Host:
plugins/listreports, per plugin, whether it actually attached on this start (e.g. the plugin marks itself in itsapply(), andplugins/listreturnsloaded; a package that is absent simply never marks itself). Keepenabledas it is — Settings → Plugins still needs it, and that page stays visible when the Project feature is off, because it is the control that turns it back on. - Client: one readable selector for “Project is present” (switch on and loaded), surfaced where
setDisabledPluginsalready lives, and the five surfaces above consult it:- no
ProjectsNavrow when absent; - no
projectsentry in the Settings nav (andopenSettings('projects')falls back to General); - the
project-boardoverlay not installed and#project=<id>ignored; - no project-agent entry in a Bot's header.
- no
- When Project is present, every surface stays exactly as today — same markup, same tests.
Not in this issue
- The core's own Project record and
projects/project-*endpoints (docs/subsystems/project.md: the core keeps the record). Hiding the surfaces is the ask; this issue does not move or delete the record. plugins/project/client.jsstaying an empty placeholder — that is the separate follow-up already recorded in the same doc.- Settings → Plugins keeping a “Project” row when the package is absent (it is the switch, not the feature). If the owner wants that row gone too, that is a new decision.
Acceptance
- With the project switch off, and again with the package removed: sidebar has no “项目 / Projects” row; Settings has no “项目 / Projects” page and
openSettings('projects')lands on General;#project=<id>shows no board; no project-agent entry in any Bot header. Nothing else in the UI names a Project. - With it on: unchanged, existing client tests green.
- A client test covers the gate for each surface; a host test covers
plugins/list.loadedfor an attached and a non-attached plugin. - No test asserts the old unconditional behaviour (update those that do rather than deleting them).
Activity
Birfy commentedon Nov 11, 2026
Written-by: Dev
Claimed #2877 — branch
dev/2877-project-ui-gate, PR #2878 (tip 2f2aa5b). First step: the host answer (plugins/listgainsloaded). Tests-ok is green at the tip; ready for review.fix(client): keep the slot filter to the start's read on a switch flip (
chore(patches): regenerate 0008 with the CI git so canonical is a no-…
fix(client): draw no Project surface when the Project plugin is off o…
client: show a plugin that is not installed as not installed, and sur…
Release v0.2.30 (ThinkFlowLab#2916)