Skip to content

feat(context): PR-D C1b — mm wiki {agent,command} override CLI mirror (ADR-0008)#629

Merged
memtomem merged 3 commits intomainfrom
feat/wiki-agent-command-override-cli
May 1, 2026
Merged

feat(context): PR-D C1b — mm wiki {agent,command} override CLI mirror (ADR-0008)#629
memtomem merged 3 commits intomainfrom
feat/wiki-agent-command-override-cli

Conversation

@memtomem
Copy link
Copy Markdown
Owner

@memtomem memtomem commented May 1, 2026

Summary

Closes the CLI gap left by PR-D C1a (#628), which lifted wiki/override.py:render_seed_bytes for agents / commands but shipped no CLI to drive them. Users now have a symmetric surface across all three asset types:

mm wiki skill   override <name> --vendor <claude|gemini|codex>
mm wiki agent   override <name> --vendor <claude|gemini|codex>
mm wiki command override <name> --vendor <claude|gemini|codex>

Vendor renderers report which canonical fields they cannot represent (gemini agents drop skills/isolation; gemini commands drop argument-hint/allowed-tools/model); the new CLI surfaces those via stderr warning: so the user editing the override knows what the runtime won't see.

Series position (ADR-0008 PR-D)

Status
PR-A wiki scaffold merged #622
PR-B mm context install + lockfile merged #625
PR-C wiki override + agent/command install widening merged #624
PR-D-prep fan-out override application sites merged #627
PR-D C1a activate agents/commands override + render lift merged #628
PR-D C1b CLI mirror + dropped warning (this PR) here
C2 mm context update next
C3 / C4 status, install --all, migrate sequenced after C2

Why two commits

The plan splits the work along a refactor / new-feature axis so each commit is a working, reviewable unit:

  • 0cc705d refactor — extract _run_seed_override helper for override CLI. Three call sites are about to share a body, so the YAGNI window for the helper is open now. wiki/override.py grows a SeedResult(path, dropped) dataclass, and render_seed_bytes / seed_override propagate the renderer's dropped list (C1a silently discarded it). skill_override_cmd collapses to the helper. The five known errors collapse into one except (...) tuple — verified disjoint via inheritance grep (WikiNotFoundError / OverrideExistsError / NotImplementedErrorRuntimeError; FileNotFoundErrorOSError; InvalidNameErrorValueError).
  • e313da5 feat — mm wiki {agent,command} override + dropped-fields warning. Two new wiki.group blocks, each with an override subcommand that delegates to the helper. The helper now exercises the dropped-fields stderr branch (skills always have dropped == []; agents / commands populate it depending on vendor). ("commands", "codex") keeps its permanent placeholder behaviour — seed_override raises NotImplementedError and the helper surfaces it as a classified ClickException rather than a Python traceback.

Collapse safety gate

feedback_pre_collapse_parity_grep pattern: surviving path's invariants are pinned before / during the N→1 collapse. Skills had ten existing CLI tests (all assert on result.output, the combined stream), so a silent regression where the new dropped-fields warning fired for skills would not surface. Commit 1 adds test_wiki_skill_override_no_stderr_warning, which uses result.stderr == "" (Click 8.3 keeps result.stderr separate by default — earlier mix_stderr=False constructor arg was removed) to pin the four-axis invariant for skills:

axis how it is verified
stdout existing test_wiki_skill_override_stdout_contract
exit code existing test_wiki_skill_override_happy_path
file effect existing test_wiki_skill_override_happy_path (byte-equality)
stderr empty new test_wiki_skill_override_no_stderr_warning

The first three were already pinned before this PR; the fourth is the new collapse-specific gate.

Test surface

  • test_wiki_override.py 4 / 4 pass. Two pin tests (*_for_agents_uses_vendor_generator, *_for_commands_uses_vendor_generator) unpack the new tuple shape and assert dropped == [] for the minimal canonical fixtures (no vendor-uncovered fields → no drops). The other two are exception-only and unchanged.
  • test_wiki_cmd_override.py 32 / 32 pass:
    • 10 existing skill tests (PR-C feat(context): PR-C — wiki override + agent/command install widening (ADR-0008) #624) — unchanged behaviour after the helper collapse.
    • 1 new test_wiki_skill_override_no_stderr_warning (commit 1 safety gate).
    • 10 agent mirror tests covering happy path / dropped warning / refuse / force / unknown vendor / missing agent / missing wiki / editor / no-editor / stdout contract.
    • 11 command mirror tests covering the same plus test_wiki_command_override_codex_classified_error (the permanent placeholder pin).
  • Full suite: uv run pytest packages/memtomem/tests/ -m "not ollama" -q3475 pass, 46 deselected (ollama), +22 over C1a's 3453 (1 safety gate + 21 mirror).
  • Lint: ruff check + ruff format --check clean across packages/memtomem/src + packages/memtomem/tests.

Manual smoke (mode 2 isolation, MEMTOMEM_WIKI_PATH override)

=== gemini agent → stderr warning ===
warning: vendor 'gemini' will not represent these fields: skills, isolation
PASS: warning, skills, isolation, stdout summary

=== claude agent → stderr empty ===
PASS: stderr empty, stdout summary

=== codex command → classified error, no traceback ===
exit 1; stderr: Error: 'codex' commands not yet supported — see OVERRIDE_FORMATS placeholder
PASS: non-zero exit, classified message, no traceback

=== gemini command → stderr warning + .toml extension ===
warning: vendor 'gemini' will not represent these fields: argument-hint, allowed-tools, model
PASS: warning, argument-hint, allowed-tools, model, .toml extension

Out of scope (subsequent PRs)

  • mm wiki <type> {edit, diff, lint} — defined in ADR-0008 §Subcommands but C1b is explicitly override-only.
  • mm context update / status / install --all / migrate — C2 / C3 / C4.

Test coverage gap (follow-up)

  • Codex agents drop set (tools / skills / isolation / kind / temperature) is exercised only through the shared helper path, not via a vendor-specific fixture. Gemini agents (skills / isolation) and gemini commands (argument-hint / allowed-tools / model) cover the warning code path; codex agents share that same path so the marginal regression-detection value is low. Add a codex-specific fixture when generator drop sets change or as part of a vendor-coverage cleanup PR — not C2 scope, since C2's dirty detection deals with file mtime, not vendor renderer drops.

Post-review fixup

  • 0a114df fixup(c1b): newline guard in test fixture helpers_seed_agent / _seed_command now normalise frontmatter_extra to end with a trailing \n. Current call sites already do this; the guard prevents a future fixture from silently producing a malformed YAML frontmatter (closing --- merging into the previous line). Test-only impact, +12 / -2 lines.

Test plan

  • uv run pytest packages/memtomem/tests/test_wiki_cmd_override.py packages/memtomem/tests/test_wiki_override.py -v — 36 pass.
  • uv run pytest packages/memtomem/tests/ -m "not ollama" -q — 3475 pass.
  • uv run ruff check packages/memtomem/src packages/memtomem/tests — clean.
  • uv run ruff format --check packages/memtomem/src packages/memtomem/tests — clean.
  • Manual mode-2 smoke — 4 / 4 pass.
  • CI green (waiting).

🤖 Generated with Claude Code

pandas-studio and others added 3 commits May 1, 2026 09:02
Sets up the shared CLI scaffolding that PR-D C1b will use to add
``mm wiki agent override`` and ``mm wiki command override`` alongside
the existing ``mm wiki skill override``. No behavior change for users.

What changes:

- ``wiki/override.py``: new ``SeedResult`` dataclass. ``render_seed_bytes``
  returns ``(bytes, dropped: list[str])`` and ``seed_override`` returns
  ``SeedResult(path, dropped)``. C1a (#628) silently discarded the
  ``dropped`` list from the vendor renderers; the new shape carries it
  through to the caller so the upcoming agent / command CLI can warn the
  user about fields the vendor format cannot represent.
- ``cli/wiki_cmd.py``: new ``_run_seed_override`` helper centralises the
  seed → stdout summary → optional stderr drop-warning → optional
  ``$EDITOR`` flow. The five known errors (``WikiNotFoundError``,
  ``OverrideExistsError``, ``FileNotFoundError``, ``InvalidNameError``,
  ``NotImplementedError``) are siblings (verified disjoint), so they
  collapse into one ``except (...)`` tuple that maps to a classified
  ``ClickException``. The skill-only ``is_relative_to`` fallback is
  removed: ``seed_override`` invariant is target-under-store.root, and a
  violation should surface as ``ValueError``, not as a silent path
  mismatch. ``skill_override_cmd`` now delegates to the helper.

Migration safety gate:

- ``test_wiki_skill_override_no_stderr_warning`` is the collapse
  invariant. Skills always seed via byte-copy of the canonical
  ``SKILL.md``, so ``SeedResult.dropped == []`` and the new warning path
  must stay quiescent. The other ten skill tests assert on
  ``result.output`` (combined) and would not catch a silent regression
  if the warning fired for skills. Pattern follows
  ``feedback_pre_collapse_parity_grep``: surviving path's invariants
  pinned before / during the N→1 collapse.

- ``test_wiki_override.py`` pin tests: the two that consume the
  ``render_seed_bytes`` return (`*_for_agents_uses_vendor_generator`,
  `*_for_commands_uses_vendor_generator`) now unpack the tuple and
  assert ``dropped == []`` for the minimal canonical fixtures (no
  vendor-uncovered fields → no drops). The other two are exception-only
  and unchanged.

Out of scope (lands in C1b commit 2):

- ``mm wiki agent override`` / ``mm wiki command override`` CLI commands.
- The dropped-fields stderr warning is wired in the helper but only
  exercised once those commands and their mirror tests land — no skill
  fixture today produces drops.

Tests: ``packages/memtomem/tests/test_wiki_override.py`` 4/4 pass,
``test_wiki_cmd_override.py`` 11/11 pass (10 existing + 1 new safety
gate). Wider sweep: 776 wiki/override/context tests pass.

Co-Authored-By: Claude <[email protected]>
Closes the CLI gap left by PR-D C1a (#628), which lifted the
agents / commands branch in ``wiki/override.py:render_seed_bytes`` but
shipped no CLI to drive it. Users now have a single, symmetric surface
across all three asset types:

    mm wiki skill   override <name> --vendor <claude|gemini|codex>
    mm wiki agent   override <name> --vendor <claude|gemini|codex>
    mm wiki command override <name> --vendor <claude|gemini|codex>

What changes:

- ``cli/wiki_cmd.py`` adds ``agent_group`` and ``command_group`` with
  ``override`` subcommands. Both delegate to ``_run_seed_override`` (the
  shared helper landed in the previous commit), so the trust-UX is
  identical: classified ClickException for known errors, no Python
  traceback leaks, and any vendor-renderer drops surface as a yellow
  stderr line so the user knows what the runtime won't see in the
  override.

- ``("commands", "codex")`` keeps its permanent placeholder behaviour —
  ``seed_override`` raises ``NotImplementedError`` with a diagnostic
  message and the helper surfaces it as a classified ClickException
  rather than a traceback. There is no ``codex_commands`` generator, so
  this row in ``OVERRIDE_FORMATS`` cannot ship until Codex grows a
  slash-command surface.

- The dropped-fields warning is wired through the helper. For agents,
  ``gemini`` drops ``skills`` / ``isolation``; for commands, ``gemini``
  drops ``argument-hint`` / ``allowed-tools`` / ``model``. ``claude``
  agents drop nothing in the common case, ``codex`` agents drop
  ``tools`` / ``skills`` / ``isolation`` / ``kind`` / ``temperature``.
  Skills always seed via byte-copy and never warn (pinned by the
  commit-1 safety gate test).

- ``tests/test_wiki_cmd_override.py`` adds 21 mirror tests (10 agent +
  11 command, including the codex commands placeholder) plus the
  ``_seed_agent`` / ``_seed_command`` test helpers. Coverage parallels
  the existing skills surface: happy path, dropped-fields warning,
  refuse-vs-force, ``--force`` writes ``.bak``, unknown vendor, missing
  asset (with overrides-dir cleanliness assertion), missing wiki,
  ``--editor`` invocation, no-editor default, and stdout contract. The
  stdout-contract tests pin the variable extension (``codex.toml``,
  ``gemini.toml``) so a regression in OVERRIDE_FORMATS would surface
  immediately.

Out of scope (subsequent PRs in the ADR-0008 sequence):

- ``mm wiki <type> {edit, diff, lint}`` — defined in ADR §Subcommands
  but C1b is explicitly override-only.
- ``mm context update`` / ``status`` / ``install --all`` / ``migrate``
  — C2 / C3 / C4.

Tests: ``test_wiki_cmd_override.py`` 32/32 pass (10 skill + 1 safety
gate + 10 agent + 11 command). Full suite: 3475 pass, 46 deselected
(ollama). ``ruff check`` + ``ruff format --check`` clean across
``packages/memtomem/src`` and ``packages/memtomem/tests``.

Co-Authored-By: Claude <[email protected]>
`_seed_agent` / `_seed_command` frontmatter assembly silently breaks
if `frontmatter_extra` is missing a trailing newline — the closing
``---`` merges into the previous line and YAML parse fails with a
cryptic error. Current call sites all pass values that end with ``\n``,
but a future fixture can forget it.

Per self-review item #1: defensive 4 lines per helper, test-only impact.
The failure mode would otherwise look like a "why doesn't this fixture
work?" debugging session for whoever adds the next drop-set fixture.

Co-Authored-By: Claude <[email protected]>
@memtomem memtomem merged commit 4054e53 into main May 1, 2026
7 checks passed
@memtomem memtomem deleted the feat/wiki-agent-command-override-cli branch May 1, 2026 00:23
@github-actions github-actions Bot locked and limited conversation to collaborators May 1, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants