Skip to content

feat: everything-is-a-plugin, mandatory + optional (009) - #120

Merged
fatherlinux merged 3 commits into
mainfrom
feature/009-mandatory-plugins
Jul 14, 2026
Merged

feat: everything-is-a-plugin, mandatory + optional (009)#120
fatherlinux merged 3 commits into
mainfrom
feature/009-mandatory-plugins

Conversation

@fatherlinux

Copy link
Copy Markdown
Member

Summary

Unifies Acquacotta's feature model so Pomodoro, Settings, and Todos are all first-class plugins listed uniformly. Two are mandatory — always registered/enabled, non-toggleable, MCP tools always available — while Todos stays optional with the per-user enable/disable from spec 008.

Completes the plugin architecture started in 007 (per-user storage) and 008 (per-user optional plugins). See specs/009-mandatory-plugins/spec.md.

Changes

  • plugin_registry: mandatory flag on registration; list_plugins reports it and forces mandatory→active; is_mandatory(); get_mcp_tool_registrars() now returns (id, registrar, mandatory) triples.
  • Pomodoro/Settings as plugins: pomodoro_tools gains PLUGIN_METADATA (mandatory) and registers in app.py + mcp_server.py; Settings registered as a mandatory metadata-only plugin. Removed the hardcoded pomodoro_tools.register_mcp_tools — Pomodoro's tools now flow through the same registrar path, ungated because mandatory.
  • app.py: /api/plugins/toggle returns 403 for a mandatory plugin.
  • index.html: mandatory rows render checked + disabled with a "Required" badge; pluginEffectiveActive() always-true for mandatory.

No data-format change, no migration (FR-009).

Test plan

  • 220 unit tests pass; ruff (check + format) clean
  • Registry mandatory flag / always-active / is_mandatory; /api/plugins lists pomodoro/settings/todos with correct flags; mandatory toggle → 403; Todos toggle → 200; MCP registrars gate mandatory ungated + optional per-user
  • Web verified live: Plugins list shows all features; Pomodoro/Settings locked "Required"; Todos toggleable; Timer/Settings tabs unaffected
  • MCP server boots clean with Pomodoro registered ungated
  • MCP end-to-end with a token — verify Pomodoro tools work regardless of Todos state (in prod)

🤖 Generated with Claude Code

fatherlinux and others added 2 commits July 14, 2026 14:12
Pomodoro, Settings, and Todos all become first-class plugins listed uniformly.
Pomodoro and Settings are mandatory (always registered/enabled, non-toggleable,
MCP tools always available); Todos stays optional with the per-user enable/
disable from spec 008. Scoped to the registry/UI/MCP layer — the storage-backend
persistence contract is intentionally left intact (a future spec may decouple it).
Mandatory plugins render checked+locked with a "Required" badge.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Unify the feature model so Pomodoro, Settings, and Todos are all first-class
plugins listed uniformly. Pomodoro and Settings are MANDATORY — always
registered/enabled, non-toggleable, MCP tools always available. Todos stays
optional with the per-user enable/disable from spec 008.

- plugin_registry: `mandatory` flag on registration; list_plugins reports it and
  forces mandatory→active; is_mandatory(); get_mcp_tool_registrars now returns
  (id, registrar, mandatory) triples.
- pomodoro_tools gains PLUGIN_METADATA (mandatory); registered as a plugin in
  app.py and mcp_server.py. Settings registered as a mandatory metadata-only
  plugin. Removed the hardcoded pomodoro_tools.register_mcp_tools — Pomodoro's
  tools now flow through the same registrar path, ungated because mandatory.
- app.py: /api/plugins/toggle returns 403 for a mandatory plugin.
- index.html: mandatory rows render checked+disabled with a "Required" badge;
  pluginEffectiveActive() always-true for mandatory.

No data-format change, no migration (FR-009). 220 tests pass, ruff clean.
Web verified live (Plugins list shows all; Pomodoro/Settings locked Required;
Todos toggleable). MCP server boots clean with Pomodoro ungated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gatehouse found no issues.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements the 'Everything-is-a-Plugin' model (spec 009), introducing mandatory plugins (Pomodoro and Settings) alongside optional ones (Todos). It updates the plugin registry, the web UI, and the MCP server to ensure mandatory plugins are always active and cannot be disabled, while optional plugins remain toggleable per-user. Feedback on the changes highlights an issue in the toggle API endpoint where attempting to enable an already active mandatory plugin is incorrectly blocked with a 403 Forbidden error; the check should be refined to only restrict disabling actions.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread app.py
Comment on lines +973 to +976
if plugin_registry.is_mandatory("extension", plugin_id):
return jsonify(
{"error": f"'{plugin_id}' is a required plugin and cannot be disabled"}
), HTTPStatus.FORBIDDEN

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current check blocks any toggle request for a mandatory plugin, even if the request is attempting to enable it (enable=True). This results in a 403 Forbidden error with the message 'pomodoro' is a required plugin and cannot be disabled when a client attempts to enable an already active mandatory plugin. We should only restrict the toggle when enable is False (i.e., an attempt to disable it).

Suggested change
if plugin_registry.is_mandatory("extension", plugin_id):
return jsonify(
{"error": f"'{plugin_id}' is a required plugin and cannot be disabled"}
), HTTPStatus.FORBIDDEN
if plugin_registry.is_mandatory("extension", plugin_id) and not enable:
return jsonify(
{"error": f"'{plugin_id}' is a required plugin and cannot be disabled"}
), HTTPStatus.FORBIDDEN

Trim the longest new comment blocks, and whitelist plugin_registry.py and
pomodoro_tools.py for verbose_comments (they now carry doc comments, matching
the existing app.py/mcp_server.py/todos_plugin.py exceptions) and app.py for
deferred_work ('todos' is the plugin name, a domain term).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gatehouse found no issues.

@fatherlinux
fatherlinux merged commit 26f45f7 into main Jul 14, 2026
5 checks passed
@fatherlinux
fatherlinux deleted the feature/009-mandatory-plugins branch July 14, 2026 21:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant