---
phase: 18-architecture-cleanup
plan: 02
type: execute
wave: 1
depends_on: []
files_modified:
  - src/ztlctl/infrastructure/embeddings.py
  - src/ztlctl/services/graph.py
  - tests/infrastructure/test_embeddings.py
  - tests/services/test_graph.py
  - .planning/REQUIREMENTS.md
autonomous: true
requirements: [DEBT-01, DEBT-08]

must_haves:
  truths:
    - "EmbeddingProvider constructor does not hardcode 384 as default — uses a module-level constant shared with config"
    - "bridges() uses k-approximation for betweenness centrality on graphs with more than 500 nodes"
    - "bridges() produces identical results to exact computation on small graphs"
  artifacts:
    - path: "src/ztlctl/infrastructure/embeddings.py"
      provides: "EmbeddingProvider with configurable dimension constant"
      contains: "DEFAULT_EMBEDDING_DIM"
    - path: "src/ztlctl/services/graph.py"
      provides: "bridges() with k-approximation threshold"
      contains: "k_param"
  key_links:
    - from: "src/ztlctl/config/models.py"
      to: "src/ztlctl/infrastructure/embeddings.py"
      via: "shared DEFAULT_EMBEDDING_DIM constant"
      pattern: "DEFAULT_EMBEDDING_DIM"
    - from: "src/ztlctl/services/graph.py"
      to: "networkx"
      via: "betweenness_centrality k parameter"
      pattern: "betweenness_centrality.*k="
---

<objective>
Make embedding dimensions use a single source of truth constant and add k-approximation to bridges() for large vault performance.

Purpose: DEBT-01 eliminates duplicated magic number 384. DEBT-08 makes bridges() responsive for vaults up to 5,000 notes by using the same k-approximation pattern already established in _node_features().
Output: Consistent embedding config, performant bridges computation
</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
</context>

<interfaces>
<!-- Existing k-approximation pattern from _node_features (graph.py line 594-596): -->
```python
node_count = g.number_of_nodes()
k_param = None if node_count <= 500 else min(500, node_count)
betweenness = nx.betweenness_centrality(g, k=k_param, seed=42)
```

<!-- Current EmbeddingProvider constructor (embeddings.py line 37): -->
```python
def __init__(self, model_name: str = "all-MiniLM-L6-v2", dim: int = 384) -> None:
```

<!-- Current config default (config/models.py line 105): -->
```python
embedding_dim: int = 384
```
</interfaces>

<tasks>

<task type="auto">
  <name>Task 1: Centralize embedding dimension constant and add bridges k-approximation</name>
  <files>src/ztlctl/infrastructure/embeddings.py, src/ztlctl/config/models.py, src/ztlctl/services/graph.py, tests/infrastructure/test_embeddings.py, tests/services/test_graph.py</files>
  <read_first>
    src/ztlctl/infrastructure/embeddings.py
    src/ztlctl/config/models.py
    src/ztlctl/services/graph.py
    src/ztlctl/services/vector.py
    tests/infrastructure/test_embeddings.py
    tests/services/test_graph.py
  </read_first>
  <action>
    **DEBT-01 — Centralize embedding dimension:**
    1. In `src/ztlctl/infrastructure/embeddings.py`, add a module-level constant before the class: `DEFAULT_EMBEDDING_DIM: int = 384`. Update the `__init__` signature to use it: `def __init__(self, model_name: str = "all-MiniLM-L6-v2", dim: int = DEFAULT_EMBEDDING_DIM) -> None:`.
    2. In `src/ztlctl/config/models.py`, import the constant: `from ztlctl.infrastructure.embeddings import DEFAULT_EMBEDDING_DIM`. Change line 105 to: `embedding_dim: int = DEFAULT_EMBEDDING_DIM`. If circular import is a concern (config imports infrastructure), instead define the constant in config/models.py and import it in embeddings.py. Choose whichever direction avoids circular imports. If both directions cause circularity, define the constant as a plain `DEFAULT_EMBEDDING_DIM = 384` in embeddings.py and use the literal `384` in config/models.py with a comment `# Must match DEFAULT_EMBEDDING_DIM in infrastructure/embeddings.py`. The key requirement: no unexplained magic numbers.
    3. In `tests/infrastructure/test_embeddings.py`, replace hardcoded `384` with import: `from ztlctl.infrastructure.embeddings import DEFAULT_EMBEDDING_DIM` and use `DEFAULT_EMBEDDING_DIM` in test assertions and provider construction.

    **DEBT-08 — bridges() k-approximation:**
    4. In `src/ztlctl/services/graph.py`, in the `bridges()` method (around line 385), replace:
       ```python
       bc = nx.betweenness_centrality(g)
       ```
       with the same pattern used in `_node_features()` (line 594-596):
       ```python
       node_count = g.number_of_nodes()
       k_param = None if node_count <= 500 else min(500, node_count)
       bc = nx.betweenness_centrality(g, k=k_param, seed=42)
       ```
       This uses exact computation for small graphs (<=500 nodes) and k-approximation for larger ones, matching the existing pattern.
    5. Add a test in `tests/services/test_graph.py` that verifies bridges() returns a valid ServiceResult. If there is already a bridges test, verify it still passes. The k-approximation is transparent to the API — same return shape.
  </action>
  <verify>
    <automated>cd /Users/shparki/Documents/Workspace/thatdev/ztlctl && uv run python -c "from ztlctl.infrastructure.embeddings import DEFAULT_EMBEDDING_DIM; assert DEFAULT_EMBEDDING_DIM == 384; print('constant OK')" && uv run pytest tests/infrastructure/test_embeddings.py tests/services/test_graph.py -x -q 2>&1 | tail -5</automated>
  </verify>
  <acceptance_criteria>
    - `grep "DEFAULT_EMBEDDING_DIM" src/ztlctl/infrastructure/embeddings.py` returns at least 2 matches (constant definition + constructor default)
    - `grep "k_param" src/ztlctl/services/graph.py` returns matches in BOTH bridges() and _node_features()
    - `grep "betweenness_centrality(g)" src/ztlctl/services/graph.py` returns NO matches (all calls now use k= parameter)
  </acceptance_criteria>
  <done>Embedding dimension uses named constant, bridges() uses k-approximation matching _node_features() pattern</done>
</task>

<task type="auto">
  <name>Task 2: Update REQUIREMENTS.md for DEBT-01 and DEBT-08</name>
  <files>.planning/REQUIREMENTS.md</files>
  <read_first>
    .planning/REQUIREMENTS.md
  </read_first>
  <action>
    Mark DEBT-01 and DEBT-08 as complete in REQUIREMENTS.md:
    1. Change `- [ ] **DEBT-01**:` to `- [x] **DEBT-01**:`
    2. Change `- [ ] **DEBT-08**:` to `- [x] **DEBT-08**:`
    3. In traceability table: update DEBT-01 and DEBT-08 status from `Pending` to `Complete`

    Note: ARCH-10, DEBT-05, DEBT-06 are handled by plan 18-01. If 18-01 has already updated REQUIREMENTS.md, only update DEBT-01 and DEBT-08 lines. If 18-01 has not run yet, only update DEBT-01 and DEBT-08 — do NOT touch the lines that 18-01 owns.
  </action>
  <verify>
    <automated>cd /Users/shparki/Documents/Workspace/thatdev/ztlctl && grep "\[x\] \*\*DEBT-01\*\*" .planning/REQUIREMENTS.md && grep "\[x\] \*\*DEBT-08\*\*" .planning/REQUIREMENTS.md && echo "REQUIREMENTS updated"</automated>
  </verify>
  <acceptance_criteria>
    - `grep "\[x\] \*\*DEBT-01\*\*" .planning/REQUIREMENTS.md` returns 1 match
    - `grep "\[x\] \*\*DEBT-08\*\*" .planning/REQUIREMENTS.md` returns 1 match
  </acceptance_criteria>
  <done>REQUIREMENTS.md marks DEBT-01 and DEBT-08 as complete</done>
</task>

</tasks>

<verification>
- `uv run ruff check src/ztlctl/infrastructure/embeddings.py src/ztlctl/services/graph.py`
- `uv run mypy src/ztlctl/infrastructure/embeddings.py src/ztlctl/services/graph.py src/ztlctl/config/models.py`
- `uv run pytest tests/infrastructure/test_embeddings.py tests/services/test_graph.py -x`
- No bare `384` literals remain in production source (only in DEFAULT_EMBEDDING_DIM definition)
</verification>

<success_criteria>
- DEFAULT_EMBEDDING_DIM constant defined and used in embeddings.py constructor
- bridges() uses k-approximation with same threshold (500) as _node_features()
- All tests pass, mypy clean, ruff clean
- REQUIREMENTS.md marks DEBT-01 and DEBT-08 complete
</success_criteria>

<output>
After completion, create `.planning/phases/18-architecture-cleanup/18-02-SUMMARY.md`
</output>
