Skip to content

With the Project plugin off (or absent), the core client still draws five Project surfaces #2877

Closed

Description

@Birfy

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.js renders h(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 endpoint projects, which still answers because the Project record lives in the core.
  • Settings page. core/src/client/settings.js has a hard-coded SETTINGS_PAGES array containing ['projects', 'Projects'], plus PAGE_ICONS.projects. shown === 'projects' renders ProjectsPage (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.js installs shadow('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> (projectHash in projects-nav.js) opens it directly, so a bookmark still lands on a full board.
  • Bot header seat. core/src/client/header.js:11 imports ProjectAgentEntry / projectAgentFor and draws the project-agent entry at header.js:104 whenever projectAgentFor(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

  1. Host: plugins/list reports, per plugin, whether it actually attached on this start (e.g. the plugin marks itself in its apply(), and plugins/list returns loaded; a package that is absent simply never marks itself). Keep enabled as 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.
  2. Client: one readable selector for “Project is present” (switch on and loaded), surfaced where setDisabledPlugins already lives, and the five surfaces above consult it:
    • no ProjectsNav row when absent;
    • no projects entry in the Settings nav (and openSettings('projects') falls back to General);
    • the project-board overlay not installed and #project=<id> ignored;
    • no project-agent entry in a Bot's header.
  3. 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.js staying 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.loaded for an attached and a non-attached plugin.
  • No test asserts the old unconditional behaviour (update those that do rather than deleting them).

Activity

  1. added
    agent-workingAn agent has claimed this issue (the claim comment names the branch)
    on Nov 11, 2026
  2. Birfy commented on Nov 11, 2026

    @Birfy
    CollaboratorAuthor

    Written-by: Dev

    Claimed #2877 — branch dev/2877-project-ui-gate, PR #2878 (tip 2f2aa5b). First step: the host answer (plugins/list gains loaded). Tests-ok is green at the tip; ready for review.

  3. added 2 commits that reference this issue on Nov 11, 2026
    9e17e99
    9a5248a
  4. removed
    agent-workingAn agent has claimed this issue (the claim comment names the branch)
    on Nov 11, 2026
  5. added 3 commits that reference this issue on Nov 11, 2026
    fa0d124
    8a999f6
    6a4f9fb
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions