---
phase: 17-registry-decomposition-and-plugin-runtime
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
  - src/ztlctl/actions/_register_core.py
  - src/ztlctl/actions/_creation.py
  - src/ztlctl/actions/_query.py
  - src/ztlctl/actions/_graph.py
  - src/ztlctl/actions/_lifecycle.py
  - src/ztlctl/actions/_session.py
  - src/ztlctl/actions/_check.py
  - src/ztlctl/actions/_ingest.py
  - src/ztlctl/actions/_export.py
  - src/ztlctl/actions/_admin.py
  - src/ztlctl/actions/__init__.py
  - tests/actions/test_core_registrations.py
autonomous: true
requirements: [ARCH-07]

must_haves:
  truths:
    - "Each feature area owns its ActionDefinitions in a local module inside src/ztlctl/actions/"
    - "_register_core.py is either deleted or reduced to a thin dispatcher that calls feature-local registration functions"
    - "All 66 existing ActionDefinitions remain registered — no regressions"
    - "Module-load-time registration still works (import ztlctl.actions triggers all registrations)"
  artifacts:
    - path: "src/ztlctl/actions/_creation.py"
      provides: "creation category registrations (5 actions: create_note, create_reference, create_task, create_batch, garden_seed)"
      contains: "def _register_creation_actions"
    - path: "src/ztlctl/actions/_query.py"
      provides: "query category registrations (10 actions)"
      contains: "def _register_query_actions"
    - path: "src/ztlctl/actions/_graph.py"
      provides: "graph category registrations (8 actions)"
      contains: "def _register_graph_actions"
    - path: "src/ztlctl/actions/_lifecycle.py"
      provides: "lifecycle + reweave category registrations (6 actions)"
      contains: "def _register_lifecycle_actions"
    - path: "src/ztlctl/actions/_session.py"
      provides: "session category registrations (9 actions)"
      contains: "def _register_session_actions"
    - path: "src/ztlctl/actions/_check.py"
      provides: "check + maintenance category registrations (5 actions)"
      contains: "def _register_check_actions"
    - path: "src/ztlctl/actions/_ingest.py"
      provides: "ingest category registrations (4 actions)"
      contains: "def _register_ingest_actions"
    - path: "src/ztlctl/actions/_export.py"
      provides: "export category registrations (4 actions)"
      contains: "def _register_export_actions"
    - path: "src/ztlctl/actions/_admin.py"
      provides: "vector + upgrade + init + discovery + docs category registrations (12 actions)"
      contains: "def _register_admin_actions"
  key_links:
    - from: "src/ztlctl/actions/__init__.py"
      to: "all _register_*_actions functions"
      via: "import and call at module load time"
      pattern: "_register_.*_actions\\(\\)"
---

<objective>
Decompose the monolithic `_register_core.py` (2303 lines, 66 ActionDefinitions) into 9 feature-local registration modules colocated in `src/ztlctl/actions/`.

Purpose: ARCH-07 — each feature area owns its ActionDefinitions in a local module instead of one 2300-line monolith.
Output: 9 new `_*.py` files in `src/ztlctl/actions/`, updated `__init__.py`, deleted `_register_core.py`.
</objective>

<execution_context>
@~/.claude/get-shit-done/workflows/execute-plan.md
@~/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@src/ztlctl/actions/_register_core.py
@src/ztlctl/actions/__init__.py
@src/ztlctl/actions/registry.py
@src/ztlctl/actions/definitions.py
@tests/actions/test_core_registrations.py
</context>

<tasks>

<task type="auto">
  <name>Task 1: Extract feature-local registration modules from _register_core.py</name>
  <files>
    src/ztlctl/actions/_creation.py,
    src/ztlctl/actions/_query.py,
    src/ztlctl/actions/_graph.py,
    src/ztlctl/actions/_lifecycle.py,
    src/ztlctl/actions/_session.py,
    src/ztlctl/actions/_check.py,
    src/ztlctl/actions/_ingest.py,
    src/ztlctl/actions/_export.py,
    src/ztlctl/actions/_admin.py
  </files>
  <read_first>
    src/ztlctl/actions/_register_core.py,
    src/ztlctl/actions/definitions.py,
    src/ztlctl/actions/registry.py
  </read_first>
  <action>
    Split _register_core.py into 9 feature-local modules. Each module exports ONE public function that registers its actions. The split follows the category section comments already in the monolith:

    1. `_creation.py` — `_register_creation_actions()` — lines 40-319 (creation category, 5 actions: create_note, create_reference, create_task, create_batch, garden_seed). Lazy imports: CreateController.

    2. `_query.py` — `_register_query_actions()` — lines 321-740 (query category, 10 actions: count_items, search, get, list_items, work_queue, list_tags, decision_support, topic_packet, draft_from_topic, vault_review). Lazy imports: QueryController.

    3. `_graph.py` — `_register_graph_actions()` — lines 742-947 (graph category, 8 actions: related, themes, rank, path, gaps, bridges, graph_unlink, graph_stats). Lazy imports: GraphController.

    4. `_lifecycle.py` — `_register_lifecycle_actions()` — lines 949-1043 (lifecycle category, 3 actions: update, archive, restore) PLUS lines 1045-1141 (reweave category, 3 actions: reweave, prune, undo_prune). Lazy imports: UpdateController, ReweaveController.

    5. `_session.py` — `_register_session_actions()` — lines 1143-1404 (session category, 9 actions). Lazy imports: SessionController.

    6. `_check.py` — `_register_check_actions()` — lines 1406-1511 (check category, 4 actions) PLUS the maintenance action (event_purge, 1 action). Lazy imports: CheckController.

    7. `_ingest.py` — `_register_ingest_actions()` — lines 1513-1743 (ingest category, 4 actions: ingest_url, ingest_file, ingest_text, context_assembly). Lazy imports: IngestController.

    8. `_export.py` — `_register_export_actions()` — lines 1745-1904 (export category, 4 actions: export_markdown, export_index, export_graph, export_assets). Lazy imports: ExportController.

    9. `_admin.py` — `_register_admin_actions()` — combines the remaining small categories:
       - vector (2 actions, lines 1906-1938): VectorController
       - upgrade (3 actions, lines 1940-1987): UpgradeController
       - workflow (3 actions, lines 1989-2100): WorkflowController
       - init (3 actions, lines 2102-2200): InitController
       - discovery (3 actions, lines 2202-2242): DiscoveryController
       - docs (1 action, lines 2244-end): DocsController

    Each module structure:
    ```python
    """[Category] ActionDefinition registrations."""
    from __future__ import annotations
    from typing import Any

    def _register_[name]_actions() -> None:
        """Register [category] ActionDefinitions."""
        from ztlctl.actions.definitions import ActionDefinition, ActionParam
        from ztlctl.actions.registry import get_action_registry

        # Lazy controller imports
        from ztlctl.controllers.[controller] import [Controller]

        registry = get_action_registry()

        # ... exact same ActionDefinition registrations, copy verbatim ...
    ```

    CRITICAL: Copy each ActionDefinition registration EXACTLY as-is from _register_core.py. Do NOT modify handler lambdas, param definitions, descriptions, or any field values. This is a pure mechanical extract — zero behavioral changes.
  </action>
  <verify>
    <automated>cd /Users/shparki/Documents/Workspace/thatdev/ztlctl && uv run python -c "
from ztlctl.actions._creation import _register_creation_actions
from ztlctl.actions._query import _register_query_actions
from ztlctl.actions._graph import _register_graph_actions
from ztlctl.actions._lifecycle import _register_lifecycle_actions
from ztlctl.actions._session import _register_session_actions
from ztlctl.actions._check import _register_check_actions
from ztlctl.actions._ingest import _register_ingest_actions
from ztlctl.actions._export import _register_export_actions
from ztlctl.actions._admin import _register_admin_actions
print('All 9 modules importable')
"</automated>
  </verify>
  <acceptance_criteria>
    - src/ztlctl/actions/_creation.py contains `def _register_creation_actions`
    - src/ztlctl/actions/_query.py contains `def _register_query_actions`
    - src/ztlctl/actions/_graph.py contains `def _register_graph_actions`
    - src/ztlctl/actions/_lifecycle.py contains `def _register_lifecycle_actions`
    - src/ztlctl/actions/_session.py contains `def _register_session_actions`
    - src/ztlctl/actions/_check.py contains `def _register_check_actions`
    - src/ztlctl/actions/_ingest.py contains `def _register_ingest_actions`
    - src/ztlctl/actions/_export.py contains `def _register_export_actions`
    - src/ztlctl/actions/_admin.py contains `def _register_admin_actions`
    - Each module has `from __future__ import annotations` as first import
    - Each module uses lazy controller imports inside the function body (not module-level)
    - No module imports from _register_core.py
  </acceptance_criteria>
  <done>9 feature-local registration modules exist, each importable without error, containing the exact same ActionDefinition registrations as _register_core.py</done>
</task>

<task type="auto">
  <name>Task 2: Wire __init__.py to call feature-local modules and delete _register_core.py</name>
  <files>
    src/ztlctl/actions/__init__.py,
    src/ztlctl/actions/_register_core.py,
    tests/actions/test_core_registrations.py
  </files>
  <read_first>
    src/ztlctl/actions/__init__.py,
    src/ztlctl/actions/_register_core.py,
    tests/actions/test_core_registrations.py,
    src/ztlctl/actions/_creation.py
  </read_first>
  <action>
    1. Replace `src/ztlctl/actions/__init__.py` contents. Instead of importing and calling `_register_core_actions()`, import and call all 9 feature-local registration functions:

    ```python
    """ztlctl actions package — ActionParam, ActionDefinition, ActionRegistry."""

    from ztlctl.actions._admin import _register_admin_actions
    from ztlctl.actions._check import _register_check_actions
    from ztlctl.actions._creation import _register_creation_actions
    from ztlctl.actions._export import _register_export_actions
    from ztlctl.actions._graph import _register_graph_actions
    from ztlctl.actions._ingest import _register_ingest_actions
    from ztlctl.actions._lifecycle import _register_lifecycle_actions
    from ztlctl.actions._query import _register_query_actions
    from ztlctl.actions._session import _register_session_actions
    from ztlctl.actions.definitions import ActionDefinition, ActionParam
    from ztlctl.actions.registry import ActionRegistry, get_action_registry

    _register_creation_actions()
    _register_query_actions()
    _register_graph_actions()
    _register_lifecycle_actions()
    _register_session_actions()
    _register_check_actions()
    _register_ingest_actions()
    _register_export_actions()
    _register_admin_actions()

    __all__ = ["ActionDefinition", "ActionParam", "ActionRegistry", "get_action_registry"]
    ```

    2. Delete `src/ztlctl/actions/_register_core.py` entirely.

    3. Update `tests/actions/test_core_registrations.py`:
       - The test imports `from ztlctl.actions import get_action_registry` — this still works, no change needed.
       - Add a new test class `TestDecomposedModules` that verifies the decomposition:
         - Test that `_register_core.py` does NOT exist (it's been deleted): `assert not Path("src/ztlctl/actions/_register_core.py").exists()`
         - Test that all 9 feature-local modules exist
         - Test that the total registration count is still >= 66 (current count: 66 registrations)
       - Update the `test_minimum_action_count` threshold from 45 to 60 (closer to actual 66, reflecting the current state post-Phase-16)

    4. Run full test suite to confirm zero regressions.
  </action>
  <verify>
    <automated>cd /Users/shparki/Documents/Workspace/thatdev/ztlctl && uv run pytest tests/actions/test_core_registrations.py -x -v && uv run ruff check src/ztlctl/actions/ && uv run mypy src/ztlctl/actions/</automated>
  </verify>
  <acceptance_criteria>
    - src/ztlctl/actions/__init__.py contains `_register_creation_actions()`
    - src/ztlctl/actions/__init__.py contains `_register_admin_actions()`
    - src/ztlctl/actions/__init__.py does NOT contain `_register_core`
    - src/ztlctl/actions/_register_core.py does NOT exist
    - `uv run pytest tests/actions/test_core_registrations.py` exits 0
    - `uv run pytest` (full suite) exits 0
    - `uv run ruff check src/ztlctl/actions/` exits 0
    - `uv run mypy src/ztlctl/actions/` exits 0
  </acceptance_criteria>
  <done>_register_core.py is deleted, __init__.py calls all 9 feature-local registration functions, all tests pass including the decomposition validation test, lint and type checks clean</done>
</task>

</tasks>

<verification>
1. `uv run pytest tests/actions/test_core_registrations.py -x -v` — all registration tests pass
2. `uv run pytest` — full test suite passes (no regressions from decomposition)
3. `uv run ruff check . && uv run ruff format --check . && uv run mypy src/` — lint, format, types clean
4. `uv run python -c "from ztlctl.actions import get_action_registry; r = get_action_registry(); print(f'{len(r.list_actions())} actions registered')"` — prints 66+ actions
5. `ls src/ztlctl/actions/_register_core.py` — file does NOT exist (deleted)
</verification>

<success_criteria>
- _register_core.py monolith is deleted
- 9 feature-local registration modules exist in src/ztlctl/actions/
- All 66 ActionDefinitions still registered (zero regressions)
- Module-load-time registration works via `import ztlctl.actions`
- Full test suite, ruff, mypy pass
</success_criteria>

<output>
After completion, create `.planning/phases/17-registry-decomposition-and-plugin-runtime/17-01-SUMMARY.md`
</output>
