You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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/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.py — NEW
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.
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_memoryfrontmatter parsing andinclude_skip(), replacing both with plugin ownership.Location
agent_notes/registries/skill_registry.py— droprequires_memoryparsing (_parse_skill_frontmatter, ~L77-90)agent_notes/domain/skill.py:16— removerequires_memoryfieldagent_notes/memory/install.py:112— removefilter_skills_by_backendagent_notes/services/installer.py:172— call the plugin-aware filter insteadagent_notes/cost/render.py— removeinclude_skip(); the include set now comes from enabled pluginsagent_notes/services/rendering.py— build the include skip-set fromregistry.enabled(config)contributed includestests/unit/registries/test_plugin_equivalence.py— NEWChanges
The current model is subtractive: everything ships, then
filter_skills_by_backendremoves memory-gated skills andinclude_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 in enabled_plugin.skillsfor any owned skill; unowned skills always included. This reproducesrequires_memoryexactly 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.cost_report_enabled: falsetoday already skipscost_reporting, so this sub-issue must keepcost_reportingskipped by default to stay byte-identical. Bridge: read the legacycost_report_enabledflag 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.pybuilds dist with the default plugin set and asserts it is byte-identical to agit worktreebuilt at the branch point, under a pinnedXDG_CONFIG_HOME. This is the safety mechanism: no surface may appear or disappear from the default build.Verification
Dist: byte-identical.