Skip to content

Route declarative surfaces (skills, agents, rules, includes) through the plugin registry #35

Description

@verkligheten

Parent Epic

Part of #32 — Plugin system: toggleable internal subsystems

Blocked by #33 (registry). Blocks #37, #38, #39. This is the first equivalence-gated sub-issue.

Task

Route the four declarative contribution surfaces — skills, agents, rules, shared includes — through the plugin registry instead of ad-hoc per-feature logic. Delete requires_memory frontmatter parsing and include_skip(), replacing both with plugin ownership.

Location

  • agent_notes/registries/skill_registry.py — drop requires_memory parsing (_parse_skill_frontmatter, ~L77-90)
  • agent_notes/domain/skill.py:16 — remove requires_memory field
  • agent_notes/memory/install.py:112 — remove filter_skills_by_backend
  • agent_notes/services/installer.py:172 — call the plugin-aware filter instead
  • agent_notes/cost/render.py — remove include_skip(); the include set now comes from enabled plugins
  • agent_notes/services/rendering.py — build the include skip-set from registry.enabled(config) contributed includes
  • tests/unit/registries/test_plugin_equivalence.pyNEW

Changes

The current model is subtractive: everything ships, then filter_skills_by_backend removes memory-gated skills and include_skip() removes the cost include. Invert it to additive: a surface ships iff some enabled plugin contributes it, OR it belongs to no plugin (unowned surfaces are always shipped).

  • Skill filtering: skill in enabled_plugin.skills for any owned skill; unowned skills always included. This reproduces requires_memory exactly once memory declares its skills (in Model memory as a provider + strategy (local default / obsidian; no disable) #39) — until then, no plugin owns any skill, so all ship, which is the current default state.
  • Include skip-set: the set of all plugin-contributed include names minus those contributed by enabled plugins. With cost-report not yet a plugin (Convert cost-report to a plugin #37), no include is owned, so nothing is skipped — but the default cost_report_enabled: false today already skips cost_reporting, so this sub-issue must keep cost_reporting skipped by default to stay byte-identical. Bridge: read the legacy cost_report_enabled flag here and treat it as an implicit plugin-enable until Convert cost-report to a plugin #37 migrates it. Document the bridge as temporary in the code.

Equivalence gate

test_plugin_equivalence.py builds dist with the default plugin set and asserts it is byte-identical to a git worktree built at the branch point, under a pinned XDG_CONFIG_HOME. This is the safety mechanism: no surface may appear or disappear from the default build.

Verification

uv run pytest tests/unit/registries/test_plugin_equivalence.py -v
uv run pytest tests/ -v
XDG_CONFIG_HOME=tests/fixtures/state-local agent-notes build && git diff --stat agent_notes/dist/
XDG_CONFIG_HOME=tests/fixtures/state-none  agent-notes build && git diff --stat agent_notes/dist/

Dist: byte-identical.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions