Skip to content

#32 feat: plugin system (cost-report) + memory provider model (#39) - #42

Open
verkligheten wants to merge 30 commits into
developfrom
refactor/plugin-system
Open

#32 feat: plugin system (cost-report) + memory provider model (#39)#42
verkligheten wants to merge 30 commits into
developfrom
refactor/plugin-system

Conversation

@verkligheten

Copy link
Copy Markdown
Member

Two related refactors on one branch (epic #32): a plugin system for agent-notes' own toggleable subsystems, and a memory provider model (rescoped #39). 20 commits, 59 files, +3050 / −303. Suite: 1867 passed, 15 deselected.

Plugin system — toggle subsystems like oh-my-zsh (#33#37)

A subsystem becomes a plugin: a data/plugins/<name>/plugin.yaml manifest declaring what it contributes (skills, includes, hooks, allow-entries), a plugin_registry, and an agent-notes plugins list|enable|disable|info command. Enabled plugins are a {name: bool} map in user config; disable actually uninstalls, it does not no-op.

Config-UX hardening + a real dist gate (#32)

  • XDG parity: config_path() now honors $XDG_CONFIG_HOME like state_store already did — state and config no longer resolve differently. This also made hermetic testing possible.
  • Converged config cost-report onto the plugins command (one source of truth).
  • Hermetic CLI smoke-test suite (tests/functional/commands/test_plugins_cli.py, 25 tests) driving the real CLI in an isolated XDG_CONFIG_HOME — proven not to touch the real user config.
  • A no-teeth gate caught and replaced. The first Route declarative surfaces (skills, agents, rules, includes) through the plugin registry #35 equivalence test asserted git diff --stat agent_notes/dist/ — but dist/ is gitignored, so that diff is always empty and the gate could never fail. Replaced with a real additive-equivalence unit gate (proven to fail on a broken invariant) plus a committed scripts/dev/verify_dist_equiv.sh that builds HEAD vs the branch point and sha256-compares the trees.
  • validate: the lead orchestrator is allowed 500 lines (300 for every other agent), so enabling cost-report no longer trips the line limit.

Skeptical-verification rule for the lead

Added a shared include (data/agents/shared/skeptical_verification.md) referenced from both lead surfaces: doubt claims that look true, verify agent-report facts against the artifact, ask "what would make this check fail?", and dispatch the devil agent to challenge non-trivial designs. (Distinct concern from the epic; cleanly revertible if you'd rather it live elsewhere.)

Memory provider model — rescoped #39

The original "convert memory to a boolean plugin" framing was rejected after a devil review verified three blockers (obsidian-only skills would regress local users; a YAML-callable escape-hatch breaks "manifest is data"; a second flag fights state.memory.backend). Memory is not a toggle — it's a provider:

  • Provider: local (default — the AI CLI's native memory) or obsidian. No none/disable; legacy backend: none migrates to local on load.
  • Strategy (obsidian only, new field): single-brain (one shared vault) or per-project — both selectable. New notes are placed per the current strategy; switching never touches or converts existing notes, and reads span both layouts so nothing is hidden.
  • Wizard/config pick provider then strategy; the none option is gone.
  • local steps aside entirely. agent-notes injects no memory instructions for local — the AI CLI handles memory its default way. This required making the orchestrator memory protocol backend-conditional: {{MEMORY_INSTRUCTIONS}} now carries the full protocol for obsidian and a one-line step-aside for local, the hardcoded ## Memory protocol (HARD RULE) blocks were removed from global-claude.md/lead.md, and the guardrails Done Gate is backend-neutral. Verified: a local build's CLAUDE.md has 0 agent-notes memory add; an obsidian build has the full protocol.

Verification

  • uv run pytest tests/ → 1867 passed, 15 deselected
  • Dist byte-identity checked with scripts/dev/verify_dist_equiv.sh (NOT git diff on the gitignored dist) at every plugin/memory task boundary; the only intentional dist diffs are cost-report-enabled and the local memory step-aside, both reviewed.

Delivers / remains

Delivers #33, #34, #35, #36, #37, #39. Epic #32 is not fully closed — remaining: #38 (credential-guard as a plugin), #40 (wizard plugin-selection consolidation), #41 (docs). Targets develop, so these issues close when develop reaches main.

…uivalence gate

Old test asserted git diff on agent_notes/dist/ was empty after build, but dist/
is gitignored so the diff is always empty — the gate could never fail.

New gate makes two real assertions:
  a. rendering._plugin_include_skip(cfg) == legacy cost.render.include_skip(cfg)
     for {}, {cost_report_enabled: True}, {cost_report_enabled: False} — collapses
     to the legacy bridge when no manifest owns any include.
  b. The installer plugin skill filter removes nothing when the registry owns no
     skills (_disabled_owned is empty with an empty registry).

Proven red: adding skip.add("phantom_include") to _plugin_include_skip caused 3
failures (AssertionError: {'cost_reporting', 'phantom_include'} == {'cost_reporting'}).
Reverted; all 4 new tests pass green. Removes the now-unused state-local/state-none
fixture dirs. Full suite: 1800 passed, 15 deselected.
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